diff options
104 files changed, 1836 insertions, 1047 deletions
diff --git a/.travis.yml b/.travis.yml index ca3a24ae8..ffb31fc08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,9 @@ env: - DOCKER_PACKAGES="build-essential libtool cmake autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache" matrix: # ARM v7 - - HOST=arm-linux-gnueabihf PACKAGES="gperf g++-arm-linux-gnueabihf" + - HOST=arm-linux-gnueabihf PACKAGES="python3 gperf g++-arm-linux-gnueabihf" # ARM v8 - - HOST=aarch64-linux-gnu PACKAGES="gperf g++-aarch64-linux-gnu" + - HOST=aarch64-linux-gnu PACKAGES="python3 gperf g++-aarch64-linux-gnu" # i686 Win - HOST=i686-w64-mingw32 PACKAGES="python3 nsis g++-mingw-w64-i686" # i686 Linux @@ -33,7 +33,7 @@ env: # Win64 - HOST=x86_64-w64-mingw32 PACKAGES="cmake python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 bc" RUN_TESTS=true # x86_64 Linux - - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" RUN_TESTS=true + - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev" RUN_TESTS=true # Cross-Mac - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 @@ -52,6 +52,7 @@ before_script: - if [ -n "$OSX_SDK" -a ! -f contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C contrib/depends/SDKs -xf contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [[ $HOST = *-mingw32 ]]; then $DOCKER_EXEC bash -c "update-alternatives --set $HOST-g++ \$(which $HOST-g++-posix)"; fi + - if [[ $HOST = *-mingw32 ]]; then $DOCKER_EXEC bash -c "update-alternatives --set $HOST-gcc \$(which $HOST-gcc-posix)"; fi - if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC bash -c "CONFIG_SHELL= make $MAKEJOBS -C contrib/depends HOST=$HOST $DEP_OPTS"; fi script: - git submodule init && git submodule update diff --git a/CMakeLists.txt b/CMakeLists.txt index d942d31cc..31c3dbd21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -592,9 +592,6 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAG}") set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") - if(NOT MINGW) - set(WARNINGS_AS_ERRORS_FLAG "-Werror") - endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") if(ARM) set(WARNINGS "${WARNINGS} -Wno-error=inline-asm") @@ -689,6 +686,13 @@ else() set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") endif() + if(BACKCOMPAT) + add_definitions(-DFDELT_TYPE=long\ int) + add_linker_flag_if_supported(-Wl,--wrap=__divmoddi4 LD_BACKCOMPAT_FLAGS) + add_linker_flag_if_supported(-Wl,--wrap=glob LD_BACKCOMPAT_FLAGS) + message(STATUS "Using Lib C back compat flags: ${LD_BACKCOMPAT_FLAGS}") + endif() + # some windows linker bits if (WIN32) add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS) @@ -701,7 +705,7 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${LD_BACKCOMPAT_FLAGS}") # With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that # is fixed in the code (Issue #847), force compiler to be conservative. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3de9cd5ce..883d366aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,8 @@ posted to #monero-dev on irc.freenode.net). Patches should be self contained. A good rule of thumb is to have one patch per separate issue, feature, or logical change. Also, no -other changes, such as random whitespace changes or reindentation. +other changes, such as random whitespace changes, reindentation, +or fixing typoes, spelling, or wording, unless user visible. Following the code style of the particular chunk of code you're modifying is encouraged. Proper squashing should be done (eg, if you're making a buggy patch, then a later patch to fix the bug, diff --git a/Dockerfile b/Dockerfile index 0728c6ce8..9fe7cfb8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,8 @@ RUN set -ex && \ automake \ bzip2 \ xsltproc \ - gperf + gperf \ + unzip WORKDIR /usr/local @@ -147,6 +148,20 @@ RUN set -ex \ && make \ && make install +# Protobuf +ARG PROTOBUF_VERSION=v3.6.1 +ARG PROTOBUF_HASH=48cb18e5c419ddd23d9badcfe4e9df7bde1979b2 +RUN set -ex \ + && git clone https://github.com/protocolbuffers/protobuf -b ${PROTOBUF_VERSION} \ + && cd protobuf \ + && test `git rev-parse HEAD` = ${PROTOBUF_HASH} || exit 1 \ + && git submodule update --init --recursive \ + && ./autogen.sh \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --enable-static --disable-shared \ + && make \ + && make install \ + && ldconfig + WORKDIR /src COPY . . @@ -521,6 +521,8 @@ The required packages are the names for each toolchain on apt. Depending on your Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. +The produced binaries still link libc dynamically. If the binary is compiled on a current distribution, it might not run on an older distribution with an older installation of libc. Passing `-DBACKCOMPAT=ON` to cmake will make sure that the binary will run on systems having at least libc version 2.17. + ## Installing Monero from a package **DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake index bcd3cfc2c..71214363d 100644 --- a/cmake/CheckTrezor.cmake +++ b/cmake/CheckTrezor.cmake @@ -2,13 +2,55 @@ 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) +# Helper function to fix cmake < 3.6.0 FindProtobuf variables +function(_trezor_protobuf_fix_vars) + if(${CMAKE_VERSION} VERSION_LESS "3.6.0") + foreach(UPPER + PROTOBUF_SRC_ROOT_FOLDER + PROTOBUF_IMPORT_DIRS + PROTOBUF_DEBUG + PROTOBUF_LIBRARY + PROTOBUF_PROTOC_LIBRARY + PROTOBUF_INCLUDE_DIR + PROTOBUF_PROTOC_EXECUTABLE + PROTOBUF_LIBRARY_DEBUG + PROTOBUF_PROTOC_LIBRARY_DEBUG + PROTOBUF_LITE_LIBRARY + PROTOBUF_LITE_LIBRARY_DEBUG + ) + if (DEFINED ${UPPER}) + string(REPLACE "PROTOBUF_" "Protobuf_" Camel ${UPPER}) + if (NOT DEFINED ${Camel}) + set(${Camel} ${${UPPER}} PARENT_SCOPE) + endif() + endif() + endforeach() + endif() +endfunction() + # Use Trezor master switch if (USE_DEVICE_TREZOR) # Protobuf is required to build protobuf messages for Trezor include(FindProtobuf OPTIONAL) find_package(Protobuf) - if(NOT Protobuf_FOUND) + _trezor_protobuf_fix_vars() + + # Protobuf handling the cache variables set in docker. + if(NOT Protobuf_FOUND AND NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) message(STATUS "Could not find Protobuf") + elseif(NOT Protobuf_LIBRARY OR NOT EXISTS "${Protobuf_LIBRARY}") + message(STATUS "Protobuf library not found: ${Protobuf_LIBRARY}") + unset(Protobuf_FOUND) + elseif(NOT Protobuf_PROTOC_EXECUTABLE OR NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") + message(STATUS "Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") + unset(Protobuf_FOUND) + elseif(NOT Protobuf_INCLUDE_DIR OR NOT EXISTS "${Protobuf_INCLUDE_DIR}") + message(STATUS "Protobuf include dir not found: ${Protobuf_INCLUDE_DIR}") + unset(Protobuf_FOUND) + else() + message(STATUS "Protobuf lib: ${Protobuf_LIBRARY}, inc: ${Protobuf_INCLUDE_DIR}, protoc: ${Protobuf_PROTOC_EXECUTABLE}") + set(Protobuf_INCLUDE_DIRS ${Protobuf_INCLUDE_DIR}) + set(Protobuf_FOUND 1) # override found if all rquired info was provided by variables endif() else() @@ -37,9 +79,32 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR) endif() endif() -# Try to build protobuf messages +# Protobuf compilation test if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) - set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIRS}") + execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} -I "${CMAKE_SOURCE_DIR}/cmake" -I "${Protobuf_INCLUDE_DIR}" "${CMAKE_SOURCE_DIR}/cmake/test-protobuf.proto" --cpp_out ${CMAKE_BINARY_DIR} RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) + if(RET) + message(STATUS "Protobuf test generation failed: ${OUT} ${ERR}") + endif() + + try_compile(Protobuf_COMPILE_TEST_PASSED + "${CMAKE_BINARY_DIR}" + SOURCES + "${CMAKE_BINARY_DIR}/test-protobuf.pb.cc" + "${CMAKE_SOURCE_DIR}/cmake/test-protobuf.cpp" + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${Protobuf_INCLUDE_DIR};${CMAKE_BINARY_DIR}" + "-DCMAKE_CXX_STANDARD=11" + LINK_LIBRARIES ${Protobuf_LIBRARY} + OUTPUT_VARIABLE OUTPUT + ) + if(NOT Protobuf_COMPILE_TEST_PASSED) + message(STATUS "Protobuf Compilation test failed: ${OUTPUT}.") + endif() +endif() + +# Try to build protobuf messages +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) if(RET) @@ -47,9 +112,10 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) "OUT: ${OUT}, ERR: ${ERR}." "Please read src/device_trezor/trezor/tools/README.md") else() - message(STATUS "Trezor protobuf messages regenerated ${OUT}") + message(STATUS "Trezor protobuf messages regenerated out: \"${OUT}.\"") set(DEVICE_TREZOR_READY 1) add_definitions(-DDEVICE_TREZOR_READY=1) + add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_definitions(-DTREZOR_DEBUG=1) @@ -75,5 +141,26 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) include_directories(${LibUSB_INCLUDE_DIRS}) endif() endif() + + set(TREZOR_LIBUSB_LIBRARIES "") + if(LibUSB_COMPILE_TEST_PASSED) + list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES}) + message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}") + endif() + + if (BUILD_GUI_DEPS) + set(TREZOR_DEP_LIBS "") + set(TREZOR_DEP_LINKER "") + + if (Protobuf_LIBRARY) + list(APPEND TREZOR_DEP_LIBS ${Protobuf_LIBRARY}) + string(APPEND TREZOR_DEP_LINKER " -lprotobuf") + endif() + + if (TREZOR_LIBUSB_LIBRARIES) + list(APPEND TREZOR_DEP_LIBS ${TREZOR_LIBUSB_LIBRARIES}) + string(APPEND TREZOR_DEP_LINKER " -lusb-1.0") + endif() + endif() endif() endif() diff --git a/cmake/test-protobuf.cpp b/cmake/test-protobuf.cpp new file mode 100644 index 000000000..532df7cbe --- /dev/null +++ b/cmake/test-protobuf.cpp @@ -0,0 +1,43 @@ +// 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 <string> +#include <iostream> +#include <google/protobuf/message.h> +#include <google/protobuf/unknown_field_set.h> +#include "test-protobuf.pb.h" + +int main(int argc, char *argv[]) { + google::protobuf::UnknownFieldSet ufs; + ufs.ClearAndFreeMemory(); + + Success sc; + sc.set_message("test"); + sc.SerializeToOstream(&std::cerr); + return 0; +} diff --git a/cmake/test-protobuf.proto b/cmake/test-protobuf.proto new file mode 100644 index 000000000..5300aea35 --- /dev/null +++ b/cmake/test-protobuf.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "google/protobuf/descriptor.proto"; + +message Success { + optional string message = 1; +} diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 990a05c08..3bebbe0d2 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -26,10 +26,5 @@ # 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. -# warnings are cleared only for GCC on Linux -if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY)) - add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow -endif() - add_subdirectory(epee) diff --git a/contrib/depends/README.md b/contrib/depends/README.md index 597398369..c9f8b0783 100644 --- a/contrib/depends/README.md +++ b/contrib/depends/README.md @@ -53,6 +53,16 @@ Download it from apple, or search for it on github. Create a new directoty calle directory and place the entire MacOSX10.11.sdk folder in it. The depends build will then pick it up automatically (without requiring SDK_PATH). + +#Mingw builds + +Building for 32/64bit mingw requires switching alternatives to a posix mode + +```bash +update-alternatives --set x86_64-w64-mingw32-g++ x86_64-w64-mingw32-g++-posix +update-alternatives --set x86_64-w64-mingw32-gcc x86_64-w64-mingw32-gcc-posix +``` + ### Other documentation - [description.md](description.md): General description of the depends system diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index 89db5a415..e60a1c677 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -20,7 +20,7 @@ $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale -$(package)_cxxflags=-std=c++11 -fvisibility=hidden +$(package)_cxxflags=-std=c++11 $(package)_cxxflags_linux=-fPIC endef diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index 6d60cce26..d865d2a17 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -11,13 +11,21 @@ endef define $(package)_set_vars $(package)_config_opts=--disable-shared $(package)_config_opts_linux=--with-pic --disable-udev + $(package)_config_opts_mingw32=--disable-udev + $(package)_config_opts_darwin=--disable-udev endef -define $(package)_config_cmds - cp -f $(BASEDIR)/config.guess config.guess &&\ - cp -f $(BASEDIR)/config.sub config.sub &&\ - $($(package)_autoconf) -endef +ifneq ($(host_os),darwin) + define $(package)_config_cmds + cp -f $(BASEDIR)/config.guess config.guess &&\ + cp -f $(BASEDIR)/config.sub config.sub &&\ + $($(package)_autoconf) + endef +else + define $(package)_config_cmds + $($(package)_autoconf) + endef +endif define $(package)_build_cmd $(MAKE) @@ -27,5 +35,5 @@ define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef -define $(package)_postprocess_cmds cp -f lib/libusb-1.0.a lib/libusb.a +define $(package)_postprocess_cmds cp -f lib/libusb-1.0.a lib/libusb.a endef diff --git a/contrib/depends/packages/native_protobuf.mk b/contrib/depends/packages/native_protobuf.mk new file mode 100644 index 000000000..83e602341 --- /dev/null +++ b/contrib/depends/packages/native_protobuf.mk @@ -0,0 +1,28 @@ +package=protobuf3 +$(package)_version=3.6.1 +$(package)_download_path=https://github.com/protocolbuffers/protobuf/releases/download/v$($(package)_version)/ +$(package)_file_name=protobuf-cpp-$($(package)_version).tar.gz +$(package)_sha256_hash=b3732e471a9bb7950f090fd0457ebd2536a9ba0891b7f3785919c654fe2a2529 +$(package)_cxxflags=-std=c++11 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --prefix=$(build_prefix) + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -C src libprotobuf.la all +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C src install install-libLTLIBRARIES install-nobase_includeHEADERS &&\ + $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA +endef + +define $(package)_postprocess_cmds + rm lib/libprotoc.a +endef diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 234adbdd4..b3a5992d0 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,10 +1,10 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi -native_packages := native_ccache +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi protobuf libusb +native_packages := native_ccache native_protobuf darwin_native_packages = native_biplist native_ds_store native_mac_alias darwin_packages = sodium-darwin -linux_packages = eudev libusb +linux_packages = eudev ifeq ($(host_os),linux) packages += unwind diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index f59b7b5ee..547b59108 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -21,6 +21,12 @@ SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a) SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a) +SET(Protobuf_FOUND 1) +SET(Protobuf_PROTOC_EXECUTABLE @prefix@/native/bin/protoc CACHE FILEPATH "Path to the native protoc") +SET(Protobuf_INCLUDE_DIR @prefix@/include CACHE PATH "Protobuf include dir") +SET(Protobuf_INCLUDE_DIRS @prefix@/include CACHE PATH "Protobuf include dir") +SET(Protobuf_LIBRARY @prefix@/lib/libprotobuf.a CACHE FILEPATH "Protobuf library") + SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index ef839f609..e22e8ee6e 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -230,35 +230,56 @@ namespace math_helper } } - template<int default_interval, bool start_immediate = true> - class once_a_time_seconds + template<uint64_t scale, int default_interval, bool start_immediate = true> + class once_a_time { + uint64_t get_time() const + { +#ifdef _WIN32 + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +#endif + } + public: - once_a_time_seconds():m_interval(default_interval) + once_a_time():m_interval(default_interval * scale) { m_last_worked_time = 0; if(!start_immediate) - time(&m_last_worked_time); + m_last_worked_time = get_time(); } template<class functor_t> bool do_call(functor_t functr) { - time_t current_time = 0; - time(¤t_time); + uint64_t current_time = get_time(); if(current_time - m_last_worked_time > m_interval) { bool res = functr(); - time(&m_last_worked_time); + m_last_worked_time = get_time(); return res; } return true; } private: - time_t m_last_worked_time; - time_t m_interval; + uint64_t m_last_worked_time; + uint64_t m_interval; }; + + template<int default_interval, bool start_immediate = true> + class once_a_time_seconds: public once_a_time<1000000, default_interval, start_immediate> {}; + template<int default_interval, bool start_immediate = true> + class once_a_time_milliseconds: public once_a_time<1000, default_interval, start_immediate> {}; } } diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 9100a8db3..1ff9da3a7 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -38,14 +38,21 @@ #define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes #define MAX_LOG_FILES 50 -#define MCFATAL(cat,x) CLOG(FATAL,cat) << x -#define MCERROR(cat,x) CLOG(ERROR,cat) << x -#define MCWARNING(cat,x) CLOG(WARNING,cat) << x -#define MCINFO(cat,x) CLOG(INFO,cat) << x -#define MCDEBUG(cat,x) CLOG(DEBUG,cat) << x -#define MCTRACE(cat,x) CLOG(TRACE,cat) << x -#define MCLOG(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::NormalLog, cat) << x -#define MCLOG_FILE(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::FileOnlyLog, cat) << x +#define MCLOG_TYPE(level, cat, type, x) do { \ + if (ELPP->vRegistry()->allowed(level, cat)) { \ + el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \ + } \ + } while (0) + +#define MCLOG(level, cat, x) MCLOG_TYPE(level, cat, el::base::DispatchAction::NormalLog, x) +#define MCLOG_FILE(level, cat, x) MCLOG_TYPE(level, cat, el::base::DispatchAction::FileOnlyLog, x) + +#define MCFATAL(cat,x) MCLOG(el::Level::Fatal,cat, x) +#define MCERROR(cat,x) MCLOG(el::Level::Error,cat, x) +#define MCWARNING(cat,x) MCLOG(el::Level::Warning,cat, x) +#define MCINFO(cat,x) MCLOG(el::Level::Info,cat, x) +#define MCDEBUG(cat,x) MCLOG(el::Level::Debug,cat, x) +#define MCTRACE(cat,x) MCLOG(el::Level::Trace,cat, x) #define MCLOG_COLOR(level,cat,color,x) MCLOG(level,cat,"\033[1;" color "m" << x << "\033[0m") #define MCLOG_RED(level,cat,x) MCLOG_COLOR(level,cat,"31",x) diff --git a/contrib/epee/include/net/buffer.h b/contrib/epee/include/net/buffer.h new file mode 100644 index 000000000..56c67f6bf --- /dev/null +++ b/contrib/epee/include/net/buffer.h @@ -0,0 +1,62 @@ +// 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 <vector> +#include "misc_log_ex.h" +#include "span.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.buffer" + +//#define NET_BUFFER_LOG(x) MDEBUG(x) +#define NET_BUFFER_LOG(x) ((void)0) + +namespace epee +{ +namespace net_utils +{ +class buffer +{ +public: + buffer(size_t reserve = 0): offset(0) { storage.reserve(reserve); } + + void append(const void *data, size_t sz); + void erase(size_t sz) { NET_BUFFER_LOG("erasing " << sz << "/" << size()); CHECK_AND_ASSERT_THROW_MES(offset + sz <= storage.size(), "erase: sz too large"); offset += sz; if (offset == storage.size()) { storage.resize(0); offset = 0; } } + epee::span<const uint8_t> span(size_t sz) const { CHECK_AND_ASSERT_THROW_MES(sz <= size(), "span is too large"); return epee::span<const uint8_t>(storage.data() + offset, sz); } + // carve must keep the data in scope till next call, other API calls (such as append, erase) can invalidate the carved buffer + epee::span<const uint8_t> carve(size_t sz) { CHECK_AND_ASSERT_THROW_MES(sz <= size(), "span is too large"); offset += sz; return epee::span<const uint8_t>(storage.data() + offset - sz, sz); } + size_t size() const { return storage.size() - offset; } + +private: + std::vector<uint8_t> storage; + size_t offset; +}; +} +} diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 997c801d1..64d035df9 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -92,7 +92,7 @@ handled = true; \ uint64_t ticks = misc_utils::get_tick_count(); \ boost::value_initialized<command_type::request> req; \ - bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), query_info.m_body); \ + bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ uint64_t ticks1 = misc_utils::get_tick_count(); \ boost::value_initialized<command_type::response> resp;\ diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h index 7d060f5ef..a88a1eb49 100644 --- a/contrib/epee/include/net/levin_base.h +++ b/contrib/epee/include/net/levin_base.h @@ -80,8 +80,8 @@ namespace levin template<class t_connection_context = net_utils::connection_context_base> struct levin_commands_handler { - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0; - virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0; + virtual int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, t_connection_context& context)=0; + virtual int notify(int command, const epee::span<const uint8_t> in_buff, t_connection_context& context)=0; virtual void callback(t_connection_context& context){}; virtual void on_connection_new(t_connection_context& context){}; diff --git a/contrib/epee/include/net/levin_client.h b/contrib/epee/include/net/levin_client.h index 335f6ba02..76d528234 100644 --- a/contrib/epee/include/net/levin_client.h +++ b/contrib/epee/include/net/levin_client.h @@ -57,7 +57,7 @@ namespace levin bool is_connected(); bool disconnect(); - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out); + virtual int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out); virtual int notify(int command, const std::string& in_buff); protected: @@ -72,7 +72,7 @@ namespace levin { public: - int invoke(int command, const std::string& in_buff, std::string& buff_out); + int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out); int notify(int command, const std::string& in_buff); }; diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl index a580e81fd..2f048b027 100644 --- a/contrib/epee/include/net/levin_client.inl +++ b/contrib/epee/include/net/levin_client.inl @@ -74,7 +74,7 @@ levin_client_impl::~levin_client_impl() } //------------------------------------------------------------------------------ inline -int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out) +int levin_client_impl::invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out) { if(!is_connected()) return -1; @@ -133,7 +133,7 @@ int levin_client_impl::notify(int command, const std::string& in_buff) //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ inline - int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out) + int levin_client_impl2::invoke(int command, epee::span<const uint8_t>string& in_buff, std::string& buff_out) { if(!is_connected()) return -1; diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h index b3a46465b..ed92f4b95 100644 --- a/contrib/epee/include/net/levin_client_async.h +++ b/contrib/epee/include/net/levin_client_async.h @@ -431,7 +431,7 @@ namespace levin } CRITICAL_REGION_END(); - LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + LOG_PRINT_L4("LEVIN_PACKET_RECEIVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); if(is_request) { diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 6b1528caf..a1ea3e680 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -34,6 +34,7 @@ #include <atomic> #include "levin_base.h" +#include "buffer.h" #include "misc_language.h" #include "syncobj.h" #include "misc_os_dependent.h" @@ -85,11 +86,11 @@ public: uint64_t m_max_packet_size; uint64_t m_invoke_timeout; - int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id); + int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, boost::uuids::uuid connection_id); template<class callback_t> - int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); + int invoke_async(int command, const epee::span<const uint8_t> in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); - int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id); + int notify(int command, const epee::span<const uint8_t> in_buff, boost::uuids::uuid connection_id); bool close(boost::uuids::uuid connection_id); bool update_connection_context(const t_connection_context& contxt); bool request_callback(boost::uuids::uuid connection_id); @@ -143,7 +144,7 @@ public: config_type& m_config; t_connection_context& m_connection_context; - std::string m_cache_in_buffer; + net_utils::buffer m_cache_in_buffer; stream_state m_state; int32_t m_oponent_protocol_ver; @@ -151,7 +152,7 @@ public: struct invoke_response_handler_base { - virtual bool handle(int res, const std::string& buff, connection_context& context)=0; + virtual bool handle(int res, const epee::span<const uint8_t> buff, connection_context& context)=0; virtual bool is_timer_started() const=0; virtual void cancel()=0; virtual bool cancel_timer()=0; @@ -173,7 +174,7 @@ public: if(ec == boost::asio::error::operation_aborted) return; MINFO(con.get_context_ref() << "Timeout on invoke operation happened, command: " << command << " timeout: " << timeout); - std::string fake; + epee::span<const uint8_t> fake; cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); con.close(); con.finish_outer_call(); @@ -191,7 +192,7 @@ public: bool m_timer_cancelled; uint64_t m_timeout; int m_command; - virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context) + virtual bool handle(int res, const epee::span<const uint8_t> buff, typename async_protocol_handler::connection_context& context) { if(!cancel_timer()) return false; @@ -207,7 +208,7 @@ public: { if(cancel_timer()) { - std::string fake; + epee::span<const uint8_t> fake; m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref()); m_con.finish_outer_call(); } @@ -237,7 +238,7 @@ public: if(ec == boost::asio::error::operation_aborted) return; MINFO(con.get_context_ref() << "Timeout on invoke operation happened, command: " << command << " timeout: " << timeout); - std::string fake; + epee::span<const uint8_t> fake; cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); con.close(); con.finish_outer_call(); @@ -265,6 +266,7 @@ public: m_pservice_endpoint(psnd_hndlr), m_config(config), m_connection_context(conn_context), + m_cache_in_buffer(256 * 1024), m_state(stream_state_head) { m_close_called = 0; @@ -405,18 +407,11 @@ public: break; } { - std::string buff_to_invoke; - if(m_cache_in_buffer.size() == m_current_head.m_cb) - buff_to_invoke.swap(m_cache_in_buffer); - else - { - buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); - m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); - } + epee::span<const uint8_t> buff_to_invoke = m_cache_in_buffer.carve((std::string::size_type)m_current_head.m_cb); bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); - MDEBUG(m_connection_context << "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb + MDEBUG(m_connection_context << "LEVIN_PACKET_RECEIVED. [len=" << m_current_head.m_cb << ", flags" << m_current_head.m_flags << ", r?=" << m_current_head.m_have_to_return_data <<", cmd = " << m_current_head.m_command @@ -449,8 +444,8 @@ public: }else { CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); - buff_to_invoke.swap(m_local_inv_buff); - buff_to_invoke.clear(); + m_local_inv_buff = std::string((const char*)buff_to_invoke.data(), buff_to_invoke.size()); + buff_to_invoke = epee::span<const uint8_t>((const uint8_t*)NULL, 0); m_invoke_result_code = m_current_head.m_return_code; CRITICAL_REGION_END(); boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1); @@ -503,7 +498,7 @@ public: { if(m_cache_in_buffer.size() < sizeof(bucket_head2)) { - if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.data()) != SWAP64LE(LEVIN_SIGNATURE)) + if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.span(8).data()) != SWAP64LE(LEVIN_SIGNATURE)) { MWARNING(m_connection_context << "Signature mismatch, connection will be closed"); return false; @@ -513,9 +508,9 @@ public: } #if BYTE_ORDER == LITTLE_ENDIAN - bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.data(); + bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data(); #else - bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.data(); + bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data(); phead.m_signature = SWAP64LE(phead.m_signature); phead.m_cb = SWAP64LE(phead.m_cb); phead.m_command = SWAP32LE(phead.m_command); @@ -530,7 +525,7 @@ public: } m_current_head = phead; - m_cache_in_buffer.erase(0, sizeof(bucket_head2)); + m_cache_in_buffer.erase(sizeof(bucket_head2)); m_state = stream_state_body; m_oponent_protocol_ver = m_current_head.m_protocol_version; if(m_current_head.m_cb > m_config.m_max_packet_size) @@ -562,7 +557,7 @@ public: } template<class callback_t> - bool async_invoke(int command, const std::string& in_buff, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + bool async_invoke(int command, const epee::span<const uint8_t> in_buff, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) { misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( boost::bind(&async_protocol_handler::finish_outer_call, this)); @@ -606,7 +601,7 @@ public: break; } - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + if(!m_pservice_endpoint->do_send(in_buff.data(), in_buff.size())) { LOG_ERROR_CC(m_connection_context, "Failed to do_send"); err_code = LEVIN_ERROR_CONNECTION; @@ -623,7 +618,7 @@ public: if (LEVIN_OK != err_code) { - std::string stub_buff; + epee::span<const uint8_t> stub_buff{(const uint8_t*)"", 0}; // Never call callback inside critical section, that can cause deadlock cb(err_code, stub_buff, m_connection_context); return false; @@ -632,7 +627,7 @@ public: return true; } - int invoke(int command, const std::string& in_buff, std::string& buff_out) + int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out) { misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( boost::bind(&async_protocol_handler::finish_outer_call, this)); @@ -662,7 +657,7 @@ public: return LEVIN_ERROR_CONNECTION; } - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + if(!m_pservice_endpoint->do_send(in_buff.data(), in_buff.size())) { LOG_ERROR_CC(m_connection_context, "Failed to do_send"); return LEVIN_ERROR_CONNECTION; @@ -706,7 +701,7 @@ public: return m_invoke_result_code; } - int notify(int command, const std::string& in_buff) + int notify(int command, const epee::span<const uint8_t> in_buff) { misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( boost::bind(&async_protocol_handler::finish_outer_call, this)); @@ -734,7 +729,7 @@ public: return -1; } - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + if(!m_pservice_endpoint->do_send(in_buff.data(), in_buff.size())) { LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); return -1; @@ -839,7 +834,7 @@ int async_protocol_handler_config<t_connection_context>::find_and_lock_connectio } //------------------------------------------------------------------------------------------ template<class t_connection_context> -int async_protocol_handler_config<t_connection_context>::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id) +int async_protocol_handler_config<t_connection_context>::invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, boost::uuids::uuid connection_id) { async_protocol_handler<t_connection_context>* aph; int r = find_and_lock_connection(connection_id, aph); @@ -847,7 +842,7 @@ int async_protocol_handler_config<t_connection_context>::invoke(int command, con } //------------------------------------------------------------------------------------------ template<class t_connection_context> template<class callback_t> -int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout) +int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const epee::span<const uint8_t> in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout) { async_protocol_handler<t_connection_context>* aph; int r = find_and_lock_connection(connection_id, aph); @@ -896,7 +891,7 @@ void async_protocol_handler_config<t_connection_context>::set_handler(levin_comm } //------------------------------------------------------------------------------------------ template<class t_connection_context> -int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) +int async_protocol_handler_config<t_connection_context>::notify(int command, const epee::span<const uint8_t> in_buff, boost::uuids::uuid connection_id) { async_protocol_handler<t_connection_context>* aph; int r = find_and_lock_connection(connection_id, aph); diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index b1296a0b7..cfb5b1a17 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -163,4 +163,12 @@ namespace epee static_assert(!has_padding<T>(), "source type may have padding"); return {reinterpret_cast<std::uint8_t*>(std::addressof(src)), sizeof(T)}; } + + //! make a span from a std::string + template<typename T> + span<const T> strspan(const std::string &s) noexcept + { + static_assert(std::is_same<T, char>() || std::is_same<T, unsigned char>() || std::is_same<T, int8_t>() || std::is_same<T, uint8_t>(), "Unexpected type"); + return {reinterpret_cast<const T*>(s.data()), s.size()}; + } } diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index d93084ab0..18b7f10c1 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -97,7 +97,7 @@ namespace epee return false; } - return serialization::load_t_from_binary(result_struct, pri->m_body); + return serialization::load_t_from_binary(result_struct, epee::strspan<uint8_t>(pri->m_body)); } template<class t_request, class t_response, class t_transport> diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h index d77e7a1f8..06eb9bdaf 100644 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -28,6 +28,7 @@ #include "portable_storage_template_helper.h" #include <boost/utility/value_init.hpp> +#include "span.h" #include "net/levin_base.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -114,7 +115,7 @@ namespace epee const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation std::string buff_to_send; stg.store_to_binary(buff_to_send); - int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool + int res = transport.invoke_async(command, epee::strspan<uint8_t>(buff_to_send), conn_id, [cb, command](int code, const epee::span<const uint8_t> buff, typename t_transport::connection_context& context)->bool { t_result result_struct = AUTO_VAL_INIT(result_struct); if( code <=0 ) @@ -156,7 +157,7 @@ namespace epee std::string buff_to_send; stg.store_to_binary(buff_to_send); - int res = transport.notify(command, buff_to_send, conn_id); + int res = transport.notify(command, epee::strspan<uint8_t>(buff_to_send), conn_id); if(res <=0 ) { MERROR("Failed to notify command " << command << " return code " << res); @@ -167,7 +168,7 @@ namespace epee //---------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------- template<class t_owner, class t_in_type, class t_out_type, class t_context, class callback_t> - int buff_to_t_adapter(int command, const std::string& in_buff, std::string& buff_out, callback_t cb, t_context& context ) + int buff_to_t_adapter(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, callback_t cb, t_context& context ) { serialization::portable_storage strg; if(!strg.load_from_binary(in_buff)) @@ -197,7 +198,7 @@ namespace epee } template<class t_owner, class t_in_type, class t_context, class callback_t> - int buff_to_t_adapter(t_owner* powner, int command, const std::string& in_buff, callback_t cb, t_context& context) + int buff_to_t_adapter(t_owner* powner, int command, const epee::span<const uint8_t> in_buff, callback_t cb, t_context& context) { serialization::portable_storage strg; if(!strg.load_from_binary(in_buff)) @@ -215,14 +216,14 @@ namespace epee } #define CHAIN_LEVIN_INVOKE_MAP2(context_type) \ - int invoke(int command, const std::string& in_buff, std::string& buff_out, context_type& context) \ + int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, context_type& context) \ { \ bool handled = false; \ return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ } #define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \ - int notify(int command, const std::string& in_buff, context_type& context) \ + int notify(int command, const epee::span<const uint8_t> in_buff, context_type& context) \ { \ bool handled = false; std::string fake_str;\ return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ @@ -230,27 +231,27 @@ namespace epee #define CHAIN_LEVIN_INVOKE_MAP() \ - int invoke(int command, const std::string& in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ + int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ { \ bool handled = false; \ return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ } #define CHAIN_LEVIN_NOTIFY_MAP() \ - int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + int notify(int command, const epee::span<const uint8_t> in_buff, epee::net_utils::connection_context_base& context) \ { \ bool handled = false; std::string fake_str;\ return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ } #define CHAIN_LEVIN_NOTIFY_STUB() \ - int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + int notify(int command, const epee::span<const uint8_t> in_buff, epee::net_utils::connection_context_base& context) \ { \ return -1; \ } #define BEGIN_INVOKE_MAP2(owner_type) \ - template <class t_context> int handle_invoke_map(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, t_context& context, bool& handled) \ + template <class t_context> int handle_invoke_map(bool is_notify, int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, t_context& context, bool& handled) \ { \ typedef owner_type internal_owner_type_name; diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 31495ae13..d73fbde3a 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -91,11 +91,15 @@ namespace misc_utils */ inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) { - val.clear(); - val.reserve(std::distance(star_end_string, buf_end)); bool escape_mode = false; std::string::const_iterator it = star_end_string; ++it; + std::string::const_iterator fi = it; + while (fi != buf_end && *fi != '\\' && *fi != '\"') + ++fi; + val.assign(it, fi); + val.reserve(std::distance(star_end_string, buf_end)); + it = fi; for(;it != buf_end;it++) { if(escape_mode/*prev_ch == '\\'*/) diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index 0f0c6210f..d0e40d606 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -35,6 +35,7 @@ #include "portable_storage_to_json.h" #include "portable_storage_from_json.h" #include "portable_storage_val_converters.h" +#include "span.h" #include "int-util.h" namespace epee @@ -81,7 +82,8 @@ namespace epee //------------------------------------------------------------------------------- bool store_to_binary(binarybuffer& target); - bool load_from_binary(const binarybuffer& target); + bool load_from_binary(const epee::span<const uint8_t> target); + bool load_from_binary(const std::string& target) { return load_from_binary(epee::strspan<uint8_t>(target)); } template<class trace_policy> bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true); @@ -146,7 +148,7 @@ namespace epee CATCH_ENTRY("portable_storage::store_to_binary", false) } inline - bool portable_storage::load_from_binary(const binarybuffer& source) + bool portable_storage::load_from_binary(const epee::span<const uint8_t> source) { m_root.m_entries.clear(); if(source.size() < sizeof(storage_block_header)) diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h index 93132548b..da84fd8ea 100644 --- a/contrib/epee/include/storages/portable_storage_base.h +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -31,7 +31,8 @@ #include <boost/variant.hpp> #include <boost/any.hpp> #include <string> -#include <list> +#include <vector> +#include <deque> #define PORTABLE_STORAGE_SIGNATUREA 0x01011101 #define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare @@ -71,6 +72,9 @@ namespace epee { struct section; + template<typename T> struct entry_container { typedef std::vector<T> type; static void reserve(type &t, size_t n) { t.reserve(n); } }; + template<> struct entry_container<bool> { typedef std::deque<bool> type; static void reserve(type &t, size_t n) {} }; + /************************************************************************/ /* */ /************************************************************************/ @@ -119,8 +123,13 @@ namespace epee return m_array.back(); } - std::list<t_entry_type> m_array; - mutable typename std::list<t_entry_type>::const_iterator m_it; + void reserve(size_t n) + { + entry_container<t_entry_type>::reserve(m_array, n); + } + + typename entry_container<t_entry_type>::type m_array; + mutable typename entry_container<t_entry_type>::type::const_iterator m_it; }; diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index f9cc22d27..2884f8c5e 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -136,6 +136,7 @@ namespace epee //for pod types array_entry_t<type_name> sa; size_t size = read_varint(); + sa.reserve(size); //TODO: add some optimization here later while(size--) sa.m_array.push_back(read<type_name>()); diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h index bbd8413fc..13c870d44 100644 --- a/contrib/epee/include/storages/portable_storage_template_helper.h +++ b/contrib/epee/include/storages/portable_storage_template_helper.h @@ -84,7 +84,7 @@ namespace epee } //----------------------------------------------------------------------------------------------------------- template<class t_struct> - bool load_t_from_binary(t_struct& out, const std::string& binary_buff) + bool load_t_from_binary(t_struct& out, const epee::span<const uint8_t> binary_buff) { portable_storage ps; bool rs = ps.load_from_binary(binary_buff); @@ -95,6 +95,12 @@ namespace epee } //----------------------------------------------------------------------------------------------------------- template<class t_struct> + bool load_t_from_binary(t_struct& out, const std::string& binary_buff) + { + return load_t_from_binary(out, epee::strspan<uint8_t>(binary_buff)); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> bool load_t_from_binary_file(t_struct& out, const std::string& binary_file) { std::string f_buff; diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 6a063cc36..f7539fb6c 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -40,8 +40,6 @@ #include <cstdlib> #include <string> #include <type_traits> -#include <boost/uuid/uuid.hpp> -#include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string/predicate.hpp> #include "hex.h" @@ -83,50 +81,18 @@ namespace epee { namespace string_tools { - //---------------------------------------------------------------------------- - inline std::string get_str_from_guid_a(const boost::uuids::uuid& rid) - { - return boost::lexical_cast<std::string>(rid); - } - //---------------------------------------------------------------------------- - inline bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) - { - std::string local_str_id = str_id; - if(local_str_id.size() < 36) - return false; - - if('{' == *local_str_id.begin()) - local_str_id.erase(0, 1); - - if('}' == *(--local_str_id.end())) - local_str_id.erase(--local_str_id.end()); - - try - { - inetifer = boost::lexical_cast<boost::uuids::uuid>(local_str_id); - return true; - } - catch(...) - { - return false; - } - } //---------------------------------------------------------------------------- inline std::string buff_to_hex_nodelimer(const std::string& src) { return to_hex::string(to_byte_span(to_span(src))); } //---------------------------------------------------------------------------- - template<class CharT> - bool parse_hexstr_to_binbuff(const std::basic_string<CharT>& s, std::basic_string<CharT>& res) + inline bool parse_hexstr_to_binbuff(const epee::span<const char> s, epee::span<char>& res) { - res.clear(); - if (s.size() & 1) - return false; - try - { - res.resize(s.size() / 2); - unsigned char *dst = (unsigned char *)res.data(); + if (s.size() != res.size() * 2) + return false; + + unsigned char *dst = (unsigned char *)&res[0]; const unsigned char *src = (const unsigned char *)s.data(); for(size_t i = 0; i < s.size(); i += 2) { @@ -140,28 +106,15 @@ namespace string_tools } return true; - } - catch(...) - { - return false; - } } //---------------------------------------------------------------------------- - template<class t_pod_type> - bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod) + inline bool parse_hexstr_to_binbuff(const std::string& s, std::string& res) { - static_assert(std::is_pod<t_pod_type>::value, "expected pod type"); - std::string buf; - bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); - if (!res || buf.size() != sizeof(t_pod_type)) - { + if (s.size() & 1) return false; - } - else - { - buf.copy(reinterpret_cast<char *>(&t_pod), sizeof(t_pod_type)); - return true; - } + res.resize(s.size() / 2); + epee::span<char> rspan((char*)&res[0], res.size()); + return parse_hexstr_to_binbuff(epee::to_span(s), rspan); } //---------------------------------------------------------------------------- PUSH_WARNINGS @@ -360,17 +313,10 @@ POP_WARNINGS bool hex_to_pod(const std::string& hex_str, t_pod_type& s) { static_assert(std::is_pod<t_pod_type>::value, "expected pod type"); - std::string hex_str_tr = trim(hex_str); if(sizeof(s)*2 != hex_str.size()) return false; - std::string bin_buff; - if(!parse_hexstr_to_binbuff(hex_str_tr, bin_buff)) - return false; - if(bin_buff.size()!=sizeof(s)) - return false; - - s = *(t_pod_type*)bin_buff.data(); - return true; + epee::span<char> rspan((char*)&s, sizeof(s)); + return parse_hexstr_to_binbuff(epee::to_span(hex_str), rspan); } //---------------------------------------------------------------------------- template<class t_pod_type> diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index bc437deb9..cea50c9dd 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -27,7 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c - connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp) + connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp) if (USE_READLINE AND GNU_READLINE_FOUND) add_library(epee_readline STATIC readline_buffer.cpp) endif() diff --git a/contrib/epee/src/buffer.cpp b/contrib/epee/src/buffer.cpp new file mode 100644 index 000000000..d637b905e --- /dev/null +++ b/contrib/epee/src/buffer.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string.h> +#include "net/buffer.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.buffer" + +namespace epee +{ +namespace net_utils +{ + +void buffer::append(const void *data, size_t sz) +{ + const size_t capacity = storage.capacity(); + const size_t avail = capacity - storage.size(); + + CHECK_AND_ASSERT_THROW_MES(storage.size() < std::numeric_limits<size_t>::max() - sz, "Too much data to append"); + + // decide when to move + if (sz > avail) + { + // we have to reallocate or move + const bool move = size() + sz <= capacity; + if (move) + { + const size_t bytes = storage.size() - offset; + NET_BUFFER_LOG("appending " << sz << " from " << size() << " by moving " << bytes << " from offset " << offset << " first (forced)"); + memmove(storage.data(), storage.data() + offset, bytes); + storage.resize(bytes); + offset = 0; + } + else + { + NET_BUFFER_LOG("appending " << sz << " from " << size() << " by reallocating"); + std::vector<uint8_t> new_storage; + size_t reserve = (((size() + sz) * 3 / 2) + 4095) & ~4095; + new_storage.reserve(reserve); + new_storage.resize(size()); + memcpy(new_storage.data(), storage.data() + offset, storage.size() - offset); + offset = 0; + std::swap(storage, new_storage); + } + } + else + { + // we have space already + if (size() <= 4096 && offset > 4096 * 16 && offset >= capacity / 2) + { + // we have little to move, and we're far enough into the buffer that it's probably a win to move anyway + const size_t pos = storage.size() - offset; + NET_BUFFER_LOG("appending " << sz << " from " << size() << " by moving " << pos << " from offset " << offset << " first (unforced)"); + memmove(storage.data(), storage.data() + offset, storage.size() - offset); + storage.resize(pos); + offset = 0; + } + else + { + NET_BUFFER_LOG("appending " << sz << " from " << size() << " by writing to existing capacity"); + } + } + + // add the new data + storage.insert(storage.end(), (const uint8_t*)data, (const uint8_t*)data + sz); + + NET_BUFFER_LOG("storage now " << offset << "/" << storage.size() << "/" << storage.capacity()); +} + +} +} diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index 078a84ce3..446fa3315 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -40,6 +40,9 @@ #include <atomic> +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "mlocker" + // did an mlock operation previously fail? we only // want to log an error once and be done with it static std::atomic<bool> previously_failed{ false }; diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 00d848388..9b6b832d1 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -103,7 +103,7 @@ static const char *get_default_categories(int level) categories = "*:WARNING,net:FATAL,net.http:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: - categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO,perf.*:DEBUG"; break; case 2: categories = "*:DEBUG"; diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp index 354c3d2c3..263b344b4 100644 --- a/contrib/epee/src/net_utils_base.cpp +++ b/contrib/epee/src/net_utils_base.cpp @@ -1,7 +1,9 @@ #include "net/net_utils_base.h" -#include "string_tools.h" +#include <boost/uuid/uuid_io.hpp> + +#include "string_tools.h" #include "net/local_ip.h" namespace epee { namespace net_utils @@ -73,7 +75,7 @@ namespace epee { namespace net_utils std::string print_connection_context(const connection_context_base& ctx) { std::stringstream ss; - ss << ctx.m_remote_address.str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); + ss << ctx.m_remote_address.str() << " " << ctx.m_connection_id << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } diff --git a/contrib/epee/tests/src/CMakeLists.txt b/contrib/epee/tests/src/CMakeLists.txt index c7d31735b..4807fa7ea 100644 --- a/contrib/epee/tests/src/CMakeLists.txt +++ b/contrib/epee/tests/src/CMakeLists.txt @@ -14,8 +14,8 @@ IF (MSVC) include_directories(SYSTEM platform/msvc) ELSE() # set stuff for other systems - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall -Werror") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror -Wno-reorder") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder") ENDIF() diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 473a7720d..3bb25c314 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -152,7 +152,7 @@ script: | for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON make DISTNAME=monero-${i} mv bin ${DISTNAME} diff --git a/contrib/gitian/symbol-check.py b/contrib/gitian/symbol-check.py deleted file mode 100755 index 6808e77da..000000000 --- a/contrib/gitian/symbol-check.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014 Wladimir J. van der Laan -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -A script to check that the (Linux) executables produced by gitian only contain -allowed gcc, glibc and libstdc++ version symbols. This makes sure they are -still compatible with the minimum supported Linux distribution versions. - -Example usage: - - find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py -''' -import subprocess -import re -import sys -import os - -# Debian 6.0.9 (Squeeze) has: -# -# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) -# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) -# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) -# -# Ubuntu 10.04.4 (Lucid Lynx) has: -# -# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) -# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) -# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) -# -# Taking the minimum of these as our target. -# -# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: -# GCC 4.4.0: GCC_4.4.0 -# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 -# (glibc) GLIBC_2_11 -# -MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11) -} -# See here for a description of _IO_stdin_used: -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 - -# Ignore symbols that are exported as part of every executable -IGNORE_EXPORTS = { -'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' -} -READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') -CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') -# Allowed NEEDED libraries -ALLOWED_LIBRARIES = { -# bitcoind and bitcoin-qt -'libgcc_s.so.1', # GCC base support -'libc.so.6', # C library -'libpthread.so.0', # threading -'libanl.so.1', # DNS resolve -'libm.so.6', # math library -'librt.so.1', # real-time (clock) -'ld-linux-x86-64.so.2', # 64-bit dynamic linker -'ld-linux.so.2', # 32-bit dynamic linker -# bitcoin-qt only -'libX11-xcb.so.1', # part of X11 -'libX11.so.6', # part of X11 -'libxcb.so.1', # part of X11 -'libfontconfig.so.1', # font support -'libfreetype.so.6', # font parsing -'libdl.so.2' # programming interface to dynamic linker -} - -class CPPFilt(object): - ''' - Demangle C++ symbol names. - - Use a pipe to the 'c++filt' command. - ''' - def __init__(self): - self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) - - def __call__(self, mangled): - self.proc.stdin.write(mangled + '\n') - self.proc.stdin.flush() - return self.proc.stdout.readline().rstrip() - - def close(self): - self.proc.stdin.close() - self.proc.stdout.close() - self.proc.wait() - -def read_symbols(executable, imports=True): - ''' - Parse an ELF executable and return a list of (symbol,version) tuples - for dynamic, imported symbols. - ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) - syms = [] - for line in stdout.splitlines(): - line = line.split() - if len(line)>7 and re.match('[0-9]+:$', line[0]): - (sym, _, version) = line[7].partition('@') - is_import = line[6] == 'UND' - if version.startswith('@'): - version = version[1:] - if is_import == imports: - syms.append((sym, version)) - return syms - -def check_version(max_versions, version): - if '_' in version: - (lib, _, ver) = version.rpartition('_') - else: - lib = version - ver = '0' - ver = tuple([int(x) for x in ver.split('.')]) - if not lib in max_versions: - return False - return ver <= max_versions[lib] - -def read_libraries(filename): - p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - libraries = [] - for line in stdout.splitlines(): - tokens = line.split() - if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) - if match: - libraries.append(match.group(1)) - else: - raise ValueError('Unparseable (NEEDED) specification') - return libraries - -if __name__ == '__main__': - cppfilt = CPPFilt() - retval = 0 - for filename in sys.argv[1:]: - # Check imported symbols - for sym,version in read_symbols(filename, True): - if version and not check_version(MAX_VERSIONS, version): - print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) - retval = 1 - # Check exported symbols - for sym,version in read_symbols(filename, False): - if sym in IGNORE_EXPORTS: - continue - print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) - retval = 1 - # Check dependency libraries - for library_name in read_libraries(filename): - if library_name not in ALLOWED_LIBRARIES: - print('%s: NEEDED library %s is not allowed' % (filename, library_name)) - retval = 1 - - sys.exit(retval) - - diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index aec4a150d..f5f7481f8 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2130,24 +2130,23 @@ static int priority(Level level) { return 7; } -bool VRegistry::allowed(Level level, const char* category) { +bool VRegistry::allowed(Level level, const std::string &category) { base::threading::ScopedLock scopedLock(lock()); - const std::string scategory = category; - const std::map<std::string, int>::const_iterator it = m_cached_allowed_categories.find(scategory); + const std::map<std::string, int>::const_iterator it = m_cached_allowed_categories.find(category); if (it != m_cached_allowed_categories.end()) return priority(level) <= it->second; - if (m_categories.empty() || category == nullptr) { + if (m_categories.empty()) { return false; } else { std::vector<std::pair<std::string, Level>>::const_reverse_iterator it = m_categories.rbegin(); for (; it != m_categories.rend(); ++it) { - if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { + if (base::utils::Str::wildCardMatch(category.c_str(), it->first.c_str())) { const int p = priority(it->second); - m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), p)); + m_cached_allowed_categories.insert(std::make_pair(category, p)); return priority(level) <= p; } } - m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), -1)); + m_cached_allowed_categories.insert(std::make_pair(category, -1)); return false; } } @@ -2694,6 +2693,12 @@ Writer& Writer::construct(int count, const char* loggerIds, ...) { return *this; } +Writer& Writer::construct(const char *loggerId) { + initializeLogger(ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically))); + m_messageBuilder.initialize(m_logger); + return *this; +} + void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { if (lookup) { m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); @@ -2715,13 +2720,26 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee } if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : - ELPP->vRegistry()->allowed(m_level, loggerId.c_str()); + ELPP->vRegistry()->allowed(m_level, loggerId); } else { m_proceed = m_logger->enabled(m_level); } } } +void Writer::initializeLogger(Logger *logger, bool needLock) { + m_logger = logger; + if (m_logger == nullptr) { + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + m_proceed = true; + } +} + void Writer::processDispatch() { #if ELPP_LOGGING_ENABLED if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index acf2a7674..d9a2dc3d1 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -2463,7 +2463,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { void setModules(const char* modules); - bool allowed(Level level, const char* category); + bool allowed(Level level, const std::string &category); bool allowed(base::type::VerboseLevel vlevel, const char* file); @@ -3290,6 +3290,7 @@ class Writer : base::NoCopy { Writer& construct(Logger* logger, bool needLock = true); Writer& construct(int count, const char* loggerIds, ...); + Writer& construct(const char *loggerId); protected: LogMessage* m_msg; Level m_level; @@ -3305,6 +3306,7 @@ class Writer : base::NoCopy { friend class el::Helpers; void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void initializeLogger(Logger *logger, bool needLock = true); void processDispatch(); void triggerDispatch(void); }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ee7effdd..a0b62da77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,11 +34,6 @@ if (WIN32 OR STATIC) add_definitions(-DMINIUPNP_STATICLIB) endif () -# warnings are cleared only for GCC on Linux -if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY)) - add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow -endif() - function (monero_private_headers group) source_group("${group}\\Private" FILES diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 6c79120e8..60a7326f8 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -714,29 +714,6 @@ bool BlockchainBDB::for_all_outputs(std::function<bool(uint64_t amount, const cr return ret; } -blobdata BlockchainBDB::output_to_blob(const tx_out& output) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - blobdata b; - if (!t_serializable_object_to_blob(output, b)) - throw1(DB_ERROR("Error serializing output to blob")); - return b; -} - -tx_out BlockchainBDB::output_from_blob(const blobdata& blob) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - std::stringstream ss; - ss << blob; - binary_archive<false> ba(ss); - tx_out o; - - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); - - return o; -} - uint64_t BlockchainBDB::get_output_global_index(const uint64_t& amount, const uint64_t& index) { LOG_PRINT_L3("BlockchainBDB::" << __func__); @@ -1655,7 +1632,7 @@ output_data_t BlockchainBDB::get_output_key(const uint64_t& global_index) const return v; } -output_data_t BlockchainBDB::get_output_key(const uint64_t& amount, const uint64_t& index) +output_data_t BlockchainBDB::get_output_key(const uint64_t& amount, const uint64_t& index) const { LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); @@ -1664,7 +1641,7 @@ output_data_t BlockchainBDB::get_output_key(const uint64_t& amount, const uint64 return get_output_key(glob_index); } -tx_out_index BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) +tx_out_index BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { LOG_PRINT_L3("BlockchainBDB::" << __func__); std::vector < uint64_t > offsets; diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 76d0a0517..e80adae9e 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -392,24 +392,6 @@ private: virtual void drop_hard_fork_info(); /** - * @brief convert a tx output to a blob for storage - * - * @param output the output to convert - * - * @return the resultant blob - */ - blobdata output_to_blob(const tx_out& output) const; - - /** - * @brief convert a tx output blob to a tx output - * - * @param blob the blob to convert - * - * @return the resultant tx output - */ - tx_out output_from_blob(const blobdata& blob) const; - - /** * @brief get the global index of the index-th output of the given amount * * @param amount the output amount diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index be0ffeac3..c25798c1e 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -170,7 +170,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti uint64_t tx_id = add_transaction_data(blk_hash, tx, tx_hash, tx_prunable_hash); - std::vector<uint64_t> amount_output_indices; + std::vector<uint64_t> amount_output_indices(tx.vout.size()); // iterate tx.vout using indices instead of C++11 foreach syntax because // we need the index @@ -183,13 +183,13 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti cryptonote::tx_out vout = tx.vout[i]; rct::key commitment = rct::zeroCommit(vout.amount); vout.amount = 0; - amount_output_indices.push_back(add_output(tx_hash, vout, i, tx.unlock_time, - &commitment)); + amount_output_indices[i] = add_output(tx_hash, vout, i, tx.unlock_time, + &commitment); } else { - amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time, - tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL)); + amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time, + tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL); } } add_tx_amount_output_indices(tx_id, amount_output_indices); diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index f13aa0cae..5c80bfe4a 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1258,7 +1258,7 @@ public: * * @return the requested output data */ - virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) = 0; + virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const = 0; /** * @brief gets an output's tx hash and index @@ -1310,7 +1310,7 @@ public: * @param offsets a list of amount-specific output indices * @param outputs return-by-reference a list of outputs' metadata */ - virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) = 0; + virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const = 0; /* * FIXME: Need to check with git blame and ask what this does to @@ -1329,10 +1329,11 @@ public: * If an output cannot be found, the subclass should throw OUTPUT_DNE. * * @param tx_id a transaction ID + * @param n_txes how many txes to get data for, starting with tx_id * * @return a list of amount-specific output indices */ - virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_id) const = 0; + virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes = 1) const = 0; /** * @brief check if a key image is stored as spent diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 4bfc80863..833d6bd05 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -456,6 +456,12 @@ inline int lmdb_txn_renew(MDB_txn *txn) return res; } +inline void BlockchainLMDB::check_open() const +{ + if (!m_open) + throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); +} + void BlockchainLMDB::do_resize(uint64_t increase_size) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -1019,7 +1025,8 @@ void BlockchainLMDB::remove_tx_outputs(const uint64_t tx_id, const transaction& { LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::vector<uint64_t> amount_output_indices = get_tx_amount_output_indices(tx_id); + std::vector<std::vector<uint64_t>> amount_output_indices_set = get_tx_amount_output_indices(tx_id, 1); + const std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.front(); if (amount_output_indices.empty()) { @@ -1166,36 +1173,6 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image) } } -blobdata BlockchainLMDB::output_to_blob(const tx_out& output) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - blobdata b; - if (!t_serializable_object_to_blob(output, b)) - throw1(DB_ERROR("Error serializing output to blob")); - return b; -} - -tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::stringstream ss; - ss << blob; - binary_archive<false> ba(ss); - tx_out o; - - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); - - return o; -} - -void BlockchainLMDB::check_open() const -{ -// LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (!m_open) - throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); -} - BlockchainLMDB::~BlockchainLMDB() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -2559,7 +2536,7 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const return num_elems; } -output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) +output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -2628,7 +2605,7 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, con return indices[0]; } -std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_t tx_id) const +std::vector<std::vector<uint64_t>> BlockchainLMDB::get_tx_amount_output_indices(uint64_t tx_id, size_t n_txes) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -2637,35 +2614,40 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_ TXN_PREFIX_RDONLY(); RCURSOR(tx_outputs); - int result = 0; MDB_val_set(k_tx_id, tx_id); MDB_val v; - std::vector<uint64_t> amount_output_indices; + std::vector<std::vector<uint64_t>> amount_output_indices_set; + amount_output_indices_set.reserve(n_txes); - result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_id, &v, MDB_SET); - if (result == MDB_NOTFOUND) - LOG_PRINT_L0("WARNING: Unexpected: tx has no amount indices stored in " - "tx_outputs, but it should have an empty entry even if it's a tx without " - "outputs"); - else if (result) - throw0(DB_ERROR(lmdb_error("DB error attempting to get data for tx_outputs[tx_index]", result).c_str())); + MDB_cursor_op op = MDB_SET; + while (n_txes-- > 0) + { + int result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_id, &v, op); + if (result == MDB_NOTFOUND) + LOG_PRINT_L0("WARNING: Unexpected: tx has no amount indices stored in " + "tx_outputs, but it should have an empty entry even if it's a tx without " + "outputs"); + else if (result) + throw0(DB_ERROR(lmdb_error("DB error attempting to get data for tx_outputs[tx_index]", result).c_str())); - const uint64_t* indices = (const uint64_t*)v.mv_data; - int num_outputs = v.mv_size / sizeof(uint64_t); + op = MDB_NEXT; - amount_output_indices.reserve(num_outputs); - for (int i = 0; i < num_outputs; ++i) - { - // LOG_PRINT_L0("amount output index[" << 2*i << "]" << ": " << paired_indices[2*i] << " global output index: " << paired_indices[2*i+1]); - amount_output_indices.push_back(indices[i]); + const uint64_t* indices = (const uint64_t*)v.mv_data; + size_t num_outputs = v.mv_size / sizeof(uint64_t); + + amount_output_indices_set.resize(amount_output_indices_set.size() + 1); + std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.back(); + amount_output_indices.reserve(num_outputs); + for (size_t i = 0; i < num_outputs; ++i) + { + amount_output_indices.push_back(indices[i]); + } } - indices = nullptr; TXN_POSTFIX_RDONLY(); - return amount_output_indices; + return amount_output_indices_set; } - bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3192,7 +3174,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const check_open(); uint64_t m_height = height(); - if (m_height % 1000 == 0) + if (m_height % 1024 == 0) { // for batch mode, DB resize check is done at start of batch transaction if (! m_batch_active && need_resize()) @@ -3266,7 +3248,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 TXN_POSTFIX_RDONLY(); } -void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) +void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) const { if (amounts.size() != 1 && amounts.size() != offsets.size()) throw0(DB_ERROR("Invalid sizes of amounts and offets")); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 6db241240..3e6e754d2 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -242,8 +242,8 @@ public: virtual uint64_t get_num_outputs(const uint64_t& amount) const; - virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); - virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false); + virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const; + virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const; virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; virtual void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices, @@ -252,7 +252,7 @@ public: virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const; virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const; - virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_id) const; + virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes) const; virtual bool has_key_image(const crypto::key_image& img) const; @@ -359,25 +359,7 @@ private: virtual void check_hard_fork_info(); virtual void drop_hard_fork_info(); - /** - * @brief convert a tx output to a blob for storage - * - * @param output the output to convert - * - * @return the resultant blob - */ - blobdata output_to_blob(const tx_out& output) const; - - /** - * @brief convert a tx output blob to a tx output - * - * @param blob the blob to convert - * - * @return the resultant tx output - */ - tx_out output_from_blob(const blobdata& blob) const; - - void check_open() const; + inline void check_open() const; virtual bool is_read_only() const; diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 73819bd25..8b007e901 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1131,7 +1131,7 @@ int main(int argc, char* argv[]) return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-find-spent-outputs.log"), true); + mlog_configure(mlog_get_default_log_path("monero-blockchain-mark-spent-outputs.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index 30d85adbf..ff48af6dc 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -39,7 +39,7 @@ foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks) cd ${CMAKE_CURRENT_BINARY_DIR} && echo "'#include\t<stddef.h>'" > ${OUTPUT_C_SOURCE} && echo "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} && - od -v -An -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9]\\{1,\\}/&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} && + od -v -An -tx1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9a-fA-F]\\{1,\\}/0x&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} && echo "'};'" >> ${OUTPUT_C_SOURCE} && echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE} ) diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 1807d44d9..96f575b2d 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -74,7 +74,7 @@ namespace cryptonote bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) { crypto::hash h = crypto::null_hash; - bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); + bool r = epee::string_tools::hex_to_pod(hash_str, h); CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!"); // return false if adding at a height we already have AND the hash is different @@ -292,7 +292,7 @@ namespace cryptonote // parse the second part as crypto::hash, // if this fails move on to the next record std::string hashStr = record.substr(pos + 1); - if (!epee::string_tools::parse_tpod_from_hex_string(hashStr, hash)) + if (!epee::string_tools::hex_to_pod(hashStr, hash)) { continue; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 5da23c944..3b1eb6d23 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -50,6 +50,10 @@ if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) endif() +if (BACKCOMPAT) + list(APPEND common_sources compat/glibc_compat.cpp) +endif() + set(common_headers) set(common_private_headers diff --git a/src/common/compat/glibc_compat.cpp b/src/common/compat/glibc_compat.cpp new file mode 100644 index 000000000..bf567987d --- /dev/null +++ b/src/common/compat/glibc_compat.cpp @@ -0,0 +1,98 @@ +#include <cstddef> +#include <cstdint> +#include <strings.h> +#include <string.h> +#include <glob.h> +#include <unistd.h> +#include <fnmatch.h> + +#if defined(HAVE_SYS_SELECT_H) +#include <sys/select.h> +#endif + +// Prior to GLIBC_2.14, memcpy was aliased to memmove. +extern "C" void* memmove(void* a, const void* b, size_t c); +//extern "C" void* memset(void* a, int b, long unsigned int c); +extern "C" void* memcpy(void* a, const void* b, size_t c) +{ + return memmove(a, b, c); +} + +extern "C" void __chk_fail(void) __attribute__((__noreturn__)); + +#if defined(__i386__) || defined(__arm__) + +extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); + +extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) +{ + int32_t c1 = 0, c2 = 0; + int64_t uu = u, vv = v; + int64_t w; + int64_t r; + + if (uu < 0) { + c1 = ~c1, c2 = ~c2, uu = -uu; + } + if (vv < 0) { + c1 = ~c1, vv = -vv; + } + + w = __udivmoddi4(uu, vv, (uint64_t*)&r); + if (c1) + w = -w; + if (c2) + r = -r; + + *rp = r; + return w; +} +#endif + +/* glibc-internal users use __explicit_bzero_chk, and explicit_bzero + redirects to that. */ +#undef explicit_bzero +/* Set LEN bytes of S to 0. The compiler will not delete a call to + this function, even if S is dead after the call. */ +void +explicit_bzero (void *s, size_t len) +{ + memset (s, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} + +// Redefine explicit_bzero_chk +void +__explicit_bzero_chk (void *dst, size_t len, size_t dstlen) +{ + /* Inline __memset_chk to avoid a PLT reference to __memset_chk. */ + if (__glibc_unlikely (dstlen < len)) + __chk_fail (); + memset (dst, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} +/* libc-internal references use the hidden + __explicit_bzero_chk_internal symbol. This is necessary if + __explicit_bzero_chk is implemented as an IFUNC because some + targets do not support hidden references to IFUNC symbols. */ +#define strong_alias (__explicit_bzero_chk, __explicit_bzero_chk_internal) + +#undef glob +extern "C" int glob_old(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob); +#ifdef __i386__ +__asm__(".symver glob_old,glob@GLIBC_2.1"); +#elif defined(__amd64__) +__asm__(".symver glob_old,glob@GLIBC_2.2.5"); +#elif defined(__arm__) +__asm(".symver glob_old,glob@GLIBC_2.4"); +#elif defined(__aarch64__) +__asm__(".symver glob_old,glob@GLIBC_2.17"); +#endif + +extern "C" int __wrap_glob(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob) +{ + return glob_old(pattern, flags, errfunc, pglob); +} + diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index d9f1f65c1..3e1357833 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -33,6 +33,13 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "perf" +#define PERF_LOG_ALWAYS(level, cat, x) \ + el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::FileOnlyLog).construct(cat) << x +#define PERF_LOG(level, cat, x) \ + do { \ + if (ELPP->vRegistry()->allowed(level, cat)) PERF_LOG_ALWAYS(level, cat, x); \ + } while(0) + namespace tools { uint64_t get_tick_count() @@ -106,9 +113,11 @@ PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused) LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l) { + const bool log = ELPP->vRegistry()->allowed(level, cat.c_str()); if (!performance_timers) { - MCLOG(level, cat.c_str(), "PERF ----------"); + if (log) + PERF_LOG_ALWAYS(level, cat.c_str(), "PERF ----------"); performance_timers = new std::vector<LoggingPerformanceTimer*>(); performance_timers->reserve(16); // how deep before realloc } @@ -117,8 +126,11 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std LoggingPerformanceTimer *pt = performance_timers->back(); if (!pt->started && !pt->paused) { - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; - MCLOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + if (log) + { + size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; + PERF_LOG_ALWAYS(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + } pt->started = true; } } @@ -135,10 +147,14 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer() { pause(); performance_timers->pop_back(); - char s[12]; - snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; - MCLOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); + const bool log = ELPP->vRegistry()->allowed(level, cat.c_str()); + if (log) + { + char s[12]; + snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); + size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; + PERF_LOG_ALWAYS(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); + } if (performance_timers->empty()) { delete performance_timers; @@ -162,4 +178,20 @@ void PerformanceTimer::resume() paused = false; } +void PerformanceTimer::reset() +{ + if (paused) + ticks = 0; + else + ticks = get_tick_count(); +} + +uint64_t PerformanceTimer::value() const +{ + uint64_t v = ticks; + if (!paused) + v = get_tick_count() - v; + return ticks_to_ns(v); +} + } diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 584434a0d..d859cf576 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -51,8 +51,8 @@ public: ~PerformanceTimer(); void pause(); void resume(); - - uint64_t value() const { return ticks; } + void reset(); + uint64_t value() const; protected: uint64_t ticks; @@ -63,7 +63,7 @@ protected: class LoggingPerformanceTimer: public PerformanceTimer { public: - LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Debug); + LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Info); ~LoggingPerformanceTimer(); private: diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp index 0a2ce8387..b2d03f62f 100644 --- a/src/common/spawn.cpp +++ b/src/common/spawn.cpp @@ -35,6 +35,7 @@ #include <windows.h> #else #include <sys/wait.h> +#include <signal.h> #endif #include "misc_log_ex.h" @@ -114,7 +115,10 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait) if (pid > 0) { if (!wait) + { + signal(SIGCHLD, SIG_IGN); return 0; + } while (1) { diff --git a/src/common/util.cpp b/src/common/util.cpp index 448f792ff..28745eea4 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -82,6 +82,32 @@ using namespace epee; #include <boost/asio.hpp> #include <openssl/sha.h> +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "util" + +namespace +{ + +#ifndef _WIN32 +static int flock_exnb(int fd) +{ + struct flock fl; + int ret; + + memset(&fl, 0, sizeof(fl)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + ret = fcntl(fd, F_SETLK, &fl); + if (ret < 0) + MERROR("Error locking fd " << fd << ": " << errno << " (" << strerror(errno) << ")"); + return ret; +} +#endif + +} + namespace tools { std::function<void(int)> signal_handler::m_handler; @@ -182,7 +208,7 @@ namespace tools struct stat wstats = {}; if (fstat(fdw, std::addressof(wstats)) == 0 && rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino && - flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0) + flock_exnb(fdw) == 0 && ftruncate(fdw, 0) == 0) { std::FILE* file = fdopen(fdw, "w"); if (file) return {file, std::move(name)}; @@ -235,10 +261,10 @@ namespace tools MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); } #else - m_fd = open(filename.c_str(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666); + m_fd = open(filename.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0666); if (m_fd != -1) { - if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) + if (flock_exnb(m_fd) == -1) { MERROR("Failed to lock " << filename << ": " << std::strerror(errno)); close(m_fd); diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 645e99f91..196b20e2a 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -211,6 +211,8 @@ namespace cryptonote void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); } void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); } + void set_hash(const crypto::hash &h) { hash = h; set_hash_valid(true); } + void set_blob_size(size_t sz) { blob_size = sz; set_blob_size_valid(true); } BEGIN_SERIALIZE_OBJECT() if (!typename Archive<W>::is_saving()) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 55d7d23f8..82428f196 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -184,6 +184,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data"); tx.invalidate_hashes(); + tx.set_blob_size(tx_blob.size()); return true; } //--------------------------------------------------------------- @@ -379,11 +380,19 @@ namespace cryptonote //--------------------------------------------------------------- uint64_t get_transaction_weight(const transaction &tx) { - std::ostringstream s; - binary_archive<true> a(s); - ::serialization::serialize(a, const_cast<transaction&>(tx)); - const cryptonote::blobdata blob = s.str(); - return get_transaction_weight(tx, blob.size()); + size_t blob_size; + if (tx.is_blob_size_valid()) + { + blob_size = tx.blob_size; + } + else + { + std::ostringstream s; + binary_archive<true> a(s); + ::serialization::serialize(a, const_cast<transaction&>(tx)); + blob_size = s.str().size(); + } + return get_transaction_weight(tx, blob_size); } //--------------------------------------------------------------- bool get_tx_fee(const transaction& tx, uint64_t & fee) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c6a3c6180..19dec069c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -171,6 +171,11 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : LOG_PRINT_L3("Blockchain::" << __func__); } //------------------------------------------------------------------ +Blockchain::~Blockchain() +{ + deinit(); +} +//------------------------------------------------------------------ bool Blockchain::have_tx(const crypto::hash &id) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -550,15 +555,13 @@ bool Blockchain::deinit() // as this should be called if handling a SIGSEGV, need to check // if m_db is a NULL pointer (and thus may have caused the illegal // memory operation), otherwise we may cause a loop. - if (m_db == NULL) - { - throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!"); - } - try { - m_db->close(); - MTRACE("Local blockchain read/write activity stopped successfully"); + if (m_db) + { + m_db->close(); + MTRACE("Local blockchain read/write activity stopped successfully"); + } } catch (const std::exception& e) { @@ -918,8 +921,10 @@ difficulty_type Blockchain::get_difficulty_for_next_block() //------------------------------------------------------------------ std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const { - std::vector<time_t> timestamps(blocks); uint64_t height = m_db->height(); + if (blocks > height) + blocks = height; + std::vector<time_t> timestamps(blocks); while (blocks--) timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1); return timestamps; @@ -1269,7 +1274,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m uint64_t already_generated_coins; uint64_t pool_cookie; - CRITICAL_REGION_BEGIN(m_blockchain_lock); + m_tx_pool.lock(); + const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); }); + CRITICAL_REGION_LOCAL(m_blockchain_lock); height = m_db->height(); if (m_btc_valid) { // The pool cookie is atomic. The lack of locking is OK, as if it changes @@ -1305,8 +1312,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m median_weight = m_current_block_cumul_weight_limit / 2; already_generated_coins = m_db->get_block_already_generated_coins(height - 1); - CRITICAL_REGION_END(); - size_t txs_weight; uint64_t fee; if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version())) @@ -1317,7 +1322,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_weight = 0; uint64_t real_fee = 0; - CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); for(crypto::hash &cur_hash: b.tx_hashes) { auto cur_res = m_tx_pool.m_transactions.find(cur_hash); @@ -1361,7 +1365,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m { LOG_ERROR("Creating block template: error: wrongly calculated fee"); } - CRITICAL_REGION_END(); MDEBUG("Creating block template: height " << height << ", median weight " << median_weight << ", already generated coins " << already_generated_coins << @@ -2297,7 +2300,7 @@ bool Blockchain::check_for_double_spend(const transaction& tx, key_images_contai return true; } //------------------------------------------------------------------ -bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const +bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2307,16 +2310,25 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u MERROR_VER("get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); return false; } + indexs = m_db->get_tx_amount_output_indices(tx_index, n_txes); + CHECK_AND_ASSERT_MES(n_txes == indexs.size(), false, "Wrong indexs size"); - // get amount output indexes, currently referred to in parts as "output global indices", but they are actually specific to amounts - indexs = m_db->get_tx_amount_output_indices(tx_index); - if (indexs.empty()) + return true; +} +//------------------------------------------------------------------ +bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + uint64_t tx_index; + if (!m_db->tx_exists(tx_id, tx_index)) { - // empty indexs is only valid if the vout is empty, which is legal but rare - cryptonote::transaction tx = m_db->get_tx(tx_id); - CHECK_AND_ASSERT_MES(tx.vout.empty(), false, "internal error: global indexes for transaction " << tx_id << " is empty, and tx vout is not"); + MERROR_VER("get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); + return false; } - + std::vector<std::vector<uint64_t>> indices = m_db->get_tx_amount_output_indices(tx_index, 1); + CHECK_AND_ASSERT_MES(indices.size() == 1, false, "Wrong indices size"); + indexs = indices.front(); return true; } //------------------------------------------------------------------ @@ -3841,33 +3853,11 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) } //------------------------------------------------------------------ -void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const +void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) const { try { m_db->get_output_key(epee::span<const uint64_t>(&amount, 1), offsets, outputs, true); - if (outputs.size() < offsets.size()) - { - const uint64_t n_outputs = m_db->get_num_outputs(amount); - for (size_t i = outputs.size(); i < offsets.size(); ++i) - { - uint64_t idx = offsets[i]; - if (idx < n_outputs) - { - MWARNING("Index " << idx << " not found in db for amount " << amount << ", but it is less than the number of entries"); - break; - } - else if (idx < n_outputs + extra_tx_map.size()) - { - outputs.push_back(extra_tx_map[idx - n_outputs]); - } - else - { - MWARNING("missed " << amount << "/" << idx << " in " << extra_tx_map.size() << " (chain " << n_outputs << ")"); - break; - } - } - } } catch (const std::exception& e) { @@ -3982,34 +3972,6 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector // vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries // and is threaded if possible. The table (m_scan_table) will be used later when querying output // keys. -static bool update_output_map(std::map<uint64_t, std::vector<output_data_t>> &extra_tx_map, const transaction &tx, uint64_t height, bool miner) -{ - MTRACE("Blockchain::" << __func__); - for (size_t i = 0; i < tx.vout.size(); ++i) - { - const auto &out = tx.vout[i]; - if (out.target.type() != typeid(txout_to_key)) - continue; - const txout_to_key &out_to_key = boost::get<txout_to_key>(out.target); - rct::key commitment; - uint64_t amount = out.amount; - if (miner && tx.version == 2) - { - commitment = rct::zeroCommit(amount); - amount = 0; - } - else if (tx.version > 1) - { - CHECK_AND_ASSERT_MES(i < tx.rct_signatures.outPk.size(), false, "Invalid outPk size"); - commitment = tx.rct_signatures.outPk[i].mask; - } - else - commitment = rct::zero(); - extra_tx_map[amount].push_back(output_data_t{out_to_key.key, tx.unlock_time, height, commitment}); - } - return true; -} - bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete_entry> &blocks_entry) { MTRACE("Blockchain::" << __func__); @@ -4178,7 +4140,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete // [input] stores all absolute_offsets for each amount std::map<uint64_t, std::vector<uint64_t>> offset_map; // [output] stores all output_data_t for each absolute_offset - std::map<uint64_t, std::vector<output_data_t>> tx_map, extra_tx_map; + std::map<uint64_t, std::vector<output_data_t>> tx_map; std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs); #define SCAN_TABLE_QUIT(m) \ @@ -4195,8 +4157,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete if (m_cancel) return false; - if (!update_output_map(extra_tx_map, blocks[block_index].miner_tx, height + block_index, true)) - SCAN_TABLE_QUIT("Error building extra tx map."); for (const auto &tx_blob : entry.txs) { if (tx_index >= txes.size()) @@ -4255,8 +4215,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete offset_map[in_to_key.amount].push_back(offset); } - if (!update_output_map(extra_tx_map, tx, height + block_index, false)) - SCAN_TABLE_QUIT("Error building extra tx map."); } ++block_index; } @@ -4281,7 +4239,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete for (size_t i = 0; i < amounts.size(); i++) { uint64_t amount = amounts[i]; - tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::cref(extra_tx_map[amount])), true); + tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount])), true); } waiter.wait(&tpool); } @@ -4290,7 +4248,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete for (size_t i = 0; i < amounts.size(); i++) { uint64_t amount = amounts[i]; - output_scan_worker(amount, offset_map[amount], tx_map[amount], extra_tx_map[amount]); + output_scan_worker(amount, offset_map[amount], tx_map[amount]); } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 877828f81..5a1c4b9ad 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -121,6 +121,11 @@ namespace cryptonote Blockchain(tx_memory_pool& tx_pool); /** + * @brief Blockchain destructor + */ + ~Blockchain(); + + /** * @brief Initialize the Blockchain state * * @param db a pointer to the backing store to use for the blockchain @@ -516,10 +521,12 @@ namespace cryptonote * * @param tx_id the hash of the transaction to fetch indices for * @param indexs return-by-reference the global indices for the transaction's outputs + * @param n_txes how many txes in a row to get results for * * @return false if the transaction does not exist, or if no indices are found, otherwise true */ bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const; + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const; /** * @brief stores the blockchain @@ -918,7 +925,7 @@ namespace cryptonote * @param outputs return-by-reference the outputs collected */ void output_scan_worker(const uint64_t amount,const std::vector<uint64_t> &offsets, - std::vector<output_data_t> &outputs, const std::vector<output_data_t> &extra_tx_map) const; + std::vector<output_data_t> &outputs) const; /** * @brief computes the "short" and "long" hashes for a set of blocks diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f3249ea92..1fa6969a6 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1220,6 +1220,11 @@ namespace cryptonote return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); } //----------------------------------------------------------------------------------------------- + bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const + { + return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, n_txes, indexs); + } + //----------------------------------------------------------------------------------------------- void core::pause_mine() { m_miner.pause(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index cc53fce58..fe86f8d39 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -534,6 +534,7 @@ namespace cryptonote * @note see Blockchain::get_tx_outputs_gindexs */ bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const; + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const; /** * @copydoc Blockchain::get_tail_id diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index d4b4e4d34..1c8f1c62c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -480,6 +480,10 @@ namespace cryptonote MERROR("Failed to parse tx from txpool"); return false; } + else + { + tx.set_hash(id); + } tx_weight = meta.weight; fee = meta.fee; relayed = meta.relayed; @@ -659,7 +663,8 @@ namespace cryptonote // continue return true; } - txs.push_back(tx); + tx.set_hash(txid); + txs.push_back(std::move(tx)); return true; }, true, include_unrelayed_txes); } @@ -782,6 +787,7 @@ namespace cryptonote // continue return true; } + tx.set_hash(txid); txi.tx_json = obj_to_json_str(tx); txi.blob_size = bd->size(); txi.weight = meta.weight; @@ -798,7 +804,7 @@ namespace cryptonote txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0; txi.do_not_relay = meta.do_not_relay; txi.double_spend_seen = meta.double_spend_seen; - tx_infos.push_back(txi); + tx_infos.push_back(std::move(txi)); return true; }, true, include_sensitive_data); @@ -847,14 +853,13 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ cryptonote::rpc::tx_in_pool txi; txi.tx_hash = txid; - transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) + if (!parse_and_validate_tx_from_blob(*bd, txi.tx)) { MERROR("Failed to parse tx from txpool"); // continue return true; } - txi.tx = tx; + txi.tx.set_hash(txid); txi.blob_size = bd->size(); txi.weight = meta.weight; txi.fee = meta.fee; @@ -881,7 +886,7 @@ namespace cryptonote } const crypto::key_image& k_image = kee.first; - key_image_infos[k_image] = tx_hashes; + key_image_infos[k_image] = std::move(tx_hashes); } return true; } @@ -990,21 +995,23 @@ namespace cryptonote { struct transction_parser { - transction_parser(const cryptonote::blobdata &txblob, transaction &tx): txblob(txblob), tx(tx), parsed(false) {} + transction_parser(const cryptonote::blobdata &txblob, const crypto::hash &txid, transaction &tx): txblob(txblob), txid(txid), tx(tx), parsed(false) {} cryptonote::transaction &operator()() { if (!parsed) { if (!parse_and_validate_tx_from_blob(txblob, tx)) throw std::runtime_error("failed to parse transaction blob"); + tx.set_hash(txid); parsed = true; } return tx; } const cryptonote::blobdata &txblob; + const crypto::hash &txid; transaction &tx; bool parsed; - } lazy_tx(txblob, tx); + } lazy_tx(txblob, txid, tx); //not the best implementation at this time, sorry :( //check is ring_signature already checked ? diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 05f4189fb..c1989f093 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -31,6 +31,7 @@ #include <vector> #include <unordered_map> #include <boost/uuid/nil_generator.hpp> +#include <boost/uuid/uuid_io.hpp> #include "string_tools.h" #include "cryptonote_protocol_defs.h" #include "block_queue.h" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index d7b74c06d..3c5b22b4a 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -131,6 +131,7 @@ namespace cryptonote bool should_download_next_span(cryptonote_connection_context& context) const; void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); bool kick_idle_peers(); + bool check_standby_peers(); int try_add_next_blocks(cryptonote_connection_context &context); t_core& m_core; @@ -143,6 +144,7 @@ namespace cryptonote 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; boost::mutex m_buffer_mutex; double get_avg_block_size(); @@ -155,7 +157,7 @@ namespace cryptonote std::string blob; epee::serialization::store_t_to_binary(arg, blob); //handler_response_blocks_now(blob.size()); // XXX - return m_p2p->invoke_notify_to_peer(t_parameter::ID, blob, context); + return m_p2p->invoke_notify_to_peer(t_parameter::ID, epee::strspan<uint8_t>(blob), context); } template<class t_parameter> @@ -164,7 +166,7 @@ namespace cryptonote LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << typeid(t_parameter).name() << " -->"); std::string arg_buff; epee::serialization::store_t_to_binary(arg, arg_buff); - return m_p2p->relay_notify_to_all(t_parameter::ID, arg_buff, exclude_context); + return m_p2p->relay_notify_to_all(t_parameter::ID, epee::strspan<uint8_t>(arg_buff), exclude_context); } }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 1de0cde07..01f70cba1 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1210,6 +1210,7 @@ skip: bool t_cryptonote_protocol_handler<t_core>::on_idle() { 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)); return m_core.on_idle(); } //------------------------------------------------------------------------------------------------------------------------ @@ -1245,6 +1246,22 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ 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 + { + if (context.m_state == cryptonote_connection_context::state_standby) + { + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); + } + return true; + }); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); @@ -1338,14 +1355,13 @@ skip: bool start_from_current_chain = false; if (!force_next_span) { - bool first = true; - while (1) + do { size_t nblocks = m_block_queue.get_num_filled_spans(); size_t size = m_block_queue.get_data_size(); if (nblocks < BLOCK_QUEUE_NBLOCKS_THRESHOLD || size < BLOCK_QUEUE_SIZE_THRESHOLD) { - if (!first) + if (context.m_state != cryptonote_connection_context::state_standby) { LOG_DEBUG_CC(context, "Block queue is " << nblocks << " and " << size << ", resuming"); } @@ -1368,10 +1384,9 @@ skip: break; } - if (first) + if (context.m_state != cryptonote_connection_context::state_standby) { LOG_DEBUG_CC(context, "Block queue is " << nblocks << " and " << size << ", pausing"); - first = false; context.m_state = cryptonote_connection_context::state_standby; } @@ -1385,13 +1400,8 @@ skip: return true; } - for (size_t n = 0; n < 50; ++n) - { - if (m_stopping) - return true; - boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); - } - } + return true; + } while(0); context.m_state = cryptonote_connection_context::state_synchronizing; } @@ -1710,13 +1720,13 @@ skip: { std::string fluffyBlob; epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob); - m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections); + m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, epee::strspan<uint8_t>(fluffyBlob), fluffyConnections); } if (!fullConnections.empty()) { std::string fullBlob; epee::serialization::store_t_to_binary(arg, fullBlob); - m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections); + m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, epee::strspan<uint8_t>(fullBlob), fullConnections); } return true; diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 853cde9c3..b5b747e97 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -163,9 +163,21 @@ bool t_command_parser_executor::print_height(const std::vector<std::string>& arg bool t_command_parser_executor::print_block(const std::vector<std::string>& args) { + bool include_hex = false; + + // Assumes that optional flags come after mandatory argument <transaction_hash> + for (unsigned int i = 1; i < args.size(); ++i) { + if (args[i] == "+hex") + include_hex = true; + else + { + std::cout << "unexpected argument: " << args[i] << std::endl; + return true; + } + } if (args.empty()) { - std::cout << "expected: print_block (<block_hash> | <block_height>)" << std::endl; + std::cout << "expected: print_block (<block_hash> | <block_height>) [+hex]" << std::endl; return false; } @@ -173,14 +185,14 @@ bool t_command_parser_executor::print_block(const std::vector<std::string>& args try { uint64_t height = boost::lexical_cast<uint64_t>(arg); - return m_executor.print_block_by_height(height); + return m_executor.print_block_by_height(height, include_hex); } catch (const boost::bad_lexical_cast&) { crypto::hash block_hash; if (parse_hash256(arg, block_hash)) { - return m_executor.print_block_by_hash(block_hash); + return m_executor.print_block_by_hash(block_hash, include_hex); } } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 015e1e1f9..4c7d68686 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -66,7 +66,7 @@ namespace { void print_block_header(cryptonote::block_header_response const & header) { tools::success_msg_writer() - << "timestamp: " << boost::lexical_cast<std::string>(header.timestamp) << std::endl + << "timestamp: " << boost::lexical_cast<std::string>(header.timestamp) << " (" << tools::get_human_readable_timestamp(header.timestamp) << ")" << std::endl << "previous hash: " << header.prev_hash << std::endl << "nonce: " << boost::lexical_cast<std::string>(header.nonce) << std::endl << "is orphan: " << header.orphan_status << std::endl @@ -569,7 +569,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u if (!first) tools::msg_writer() << "" << std::endl; tools::msg_writer() - << "height: " << header.height << ", timestamp: " << header.timestamp + << "height: " << header.height << ", timestamp: " << header.timestamp << " (" << tools::get_human_readable_timestamp(header.timestamp) << ")" << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl << "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl @@ -663,7 +663,7 @@ bool t_rpc_command_executor::print_height() { return true; } -bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) { +bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash, bool include_hex) { cryptonote::COMMAND_RPC_GET_BLOCK::request req; cryptonote::COMMAND_RPC_GET_BLOCK::response res; epee::json_rpc::error error_resp; @@ -689,13 +689,15 @@ bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) { } } + if (include_hex) + tools::success_msg_writer() << res.blob << std::endl; print_block_header(res.block_header); tools::success_msg_writer() << res.json << ENDL; return true; } -bool t_rpc_command_executor::print_block_by_height(uint64_t height) { +bool t_rpc_command_executor::print_block_by_height(uint64_t height, bool include_hex) { cryptonote::COMMAND_RPC_GET_BLOCK::request req; cryptonote::COMMAND_RPC_GET_BLOCK::response res; epee::json_rpc::error error_resp; @@ -721,6 +723,8 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) { } } + if (include_hex) + tools::success_msg_writer() << res.blob << std::endl; print_block_header(res.block_header); tools::success_msg_writer() << res.json << ENDL; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 592584a5f..1541a1a8e 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -91,9 +91,9 @@ public: bool print_height(); - bool print_block_by_hash(crypto::hash block_hash); + bool print_block_by_hash(crypto::hash block_hash, bool include_hex); - bool print_block_by_height(uint64_t height); + bool print_block_by_height(uint64_t height, bool include_hex); bool print_transaction(crypto::hash transaction_hash, bool include_hex, bool include_json); diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index b27c843b6..7f979389a 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -67,14 +67,6 @@ set(trezor_private_headers) if(DEVICE_TREZOR_READY) message(STATUS "Trezor support enabled") - add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) - - set(TREZOR_LIBUSB_LIBRARIES "") - if(LibUSB_COMPILE_TEST_PASSED) - list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES}) - message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}") - endif() - monero_private_headers(device_trezor ${device_private_headers}) diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index c4a92426c..13506a67f 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -877,6 +877,9 @@ namespace tx { valueS.SetString(m_ct.enc_salt2.c_str(), m_ct.enc_salt2.size()); json.AddMember("salt2", valueS, json.GetAllocator()); + valueS.SetString(m_ct.tx_prefix_hash.c_str(), m_ct.tx_prefix_hash.size()); + json.AddMember("tx_prefix_hash", valueS, json.GetAllocator()); + valueS.SetString(m_ct.enc_keys.c_str(), m_ct.enc_keys.size()); json.AddMember("enc_keys", valueS, json.GetAllocator()); diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index a15c2b9ae..d317a2d8d 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -50,8 +50,8 @@ namespace Language class Singleton
{
Singleton() {}
- Singleton(Singleton &s) {}
- Singleton& operator=(const Singleton&) {}
+ Singleton(Singleton &s) = delete;
+ Singleton& operator=(const Singleton&) = delete;
public:
static T* instance()
{
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 8930418bd..0ef2dbb30 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -180,10 +180,10 @@ namespace nodetool virtual void on_connection_close(p2p_connection_context& context); virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- - virtual bool relay_notify_to_list(int command, const std::string& data_buff, const std::list<boost::uuids::uuid> &connections); - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context); - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context); + virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid> &connections); + virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f); @@ -197,12 +197,12 @@ namespace nodetool const boost::program_options::variables_map& vm ); bool idle_worker(); - bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); + bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); bool get_local_node_data(basic_node_data& node_data); //bool get_local_handshake_data(handshake_data& hshd); - bool merge_peerlist_with_local(const std::list<peerlist_entry>& bs); - bool fix_time_delta(std::list<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta); + bool merge_peerlist_with_local(const std::vector<peerlist_entry>& bs); + bool fix_time_delta(std::vector<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta); bool connections_maker(); bool peer_sync_idle_maker(); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 73a021a6a..25ac1ba18 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -33,6 +33,7 @@ #include <algorithm> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> +#include <boost/uuid/uuid_io.hpp> #include <boost/bind.hpp> #include <atomic> @@ -740,7 +741,7 @@ namespace nodetool if(rsp.node_data.network_id != m_network_id) { - LOG_WARNING_CC(context, "COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE Failed, wrong network! (" << rsp.node_data.network_id << "), closing connection."); return; } @@ -1374,7 +1375,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::fix_time_delta(std::list<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta) + bool node_server<t_payload_net_handler>::fix_time_delta(std::vector<peerlist_entry>& local_peerlist, time_t local_time, int64_t& delta) { //fix time delta time_t now = 0; @@ -1394,10 +1395,10 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) { int64_t delta = 0; - std::list<peerlist_entry> peerlist_ = peerlist; + std::vector<peerlist_entry> peerlist_ = peerlist; if(!fix_time_delta(peerlist_, local_time, delta)) return false; LOG_DEBUG_CC(context, "REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size()); @@ -1516,7 +1517,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, const std::string& data_buff, const std::list<boost::uuids::uuid> &connections) + bool node_server<t_payload_net_handler>::relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid> &connections) { for(const auto& c_id: connections) { @@ -1526,7 +1527,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context) { std::list<boost::uuids::uuid> connections; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) @@ -1545,14 +1546,14 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context) { int res = m_net_server.get_config_object().notify(command, req_buff, context.m_connection_id); return res > 0; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) { int res = m_net_server.get_config_object().invoke(command, req_buff, resp_buff, context.m_connection_id); return res > 0; @@ -1686,7 +1687,7 @@ namespace nodetool if(arg.node_data.network_id != m_network_id) { - LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); + LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id); drop_connection(context); add_host_fail(context.m_remote_address); return 1; @@ -1779,8 +1780,8 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::log_peerlist() { - std::list<peerlist_entry> pl_white; - std::list<peerlist_entry> pl_gray; + std::vector<peerlist_entry> pl_white; + std::vector<peerlist_entry> pl_gray; m_peerlist.get_peerlist_full(pl_gray, pl_white); MINFO(ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_white) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ); return true; @@ -1802,7 +1803,7 @@ namespace nodetool { ss << cntxt.m_remote_address.str() << " \t\tpeer_id " << cntxt.peer_id - << " \t\tconn_id " << epee::string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") + << " \t\tconn_id " << cntxt.m_connection_id << (cntxt.m_is_income ? " INC":" OUT") << std::endl; return true; }); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 218250efa..656c6155b 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -43,10 +43,10 @@ namespace nodetool template<class t_connection_context> struct i_p2p_endpoint { - virtual bool relay_notify_to_list(int command, const std::string& data_buff, const std::list<boost::uuids::uuid>& connections)=0; - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid>& connections)=0; + virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_connections_count()=0; @@ -61,19 +61,19 @@ namespace nodetool template<class t_connection_context> struct p2p_endpoint_stub: public i_p2p_endpoint<t_connection_context> { - virtual bool relay_notify_to_list(int command, const std::string& data_buff, const std::list<boost::uuids::uuid>& connections) + virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, const std::list<boost::uuids::uuid>& connections) { return false; } - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + virtual bool relay_notify_to_all(int command, const epee::span<const uint8_t> data_buff, const epee::net_utils::connection_context_base& context) { return false; } - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) { return false; } - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context) { return true; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 1d609a37e..e7aad5abe 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -68,9 +68,9 @@ namespace nodetool bool deinit(); size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} - bool merge_peerlist(const std::list<peerlist_entry>& outer_bs); - bool get_peerlist_head(std::list<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); - bool get_peerlist_full(std::list<peerlist_entry>& pl_gray, std::list<peerlist_entry>& pl_white); + bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs); + bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_full(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white); bool get_white_peer_by_index(peerlist_entry& p, size_t i); bool get_gray_peer_by_index(peerlist_entry& p, size_t i); bool append_with_peer_white(const peerlist_entry& pr); @@ -265,7 +265,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::merge_peerlist(const std::list<peerlist_entry>& outer_bs) + bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs) { CRITICAL_REGION_LOCAL(m_peerlist_lock); for(const peerlist_entry& be: outer_bs) @@ -315,12 +315,13 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::get_peerlist_head(std::list<peerlist_entry>& bs_head, uint32_t depth) + bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth) { CRITICAL_REGION_LOCAL(m_peerlist_lock); peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>(); uint32_t cnt = 0; + bs_head.reserve(depth); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index)) { if(!vl.last_seen) @@ -335,16 +336,18 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::get_peerlist_full(std::list<peerlist_entry>& pl_gray, std::list<peerlist_entry>& pl_white) + bool peerlist_manager::get_peerlist_full(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white) { CRITICAL_REGION_LOCAL(m_peerlist_lock); peers_indexed::index<by_time>::type& by_time_index_gr=m_peers_gray.get<by_time>(); + pl_gray.resize(pl_gray.size() + by_time_index_gr.size()); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_gr)) { pl_gray.push_back(vl); } peers_indexed::index<by_time>::type& by_time_index_wt=m_peers_white.get<by_time>(); + pl_white.resize(pl_white.size() + by_time_index_wt.size()); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_wt)) { pl_white.push_back(vl); diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index e793e19b6..bb9d2635c 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -114,7 +114,7 @@ namespace nodetool #pragma pack(pop) inline - std::string print_peerlist_to_string(const std::list<peerlist_entry>& pl) + std::string print_peerlist_to_string(const std::vector<peerlist_entry>& pl) { time_t now_time = 0; time(&now_time); @@ -189,7 +189,7 @@ namespace nodetool { basic_node_data node_data; t_playload_type payload_data; - std::list<peerlist_entry> local_peerlist_new; + std::vector<peerlist_entry> local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(node_data) @@ -198,7 +198,7 @@ namespace nodetool { // saving: save both, so old and new peers can understand it KV_SERIALIZE(local_peerlist_new) - std::list<peerlist_entry_base<network_address_old>> local_peerlist; + std::vector<peerlist_entry_base<network_address_old>> local_peerlist; for (const auto &p: this_ref.local_peerlist_new) { if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -217,7 +217,7 @@ namespace nodetool // loading: load old list only if there is no new one if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) { - std::list<peerlist_entry_base<network_address_old>> local_peerlist; + 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})); @@ -248,7 +248,7 @@ namespace nodetool { uint64_t local_time; t_playload_type payload_data; - std::list<peerlist_entry> local_peerlist_new; + std::vector<peerlist_entry> local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(local_time) @@ -257,7 +257,7 @@ namespace nodetool { // saving: save both, so old and new peers can understand it KV_SERIALIZE(local_peerlist_new) - std::list<peerlist_entry_base<network_address_old>> local_peerlist; + std::vector<peerlist_entry_base<network_address_old>> local_peerlist; for (const auto &p: this_ref.local_peerlist_new) { if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -276,7 +276,7 @@ namespace nodetool // loading: load old list only if there is no new one if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) { - std::list<peerlist_entry_base<network_address_old>> local_peerlist; + 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})); @@ -389,9 +389,9 @@ namespace nodetool struct response { - std::list<peerlist_entry> local_peerlist_white; - std::list<peerlist_entry> local_peerlist_gray; - std::list<connection_entry> connections_list; + std::vector<peerlist_entry> local_peerlist_white; + std::vector<peerlist_entry> local_peerlist_gray; + std::vector<connection_entry> connections_list; peerid_type my_id; uint64_t local_time; BEGIN_KV_SERIALIZE_MAP() diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index baa649f82..316f0e5e8 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -605,10 +605,19 @@ namespace rct { keyV tmp(rows + 1); size_t i; keyM M(cols, tmp); + ge_p3 Cp3; + CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&Cp3, C.bytes) == 0, false, "point conv failed"); + ge_cached Ccached; + ge_p3_to_cached(&Ccached, &Cp3); + ge_p1p1 p1; //create the matrix to mg sig for (i = 0; i < cols; i++) { M[i][0] = pubs[i].dest; - subKeys(M[i][1], pubs[i].mask, C); + ge_p3 p3; + CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, pubs[i].mask.bytes) == 0, false, "point conv failed"); + ge_sub(&p1, &p3, &Ccached); + ge_p1p1_to_p3(&p3, &p1); + ge_p3_tobytes(M[i][1].bytes, &p3); } //DP(C); return MLSAG_Ver(message, M, mg, rows); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 3851af3c8..d20000a53 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -252,19 +252,11 @@ namespace cryptonote pruned_size += bd.first.first.size(); unpruned_size += bd.first.first.size(); res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); - res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - if (!req.no_miner_tx) - { - bool r = m_core.get_tx_outputs_gindexs(bd.first.second, res.output_indices.back().indices.back().indices); - if (!r) - { - res.status = "Failed"; - return false; - } - } ntxes += bd.second.size(); + res.output_indices.back().indices.reserve(1 + bd.second.size()); + if (req.no_miner_tx) + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); res.blocks.back().txs.reserve(bd.second.size()); - res.output_indices.back().indices.reserve(bd.second.size()); for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { unpruned_size += i->second.size(); @@ -272,14 +264,25 @@ namespace cryptonote i->second.clear(); i->second.shrink_to_fit(); pruned_size += res.blocks.back().txs.back().size(); + } - res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - bool r = m_core.get_tx_outputs_gindexs(i->first, res.output_indices.back().indices.back().indices); + const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1); + if (n_txes_to_lookup > 0) + { + std::vector<std::vector<uint64_t>> indices; + bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices); if (!r) { res.status = "Failed"; return false; } + if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0)) + { + res.status = "Failed"; + return false; + } + for (size_t i = 0; i < indices.size(); ++i) + res.output_indices.back().indices.push_back({std::move(indices[i])}); } } @@ -703,31 +706,31 @@ namespace cryptonote if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed) { res.status = "Failed"; - res.reason = ""; + std::string reason = ""; if ((res.low_mixin = tvc.m_low_mixin)) - add_reason(res.reason, "bad ring size"); + add_reason(reason, "bad ring size"); if ((res.double_spend = tvc.m_double_spend)) - add_reason(res.reason, "double spend"); + add_reason(reason, "double spend"); if ((res.invalid_input = tvc.m_invalid_input)) - add_reason(res.reason, "invalid input"); + add_reason(reason, "invalid input"); if ((res.invalid_output = tvc.m_invalid_output)) - add_reason(res.reason, "invalid output"); + add_reason(reason, "invalid output"); if ((res.too_big = tvc.m_too_big)) - add_reason(res.reason, "too big"); + add_reason(reason, "too big"); if ((res.overspend = tvc.m_overspend)) - add_reason(res.reason, "overspend"); + add_reason(reason, "overspend"); if ((res.fee_too_low = tvc.m_fee_too_low)) - add_reason(res.reason, "fee too low"); + add_reason(reason, "fee too low"); if ((res.not_rct = tvc.m_not_rct)) - add_reason(res.reason, "tx is not ringct"); - const std::string punctuation = res.reason.empty() ? "" : ": "; + add_reason(reason, "tx is not ringct"); + const std::string punctuation = reason.empty() ? "" : ": "; if (tvc.m_verifivation_failed) { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << res.reason); + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << reason); } else { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << res.reason); + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << reason); } return true; } @@ -857,11 +860,11 @@ namespace cryptonote bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res) { PERF_TIMER(on_get_peer_list); - std::list<nodetool::peerlist_entry> white_list; - std::list<nodetool::peerlist_entry> gray_list; + std::vector<nodetool::peerlist_entry> white_list; + std::vector<nodetool::peerlist_entry> gray_list; m_p2p.get_peerlist_manager().get_peerlist_full(gray_list, white_list); - + res.white_list.reserve(white_list.size()); for (auto & entry : white_list) { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -871,6 +874,7 @@ namespace cryptonote res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen); } + res.gray_list.reserve(gray_list.size()); for (auto & entry : gray_list) { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 281702774..ea04485eb 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -111,6 +111,8 @@ typedef cryptonote::simple_wallet sw; #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) +#define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help; + enum TransferType { Transfer, TransferLocked, @@ -134,6 +136,7 @@ namespace const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false}; const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false}; const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; + const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""}; const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; @@ -141,6 +144,93 @@ namespace const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; + const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]"); + const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]"); + const char* USAGE_SHOW_BALANCE("balance [detail]"); + const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]"); + const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]"); + const char* USAGE_PAYMENT_ID("payment_id"); + const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]"); + const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>]"); + const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id>]"); + const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]"); + const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"); + const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]"); + const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"); + const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw]"); + const char* USAGE_SET_LOG("set_log <level>|{+,-,}<categories>"); + const char* USAGE_ACCOUNT("account\n" + " account new <label text with white spaces allowed>\n" + " account switch <index> \n" + " account label <index> <label text with white spaces allowed>\n" + " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" + " account untag <account_index_1> [<account_index_2> ...]\n" + " account tag_description <tag_name> <description>"); + const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"); + const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]"); + const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]"); + const char* USAGE_SET_VARIABLE("set <option> [<value>]"); + const char* USAGE_GET_TX_KEY("get_tx_key <txid>"); + const char* USAGE_SET_TX_KEY("set_tx_key <txid> <tx_key>"); + const char* USAGE_CHECK_TX_KEY("check_tx_key <txid> <txkey> <address>"); + const char* USAGE_GET_TX_PROOF("get_tx_proof <txid> <address> [<message>]"); + const char* USAGE_CHECK_TX_PROOF("check_tx_proof <txid> <address> <signature_file> [<message>]"); + const char* USAGE_GET_SPEND_PROOF("get_spend_proof <txid> [<message>]"); + const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]"); + const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]"); + 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_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"); + const char* USAGE_SET_DESCRIPTION("set_description [free text note]"); + const char* USAGE_SIGN("sign <filename>"); + const char* USAGE_VERIFY("verify <filename> <address> <signature>"); + const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename>"); + const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>"); + const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync"); + const char* USAGE_HW_RECONNECT("hw_reconnect"); + const char* USAGE_EXPORT_OUTPUTS("export_outputs <filename>"); + const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>"); + const char* USAGE_SHOW_TRANSFER("show_transfer <txid>"); + const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]"); + const char* USAGE_FINALIZE_MULTISIG("finalize_multisig <string> [<string>...]"); + const char* USAGE_EXCHANGE_MULTISIG_KEYS("exchange_multisig_keys <string> [<string>...]"); + const char* USAGE_EXPORT_MULTISIG_INFO("export_multisig_info <filename>"); + const char* USAGE_IMPORT_MULTISIG_INFO("import_multisig_info <filename> [<filename>...]"); + const char* USAGE_SIGN_MULTISIG("sign_multisig <filename>"); + const char* USAGE_SUBMIT_MULTISIG("submit_multisig <filename>"); + const char* USAGE_EXPORT_RAW_MULTISIG_TX("export_raw_multisig_tx <filename>"); + const char* USAGE_MMS("mms [<subcommand> [<subcommand_parameters>]]"); + const char* USAGE_MMS_INIT("mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>"); + const char* USAGE_MMS_INFO("mms info"); + const char* USAGE_MMS_SIGNER("mms signer [<number> <label> [<transport_address> [<monero_address>]]]"); + const char* USAGE_MMS_LIST("mms list"); + const char* USAGE_MMS_NEXT("mms next [sync]"); + const char* USAGE_MMS_SYNC("mms sync"); + const char* USAGE_MMS_TRANSFER("mms transfer <transfer_command_arguments>"); + const char* USAGE_MMS_DELETE("mms delete (<message_id> | all)"); + const char* USAGE_MMS_SEND("mms send [<message_id>]"); + const char* USAGE_MMS_RECEIVE("mms receive"); + const char* USAGE_MMS_EXPORT("mms export <message_id>"); + const char* USAGE_MMS_NOTE("mms note [<label> <text>]"); + const char* USAGE_MMS_SHOW("mms show <message_id>"); + const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]"); + const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config"); + const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]"); + const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config"); + const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); + const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); + const char* USAGE_SET_RING("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"); + const char* USAGE_SAVE_KNOWN_RINGS("save_known_rings"); + const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]"); + const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>"); + const char* USAGE_IS_OUTPUT_SPENT("is_output_spent <amount>/<offset>"); + const char* USAGE_VERSION("version"); + const char* USAGE_HELP("help [<command>]"); + std::string input_line(const std::string& prompt) { #ifdef HAVE_READLINE @@ -451,7 +541,7 @@ namespace } catch (const tools::error::tx_rejected& e) { - fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon")) % get_transaction_hash(e.tx())); std::string reason = e.reason(); if (!reason.empty()) fail_msg_writer() << sw::tr("Reason: ") << reason; @@ -774,7 +864,7 @@ bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vec crypto::hash payment_id; if (args.size() > 0) { - fail_msg_writer() << tr("usage: payment_id"); + PRINT_USAGE(USAGE_PAYMENT_ID); return true; } payment_id = crypto::rand<crypto::hash>(); @@ -914,7 +1004,7 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo if (args.size() < 2) { - fail_msg_writer() << tr("usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...]"); + PRINT_USAGE(USAGE_MAKE_MULTISIG); return false; } @@ -1001,7 +1091,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args) if (args.size() < 2) { - fail_msg_writer() << tr("usage: finalize_multisig <multisiginfo1> [<multisiginfo2>...]"); + PRINT_USAGE(USAGE_FINALIZE_MULTISIG); return true; } @@ -1055,7 +1145,7 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & if (args.size() < 2) { - fail_msg_writer() << tr("usage: exchange_multisig_keys <multisiginfo1> [<multisiginfo2>...]"); + PRINT_USAGE(USAGE_EXCHANGE_MULTISIG_KEYS); return false; } @@ -1113,7 +1203,7 @@ bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, b } if (args.size() != 1) { - fail_msg_writer() << tr("usage: export_multisig_info <filename>"); + PRINT_USAGE(USAGE_EXPORT_MULTISIG_INFO); return false; } @@ -1179,7 +1269,7 @@ bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, b } if (args.size() < threshold - 1) { - fail_msg_writer() << tr("usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant"); + PRINT_USAGE(USAGE_IMPORT_MULTISIG_INFO); return false; } @@ -1273,7 +1363,7 @@ bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, boo } if (args.size() != 1) { - fail_msg_writer() << tr("usage: sign_multisig <filename>"); + PRINT_USAGE(USAGE_SIGN_MULTISIG); return false; } @@ -1389,7 +1479,7 @@ bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, b } if (args.size() != 1) { - fail_msg_writer() << tr("usage: submit_multisig <filename>"); + PRINT_USAGE(USAGE_SUBMIT_MULTISIG); return false; } @@ -1470,7 +1560,7 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args) } if (args.size() != 1) { - fail_msg_writer() << tr("usage: export_raw_multisig <filename>"); + PRINT_USAGE(USAGE_EXPORT_RAW_MULTISIG_TX); return true; } @@ -1533,7 +1623,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args) crypto::hash txid; if (args.size() != 1) { - fail_msg_writer() << tr("usage: print_ring <key_image> | <txid>"); + PRINT_USAGE(USAGE_PRINT_RING); return true; } @@ -1690,7 +1780,7 @@ bool simple_wallet::set_ring(const std::vector<std::string> &args) if (args.size() < 3) { - fail_msg_writer() << tr("usage: set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"); + PRINT_USAGE(USAGE_SET_RING); return true; } @@ -1765,7 +1855,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args) uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets; if (args.size() == 0) { - fail_msg_writer() << tr("usage: mark_output_spent <amount>/<offset> | <filename> [add]"); + PRINT_USAGE(USAGE_MARK_OUTPUT_SPENT); return true; } @@ -1854,7 +1944,7 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args) std::pair<uint64_t, uint64_t> output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: mark_output_unspent <amount>/<offset>"); + PRINT_USAGE(USAGE_MARK_OUTPUT_UNSPENT); return true; } @@ -1881,7 +1971,7 @@ bool simple_wallet::blackballed(const std::vector<std::string> &args) std::pair<uint64_t, uint64_t> output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: is_output_spent <amount>/<offset>"); + PRINT_USAGE(USAGE_IS_OUTPUT_SPENT); return true; } @@ -2399,6 +2489,19 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string> return true; } +bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->track_uses(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -2456,14 +2559,14 @@ simple_wallet::simple_wallet() { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), - tr("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]"), + tr(USAGE_START_MINING), tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans).")); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), tr("Stop mining in the daemon.")); m_cmd_binder.set_handler("set_daemon", boost::bind(&simple_wallet::set_daemon, this, _1), - tr("set_daemon <host>[:<port>] [trusted|untrusted]"), + tr(USAGE_SET_DAEMON), tr("Set another daemon to connect to.")); m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), @@ -2473,70 +2576,64 @@ simple_wallet::simple_wallet() tr("Synchronize the transactions and balance.")); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), - tr("balance [detail]"), + tr(USAGE_SHOW_BALANCE), tr("Show the wallet's balance of the currently selected account.")); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), - tr("incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"), + tr(USAGE_INCOMING_TRANSFERS), tr("Show the incoming transfers, all or filtered by availability and address index.\n\n" "Output format:\n" "Amount, Spent(\"T\"|\"F\"), \"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), - tr("payments <PID_1> [<PID_2> ... <PID_N>]"), + tr(USAGE_PAYMENTS), tr("Show the payments for the given payment IDs.")); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show the blockchain height.")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), - tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]"), + tr(USAGE_TRANSFER), tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), - tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>]"), + tr(USAGE_LOCKED_TRANSFER), tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_sweep_all", boost::bind(&simple_wallet::locked_sweep_all, this, _1), - tr("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id>]"), + tr(USAGE_LOCKED_SWEEP_ALL), tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), - tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]"), + tr(USAGE_SWEEP_ALL), tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), - tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"), + tr(USAGE_SWEEP_BELOW), tr("Send all unlocked outputs below the threshold to an address.")); m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), - tr("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]"), + tr(USAGE_SWEEP_SINGLE), tr("Send a single output of the given key image to an address without change.")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), - tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"), + tr(USAGE_DONATE), tr("Donate <amount> to the development team (donate.getmonero.org).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), - tr("sign_transfer [export_raw]"), + tr(USAGE_SIGN_TRANSFER), tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported.")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file.")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), - tr("set_log <level>|{+,-,}<categories>"), + tr(USAGE_SET_LOG), tr("Change the current log detail (level must be <0-4>).")); m_cmd_binder.set_handler("account", boost::bind(&simple_wallet::account, this, _1), - tr("account\n" - " account new <label text with white spaces allowed>\n" - " account switch <index> \n" - " account label <index> <label text with white spaces allowed>\n" - " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" - " account untag <account_index_1> [<account_index_2> ...]\n" - " account tag_description <tag_name> <description>"), + tr(USAGE_ACCOUNT), tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n" "If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n" "If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n" @@ -2546,15 +2643,15 @@ simple_wallet::simple_wallet() "If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>.")); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), - tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"), + tr(USAGE_ADDRESS), tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text.")); m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::print_integrated_address, this, _1), - tr("integrated_address [<payment_id> | <address>]"), + tr(USAGE_INTEGRATED_ADDRESS), tr("Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID")); m_cmd_binder.set_handler("address_book", boost::bind(&simple_wallet::address_book, this, _1), - tr("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]"), + tr(USAGE_ADDRESS_BOOK), tr("Print all entries in the address book, optionally adding/deleting an entry to/from it.")); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), @@ -2573,7 +2670,7 @@ simple_wallet::simple_wallet() tr("Display the Electrum-style mnemonic seed")); m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), - tr("set <option> [<value>]"), + tr(USAGE_SET_VARIABLE), tr("Available options:\n " "seed language\n " " Set the wallet's seed language.\n " @@ -2626,45 +2723,45 @@ simple_wallet::simple_wallet() tr("Rescan the blockchain for spent outputs.")); m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), - tr("get_tx_key <txid>"), + tr(USAGE_GET_TX_KEY), tr("Get the transaction key (r) for a given <txid>.")); m_cmd_binder.set_handler("set_tx_key", boost::bind(&simple_wallet::set_tx_key, this, _1), - tr("set_tx_key <txid> <tx_key>"), + tr(USAGE_SET_TX_KEY), tr("Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet.")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), - tr("check_tx_key <txid> <txkey> <address>"), + tr(USAGE_CHECK_TX_KEY), tr("Check the amount going to <address> in <txid>.")); m_cmd_binder.set_handler("get_tx_proof", boost::bind(&simple_wallet::get_tx_proof, this, _1), - tr("get_tx_proof <txid> <address> [<message>]"), + tr(USAGE_GET_TX_PROOF), tr("Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key.")); m_cmd_binder.set_handler("check_tx_proof", boost::bind(&simple_wallet::check_tx_proof, this, _1), - tr("check_tx_proof <txid> <address> <signature_file> [<message>]"), + tr(USAGE_CHECK_TX_PROOF), tr("Check the proof for funds going to <address> in <txid> with the challenge string <message> if any.")); m_cmd_binder.set_handler("get_spend_proof", boost::bind(&simple_wallet::get_spend_proof, this, _1), - tr("get_spend_proof <txid> [<message>]"), + tr(USAGE_GET_SPEND_PROOF), tr("Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>.")); m_cmd_binder.set_handler("check_spend_proof", boost::bind(&simple_wallet::check_spend_proof, this, _1), - tr("check_spend_proof <txid> <signature_file> [<message>]"), + tr(USAGE_CHECK_SPEND_PROOF), tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>.")); m_cmd_binder.set_handler("get_reserve_proof", boost::bind(&simple_wallet::get_reserve_proof, this, _1), - tr("get_reserve_proof (all|<amount>) [<message>]"), + tr(USAGE_GET_RESERVE_PROOF), tr("Generate a signature proving that you own at least this much, optionally with a challenge string <message>.\n" "If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.\n" "Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account.")); m_cmd_binder.set_handler("check_reserve_proof", boost::bind(&simple_wallet::check_reserve_proof, this, _1), - tr("check_reserve_proof <address> <signature_file> [<message>]"), + tr(USAGE_CHECK_RESERVE_PROOF), tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>.")); m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), - tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"), + tr(USAGE_SHOW_TRANSFERS), // Seemingly broken formatting to compensate for the backslash before the quotes. tr("Show the incoming/outgoing transfers within an optional height range.\n\n" "Output format:\n" @@ -2680,26 +2777,27 @@ simple_wallet::simple_wallet() tr("Export to CSV the incoming/outgoing transfers within an optional height range.")); m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), - tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"), + tr(USAGE_UNSPENT_OUTPUTS), tr("Show the unspent outputs of a specified address within an optional amount range.")); m_cmd_binder.set_handler("rescan_bc", boost::bind(&simple_wallet::rescan_blockchain, this, _1), - tr("rescan_bc [hard]"), + tr(USAGE_RESCAN_BC), tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself.")); m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), - tr("set_tx_note <txid> [free text note]"), + tr(USAGE_SET_TX_NOTE), tr("Set an arbitrary string note for a <txid>.")); m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), - tr("get_tx_note <txid>"), + tr(USAGE_GET_TX_NOTE), tr("Get a string note for a txid.")); m_cmd_binder.set_handler("set_description", boost::bind(&simple_wallet::set_description, this, _1), - tr("set_description [free text note]"), + tr(USAGE_SET_DESCRIPTION), tr("Set an arbitrary description for the wallet.")); m_cmd_binder.set_handler("get_description", boost::bind(&simple_wallet::get_description, this, _1), + tr(USAGE_GET_DESCRIPTION), tr("Get the description of the wallet.")); m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), @@ -2709,45 +2807,46 @@ simple_wallet::simple_wallet() tr("Show the wallet's information.")); m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), - tr("sign <file>"), + tr(USAGE_SIGN), tr("Sign the contents of a file.")); m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), - tr("verify <filename> <address> <signature>"), + tr(USAGE_VERIFY), tr("Verify a signature on the contents of a file.")); m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), - tr("export_key_images <file>"), - tr("Export a signed set of key images to a <file>.")); + tr(USAGE_EXPORT_KEY_IMAGES), + tr("Export a signed set of key images to a <filename>.")); m_cmd_binder.set_handler("import_key_images", boost::bind(&simple_wallet::import_key_images, this, _1), - tr("import_key_images <file>"), + tr(USAGE_IMPORT_KEY_IMAGES), tr("Import a signed key images list and verify their spent status.")); m_cmd_binder.set_handler("hw_key_images_sync", boost::bind(&simple_wallet::hw_key_images_sync, this, _1), - tr("hw_key_images_sync"), + tr(USAGE_HW_KEY_IMAGES_SYNC), tr("Synchronizes key images with the hw wallet.")); m_cmd_binder.set_handler("hw_reconnect", boost::bind(&simple_wallet::hw_reconnect, this, _1), - tr("hw_reconnect"), + tr(USAGE_HW_RECONNECT), tr("Attempts to reconnect HW wallet.")); m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), - tr("export_outputs <file>"), + tr(USAGE_EXPORT_OUTPUTS), tr("Export a set of outputs owned by this wallet.")); m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), - tr("import_outputs <file>"), + tr(USAGE_IMPORT_OUTPUTS), tr("Import a set of outputs owned by this wallet.")); m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), - tr("show_transfer <txid>"), + tr(USAGE_SHOW_TRANSFER), tr("Show information about a transfer to/from this address.")); m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change the wallet's password.")); m_cmd_binder.set_handler("payment_id", boost::bind(&simple_wallet::payment_id, this, _1), + tr(USAGE_PAYMENT_ID), tr("Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.")); m_cmd_binder.set_handler("fee", boost::bind(&simple_wallet::print_fee_info, this, _1), @@ -2755,39 +2854,39 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("prepare_multisig", boost::bind(&simple_wallet::prepare_multisig, this, _1), tr("Export data needed to create a multisig wallet")); m_cmd_binder.set_handler("make_multisig", boost::bind(&simple_wallet::make_multisig, this, _1), - tr("make_multisig <threshold> <string1> [<string>...]"), + tr(USAGE_MAKE_MULTISIG), tr("Turn this wallet into a multisig wallet")); m_cmd_binder.set_handler("finalize_multisig", boost::bind(&simple_wallet::finalize_multisig, this, _1), - tr("finalize_multisig <string> [<string>...]"), + tr(USAGE_FINALIZE_MULTISIG), tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets")); m_cmd_binder.set_handler("exchange_multisig_keys", boost::bind(&simple_wallet::exchange_multisig_keys, this, _1), - tr("exchange_multisig_keys <string> [<string>...]"), + tr(USAGE_EXCHANGE_MULTISIG_KEYS), tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets")); m_cmd_binder.set_handler("export_multisig_info", boost::bind(&simple_wallet::export_multisig, this, _1), - tr("export_multisig_info <filename>"), + tr(USAGE_EXPORT_MULTISIG_INFO), tr("Export multisig info for other participants")); m_cmd_binder.set_handler("import_multisig_info", boost::bind(&simple_wallet::import_multisig, this, _1), - tr("import_multisig_info <filename> [<filename>...]"), + tr(USAGE_IMPORT_MULTISIG_INFO), tr("Import multisig info from other participants")); m_cmd_binder.set_handler("sign_multisig", boost::bind(&simple_wallet::sign_multisig, this, _1), - tr("sign_multisig <filename>"), + tr(USAGE_SIGN_MULTISIG), tr("Sign a multisig transaction from a file")); m_cmd_binder.set_handler("submit_multisig", boost::bind(&simple_wallet::submit_multisig, this, _1), - tr("submit_multisig <filename>"), + tr(USAGE_SUBMIT_MULTISIG), tr("Submit a signed multisig transaction from a file")); m_cmd_binder.set_handler("export_raw_multisig_tx", boost::bind(&simple_wallet::export_raw_multisig, this, _1), - tr("export_raw_multisig_tx <filename>"), + tr(USAGE_EXPORT_RAW_MULTISIG_TX), tr("Export a signed multisig transaction to a file")); m_cmd_binder.set_handler("mms", boost::bind(&simple_wallet::mms, this, _1), - tr("mms [<subcommand> [<subcommand_parameters>]]"), + tr(USAGE_MMS), tr("Interface with the MMS (Multisig Messaging System)\n" "<subcommand> is one of:\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" @@ -2795,112 +2894,112 @@ simple_wallet::simple_wallet() "Get help about a subcommand with: help mms <subcommand>, or mms help <subcommand>")); m_cmd_binder.set_handler("mms init", boost::bind(&simple_wallet::mms, this, _1), - tr("mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>"), + tr(USAGE_MMS_INIT), tr("Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig")); m_cmd_binder.set_handler("mms info", boost::bind(&simple_wallet::mms, this, _1), - tr("mms info"), + tr(USAGE_MMS_INFO), tr("Display current MMS configuration")); m_cmd_binder.set_handler("mms signer", boost::bind(&simple_wallet::mms, this, _1), - tr("mms signer [<number> <label> [<transport_address> [<monero_address>]]]"), + tr(USAGE_MMS_SIGNER), tr("Set or modify authorized signer info (single-word label, transport address, Monero address), or list all signers")); m_cmd_binder.set_handler("mms list", boost::bind(&simple_wallet::mms, this, _1), - tr("mms list"), + tr(USAGE_MMS_LIST), tr("List all messages")); m_cmd_binder.set_handler("mms next", boost::bind(&simple_wallet::mms, this, _1), - tr("mms next [sync]"), + tr(USAGE_MMS_NEXT), tr("Evaluate the next possible multisig-related action(s) according to wallet state, and execute or offer for choice\n" "By using 'sync' processing of waiting messages with multisig sync info can be forced regardless of wallet state")); m_cmd_binder.set_handler("mms sync", boost::bind(&simple_wallet::mms, this, _1), - tr("mms sync"), + tr(USAGE_MMS_SYNC), tr("Force generation of multisig sync info regardless of wallet state, to recover from special situations like \"stale data\" errors")); m_cmd_binder.set_handler("mms transfer", boost::bind(&simple_wallet::mms, this, _1), - tr("mms transfer <transfer_command_arguments>"), + tr(USAGE_MMS_TRANSFER), tr("Initiate transfer with MMS support; arguments identical to normal 'transfer' command arguments, for info see there")); m_cmd_binder.set_handler("mms delete", boost::bind(&simple_wallet::mms, this, _1), - tr("mms delete (<message_id> | all)"), + tr(USAGE_MMS_DELETE), tr("Delete a single message by giving its id, or delete all messages by using 'all'")); m_cmd_binder.set_handler("mms send", boost::bind(&simple_wallet::mms, this, _1), - tr("mms send [<message_id>]"), + tr(USAGE_MMS_SEND), tr("Send a single message by giving its id, or send all waiting messages")); m_cmd_binder.set_handler("mms receive", boost::bind(&simple_wallet::mms, this, _1), - tr("mms receive"), + tr(USAGE_MMS_RECEIVE), tr("Check right away for new messages to receive")); m_cmd_binder.set_handler("mms export", boost::bind(&simple_wallet::mms, this, _1), - tr("mms export <message_id"), + tr(USAGE_MMS_EXPORT), tr("Write the content of a message to a file \"mms_message_content\"")); m_cmd_binder.set_handler("mms note", boost::bind(&simple_wallet::mms, this, _1), - tr("mms note [<label> <text>]"), + tr(USAGE_MMS_NOTE), tr("Send a one-line message to an authorized signer, identified by its label, or show any waiting unread notes")); m_cmd_binder.set_handler("mms show", boost::bind(&simple_wallet::mms, this, _1), - tr("mms show <message_id>"), + tr(USAGE_MMS_SHOW), tr("Show detailed info about a single message")); m_cmd_binder.set_handler("mms set", boost::bind(&simple_wallet::mms, this, _1), - tr("mms set <option_name> [<option_value>]"), + tr(USAGE_MMS_SET), tr("Available options:\n " "auto-send <1|0>\n " " Whether to automatically send newly generated messages right away.\n ")); m_cmd_binder.set_handler("mms send_message_config", boost::bind(&simple_wallet::mms, this, _1), - tr("mms send_signer_config"), + tr(USAGE_MMS_SEND_SIGNER_CONFIG), tr("Send completed signer config to all other authorized signers")); m_cmd_binder.set_handler("mms start_auto_config", boost::bind(&simple_wallet::mms, this, _1), - tr("mms start_auto_config [<label> <label> ...]"), + tr(USAGE_MMS_START_AUTO_CONFIG), tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); m_cmd_binder.set_handler("mms stop_auto_config", boost::bind(&simple_wallet::mms, this, _1), - tr("mms stop_auto_config"), + tr(USAGE_MMS_STOP_AUTO_CONFIG), tr("Delete any auto-config tokens and abort a auto-config process")); m_cmd_binder.set_handler("mms auto_config", boost::bind(&simple_wallet::mms, this, _1), - tr("mms auto_config <auto_config_token>"), + tr(USAGE_MMS_AUTO_CONFIG), tr("Start auto-config by using the token received from the auto-config manager")); m_cmd_binder.set_handler("print_ring", boost::bind(&simple_wallet::print_ring, this, _1), - tr("print_ring <key_image> | <txid>"), + tr(USAGE_PRINT_RING), tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)\n\n" "Output format:\n" "Key Image, \"absolute\", list of rings")); m_cmd_binder.set_handler("set_ring", boost::bind(&simple_wallet::set_ring, this, _1), - tr("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"), + tr(USAGE_SET_RING), tr("Set the ring used for a given key image, so it can be reused in a fork")); m_cmd_binder.set_handler("save_known_rings", boost::bind(&simple_wallet::save_known_rings, this, _1), - tr("save_known_rings"), + tr(USAGE_SAVE_KNOWN_RINGS), tr("Save known rings to the shared rings database")); m_cmd_binder.set_handler("mark_output_spent", boost::bind(&simple_wallet::blackball, this, _1), - tr("mark_output_spent <amount>/<offset> | <filename> [add]"), + tr(USAGE_MARK_OUTPUT_SPENT), tr("Mark output(s) as spent so they never get selected as fake outputs in a ring")); m_cmd_binder.set_handler("mark_output_unspent", boost::bind(&simple_wallet::unblackball, this, _1), - tr("mark_output_unspent <amount>/<offset>"), + tr(USAGE_MARK_OUTPUT_UNSPENT), tr("Marks an output as unspent so it may get selected as a fake output in a ring")); m_cmd_binder.set_handler("is_output_spent", boost::bind(&simple_wallet::blackballed, this, _1), - tr("is_output_spent <amount>/<offset>"), + tr(USAGE_IS_OUTPUT_SPENT), tr("Checks whether an output is marked as spent")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), - tr("version"), + tr(USAGE_VERSION), tr("Returns version information")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), - tr("help [<command>]"), + tr(USAGE_HELP), tr("Show the help section or the documentation about a <command>.")); } //---------------------------------------------------------------------------------------------------- @@ -2947,6 +3046,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); + success_msg_writer() << "track-uses = " << m_wallet->track_uses(); success_msg_writer() << "device_name = " << m_wallet->device_name(); return true; } @@ -3003,6 +3103,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>")); CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); @@ -3014,7 +3115,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) { if(args.size() > 1) { - fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>"); + PRINT_USAGE(USAGE_SET_LOG); return true; } if(!args.empty()) @@ -3024,7 +3125,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) { if(4 < level) { - fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4> | <categories>"); + fail_msg_writer() << boost::format(tr("wrong number range, use: %s")) % USAGE_SET_LOG; return true; } mlog_set_log_level(level); @@ -3160,6 +3261,28 @@ static bool might_be_partial_seed(const epee::wipeable_string &words) return seed.size() < 24; } //---------------------------------------------------------------------------------------------------- +static bool datestr_to_int(const std::string &heightstr, uint16_t &year, uint8_t &month, uint8_t &day) +{ + if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-') + { + fail_msg_writer() << tr("date format must be YYYY-MM-DD"); + return false; + } + try + { + year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4)); + // lexical_cast<uint8_t> won't work because uint8_t is treated as character type + month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2)); + day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2)); + } + catch (const boost::bad_lexical_cast &) + { + fail_msg_writer() << tr("bad height parameter: ") << heightstr; + return false; + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ @@ -3600,7 +3723,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) tools::scoped_message_writer wrt = tools::msg_writer(); wrt << tr("No restore height is specified."); wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height."); - wrt << tr("Use --restore-height if you want to restore an already setup account from a specific height"); + wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height"); } std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof() || !command_line::is_yes(confirm)) @@ -3629,7 +3752,26 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty()) { - m_wallet->explicit_refresh_from_block_height(!command_line::is_arg_defaulted(vm, arg_restore_height)); + m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) || + command_line::is_arg_defaulted(vm, arg_restore_date))); + if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date)) + { + uint16_t year; + uint8_t month; + uint8_t day; + if (!datestr_to_int(m_restore_date, year, month, day)) + return false; + try + { + m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day); + success_msg_writer() << tr("Restore height is: ") << m_restore_height; + } + catch (const std::runtime_error& e) + { + fail_msg_writer() << e.what(); + return false; + } + } } if (!m_wallet->explicit_refresh_from_block_height() && m_restoring) { @@ -3661,20 +3803,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr; continue; } - if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-') - { - fail_msg_writer() << tr("date format must be YYYY-MM-DD"); - continue; - } uint16_t year; uint8_t month; // 1, 2, ..., 12 uint8_t day; // 1, 2, ..., 31 try { - year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4)); - // lexical_cast<uint8_t> won't work because uint8_t is treated as character type - month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2)); - day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2)); + if (!datestr_to_int(heightstr, year, month, day)) + return false; m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day); success_msg_writer() << tr("Restore height is: ") << m_restore_height; std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): ")); @@ -3761,6 +3896,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version); m_restore_height = command_line::get_arg(vm, arg_restore_height); + m_restore_date = command_line::get_arg(vm, arg_restore_date); m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead); m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names); @@ -3773,6 +3909,14 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_deterministic_wallet || m_restore_multisig_wallet; + if (!command_line::is_arg_defaulted(vm, arg_restore_date)) + { + uint16_t year; + uint8_t month, day; + if (!datestr_to_int(m_restore_date, year, month, day)) + return false; + } + return true; } //---------------------------------------------------------------------------------------------------- @@ -4320,7 +4464,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) if (!ok) { - fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery]"); + PRINT_USAGE(USAGE_START_MINING); return true; } @@ -4362,7 +4506,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args) if (args.size() < 1) { - fail_msg_writer() << tr("missing daemon URL argument"); + PRINT_USAGE(USAGE_SET_DAEMON); return true; } @@ -4707,7 +4851,7 @@ bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::v { if (args.size() > 1 || (args.size() == 1 && args[0] != "detail")) { - fail_msg_writer() << tr("usage: balance [detail]"); + PRINT_USAGE(USAGE_SHOW_BALANCE); return true; } LOCK_IDLE_SCOPE(); @@ -4719,7 +4863,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args { if (args.size() > 3) { - fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"); + PRINT_USAGE(USAGE_INCOMING_TRANSFERS); return true; } auto local_args = args; @@ -4728,6 +4872,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args bool filter = false; bool available = false; bool verbose = false; + bool uses = false; if (local_args.size() > 0) { if (local_args[0] == "available") @@ -4743,12 +4888,22 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args local_args.erase(local_args.begin()); } } - if (local_args.size() > 0 && local_args[0] == "verbose") + while (local_args.size() > 0) { - verbose = true; + if (local_args[0] == "verbose") + verbose = true; + else if (local_args[0] == "uses") + uses = true; + else + { + fail_msg_writer() << tr("Invalid keyword: ") << local_args.front(); + break; + } local_args.erase(local_args.begin()); } + const uint64_t blockchain_height = m_wallet->get_blockchain_current_height(); + PAUSE_READLINE(); std::set<uint32_t> subaddr_indices; @@ -4761,7 +4916,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args if (local_args.size() > 0) { - fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"); + PRINT_USAGE(USAGE_INCOMING_TRANSFERS); return true; } @@ -4782,9 +4937,16 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str(); message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string; } - std::string verbose_string; + std::string extra_string; if (verbose) - verbose_string = (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str(); + extra_string += (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str(); + if (uses) + { + std::vector<uint64_t> heights; + for (const auto &e: td.m_uses) heights.push_back(e.first); + const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height); + extra_string += tr("Heights: ") + line.first + "\n" + line.second; + } message_writer(td.m_spent ? console_color_magenta : console_color_green, false) << boost::format("%21s%8s%12s%8s%16u%68s%16u%s") % print_money(td.amount()) % @@ -4794,7 +4956,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args td.m_global_output_index % td.m_txid % td.m_subaddr_index.minor % - verbose_string; + extra_string; ++transfers_found; } } @@ -4826,7 +4988,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args) { if(args.empty()) { - fail_msg_writer() << tr("expected at least one payment ID"); + PRINT_USAGE(USAGE_PAYMENTS); return true; } @@ -4946,6 +5108,33 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const +{ + std::stringstream ostr; + + for (uint64_t h: heights) + blockchain_height = std::max(blockchain_height, h); + + for (size_t j = 0; j < heights.size(); ++j) + ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j]; + + // visualize the distribution, using the code by moneroexamples onion-monero-viewer + const uint64_t resolution = 79; + std::string ring_str(resolution, '_'); + for (size_t j = 0; j < heights.size(); ++j) + { + uint64_t pos = (heights[j] * resolution) / blockchain_height; + ring_str[pos] = 'o'; + } + if (highlight_height < blockchain_height) + { + uint64_t pos = (highlight_height * resolution) / blockchain_height; + ring_str[pos] = '*'; + } + + return std::make_pair(ostr.str(), ring_str); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr) { uint32_t version; @@ -5016,21 +5205,18 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending } } ostr << tr("\nOriginating block heights: "); - for (size_t j = 0; j < absolute_offsets.size(); ++j) - ostr << tr(j == source.real_output ? " *" : " ") << res.outs[j].height; spent_key_height[i] = res.outs[source.real_output].height; spent_key_txid [i] = res.outs[source.real_output].txid; - // visualize the distribution, using the code by moneroexamples onion-monero-viewer - const uint64_t resolution = 79; - std::string ring_str(resolution, '_'); + std::vector<uint64_t> heights(absolute_offsets.size(), 0); + uint64_t highlight_height = std::numeric_limits<uint64_t>::max(); for (size_t j = 0; j < absolute_offsets.size(); ++j) { - uint64_t pos = (res.outs[j].height * resolution) / blockchain_height; - ring_str[pos] = 'o'; + heights[j] = res.outs[j].height; + if (j == source.real_output) + highlight_height = heights[j]; } - uint64_t pos = (res.outs[source.real_output].height * resolution) / blockchain_height; - ring_str[pos] = '*'; - ostr << tr("\n|") << ring_str << tr("|\n"); + std::pair<std::string, std::string> ring_str = show_outputs_line(heights, highlight_height); + ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); } // warn if rings contain keys originating from the same tx or temporally very close block heights bool are_keys_from_same_tx = false; @@ -5133,19 +5319,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri payment_id_seen = true; message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } - else - { - crypto::hash8 payment_id8; - if (tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8)) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - local_args.pop_back(); - payment_id_seen = true; - } - } - if(!r) { fail_msg_writer() << tr("payment id failed to encode"); @@ -5659,7 +5832,14 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st { auto print_usage = [below]() { - fail_msg_writer() << boost::format(tr("usage: %s [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]")) % (below ? "sweep_below" : "sweep_all"); + if (below) + { + PRINT_USAGE(USAGE_SWEEP_BELOW); + } + else + { + PRINT_USAGE(USAGE_SWEEP_ALL); + } }; if (args_.size() == 0) { @@ -5791,18 +5971,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st r = add_extra_nonce_to_tx_extra(extra, extra_nonce); payment_id_seen = true; } - else - { - crypto::hash8 payment_id8; - r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8); - if(r) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - payment_id_seen = true; - } - } if(!r && local_args.size() == 3) { @@ -6055,10 +6223,6 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) { set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); } - else if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8)) - { - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - } else { fail_msg_writer() << tr("failed to parse Payment ID"); @@ -6077,7 +6241,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) if (local_args.size() != 2) { - fail_msg_writer() << tr("usage: sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]"); + PRINT_USAGE(USAGE_SWEEP_SINGLE); return true; } @@ -6241,7 +6405,7 @@ bool simple_wallet::donate(const std::vector<std::string> &args_) std::vector<std::string> local_args = args_; if(local_args.empty() || local_args.size() > 5) { - fail_msg_writer() << tr("usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"); + PRINT_USAGE(USAGE_DONATE); return true; } std::string amount_str; @@ -6256,8 +6420,18 @@ bool simple_wallet::donate(const std::vector<std::string> &args_) local_args.pop_back(); } // get amount and pop - amount_str = local_args.back(); - local_args.pop_back(); + uint64_t amount; + bool ok = cryptonote::parse_amount(amount, local_args.back()); + if (ok && amount != 0) + { + amount_str = local_args.back(); + local_args.pop_back(); + } + else + { + fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max()); + return true; + } // push back address, amount, payment id std::string address_str; if (m_wallet->nettype() != cryptonote::MAINNET) @@ -6454,7 +6628,7 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_) } if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw")) { - fail_msg_writer() << tr("usage: sign_transfer [export_raw]"); + PRINT_USAGE(USAGE_SIGN_TRANSFER); return true; } @@ -6544,7 +6718,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) return true; } if(local_args.size() != 1) { - fail_msg_writer() << tr("usage: get_tx_key <txid>"); + PRINT_USAGE(USAGE_GET_TX_KEY); return true; } @@ -6580,7 +6754,7 @@ bool simple_wallet::set_tx_key(const std::vector<std::string> &args_) std::vector<std::string> local_args = args_; if(local_args.size() != 2) { - fail_msg_writer() << tr("usage: set_tx_key <txid> <tx_key>"); + PRINT_USAGE(USAGE_SET_TX_KEY); return true; } @@ -6642,7 +6816,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) } if (args.size() != 2 && args.size() != 3) { - fail_msg_writer() << tr("usage: get_tx_proof <txid> <address> [<message>]"); + PRINT_USAGE(USAGE_GET_TX_PROOF); return true; } @@ -6683,7 +6857,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) std::vector<std::string> local_args = args_; if(local_args.size() != 3) { - fail_msg_writer() << tr("usage: check_tx_key <txid> <txkey> <address>"); + PRINT_USAGE(USAGE_CHECK_TX_KEY); return true; } @@ -6769,7 +6943,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) bool simple_wallet::check_tx_proof(const std::vector<std::string> &args) { if(args.size() != 3 && args.size() != 4) { - fail_msg_writer() << tr("usage: check_tx_proof <txid> <address> <signature_file> [<message>]"); + PRINT_USAGE(USAGE_CHECK_TX_PROOF); return true; } @@ -6852,7 +7026,7 @@ bool simple_wallet::get_spend_proof(const std::vector<std::string> &args) return true; } if(args.size() != 1 && args.size() != 2) { - fail_msg_writer() << tr("usage: get_spend_proof <txid> [<message>]"); + PRINT_USAGE(USAGE_GET_SPEND_PROOF); return true; } @@ -6893,7 +7067,7 @@ bool simple_wallet::get_spend_proof(const std::vector<std::string> &args) bool simple_wallet::check_spend_proof(const std::vector<std::string> &args) { if(args.size() != 2 && args.size() != 3) { - fail_msg_writer() << tr("usage: check_spend_proof <txid> <signature_file> [<message>]"); + PRINT_USAGE(USAGE_CHECK_SPEND_PROOF); return true; } @@ -6936,7 +7110,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) return true; } if(args.size() != 1 && args.size() != 2) { - fail_msg_writer() << tr("usage: get_reserve_proof (all|<amount>) [<message>]"); + PRINT_USAGE(USAGE_GET_RESERVE_PROOF); return true; } @@ -6982,7 +7156,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args) { if(args.size() != 2 && args.size() != 3) { - fail_msg_writer() << tr("usage: check_reserve_proof <address> <signature_file> [<message>]"); + PRINT_USAGE(USAGE_CHECK_RESERVE_PROOF); return true; } @@ -7137,6 +7311,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec const std::string type = pd.m_coinbase ? tr("block") : tr("in"); const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); transfers.push_back({ + type, pd.m_block_height, pd.m_timestamp, type, @@ -7169,6 +7344,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); transfers.push_back({ + "out", pd.m_block_height, pd.m_timestamp, "out", @@ -7206,6 +7382,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); transfers.push_back({ "pool", + "pool", pd.m_timestamp, "in", false, @@ -7246,6 +7423,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec if ((failed && is_failed) || (!is_failed && pending)) { transfers.push_back({ (is_failed ? "failed" : "pending"), + (is_failed ? "failed" : "pending"), pd.m_timestamp, "out", false, @@ -7293,7 +7471,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) for (const auto& transfer : all_transfers) { - const auto color = transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_white; + const auto color = transfer.type == "failed" ? console_color_red : transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_default; std::string destinations = "-"; if (!transfer.outputs.empty()) @@ -7419,7 +7597,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_) { if(args_.size() > 3) { - fail_msg_writer() << tr("usage: unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"); + PRINT_USAGE(USAGE_UNSPENT_OUTPUTS); return true; } auto local_args = args_; @@ -7560,7 +7738,7 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_) { if (args_[0] != "hard") { - fail_msg_writer() << tr("usage: rescan_bc [hard]"); + PRINT_USAGE(USAGE_RESCAN_BC); return true; } hard = true; @@ -7805,14 +7983,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector } else { - fail_msg_writer() << tr("usage:\n" - " account\n" - " account new <label text with white spaces allowed>\n" - " account switch <index>\n" - " account label <index> <label text with white spaces allowed>\n" - " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" - " account untag <account_index_1> [<account_index_2> ...]\n" - " account tag_description <tag_name> <description>"); + PRINT_USAGE(USAGE_ACCOUNT); } return true; } @@ -7967,7 +8138,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: } else { - fail_msg_writer() << tr("usage: address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> ]"); + PRINT_USAGE(USAGE_ADDRESS); } return true; @@ -7978,7 +8149,7 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg crypto::hash8 payment_id; if (args.size() > 1) { - fail_msg_writer() << tr("usage: integrated_address [payment ID]"); + PRINT_USAGE(USAGE_INTEGRATED_ADDRESS); return true; } if (args.size() == 0) @@ -8030,7 +8201,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v } else if (args.size() == 1 || (args[0] != "add" && args[0] != "delete")) { - fail_msg_writer() << tr("usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)]"); + PRINT_USAGE(USAGE_ADDRESS_BOOK); return true; } else if (args[0] == "add") @@ -8055,8 +8226,8 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v } else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id)) { - memcpy(payment_id.data, info.payment_id.data, 8); - description_start += 2; + fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only"); + return true; } else { @@ -8105,7 +8276,7 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args) { if (args.size() == 0) { - fail_msg_writer() << tr("usage: set_tx_note [txid] free text note"); + PRINT_USAGE(USAGE_SET_TX_NOTE); return true; } @@ -8133,7 +8304,7 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args) { if (args.size() != 1) { - fail_msg_writer() << tr("usage: get_tx_note [txid]"); + PRINT_USAGE(USAGE_GET_TX_NOTE); return true; } @@ -8174,7 +8345,7 @@ bool simple_wallet::get_description(const std::vector<std::string> &args) { if (args.size() != 0) { - fail_msg_writer() << tr("usage: get_description"); + PRINT_USAGE(USAGE_GET_DESCRIPTION); return true; } @@ -8247,7 +8418,7 @@ bool simple_wallet::sign(const std::vector<std::string> &args) } if (args.size() != 1) { - fail_msg_writer() << tr("usage: sign <filename>"); + PRINT_USAGE(USAGE_SIGN); return true; } if (m_wallet->watch_only()) @@ -8281,7 +8452,7 @@ bool simple_wallet::verify(const std::vector<std::string> &args) { if (args.size() != 3) { - fail_msg_writer() << tr("usage: verify <filename> <address> <signature>"); + PRINT_USAGE(USAGE_VERIFY); return true; } std::string filename = args[0]; @@ -8324,7 +8495,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) } if (args.size() != 1) { - fail_msg_writer() << tr("usage: export_key_images <filename>"); + PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES); return true; } if (m_wallet->watch_only()) @@ -8373,7 +8544,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args) if (args.size() != 1) { - fail_msg_writer() << tr("usage: import_key_images <filename>"); + PRINT_USAGE(USAGE_IMPORT_KEY_IMAGES); return true; } std::string filename = args[0]; @@ -8480,7 +8651,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args) } if (args.size() != 1) { - fail_msg_writer() << tr("usage: export_outputs <filename>"); + PRINT_USAGE(USAGE_EXPORT_OUTPUTS); return true; } @@ -8520,7 +8691,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args) } if (args.size() != 1) { - fail_msg_writer() << tr("usage: import_outputs <filename>"); + PRINT_USAGE(USAGE_IMPORT_OUTPUTS); return true; } std::string filename = args[0]; @@ -8552,7 +8723,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) { if (args.size() != 1) { - fail_msg_writer() << tr("usage: show_transfer <txid>"); + PRINT_USAGE(USAGE_SHOW_TRANSFER); return true; } @@ -8772,6 +8943,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_electrum_seed ); command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version); command_line::add_arg(desc_params, arg_restore_height); + command_line::add_arg(desc_params, arg_restore_date); command_line::add_arg(desc_params, arg_do_not_relay); command_line::add_arg(desc_params, arg_create_address_file); command_line::add_arg(desc_params, arg_subaddress_lookahead); @@ -8784,7 +8956,7 @@ int main(int argc, char* argv[]) bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( argc, argv, - "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", + "monero-wallet-cli [--wallet-file=<filename>|--generate-new-wallet=<filename>] [<COMMAND>]", sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), desc_params, positional_options, @@ -9843,6 +10015,7 @@ bool simple_wallet::mms(const std::vector<std::string> &args) catch (const std::exception &e) { fail_msg_writer() << tr("Error in MMS command: ") << e.what(); + PRINT_USAGE(USAGE_MMS); return true; } return true; diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 5010e3adc..f364df2ff 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -142,6 +142,7 @@ namespace cryptonote bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>()); bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>()); bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>()); bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); @@ -251,9 +252,11 @@ namespace cryptonote bool print_seed(bool encrypted); void key_images_sync_intern(); void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); + std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; struct transfer_view { + std::string type; boost::variant<uint64_t, std::string> block; uint64_t timestamp; std::string direction; @@ -367,6 +370,7 @@ namespace cryptonote std::string m_mnemonic_language; std::string m_import_path; std::string m_subaddress_lookahead; + std::string m_restore_date; // optional - converted to m_restore_height epee::wipeable_string m_electrum_seed; // electrum-style seed parameter diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 8425e4a03..2991f75c5 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -139,6 +139,12 @@ if (BUILD_GUI_DEPS) endif() install(TARGETS wallet_merged ARCHIVE DESTINATION ${lib_folder}) + + install(FILES ${TREZOR_DEP_LIBS} + DESTINATION ${lib_folder}) + file(WRITE "trezor_link_flags.txt" ${TREZOR_DEP_LINKER}) + install(FILES "trezor_link_flags.txt" + DESTINATION ${lib_folder}) endif() add_subdirectory(api) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7cd3b65bb..785d2bf36 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1149,7 +1149,7 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre } catch (const std::exception &e) { - LOG_ERROR("Error getting subaddress label: ") << e.what(); + LOG_ERROR("Error getting subaddress label: " << e.what()); setStatusError(string(tr("Failed to get subaddress label: ")) + e.what()); return ""; } @@ -1162,7 +1162,7 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex } catch (const std::exception &e) { - LOG_ERROR("Error setting subaddress label: ") << e.what(); + LOG_ERROR("Error setting subaddress label: " << e.what()); setStatusError(string(tr("Failed to set subaddress label: ")) + e.what()); } } @@ -1179,7 +1179,7 @@ string WalletImpl::getMultisigInfo() const { clearStatus(); return m_wallet->get_multisig_info(); } catch (const exception& e) { - LOG_ERROR("Error on generating multisig info: ") << e.what(); + LOG_ERROR("Error on generating multisig info: " << e.what()); setStatusError(string(tr("Failed to get multisig info: ")) + e.what()); } @@ -1196,7 +1196,7 @@ string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold) return m_wallet->make_multisig(epee::wipeable_string(m_password), info, threshold); } catch (const exception& e) { - LOG_ERROR("Error on making multisig wallet: ") << e.what(); + LOG_ERROR("Error on making multisig wallet: " << e.what()); setStatusError(string(tr("Failed to make multisig: ")) + e.what()); } @@ -1210,7 +1210,7 @@ std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &inf return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info); } catch (const exception& e) { - LOG_ERROR("Error on exchanging multisig keys: ") << e.what(); + LOG_ERROR("Error on exchanging multisig keys: " << e.what()); setStatusError(string(tr("Failed to make multisig: ")) + e.what()); } @@ -1228,7 +1228,7 @@ bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) { setStatusError(tr("Failed to finalize multisig wallet creation")); } catch (const exception& e) { - LOG_ERROR("Error on finalizing multisig wallet creation: ") << e.what(); + LOG_ERROR("Error on finalizing multisig wallet creation: " << e.what()); setStatusError(string(tr("Failed to finalize multisig wallet creation: ")) + e.what()); } @@ -1244,7 +1244,7 @@ bool WalletImpl::exportMultisigImages(string& images) { images = epee::string_tools::buff_to_hex_nodelimer(blob); return true; } catch (const exception& e) { - LOG_ERROR("Error on exporting multisig images: ") << e.what(); + LOG_ERROR("Error on exporting multisig images: " << e.what()); setStatusError(string(tr("Failed to export multisig images: ")) + e.what()); } @@ -1272,7 +1272,7 @@ size_t WalletImpl::importMultisigImages(const vector<string>& images) { return m_wallet->import_multisig(blobs); } catch (const exception& e) { - LOG_ERROR("Error on importing multisig images: ") << e.what(); + LOG_ERROR("Error on importing multisig images: " << e.what()); setStatusError(string(tr("Failed to import multisig images: ")) + e.what()); } @@ -1286,7 +1286,7 @@ bool WalletImpl::hasMultisigPartialKeyImages() const { return m_wallet->has_multisig_partial_key_images(); } catch (const exception& e) { - LOG_ERROR("Error on checking for partial multisig key images: ") << e.what(); + LOG_ERROR("Error on checking for partial multisig key images: " << e.what()); setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what()); } @@ -1314,7 +1314,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat return ptx; } catch (exception& e) { - LOG_ERROR("Error on restoring multisig transaction: ") << e.what(); + LOG_ERROR("Error on restoring multisig transaction: " << e.what()); setStatusError(string(tr("Failed to restore multisig transaction: ")) + e.what()); } diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp index ce6f4f52b..7381005c1 100644 --- a/src/wallet/message_store.cpp +++ b/src/wallet/message_store.cpp @@ -125,7 +125,7 @@ void message_store::set_signer(const multisig_wallet_state &state, const boost::optional<std::string> &transport_address, const boost::optional<cryptonote::account_public_address> monero_address) { - THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + index); + THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index)); authorized_signer &m = m_signers[index]; if (label) { @@ -146,7 +146,7 @@ void message_store::set_signer(const multisig_wallet_state &state, const authorized_signer &message_store::get_signer(uint32_t index) const { - THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + index); + THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index)); return m_signers[index]; } @@ -201,7 +201,7 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con THROW_WALLET_EXCEPTION_IF(true, tools::error::wallet_internal_error, "Invalid structure of signer config"); } uint32_t num_signers = (uint32_t)signers.size(); - THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + num_signers); + THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers)); } void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) @@ -424,7 +424,7 @@ void message_store::setup_signer_for_auto_config(uint32_t index, const std::stri // auto-config parameters. In the wallet of somebody using the token to send auto-config // data the auto-config parameters are stored in the "me" signer and taken from there // to send that data. - THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + index); + THROW_WALLET_EXCEPTION_IF(index >= m_num_authorized_signers, tools::error::wallet_internal_error, "Invalid signer index " + std::to_string(index)); authorized_signer &m = m_signers[index]; m.auto_config_token = token; crypto::hash_to_scalar(token.data(), token.size(), m.auto_config_secret_key); @@ -506,7 +506,7 @@ void message_store::process_wallet_created_data(const multisig_wallet_state &sta break; default: - THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Illegal message type " + (uint32_t)type); + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Illegal message type " + std::to_string((uint32_t)type)); break; } } @@ -573,7 +573,7 @@ size_t message_store::get_message_index_by_id(uint32_t id) const { size_t index; bool found = get_message_index_by_id(id, index); - THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + id); + THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + std::to_string(id)); return index; } @@ -601,7 +601,7 @@ message message_store::get_message_by_id(uint32_t id) const { message m; bool found = get_message_by_id(id, m); - THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + id); + THROW_WALLET_EXCEPTION_IF(!found, tools::error::wallet_internal_error, "Invalid message id " + std::to_string(id)); return m; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 0d2faca54..8b2735b01 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -192,6 +192,37 @@ namespace return false; } + + void add_reason(std::string &reasons, const char *reason) + { + if (!reasons.empty()) + reasons += ", "; + reasons += reason; + } + + std::string get_text_reason(const cryptonote::COMMAND_RPC_SEND_RAW_TX::response &res) + { + std::string reason; + if (res.low_mixin) + add_reason(reason, "bad ring size"); + if (res.double_spend) + add_reason(reason, "double spend"); + if (res.invalid_input) + add_reason(reason, "invalid input"); + if (res.invalid_output) + add_reason(reason, "invalid output"); + if (res.too_big) + add_reason(reason, "too big"); + if (res.overspend) + add_reason(reason, "overspend"); + if (res.fee_too_low) + add_reason(reason, "fee too low"); + if (res.not_rct) + add_reason(reason, "tx is not ringct"); + if (res.not_relayed) + add_reason(reason, "tx was not relayed"); + return reason; + } } namespace @@ -583,19 +614,6 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f return {nullptr, tools::password_container{}}; } -static void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) -{ - // no error - if (!status) - return; - - // empty string -> not connection - THROW_WALLET_EXCEPTION_IF(status->empty(), tools::error::no_connection_to_daemon, method); - - 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, *status); -} - std::string strjoin(const std::vector<size_t> &V, const char *sep) { std::stringstream ss; @@ -894,6 +912,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_key_reuse_mitigation2(true), m_segregation_height(0), m_ignore_fractional_outputs(true), + m_track_uses(false), m_is_initialized(false), m_kdf_rounds(kdf_rounds), is_old_file_format(false), @@ -901,6 +920,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_multisig(false), m_multisig_threshold(0), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), + m_account_public_address{crypto::null_pkey, crypto::null_pkey}, m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), m_light_wallet(false), @@ -917,6 +937,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), m_unattended(unattended), + m_devices_registered(false), m_device_last_key_image_sync(0) { } @@ -1446,8 +1467,9 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has } } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data) +void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { + PERF_TIMER(process_new_transaction); // In this function, tx (probably) only contains the base information // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) @@ -1684,6 +1706,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (!m_multisig && !m_watch_only) m_key_images[td.m_key_image] = m_transfers.size()-1; m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1; + if (output_tracker_cache) + (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = m_transfers.size() - 1; if (m_multisig) { THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info, @@ -1749,6 +1773,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_mask = rct::identity(); td.m_rct = false; } + if (output_tracker_cache) + (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = kit->second; if (m_multisig) { THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info, @@ -1780,11 +1806,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { if(in.type() != typeid(cryptonote::txin_to_key)) continue; - auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); + const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in); + auto it = m_key_images.find(in_to_key.k_image); if(it != m_key_images.end()) { transfer_details& td = m_transfers[it->second]; - uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount; + uint64_t amount = in_to_key.amount; if (amount > 0) { if(amount != td.amount()) @@ -1815,6 +1842,34 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index); } } + + if (!pool && m_track_uses) + { + PERF_TIMER(track_uses); + const uint64_t amount = in_to_key.amount; + std::vector<uint64_t> offsets = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets); + if (output_tracker_cache) + { + for (uint64_t offset: offsets) + { + const std::map<std::pair<uint64_t, uint64_t>, size_t>::const_iterator i = output_tracker_cache->find(std::make_pair(amount, offset)); + if (i != output_tracker_cache->end()) + { + size_t idx = i->second; + THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); + m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); + } + } + } + else for (transfer_details &td: m_transfers) + { + if (amount != in_to_key.amount) + continue; + for (uint64_t offset: offsets) + if (offset == td.m_global_output_index) + td.m_uses.push_back(std::make_pair(height, txid)); + } + } } uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee; @@ -1997,7 +2052,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans add_rings(tx); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset) +void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, "block transactions=" + std::to_string(bche.txs.size()) + @@ -2010,7 +2065,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry { TIME_MEASURE_START(miner_tx_handle_time); if (m_refresh_type != RefreshNoCoinbase) - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset]); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache); ++tx_cache_data_offset; TIME_MEASURE_FINISH(miner_tx_handle_time); @@ -2019,7 +2074,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx) { - process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]); + process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache); } TIME_MEASURE_FINISH(txs_handle_time); m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); @@ -2089,7 +2144,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); + THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(res.status)); THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" + boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon"); @@ -2111,13 +2166,13 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, res.status); + THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, get_rpc_status(res.status)); blocks_start_height = res.start_height; hashes = std::move(res.m_block_ids); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added) +void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { size_t current_index = start_height; blocks_added = 0; @@ -2156,7 +2211,6 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry const cryptonote::account_keys &keys = m_account.get_keys(); auto gender = [&](wallet2::is_out_data &iod) { - boost::unique_lock<hw::device> hwdev_lock(hwdev); if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation)) { MWARNING("Failed to generate key derivation from tx pubkey, skipping"); @@ -2165,12 +2219,16 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry } }; - for (auto &slot: tx_cache_data) + for (size_t i = 0; i < tx_cache_data.size(); ++i) { - for (auto &iod: slot.primary) - tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true); - for (auto &iod: slot.additional) - tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }, true); + tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() { + auto &slot = tx_cache_data[i]; + boost::unique_lock<hw::device> hwdev_lock(hwdev); + for (auto &iod: slot.primary) + gender(iod); + for (auto &iod: slot.additional) + gender(iod); + }, true); } waiter.wait(&tpool); @@ -2224,7 +2282,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry if(current_index >= m_blockchain.size()) { - process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset); + process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -2236,7 +2294,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry string_tools::pod_to_hex(m_blockchain[current_index])); detach_blockchain(current_index); - process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset); + process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); } else { @@ -2270,11 +2328,10 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch"); // prepend the last 3 blocks, should be enough to guard against a block or two's reorg - std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin(); - for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n) + auto s = std::next(prev_parsed_blocks.rbegin(), std::min((size_t)3, prev_parsed_blocks.size())).base(); + for (; s != prev_parsed_blocks.end(); ++s) { - short_chain_history.push_front(i->hash); - ++i; + short_chain_history.push_front(s->hash); } // pull the new blocks @@ -2573,7 +2630,7 @@ void wallet2::update_pool_state(bool refreshed) } else { - LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); + LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(res.status)); } } MTRACE("update_pool_state end"); @@ -2669,6 +2726,17 @@ bool wallet2::delete_address_book_row(std::size_t row_id) { } //---------------------------------------------------------------------------------------------------- +std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create_output_tracker_cache() const +{ + std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> cache{new std::map<std::pair<uint64_t, uint64_t>, size_t>()}; + for (size_t i = 0; i < m_transfers.size(); ++i) + { + const transfer_details &td = m_transfers[i]; + (*cache)[std::make_pair(td.is_rct() ? 0 : td.amount(), td.m_global_output_index)] = i; + } + return cache; +} +//---------------------------------------------------------------------------------------------------- void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { if(m_light_wallet) { @@ -2715,6 +2783,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo std::vector<cryptonote::block_complete_entry> blocks; 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; // pull the first set of blocks get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); @@ -2749,13 +2818,16 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo bool first = true; while(m_run.load(std::memory_order_relaxed)) { + uint64_t next_blocks_start_height; + std::vector<cryptonote::block_complete_entry> next_blocks; + std::vector<parsed_block> next_parsed_blocks; + bool error; try { // pull the next set of blocks while we're processing the current one - uint64_t next_blocks_start_height; - std::vector<cryptonote::block_complete_entry> next_blocks; - std::vector<parsed_block> next_parsed_blocks; - bool error = false; + error = false; + next_blocks.clear(); + next_parsed_blocks.clear(); added_blocks = 0; if (!first && blocks.empty()) { @@ -2768,7 +2840,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo { try { - process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks, output_tracker_cache.get()); } catch (const tools::error::out_of_hashchain_bounds_error&) { @@ -2793,6 +2865,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo start_height = stop_height; throw std::runtime_error(""); // loop again } + catch (const std::exception &e) + { + MERROR("Error parsing blocks: " << e.what()); + error = true; + } blocks_fetched += added_blocks; } waiter.wait(&tpool); @@ -2811,6 +2888,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo throw std::runtime_error("proxy exception in refresh thread"); } + // if we've got at least 10 blocks to refresh, assume we're starting + // a long refresh, and setup a tracking output cache if we need to + if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10) + output_tracker_cache = create_output_tracker_cache(); + // switch to the new blocks from the daemon blocks_start_height = next_blocks_start_height; blocks = std::move(next_blocks); @@ -3183,6 +3265,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetInt(m_ignore_fractional_outputs ? 1 : 0); json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator()); + value2.SetInt(m_track_uses ? 1 : 0); + json.AddMember("track_uses", value2, json.GetAllocator()); + value2.SetUint(m_subaddress_lookahead_major); json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator()); @@ -3329,6 +3414,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_key_reuse_mitigation2 = true; m_segregation_height = 0; m_ignore_fractional_outputs = true; + m_track_uses = false; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_original_keys_available = false; @@ -3481,6 +3567,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_segregation_height = field_segregation_height; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true); m_ignore_fractional_outputs = field_ignore_fractional_outputs; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false); + m_track_uses = field_track_uses; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR); m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); @@ -4423,6 +4511,23 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers) { + bool ready; + uint32_t threshold, total; + if (!multisig(&ready, &threshold, &total)) + { + MERROR("This is not a multisig wallet"); + return false; + } + if (ready) + { + MERROR("This multisig wallet is already finalized"); + return false; + } + if (threshold + 1 != total) + { + MERROR("finalize_multisig should only be used for N-1/N wallets, use exchange_multisig_keys instead"); + return false; + } exchange_multisig_keys(password, pkeys, signers); return true; } @@ -5222,7 +5327,7 @@ void wallet2::rescan_spent() m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, get_rpc_status(daemon_resp.status)); THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs)); @@ -5267,6 +5372,10 @@ void wallet2::rescan_blockchain(bool hard, bool refresh) 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(); @@ -5551,7 +5660,7 @@ void wallet2::commit_tx(pending_tx& ptx) m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); // MyMonero and OpenMonero use different status strings - THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, ores.status, ores.error); + THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error); } else { @@ -5565,7 +5674,7 @@ void wallet2::commit_tx(pending_tx& ptx) m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status, daemon_send_resp.reason); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp)); // sanity checks for (size_t idx: ptx.selected_transfers) { @@ -6456,7 +6565,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority) m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange"); THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); - THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, getbh_res.status); + THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(getbh_res.status)); if (getbh_res.headers.size() != N) { MERROR("Bad blockheaders size"); @@ -6918,7 +7027,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status); + THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status)); } // if we want to segregate fake outs pre or post fork, get distribution @@ -6941,7 +7050,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, resp_t.status); + THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, get_rpc_status(resp_t.status)); // check we got all data for(size_t idx: selected_transfers) @@ -7340,7 +7449,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, get_rpc_status(daemon_resp.status)); THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error, "daemon returned wrong response for get_outs.bin, wrong amounts count = " + std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size())); @@ -10488,7 +10597,10 @@ uint64_t wallet2::get_daemon_blockchain_height(string &err) const boost::optional<std::string> result = m_node_rpc_proxy.get_height(height); if (result) { - err = *result; + if (m_trusted_daemon) + err = *result; + else + err = "daemon error"; return 0; } @@ -10503,7 +10615,10 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) const auto result = m_node_rpc_proxy.get_target_height(target_height); if (result && *result != CORE_RPC_STATUS_OK) { - err= *result; + if (m_trusted_daemon) + err = *result; + else + err = "daemon error"; return 0; } return target_height; @@ -10771,23 +10886,23 @@ bool wallet2::export_key_images(const std::string &filename) const } //---------------------------------------------------------------------------------------------------- -std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images() const +std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const { PERF_TIMER(export_key_images_raw); std::vector<std::pair<crypto::key_image, crypto::signature>> ski; size_t offset = 0; - while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_requested) - ++offset; + if (!all) + { + while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_requested) + ++offset; + } ski.reserve(m_transfers.size() - offset); for (size_t n = offset; n < m_transfers.size(); ++n) { const transfer_details &td = m_transfers[n]; - crypto::hash hash; - crypto::cn_fast_hash(&td.m_key_image, sizeof(td.m_key_image), hash); - // get ephemeral public key const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index]; THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error, @@ -11944,7 +12059,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui else if (res.status == CORE_RPC_STATUS_BUSY) oss << "daemon is busy"; else - oss << res.status; + oss << get_rpc_status(res.status); throw std::runtime_error(oss.str()); } cryptonote::block blk_min, blk_mid, blk_max; @@ -12163,4 +12278,27 @@ void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & pass if (0 != m_callback) m_callback->on_passphrase_request(on_device, passphrase); } +//---------------------------------------------------------------------------------------------------- +std::string wallet2::get_rpc_status(const std::string &s) const +{ + if (m_trusted_daemon) + return s; + return "<error>"; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const +{ + // no error + if (!status) + return; + + MERROR("RPC error: " << method << ": status " << *status); + + // empty string -> not connection + THROW_WALLET_EXCEPTION_IF(status->empty(), tools::error::no_connection_to_daemon, method); + + 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"); +} + } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index ace01a235..364e31483 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -273,6 +273,7 @@ namespace tools bool m_key_image_partial; std::vector<rct::key> m_multisig_k; std::vector<multisig_info> m_multisig_info; // one per other participant + std::vector<std::pair<uint64_t, crypto::hash>> m_uses; bool is_rct() const { return m_rct; } uint64_t amount() const { return m_amount; } @@ -297,6 +298,7 @@ namespace tools FIELD(m_key_image_partial) FIELD(m_multisig_k) FIELD(m_multisig_info) + FIELD(m_uses) END_SERIALIZE() }; @@ -984,6 +986,8 @@ namespace tools void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; } bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; } void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } + bool track_uses() const { return m_track_uses; } + void track_uses(bool value) { m_track_uses = value; } const std::string & device_name() const { return m_device_name; } void device_name(const std::string & device_name) { m_device_name = device_name; } const std::string & device_derivation_path() const { return m_device_derivation_path; } @@ -1109,7 +1113,7 @@ namespace tools std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const; void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc); bool export_key_images(const std::string &filename) const; - std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images() const; + 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); @@ -1249,8 +1253,8 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); - void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data); - void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); + void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; bool clear(); @@ -1258,7 +1262,7 @@ namespace tools 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); void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error); - void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added); + void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); @@ -1312,6 +1316,7 @@ namespace tools std::unordered_set<crypto::public_key> &pkeys) const; void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; + std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> create_output_tracker_cache() const; void setup_new_blockchain(); void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); @@ -1321,6 +1326,9 @@ namespace tools void on_pin_request(epee::wipeable_string & pin); void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + 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; + cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; std::string m_daemon_address; @@ -1395,6 +1403,7 @@ namespace tools bool m_key_reuse_mitigation2; uint64_t m_segregation_height; bool m_ignore_fractional_outputs; + bool m_track_uses; bool m_is_initialized; NodeRPCProxy m_node_rpc_proxy; std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; @@ -1444,7 +1453,7 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 27) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) @@ -1593,6 +1602,9 @@ namespace boost return; } a & x.m_key_image_requested; + if (ver < 11) + return; + a & x.m_uses; } template <class Archive> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index d7dc2914e..dd65ee7fe 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2495,7 +2495,7 @@ namespace tools if (!m_wallet) return not_open(er); try { - std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(); + std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all); res.offset = ski.first; res.signed_key_images.resize(ski.second.size()); for (size_t n = 0; n < ski.second.size(); ++n) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index afb8c6e91..f0c1a4e9d 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 6 +#define WALLET_RPC_VERSION_MINOR 7 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1565,7 +1565,10 @@ namespace wallet_rpc { struct request { + bool all; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(all, false); END_KV_SERIALIZE_MAP() }; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 84254cfdb..455bb1efa 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -229,10 +229,10 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2_many_inputs); GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234); GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234_many_inputs); - GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_no_signers); - GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_no_signers); - GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_no_signers); - GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_23_no_threshold); + GENERATE_AND_PLAY(gen_multisig_tx_invalid_24_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_invalid_25_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_invalid_48_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_invalid_48_1_23_no_threshold); GENERATE_AND_PLAY(gen_bp_tx_valid_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index fe5feb942..46ba997be 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -644,28 +644,28 @@ bool gen_multisig_tx_invalid_45_5_23_no_threshold::generate(std::vector<test_eve return generate_with(events, 2, mixin, amount_paid, false, 4, 5, 5, {2, 3}, NULL, NULL); } -bool gen_multisig_tx_valid_24_1_no_signers::generate(std::vector<test_event_entry>& events) const +bool gen_multisig_tx_invalid_24_1_no_signers::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 4; const uint64_t amount_paid = 10000; return generate_with(events, 2, mixin, amount_paid, false, 2, 4, 1, {}, NULL, NULL); } -bool gen_multisig_tx_valid_25_1_no_signers::generate(std::vector<test_event_entry>& events) const +bool gen_multisig_tx_invalid_25_1_no_signers::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 4; const uint64_t amount_paid = 10000; return generate_with(events, 2, mixin, amount_paid, false, 2, 5, 1, {}, NULL, NULL); } -bool gen_multisig_tx_valid_48_1_no_signers::generate(std::vector<test_event_entry>& events) const +bool gen_multisig_tx_invalid_48_1_no_signers::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 4; const uint64_t amount_paid = 10000; return generate_with(events, 2, mixin, amount_paid, false, 4, 8, 1, {}, NULL, NULL); } -bool gen_multisig_tx_valid_48_1_23_no_threshold::generate(std::vector<test_event_entry>& events) const +bool gen_multisig_tx_invalid_48_1_23_no_threshold::generate(std::vector<test_event_entry>& events) const { const size_t mixin = 4; const uint64_t amount_paid = 10000; diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index 9e4cb3a23..e0d227840 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -234,26 +234,26 @@ struct gen_multisig_tx_invalid_45_5_23_no_threshold: public gen_multisig_tx_vali }; template<> struct get_test_options<gen_multisig_tx_invalid_45_5_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {}; -struct gen_multisig_tx_valid_24_1_no_signers: public gen_multisig_tx_validation_base +struct gen_multisig_tx_invalid_24_1_no_signers: public gen_multisig_tx_validation_base { bool generate(std::vector<test_event_entry>& events) const; }; -template<> struct get_test_options<gen_multisig_tx_valid_24_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; +template<> struct get_test_options<gen_multisig_tx_invalid_24_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; -struct gen_multisig_tx_valid_25_1_no_signers: public gen_multisig_tx_validation_base +struct gen_multisig_tx_invalid_25_1_no_signers: public gen_multisig_tx_validation_base { bool generate(std::vector<test_event_entry>& events) const; }; -template<> struct get_test_options<gen_multisig_tx_valid_25_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; +template<> struct get_test_options<gen_multisig_tx_invalid_25_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; -struct gen_multisig_tx_valid_48_1_no_signers: public gen_multisig_tx_validation_base +struct gen_multisig_tx_invalid_48_1_no_signers: public gen_multisig_tx_validation_base { bool generate(std::vector<test_event_entry>& events) const; }; -template<> struct get_test_options<gen_multisig_tx_valid_48_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; +template<> struct get_test_options<gen_multisig_tx_invalid_48_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {}; -struct gen_multisig_tx_valid_48_1_23_no_threshold: public gen_multisig_tx_validation_base +struct gen_multisig_tx_invalid_48_1_23_no_threshold: public gen_multisig_tx_validation_base { bool generate(std::vector<test_event_entry>& events) const; }; -template<> struct get_test_options<gen_multisig_tx_valid_48_1_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {}; +template<> struct get_test_options<gen_multisig_tx_invalid_48_1_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {}; diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index ffe500d21..7e5b73415 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_io.hpp> #include <boost/uuid/random_generator.hpp> #include <unordered_map> diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index d0c5803f5..573abd1d3 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -65,22 +65,22 @@ namespace { } - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_levin_connection_context& context) + virtual int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, test_levin_connection_context& context) { m_invoke_counter.inc(); boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; - m_last_in_buf = in_buff; + m_last_in_buf = std::string((const char*)in_buff.data(), in_buff.size()); buff_out = m_invoke_out_buf; return m_return_code; } - virtual int notify(int command, const std::string& in_buff, test_levin_connection_context& context) + virtual int notify(int command, const epee::span<const uint8_t> in_buff, test_levin_connection_context& context) { m_notify_counter.inc(); boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; - m_last_in_buf = in_buff; + m_last_in_buf = std::string((const char*)in_buff.data(), in_buff.size()); return m_return_code; } diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 7e92c21b9..6c4f7cb76 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -33,6 +33,7 @@ #include <atomic> #include <boost/asio/io_service.hpp> +#include <boost/uuid/uuid_io.hpp> #include "include_base_utils.h" #include "string_tools.h" @@ -62,7 +63,7 @@ namespace net_load_tests { } - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_connection_context& context) + virtual int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, test_connection_context& context) { //m_invoke_counter.inc(); //std::unique_lock<std::mutex> lock(m_mutex); @@ -73,7 +74,7 @@ namespace net_load_tests return LEVIN_OK; } - virtual int notify(int command, const std::string& in_buff, test_connection_context& context) + virtual int notify(int command, const epee::span<const uint8_t> in_buff, test_connection_context& context) { //m_notify_counter.inc(); //std::unique_lock<std::mutex> lock(m_mutex); diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 10e62c167..9ea71875b 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -56,22 +56,22 @@ namespace { } - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_levin_connection_context& context) + virtual int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, test_levin_connection_context& context) { m_invoke_counter.inc(); boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; - m_last_in_buf = in_buff; + m_last_in_buf = std::string((const char*)in_buff.data(), in_buff.size()); buff_out = m_invoke_out_buf; return m_return_code; } - virtual int notify(int command, const std::string& in_buff, test_levin_connection_context& context) + virtual int notify(int command, const epee::span<const uint8_t> in_buff, test_levin_connection_context& context) { m_notify_counter.inc(); boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; - m_last_in_buf = in_buff; + m_last_in_buf = std::string((const char*)in_buff.data(), in_buff.size()); return m_return_code; } diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index c384ce9a5..75cf2fdd4 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -46,6 +46,7 @@ #include "hex.h" #include "net/net_utils_base.h" #include "net/local_ip.h" +#include "net/buffer.h" #include "p2p/net_peerlist_boost_serialization.h" #include "span.h" #include "string_tools.h" @@ -764,3 +765,71 @@ TEST(NetUtils, PrivateRanges) ASSERT_EQ(is_local("0.0.30.172"), false); ASSERT_EQ(is_local("0.0.30.127"), false); } + +TEST(net_buffer, basic) +{ + epee::net_utils::buffer buf; + + ASSERT_EQ(buf.size(), 0); + EXPECT_THROW(buf.span(1), std::runtime_error); + buf.append("a", 1); + epee::span<const uint8_t> span = buf.span(1); + ASSERT_EQ(span.size(), 1); + ASSERT_EQ(span.data()[0], 'a'); + EXPECT_THROW(buf.span(2), std::runtime_error); + buf.append("bc", 2); + buf.erase(1); + EXPECT_THROW(buf.span(3), std::runtime_error); + span = buf.span(2); + ASSERT_EQ(span.size(), 2); + ASSERT_EQ(span.data()[0], 'b'); + ASSERT_EQ(span.data()[1], 'c'); + buf.erase(1); + EXPECT_THROW(buf.span(2), std::runtime_error); + span = buf.span(1); + ASSERT_EQ(span.size(), 1); + ASSERT_EQ(span.data()[0], 'c'); + EXPECT_THROW(buf.erase(2), std::runtime_error); + buf.erase(1); + EXPECT_EQ(buf.size(), 0); + EXPECT_THROW(buf.span(1), std::runtime_error); +} + +TEST(net_buffer, existing_capacity) +{ + epee::net_utils::buffer buf; + + buf.append("123456789", 9); + buf.erase(9); + buf.append("abc", 3); + buf.append("def", 3); + ASSERT_EQ(buf.size(), 6); + epee::span<const uint8_t> span = buf.span(6); + ASSERT_TRUE(!memcmp(span.data(), "abcdef", 6)); +} + +TEST(net_buffer, reallocate) +{ + epee::net_utils::buffer buf; + + buf.append(std::string(4000, ' ').c_str(), 4000); + buf.append(std::string(8000, '0').c_str(), 8000); + ASSERT_EQ(buf.size(), 12000); + epee::span<const uint8_t> span = buf.span(12000); + ASSERT_TRUE(!memcmp(span.data(), std::string(4000, ' ').c_str(), 4000)); + ASSERT_TRUE(!memcmp(span.data() + 4000, std::string(8000, '0').c_str(), 8000)); +} + +TEST(net_buffer, move) +{ + epee::net_utils::buffer buf; + + buf.append(std::string(400, ' ').c_str(), 400); + buf.erase(399); + buf.append(std::string(4000, '0').c_str(), 4000); + ASSERT_EQ(buf.size(), 4001); + epee::span<const uint8_t> span = buf.span(4001); + ASSERT_TRUE(!memcmp(span.data(), std::string(1, ' ').c_str(), 1)); + ASSERT_TRUE(!memcmp(span.data() + 1, std::string(4000, '0').c_str(), 4000)); +} + diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index 105d21ca8..cd70b7739 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -69,11 +69,22 @@ TEST(notify, works) tools::Notify notify(spec.c_str()); notify.notify("1111111111111111111111111111111111111111111111111111111111111111"); - epee::misc_utils::sleep_no_w(100); - - std::string s; - ASSERT_TRUE(epee::file_io_utils::load_file_to_string(name_template, s)); - ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111"); + bool ok = false; + for (int i = 0; i < 10; ++i) + { + epee::misc_utils::sleep_no_w(100); + std::string s; + if (epee::file_io_utils::load_file_to_string(name_template, s)) + { + if (s == "1111111111111111111111111111111111111111111111111111111111111111") + { + ok = true; + break; + } + std::cout << "got: [" << s << "]" << std::endl; + } + } boost::filesystem::remove(name_template); + ASSERT_TRUE(ok); } diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index c6572c6c2..f638c6251 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -42,7 +42,7 @@ TEST(peer_list, peer_list_general) #define ADD_GRAY_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple; ple.last_seen=last_seen_;ple.adr = addr_; ple.id = id_;plm.append_with_peer_gray(ple);} #define ADD_WHITE_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple;ple.last_seen=last_seen_; ple.adr = addr_; ple.id = id_;plm.append_with_peer_white(ple);} -#define PRINT_HEAD(step) {std::list<nodetool::peerlist_entry> bs_head; bool r = plm.get_peerlist_head(bs_head, 100);std::cout << "step " << step << ": " << bs_head.size() << std::endl;} +#define PRINT_HEAD(step) {std::vector<nodetool::peerlist_entry> bs_head; bool r = plm.get_peerlist_head(bs_head, 100);std::cout << "step " << step << ": " << bs_head.size() << std::endl;} ADD_GRAY_NODE(MAKE_IPV4_ADDRESS(123,43,12,1, 8080), 121241, 34345); ADD_GRAY_NODE(MAKE_IPV4_ADDRESS(123,43,12,2, 8080), 121241, 34345); @@ -58,7 +58,7 @@ TEST(peer_list, peer_list_general) size_t gray_list_size = plm.get_gray_peers_count(); ASSERT_EQ(gray_list_size, 1); - std::list<nodetool::peerlist_entry> bs_head; + std::vector<nodetool::peerlist_entry> bs_head; bool r = plm.get_peerlist_head(bs_head, 100); std::cout << bs_head.size() << std::endl; ASSERT_TRUE(r); @@ -78,7 +78,7 @@ TEST(peer_list, merge_peer_lists) //ADD_NODE_TO_PL("\2", \3, 0x\1, (1353346618 -(\4*60*60*24+\5*60*60+\6*60+\7 )));\n nodetool::peerlist_manager plm; plm.init(false); - std::list<nodetool::peerlist_entry> outer_bs; + std::vector<nodetool::peerlist_entry> outer_bs; #define ADD_NODE_TO_PL(ip_, port_, id_, timestamp_) { nodetool::peerlist_entry ple; epee::string_tools::get_ip_int32_from_string(ple.adr.ip, ip_); ple.last_seen = timestamp_; ple.adr.port = port_; ple.id = id_;outer_bs.push_back(ple);} diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h index 5d9ba5833..b8fef8ed3 100644 --- a/tests/unit_tests/testdb.h +++ b/tests/unit_tests/testdb.h @@ -89,14 +89,14 @@ public: virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } virtual uint64_t get_indexing_base() const { return 0; } - virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return cryptonote::output_data_t(); } + virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const { return cryptonote::output_data_t(); } virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {} - virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) {} + virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const {} virtual bool can_thread_bulk_indices() const { return false; } virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); } - virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector<uint64_t>(); } + virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const { return std::vector<std::vector<uint64_t>>(); } virtual bool has_key_image(const crypto::key_image& img) const { return false; } virtual void remove_block() { } virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} diff --git a/utils/gpg_keys/erciccione.asc b/utils/gpg_keys/erciccione.asc new file mode 100644 index 000000000..109941d55 --- /dev/null +++ b/utils/gpg_keys/erciccione.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFohQ30BCADktKHeTg48Dm4oU9JPrfHXzY3sZtTQsqsQGmYiFT8nQjRnSzic +dZe4sh2W4UYQSLFw7pEb7a51ZYkz5+gFKcj3NmIaEpNra7+SEtszoS3IALHUyGGQ ++nFDxUHwTV5OeIUXPHs2AnCVZa4yEWmavXFiPL35+dcFkwyUA5psd97dFIbtkPQ4 +nT2RCc2emy6NHVE5L7pzlBe9CeFkFyovs847d3WDa/96TT4JMRm34qvUrSvOkqti +xrui8hGwNedAw64ObiJhQLa3rvQ8bCtTLNHhpdc+zhBi3L5nIdwquMd5Bd6zHfmW +HHyXEoRTZYMRabNBSR9FrsjGBiHG9wuEK2B7ABEBAAG0NWVyY2ljY2lvbmVAcHJv +dG9ubWFpbC5jb20gPGVyY2ljY2lvbmVAcHJvdG9ubWFpbC5jb20+iQE1BBABCAAp +BQJaIUN9BgsJBwgDAgkQdir4xgjlbN8EFQgKAgMWAgECGQECGwMCHgEAAFc+CAC2 +7BDCYPWfXCKtaqP/jzay46nai66hSyuIJ2sd4RSbQlVNCwFUQ2NtUwDxtzdzB3bk +AfsqrkdSS7V82bfZPOoYapWikjA3LGd2GHvClKNmIgIg3CvrAByD9okhLYOmxChU +5/FzMgSgDdbez4Z+XXzUothirHcHR7J+PtzqMZWusuyNqfXlWgPNPbAHG/hm5RLw +s6uGOLDqhJDiqi0HjC/p9ponPAJ3g0jfECnXFp7R32EgbBHExT/g8e3L4J3Q8y/E +WrZiQsIc89srANw5YIcjlr/i3PY8tyrelpLDVBj05802WYamYx10Zd2epM4NglBs ++YjiWpSM9fTi9QKFCHyAuQENBFohQ30BCADe4FJcus4e3FzsagKdAM/iFeZ4BE2l +gVgSg8CVWIYlTAmeK+SBPfsxvwOWxmx/KkirWn3hOSA2U6mp9+g6hr9rLqWNwatM +OsitrJlWG2aPcuAmlzLFvtH0Vbilh0m4B98g2XUa94HtrlGdPAoYPquS1HsfL284 +CHpQXXOypOS+/spTK/OlpdUaPaVyqVfE3kzXYqt24RbDcsOvUzSzVRCA8kBp6azm +1qoqDboP112Ax7OkV0FGc/kd5PNMPTOnVR+4wVXWhl6gAI1I2JFvo3EJlxtddyTB +Rkx06jbYRyPj9KCEC4CBx9qsNlo7TUHgObAr+CLX4I5hkEWW0pS98vlpABEBAAGJ +AR8EGAEIABMFAlohQ30JEHYq+MYI5WzfAhsMAAD1twgAxjbETzOUeq/bWwVoGzmh +SMJajZs2c5Pv0SAwi5QZCoRefp2HYfpWcBsfZG84B0EmJb9Wwu3wrXWBf96xMTFM +neU0+L2+RcH8uH7/C2MlVaZjFv5dPPJAtwbY2BQl1bX5DrEwZ8QH7IUXZmWfjmZ3 +ww5llfCAyVCxMn+tnIJnk/7HODY0t1LmusFvZsAVsWIgdndKImXM4CTzirfmDks8 +TxpXfaJzU3B3KTIzMgEvwtTLJfgf07Foy4ITIl+1zS7gLL0fXldtx6fJJMvANsLf +JrRjafh21qfJb3I9TUH8G7C0Ln3LspBGsWZ4f+fjq4LEg7SCOAJSeBwfOtMJeCrU +/Q== +=a70Z +-----END PGP PUBLIC KEY BLOCK----- diff --git a/utils/gpg_keys/moneromooo.asc b/utils/gpg_keys/moneromooo.asc index cce180937..4275b6c0e 100644 --- a/utils/gpg_keys/moneromooo.asc +++ b/utils/gpg_keys/moneromooo.asc @@ -13,18 +13,18 @@ p7gDvxXOGxzq0sqfPTWTBdCj1OPfunHbbeH8ypwBlNpwVG40fJdya+Dqjwu25qX6 Xh5vxLzeJTBmlawa97MCliPvzzJgW9qHRVCa9lLloGVYLiUOS0N+dZ/r/QARAQAB tD5tb25lcm9tb29vLW1vbmVybyA8bW9uZXJvbW9vby1tb25lcm9AdXNlcnMubm9y ZXBseS5naXRodWIuY29tPokCPwQTAQIAKQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMB -Ah4BAheABQJbof8TBQkLMcqRAAoJEGhvB0VNbO/DOrcP/1eG3gkIgq+lXKlv/lta -ZFl8jS5iQUwxTHv8s+O7pbL2mwKt5oM8QmXVMFLlaanENmH0y/DhWRYIYuKTifDN -tXOxENCZhxgjlVxAtnMdD2J8MJANzV2MYGLbGQeFAuZHIL2LMfvUENLYx/jJpe7f -93kXZoQio7rIolnlbM1QoJLxi/7HlOt/VsopJlV2wAmOmlpyWDnOZtUtZgiCGV6a -apFzTs2m3n2GP7+8PG/W01/jVyFqixGW8cWVZORBMhjro2JqrRvw7U+Ypk2o7Em4 -SAnAJyzqpTDh9zf0QWkQXN83YThB3dk1M1FXOyKtZ8G+YVNfs5Ldr7tHNbqKi2v8 -lPaPIDQ7UmxZhy9vraKss0/3AEXaiE2eSvLc4eCcLiS4yVQ2KJpK5TUvz3cP1D5C -USsjxUZvolUELBtNaRVAsxX6OAyA8FjwJ8AKCqyR0NHvrbYhj54N1Js8PWtb+qqk -5sBLKOwEPaWM1uG6SJabrY1xu2VNLOGdJA9bRfyyzfpXksD0lFgwLKki6ReqqoMS -QiVdymhcp55J4tFwsDkzJX7296d1x3r7GAIQMLNc7YizukWfNtvUkSUsAwp+RKUx -TTwkwL1ztzSRpt3hGMJ9SfULTaQD7YAYfz4kitMBNpm90CLFa4CeINu98gE9Ogmc -R+CGQOHLUC3rXubgRCiQg5wfuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L +Ah4BAheABQJcEB4SBQkLn+mRAAoJEGhvB0VNbO/DzSIQALXk6ST5Uoxh5+NLBJgI +GR2NOCFwsU+97VOkZWZnzpk+JdUrq/I9JmWWTegGKu3it5YvM8xY1zD1l+RXlnmY +Bg7oT6N63lL8hBhxGm9Hk2A1VJlEjtgtvjbg5yLSdvjxJL1vrmchWGwFWoHT9uIh +oefemb3TQ1U+ADVsW42jXca650/rqvsUMpmQbDfHxUBIfpbxgTWz8sHuMfhFB3R0 +nzSuZ1E1Pbg5jWYxd6sXpb1VFZ7TaQNVGB+4wMe8AH8/0qziLVTXvSkoer6qxU+r +e9M9/Rgue1FJSzZogUekIqyNLsvAeATSCYzi8NoywkUbAq6AqazJtdcOD+7M2/Vl +pdQEnIKSO/9xrziUQifRsPx4LOjf2d2Hvor/rBJYwoI1cFgR5pmH30Mi7OY0mMmm +2TMUPPz7YiVY+RPPpY3Mg5oPRe6sHVAdFYBmc0PTR4v92iYgfQXzoCd5HOJI9QB3 +SwrKz7cNLQwob2za1AXZ3sUp+yok6QMt8Tk6xIbMAjeR4jVeP8iQHhiVS1zx5z/g +lCAfb0hJnykoANgIZvJR/L+tuZjVStqnXjHgreY3IjlWTaNjZ/X5uMvd6zivqI9a +aEmqw/ZDCXRMAtjUCpUvpNlqAgqqbQsx6MFDnMbd4BuKQONeAFLm/Rp31nIvt1vb ++Iu3vkXBbbjb5yMu/dcpgSRUuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L xSSopLSk7/sdLWTc2ERmjDId7dKmqrL1Kh2kqAtHY3Rq8Y839LGmbJCzI1kJyOHF o9jkEI93sqXcztLjizPVukqClOZNt3NV/nvefH6JSdqWcnC4V1mQr2Ztl0j+51i+ NYVwGjlsOMlBER+LW/s7egRqAQonrcEB5vsSAzd8mOlNKjRAnDCV+C21GDKxzb80 @@ -35,17 +35,17 @@ xRFfhoiiPyjjPRmJ+/iG3KXLzEiMfbyTFzGkX3Z9BJTxemUx8JOSVQXa++t4w39J UwzwBKNItDhtQqJpCaF43fJ4ykLMJi5gRpgqtb+T3CF0abXNII1IfS8a0fSpd48d 6hzoCVqpvWsI1fOY5Ui0BIgubNhkr4OJDCWBT5zhxjCJ3QiUSKyyqjfw1Fpuf/0Y CSA9Q9FSCq9qTppJs5ITHVjhWw2zxrJEG+P2+dvryBhV9l4T2xx1oHqlKX8zzLBG -kS8NmnxoRFQs5rZYvQARAQABiQIlBBgBAgAPAhsMBQJX3tl5BQkHbqT4AAoJEGhv -B0VNbO/DrN4QAJVTg2l91LSxb2dTikXbkRCPev1Eiqjd3d4TZIyE7yeYreHUyfcz -sytEsMUpd5nqqL/QMOwgC8Dm+77Yp480atI/c0wQh30EfJq1YP4q/gwv8EtriFo6 -ZaqYNzajmB8otz6yyb5RD9S6ocZX5b22nnNM/ihiTQJiqJKC2XHPQH3/grniU9if -WXUlY7Fo+8lRn/aRPMjYT2elsugru8GoplDMyfPRymnMJmTwNPYkg0Dnm2JUXYhH -VLO6Ebh7fUChsiGNmSp+siv7fcZIf20+LvoymgyT+49LrrQMvRleOqaJc1LBKaK6 -1yU+jLk1f9bFCMORQoTxvIVaSnjMWAeKDxi8nwuVfUJpQYXmbOOqMZgoFi6t+U7/ -T6Cz1CGKv3JgEveWyHeOvsjopej3a4Hmk7QGM/xJrd83roUjypjx5lTeMDvcPlPs -IQOy33qWguR1xoEnwAp9ov/meS7HtUOoC0m9ROZqWT6ArN+1ONplFP4GTsuW91Qz -pacQMl09F0KF1MacAdpauCOKj+wy9XPUW8v3kWZufUSgNtOLHS+kVumkqdJuJ0aB -7Ezc1yYY6DwRFOdlUpTJkRMozyPRvS15Lz/vPEhcbxMHRAg611CS2CqTEIbk2chJ -QBlj77a45lDTXGQHFVYXlbXx/HWb5zOGpNji0QhY1hOABRo78DT5yda+ -=ksQj +kS8NmnxoRFQs5rZYvQARAQABiQIlBBgBAgAPAhsMBQJcEB4eBQkLn+meAAoJEGhv +B0VNbO/DjzYP/2A6HtpvPkD+s/0+Ghmp5Rw7HIvZz0RnCsvM3qVQqVn3JTqHXyhD +5GmZrCxhliW1nRBITaKSRHXSGeE/O1BtK6eW/Z1P6bWJVd+R9vhaZlLU2sswiFzu +q3s7bL/Fo9VRc0SX6j6JDh1FZ3JcebUfY59sixHLqZuAk9m97z6H1NjS+f4pB5Tp +n1nnRgdvYk4tjlk4mmluIAwjq8Ll6gw6ntwjX9Gq9OblutTr2MZDyTIOxcG54ZN2 +2t5JA7Syh0He6dn/NLlb//bPPnic3GuYfkgKht8M1XCJfnDKju+I9qRGf1DYIjP7 +m24IDC0MM9Fo7KIUY+vcV0J/BIkkZtZ9xh0iEEnR/KqYXmClym+EiWCwPhLloKL3 +6cZr5MaiYJsodYa73x35pSGvioUrBDb78v8nCNhoTUXzZv8E0s/0YI2UZSGhVEC6 +ANNXPvKeOdibGNDM4JOwETabkEng38UE6Oa7Qonra2MWsPegh+mnZ7/sqrktQRMB +T/Xne6vapDONeLeY2hVcT0j9f/S8rgpHPjP4/hmYE9d38Euwa7TZ0lHWcmJzQy6F +7HfGR3oYAYAwRnvl+kYUGZ46u9Nodi4+wycFc4+IpwFtnlUzRBOcOilnR2X6KFJW +SiVss1i7ECcWaKCBNN1MrpwGWeuSbCQ00c3bxe6ZN6goB1t2u1KAZqMK +=HFHX -----END PGP PUBLIC KEY BLOCK----- |