diff options
183 files changed, 7966 insertions, 1475 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 88a9b8834..906a1fc6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,15 +513,8 @@ else (HIDAPI_FOUND) message(STATUS "Could not find HIDAPI") endif() -# Protobuf, optional. Required for TREZOR. -include(FindProtobuf) -find_package(Protobuf) -if(Protobuf_FOUND) - set(HAVE_PROTOBUF 1) - add_definitions(-DHAVE_PROTOBUF=1) -else(Protobuf_FOUND) - message(STATUS "Could not find Protobuf") -endif() +# Trezor support check +include(CheckTrezor) if(MSVC) add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__") @@ -918,7 +911,7 @@ endif() list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) -if (HIDAPI_FOUND) +if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED) if (APPLE) if(DEPENDS) list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") diff --git a/Dockerfile b/Dockerfile index 86d833a98..0728c6ce8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,9 +25,9 @@ RUN set -ex && \ WORKDIR /usr/local #Cmake -ARG CMAKE_VERSION=3.12.1 -ARG CMAKE_VERSION_DOT=v3.12 -ARG CMAKE_HASH=c53d5c2ce81d7a957ee83e3e635c8cda5dfe20c9d501a4828ee28e1615e57ab2 +ARG CMAKE_VERSION=3.13.0 +ARG CMAKE_VERSION_DOT=v3.13 +ARG CMAKE_HASH=4058b2f1a53c026564e8936698d56c3b352d90df067b195cb749a97a3d273c90 RUN set -ex \ && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ @@ -51,8 +51,8 @@ RUN set -ex \ ENV BOOST_ROOT /usr/local/boost_${BOOST_VERSION} # OpenSSL -ARG OPENSSL_VERSION=1.1.0h -ARG OPENSSL_HASH=5835626cde9e99656585fc7aaa2302a73a7e1340bf8c14fd635a62c66802a517 +ARG OPENSSL_VERSION=1.1.0j +ARG OPENSSL_HASH=31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246 RUN set -ex \ && curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ @@ -78,8 +78,8 @@ RUN set -ex \ && ldconfig # zmq.hpp -ARG CPPZMQ_VERSION=v4.2.3 -ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 +ARG CPPZMQ_VERSION=v4.3.0 +ARG CPPZMQ_HASH=213da0b04ae3b4d846c9abc46bab87f86bfb9cf4 RUN set -ex \ && git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ && cd cppzmq \ @@ -117,8 +117,8 @@ Dates are provided in the format YYYY-MM-DD. | 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | | 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | | 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs -| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.0 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) -| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.0 | bulletproofs required +| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.4 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) +| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | XXXXXXX | 2019-04-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake new file mode 100644 index 000000000..bcd3cfc2c --- /dev/null +++ b/cmake/CheckTrezor.cmake @@ -0,0 +1,79 @@ +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) + +# 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) + message(STATUS "Could not find Protobuf") + endif() + +else() + message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR") +endif() + +if(Protobuf_FOUND AND USE_DEVICE_TREZOR) + if (NOT "$ENV{TREZOR_PYTHON}" STREQUAL "") + set(TREZOR_PYTHON "$ENV{TREZOR_PYTHON}" CACHE INTERNAL "Copied from environment variable TREZOR_PYTHON") + else() + find_package(Python QUIET COMPONENTS Interpreter) # cmake 3.12+ + if(Python_Interpreter_FOUND) + set(TREZOR_PYTHON "${Python_EXECUTABLE}") + endif() + endif() + + if(NOT TREZOR_PYTHON) + find_package(PythonInterp) + if(PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE) + set(TREZOR_PYTHON "${PYTHON_EXECUTABLE}") + endif() + endif() + + if(NOT TREZOR_PYTHON) + message(STATUS "Trezor: Python not found") + endif() +endif() + +# Try to build protobuf messages +if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) + set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIRS}") + 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) + message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." + "OUT: ${OUT}, ERR: ${ERR}." + "Please read src/device_trezor/trezor/tools/README.md") + else() + message(STATUS "Trezor protobuf messages regenerated ${OUT}") + set(DEVICE_TREZOR_READY 1) + add_definitions(-DDEVICE_TREZOR_READY=1) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DTREZOR_DEBUG=1) + endif() + + if(USE_DEVICE_TREZOR_UDP_RELEASE) + add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1) + endif() + + if (Protobuf_INCLUDE_DIR) + include_directories(${Protobuf_INCLUDE_DIR}) + endif() + + # LibUSB support, check for particular version + # Include support only if compilation test passes + if (USE_DEVICE_TREZOR_LIBUSB) + find_package(LibUSB) + endif() + + if (LibUSB_COMPILE_TEST_PASSED) + add_definitions(-DHAVE_TREZOR_LIBUSB=1) + if(LibUSB_INCLUDE_DIRS) + include_directories(${LibUSB_INCLUDE_DIRS}) + endif() + endif() + endif() +endif() diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake new file mode 100644 index 000000000..7e3bf156e --- /dev/null +++ b/cmake/FindLibUSB.cmake @@ -0,0 +1,140 @@ +# - Find libusb for portable USB support +# This module will find libusb as published by +# http://libusb.sf.net and +# http://libusb-win32.sf.net +# +# It will use PkgConfig if present and supported, else search +# it on its own. If the LibUSB_ROOT_DIR environment variable +# is defined, it will be used as base path. +# The following standard variables get defined: +# LibUSB_FOUND: true if LibUSB was found +# LibUSB_HEADER_FILE: the location of the C header file +# LibUSB_INCLUDE_DIRS: the directory that contains the include file +# LibUSB_LIBRARIES: the library +# source: https://github.com/IntelRealSense/librealsense + +include ( CheckLibraryExists ) +include ( CheckIncludeFile ) + +find_package ( PkgConfig ) +if ( PKG_CONFIG_FOUND ) + pkg_check_modules ( PKGCONFIG_LIBUSB libusb-1.0 ) + if ( NOT PKGCONFIG_LIBUSB_FOUND ) + pkg_check_modules ( PKGCONFIG_LIBUSB libusb ) + endif ( NOT PKGCONFIG_LIBUSB_FOUND ) +endif ( PKG_CONFIG_FOUND ) + +if ( PKGCONFIG_LIBUSB_FOUND ) + set ( LibUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS} ) + foreach ( i ${PKGCONFIG_LIBUSB_LIBRARIES} ) + string ( REGEX MATCH "[^-]*" ibase "${i}" ) + find_library ( ${ibase}_LIBRARY + NAMES ${i} + PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS} + ) + if ( ${ibase}_LIBRARY ) + list ( APPEND LibUSB_LIBRARIES ${${ibase}_LIBRARY} ) + endif ( ${ibase}_LIBRARY ) + mark_as_advanced ( ${ibase}_LIBRARY ) + endforeach ( i ) + +else ( PKGCONFIG_LIBUSB_FOUND ) + find_file ( LibUSB_HEADER_FILE + NAMES + libusb.h usb.h + PATHS + $ENV{ProgramFiles}/LibUSB-Win32 + $ENV{LibUSB_ROOT_DIR} + PATH_SUFFIXES + include + libusb-1.0 + include/libusb-1.0 + ) + mark_as_advanced ( LibUSB_HEADER_FILE ) + get_filename_component ( LibUSB_INCLUDE_DIRS "${LibUSB_HEADER_FILE}" PATH ) + + if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) + # LibUSB-Win32 binary distribution contains several libs. + # Use the lib that got compiled with the same compiler. + if ( MSVC ) + if ( WIN32 ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc ) + else ( WIN32 ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc_x64 ) + endif ( WIN32 ) + elseif ( BORLAND ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/bcc ) + elseif ( CMAKE_COMPILER_IS_GNUCC ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/gcc ) + endif ( MSVC ) + endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) + + find_library ( usb_LIBRARY + NAMES + usb-1.0 libusb usb + PATHS + $ENV{ProgramFiles}/LibUSB-Win32 + $ENV{LibUSB_ROOT_DIR} + PATH_SUFFIXES + ${LibUSB_LIBRARY_PATH_SUFFIX} + ) + mark_as_advanced ( usb_LIBRARY ) + if ( usb_LIBRARY ) + set ( LibUSB_LIBRARIES ${usb_LIBRARY} ) + endif ( usb_LIBRARY ) + +endif ( PKGCONFIG_LIBUSB_FOUND ) + +if ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES ) + set ( LibUSB_FOUND true ) +endif ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES ) + +if ( LibUSB_FOUND ) + set ( CMAKE_REQUIRED_INCLUDES "${LibUSB_INCLUDE_DIRS}" ) + check_include_file ( "${LibUSB_HEADER_FILE}" LibUSB_FOUND ) +endif ( LibUSB_FOUND ) + +if ( LibUSB_FOUND ) + check_library_exists ( "${LibUSB_LIBRARIES}" usb_open "" LibUSB_FOUND ) + check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 ) + check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 ) + + # Library 1.0.16+ compilation test. + # The check_library_exists does not work well on Apple with shared libs. + if (APPLE OR LibUSB_VERSION_1.0.16) + if (APPLE) + if(DEPENDS) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + else() + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT}) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() + endif() + if (WIN32) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi) + endif() + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES}) + + try_compile(LibUSB_COMPILE_TEST_PASSED + ${CMAKE_BINARY_DIR} + "${CMAKE_SOURCE_DIR}/cmake/test-libusb-version.c" + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${LibUSB_INCLUDE_DIRS}" + "-DLINK_DIRECTORIES=${LibUSB_LIBRARIES}" + LINK_LIBRARIES ${TEST_COMPILE_EXTRA_LIBRARIES} + OUTPUT_VARIABLE OUTPUT) + unset(TEST_COMPILE_EXTRA_LIBRARIES) + message(STATUS "LibUSB Compilation test: ${LibUSB_COMPILE_TEST_PASSED}") + endif() +endif ( LibUSB_FOUND ) + +if ( NOT LibUSB_FOUND ) + if ( NOT LibUSB_FIND_QUIETLY ) + message ( STATUS "LibUSB not found, try setting LibUSB_ROOT_DIR environment variable." ) + endif ( NOT LibUSB_FIND_QUIETLY ) + if ( LibUSB_FIND_REQUIRED ) + message ( FATAL_ERROR "" ) + endif ( LibUSB_FIND_REQUIRED ) +endif ( NOT LibUSB_FOUND ) diff --git a/cmake/test-libusb-version.c b/cmake/test-libusb-version.c new file mode 100644 index 000000000..309e4ad27 --- /dev/null +++ b/cmake/test-libusb-version.c @@ -0,0 +1,52 @@ +// 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 <libusb.h> + +#define UNUSED(expr) (void)(expr) + +int main(int argc, char *argv[]) { + libusb_device **devs; + libusb_context *ctx = NULL; + + int r = libusb_init(&ctx); UNUSED(r); + ssize_t cnt = libusb_get_device_list(ctx, &devs); UNUSED(cnt); + + struct libusb_device_descriptor desc; + r = libusb_get_device_descriptor(devs[0], &desc); UNUSED(r); + uint8_t bus_id = libusb_get_bus_number(devs[0]); UNUSED(bus_id); + uint8_t addr = libusb_get_device_address(devs[0]); UNUSED(addr); + + uint8_t tmp_path[16]; + r = libusb_get_port_numbers(devs[0], tmp_path, sizeof(tmp_path)); + UNUSED(r); + UNUSED(tmp_path); + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); +} diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index b0c032c09..bf33d706d 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -3,9 +3,6 @@ SOURCES_PATH ?= $(BASEDIR)/sources BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs -NO_QT ?= -NO_WALLET ?= -NO_UPNP ?= FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources BUILD = $(shell ./config.guess) @@ -97,17 +94,10 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) -qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) -wallet_packages_$(NO_WALLET) = $(wallet_packages) -upnp_packages_$(NO_UPNP) = $(upnp_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) -ifneq ($(qt_packages_),) -native_packages += $(qt_native_packages) -endif - all_packages = $(packages) $(native_packages) meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk @@ -153,9 +143,6 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ - -e 's|@no_qt@|$(NO_QT)|' \ - -e 's|@no_wallet@|$(NO_WALLET)|' \ - -e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ $(AT)touch $@ @@ -176,9 +163,6 @@ $(host_prefix)/share/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ - -e 's|@no_qt@|$(NO_QT)|' \ - -e 's|@no_wallet@|$(NO_WALLET)|' \ - -e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@debug@|$(DEBUG)|' \ -e 's|@depends@|$(host_cmake)|' \ -e 's|@prefix@|$($(host_arch)_$(host_os)_prefix)|'\ diff --git a/contrib/depends/README.md b/contrib/depends/README.md index dd2824569..597398369 100644 --- a/contrib/depends/README.md +++ b/contrib/depends/README.md @@ -12,11 +12,11 @@ For example: make HOST=x86_64-w64-mingw32 -j4 -A prefix will be generated that's suitable for plugging into Bitcoin's -configure. In the above example, a dir named x86_64-w64-mingw32 will be -created. To use it for Bitcoin: +A toolchain will be generated that's suitable for plugging into Monero's +cmake. In the above example, a dir named x86_64-w64-mingw32 will be +created. To use it for Monero: - ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + cmake -DCMAKE_TOOLCHAIN=`pwd`/contrib/depends/x86_64-w64-mingw32 Common `host-platform-triplets` for cross compilation are: @@ -35,16 +35,10 @@ The following can be set when running make: make FOO=bar BASE_CACHE: built packages will be placed here SDK_PATH: Path where sdk's can be found (used by OSX) FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up - NO_QT: Don't download/build/cache qt and its dependencies - NO_WALLET: Don't download/build/cache libs needed to enable the wallet - NO_UPNP: Don't download/build/cache packages needed for enabling upnp DEBUG: disable some optimizations and enable more runtime checking HOST_ID_SALT: Optional salt to use when generating host package ids BUILD_ID_SALT: Optional salt to use when generating build package ids -If some packages are not built, for example `make NO_WALLET=1`, the appropriate -options will be passed to bitcoin's configure. In this case, `--disable-wallet`. - Additional targets: download: run 'make download' to fetch all sources without building them diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in index 0a4a9c327..dd91bcb2a 100644 --- a/contrib/depends/config.site.in +++ b/contrib/depends/config.site.in @@ -13,25 +13,6 @@ fi if test -z $with_qt_translationdir; then with_qt_translationdir=$depends_prefix/translations fi -if test -z $with_qt_bindir && test -z "@no_qt@"; then - with_qt_bindir=$depends_prefix/native/bin -fi -if test -z $with_protoc_bindir && test -z "@no_qt@"; then - with_protoc_bindir=$depends_prefix/native/bin -fi - - -if test -z $enable_wallet && test -n "@no_wallet@"; then - enable_wallet=no -fi - -if test -z $with_miniupnpc && test -n "@no_upnp@"; then - with_miniupnpc=no -fi - -if test -z $with_gui && test -n "@no_qt@"; then - with_gui=no -fi if test x@host_os@ = xdarwin; then BREW=no diff --git a/contrib/depends/packages/bdb.mk b/contrib/depends/packages/bdb.mk deleted file mode 100644 index 6c9876c2c..000000000 --- a/contrib/depends/packages/bdb.mk +++ /dev/null @@ -1,31 +0,0 @@ -package=bdb -$(package)_version=4.8.30 -$(package)_download_path=http://download.oracle.com/berkeley-db -$(package)_file_name=db-$($(package)_version).NC.tar.gz -$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef -$(package)_build_subdir=build_unix - -define $(package)_set_vars -$(package)_config_opts=--disable-shared --enable-cxx --disable-replication -$(package)_config_opts_mingw32=--enable-mingw -$(package)_config_opts_linux=--with-pic -$(package)_cxxflags=-std=c++11 -endef - -define $(package)_preprocess_cmds - sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \ - sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \ - cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist -endef - -define $(package)_config_cmds - ../dist/$($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) libdb_cxx-4.8.a libdb-4.8.a -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install_lib install_include -endef diff --git a/contrib/depends/packages/ldns.mk b/contrib/depends/packages/ldns.mk index a9565a581..0b7c3806a 100644 --- a/contrib/depends/packages/ldns.mk +++ b/contrib/depends/packages/ldns.mk @@ -1,6 +1,6 @@ package=ldns $(package)_version=1.6.17 -$(package)_download_path=http://www.nlnetlabs.nl/downloads/ldns/ +$(package)_download_path=https://www.nlnetlabs.nl/downloads/ldns/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=8b88e059452118e8949a2752a55ce59bc71fa5bc414103e17f5b6b06f9bcc8cd $(package)_dependencies=openssl diff --git a/contrib/depends/packages/libICE.mk b/contrib/depends/packages/libICE.mk index fc60323b1..a897d9aed 100644 --- a/contrib/depends/packages/libICE.mk +++ b/contrib/depends/packages/libICE.mk @@ -1,6 +1,6 @@ package=libICE $(package)_version=1.0.9 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202 $(package)_dependencies=xtrans xproto diff --git a/contrib/depends/packages/libSM.mk b/contrib/depends/packages/libSM.mk index 0f9307ca7..83fcd4cdb 100644 --- a/contrib/depends/packages/libSM.mk +++ b/contrib/depends/packages/libSM.mk @@ -1,6 +1,6 @@ package=libSM $(package)_version=1.2.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd $(package)_dependencies=xtrans xproto libICE diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index 47f8b3cbc..6d60cce26 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -1,8 +1,8 @@ package=libusb -$(package)_version=1.0.9 -$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.9/ +$(package)_version=1.0.22 +$(package)_download_path=https://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b +$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157 define $(package)_preprocess_cmds autoreconf -i @@ -10,7 +10,7 @@ endef define $(package)_set_vars $(package)_config_opts=--disable-shared - $(package)_config_opts_linux=--with-pic + $(package)_config_opts_linux=--with-pic --disable-udev endef define $(package)_config_cmds diff --git a/contrib/depends/packages/miniupnpc.mk b/contrib/depends/packages/miniupnpc.mk deleted file mode 100644 index 1bb8cb5d2..000000000 --- a/contrib/depends/packages/miniupnpc.mk +++ /dev/null @@ -1,28 +0,0 @@ -package=miniupnpc -$(package)_version=2.0.20170509 -$(package)_download_path=http://miniupnp.free.fr/files -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=d3c368627f5cdfb66d3ebd64ca39ba54d6ff14a61966dbecb8dd296b7039f16a - -define $(package)_set_vars -$(package)_build_opts=CC="$($(package)_cc)" -$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" -$(package)_build_opts_mingw32=-f Makefile.mingw -$(package)_build_env+=CFLAGS="$($(package)_cflags) $($(package)_cppflags)" AR="$($(package)_ar)" -endef - -define $(package)_preprocess_cmds - mkdir dll && \ - sed -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"$($(package)_version)\"|' -e 's|OS/version|$(host)|' miniupnpcstrings.h.in > miniupnpcstrings.h && \ - sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw -endef - -define $(package)_build_cmds - $(MAKE) libminiupnpc.a $($(package)_build_opts) -endef - -define $(package)_stage_cmds - mkdir -p $($(package)_staging_prefix_dir)/include/miniupnpc $($(package)_staging_prefix_dir)/lib &&\ - install *.h $($(package)_staging_prefix_dir)/include/miniupnpc &&\ - install libminiupnpc.a $($(package)_staging_prefix_dir)/lib -endef diff --git a/contrib/depends/packages/native_cdrkit.mk b/contrib/depends/packages/native_cdrkit.mk index cf694edb3..8243458ec 100644 --- a/contrib/depends/packages/native_cdrkit.mk +++ b/contrib/depends/packages/native_cdrkit.mk @@ -1,6 +1,6 @@ package=native_cdrkit $(package)_version=1.1.11 -$(package)_download_path=http://distro.ibiblio.org/fatdog/source/600/c +$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c $(package)_file_name=cdrkit-$($(package)_version).tar.bz2 $(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564 $(package)_patches=cdrkit-deterministic.patch diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index f814c14d6..234adbdd4 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,8 +1,6 @@ packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi native_packages := native_ccache -wallet_packages=bdb - darwin_native_packages = native_biplist native_ds_store native_mac_alias darwin_packages = sodium-darwin diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk index 32ca4a84c..bca2926cb 100644 --- a/contrib/depends/packages/qt.mk +++ b/contrib/depends/packages/qt.mk @@ -1,6 +1,6 @@ PACKAGE=qt $(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_download_path=https://download.qt.io/archive/qt/5.7/5.7.1/submodules $(package)_suffix=opensource-src-$($(package)_version).tar.gz $(package)_file_name=qtbase-$($(package)_suffix) $(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk index beeeb54c1..733a7f232 100644 --- a/contrib/depends/packages/unbound.mk +++ b/contrib/depends/packages/unbound.mk @@ -1,6 +1,6 @@ package=unbound $(package)_version=1.6.8 -$(package)_download_path=http://www.unbound.net/downloads/ +$(package)_download_path=https://www.unbound.net/downloads/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=e3b428e33f56a45417107448418865fe08d58e0e7fea199b855515f60884dd49 $(package)_dependencies=openssl expat ldns diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index e1bcb35b2..543f868a5 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -1,6 +1,6 @@ package=unwind $(package)_version=1.2 -$(package)_download_path=http://download.savannah.nongnu.org/releases/libunwind +$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind $(package)_file_name=lib$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992 diff --git a/contrib/depends/packages/xproto.mk b/contrib/depends/packages/xproto.mk index 50a90b268..52fe253c7 100644 --- a/contrib/depends/packages/xproto.mk +++ b/contrib/depends/packages/xproto.mk @@ -1,6 +1,6 @@ package=xproto $(package)_version=7.0.26 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f diff --git a/contrib/depends/packages/zlib.mk b/contrib/depends/packages/zlib.mk index 589490800..1600b11a0 100644 --- a/contrib/depends/packages/zlib.mk +++ b/contrib/depends/packages/zlib.mk @@ -1,6 +1,6 @@ package=zlib $(package)_version=1.2.11 -$(package)_download_path=http://www.zlib.net +$(package)_download_path=https://www.zlib.net $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 diff --git a/src/common/int-util.h b/contrib/epee/include/int-util.h index 3bcc085e2..3bcc085e2 100644 --- a/src/common/int-util.h +++ b/contrib/epee/include/int-util.h diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 0d09683d6..5fffde8d5 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -124,5 +124,14 @@ namespace misc_utils return boost::lexical_cast<std::string>(pthread_self()); #endif } + + inline bool get_gmt_time(time_t t, struct tm &tm) + { +#ifdef _WIN32 + return gmtime_s(&tm, &t); +#else + return gmtime_r(&t, &tm); +#endif + } } } diff --git a/contrib/epee/include/mlocker.h b/contrib/epee/include/mlocker.h index d2fc2ed58..a6d94b3d2 100644 --- a/contrib/epee/include/mlocker.h +++ b/contrib/epee/include/mlocker.h @@ -73,7 +73,7 @@ namespace epee mlocked(const T &&t): T(t) { mlocker::lock(this, sizeof(T)); } mlocked(const mlocked<T> &&mt): T(mt) { mlocker::lock(this, sizeof(T)); } mlocked<T> &operator=(const mlocked<T> &mt) { T::operator=(mt); return *this; } - ~mlocked() { mlocker::unlock(this, sizeof(T)); } + ~mlocked() { try { mlocker::unlock(this, sizeof(T)); } catch (...) { /* do not propagate */ } } }; template<typename T> diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 3f726a352..e6b2755af 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -36,7 +36,6 @@ #define _ABSTRACT_TCP_SERVER2_H_ -#include <boost/asio.hpp> #include <string> #include <vector> #include <boost/noncopyable.hpp> @@ -155,7 +154,8 @@ namespace net_utils //this should be the last one, because it could be wait on destructor, while other activities possible on other threads t_protocol_handler m_protocol_handler; //typename t_protocol_handler::config_type m_dummy_config; - std::list<boost::shared_ptr<connection<t_protocol_handler> > > m_self_refs; // add_ref/release support + size_t m_reference_count = 0; // reference count managed through add_ref/release support + boost::shared_ptr<connection<t_protocol_handler> > m_self_ref; // the reference to hold critical_section m_self_refs_lock; critical_section m_chunking_lock; // held while we add small chunks of the big do_send() to small do_send_chunk() critical_section m_shutdown_lock; // held while shutting down diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 9b03941ee..d8779f372 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -32,16 +32,13 @@ -//#include "net_utils_base.h" -#include <boost/lambda/bind.hpp> +#include <boost/bind.hpp> #include <boost/foreach.hpp> -#include <boost/lambda/lambda.hpp> #include <boost/uuid/random_generator.hpp> #include <boost/chrono.hpp> #include <boost/utility/value_init.hpp> #include <boost/asio/deadline_timer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> // TODO -#include <boost/thread/thread.hpp> // TODO #include <boost/thread/condition_variable.hpp> // TODO #include "warnings.h" #include "string_tools.h" @@ -230,7 +227,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) //_dbg3("[sock " << socket_.native_handle() << "] add_ref 2, m_peer_number=" << mI->m_peer_number); if(m_was_shutdown) return false; - m_self_refs.push_back(self); + ++m_reference_count; + m_self_ref = std::move(self); return true; CATCH_ENTRY_L0("connection<t_protocol_handler>::add_ref()", false); } @@ -242,10 +240,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy; LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] release"); CRITICAL_REGION_BEGIN(m_self_refs_lock); - CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection<t_protocol_handler>::release() call"); - //erasing from container without additional copy can cause start deleting object, including m_self_refs - back_connection_copy = m_self_refs.back(); - m_self_refs.pop_back(); + CHECK_AND_ASSERT_MES(m_reference_count, false, "[sock " << socket_.native_handle() << "] m_reference_count already at 0 at connection<t_protocol_handler>::release() call"); + // is this the last reference? + if (--m_reference_count == 0) { + // move the held reference to a local variable, keeping the object alive until the function terminates + std::swap(back_connection_copy, m_self_ref); + } CRITICAL_REGION_END(); return true; CATCH_ENTRY_L0("connection<t_protocol_handler>::release()", false); diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 7e8750047..9b6fc14a7 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -42,22 +42,11 @@ #define INCLUDED_p2p_connection_basic_hpp -#include <boost/asio.hpp> #include <string> -#include <vector> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> #include <atomic> +#include <memory> #include <boost/asio.hpp> -#include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/thread/thread.hpp> - -#include <memory> #include "net/net_utils_base.h" #include "syncobj.h" diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 1a97e610a..5669824c1 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -33,7 +33,8 @@ #include <boost/thread.hpp> #include <boost/bind.hpp> -#include "net/http_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "http_protocol_handler.h" #include "net/http_server_handlers_map2.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl index ab7c32c32..a580e81fd 100644 --- a/contrib/epee/include/net/levin_client.inl +++ b/contrib/epee/include/net/levin_client.inl @@ -80,10 +80,10 @@ int levin_client_impl::invoke(int command, const std::string& in_buff, std::stri return -1; bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_command = command; + head.m_command = SWAP32LE(command); if(!m_transport.send(&head, sizeof(head))) return -1; @@ -97,7 +97,7 @@ int levin_client_impl::invoke(int command, const std::string& in_buff, std::stri head = *(bucket_head*)local_buff.data(); - if(head.m_signature!=LEVIN_SIGNATURE) + if(head.m_signature!=SWAP64LE(LEVIN_SIGNATURE)) { LOG_PRINT_L1("Signature mismatch in response"); return -1; @@ -116,10 +116,10 @@ int levin_client_impl::notify(int command, const std::string& in_buff) return -1; bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = false; - head.m_command = command; + head.m_command = SWAP32LE(command); if(!m_transport.send((const char*)&head, sizeof(head))) return -1; @@ -139,12 +139,13 @@ inline return -1; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_return_code = SWAP32LE(0); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); if(!m_transport.send(&head, sizeof(head))) return -1; @@ -157,14 +158,13 @@ inline head = *(bucket_head2*)local_buff.data(); - - if(head.m_signature!=LEVIN_SIGNATURE) + if(head.m_signature != SWAP64LE(LEVIN_SIGNATURE)) { LOG_PRINT_L1("Signature mismatch in response"); return -1; } - if(!m_transport.recv_n(buff_out, head.m_cb)) + if(!m_transport.recv_n(buff_out, SWAP64LE(head.m_cb))) return -1; return head.m_return_code; @@ -177,12 +177,13 @@ inline return -1; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = false; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_return_code = SWAP32LE(0); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); if(!m_transport.send((const char*)&head, sizeof(head))) return -1; diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h index 05560dd90..da926a914 100644 --- a/contrib/epee/include/net/levin_helper.h +++ b/contrib/epee/include/net/levin_helper.h @@ -30,6 +30,7 @@ #include "levin_base.h" #include "serializeble_struct_helper.h" +#include "int-util.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -43,11 +44,11 @@ namespace levin { buff.resize(sizeof(levin::bucket_head)); levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_cb = 0; head.m_have_to_return_data = true; - head.m_command = command_id; - head.m_return_code = 1; + head.m_command = SWAP32LE(command_id); + head.m_return_code = SWAP32LE(1); head.m_reservedA = rand(); //probably some flags in future head.m_reservedB = rand(); //probably some check summ in future @@ -55,7 +56,7 @@ namespace levin if(!StorageNamed::save_struct_as_storage_to_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg)) return false; - head.m_cb = buff_strg.size(); + head.m_cb = SWAP64LE(buff_strg.size()); buff.append(buff_strg); return true; } @@ -65,15 +66,15 @@ namespace levin { buff.resize(sizeof(levin::bucket_head)); levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_cb = 0; head.m_have_to_return_data = true; - head.m_command = command_id; - head.m_return_code = 1; + head.m_command = SWAP32LE(command_id); + head.m_return_code = SWAP32LE(1); head.m_reservedA = rand(); //probably some flags in future head.m_reservedB = rand(); //probably some check summ in future - head.m_cb = data.size(); + head.m_cb = SWAP64LE(data.size()); buff.append(data); return true; } @@ -86,7 +87,17 @@ namespace levin return false; } - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); +#if BYTE_ORDER == LITTLE_ENDIAN + levin::bucket_head &head = *(levin::bucket_head*)(&buff[0]); +#else + levin::bucket_head head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_reservedA = SWAP32LE(head.m_reservedA); + head.m_reservedB = SWAP32LE(head.m_reservedB); +#endif if(head.m_signature != LEVIN_SIGNATURE) { LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message"); @@ -113,7 +124,17 @@ namespace levin return false; } - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); +#if BYTE_ORDER == LITTLE_ENDIAN + levin::bucket_head &head = *(levin::bucket_head*)(&buff[0]); +#else + levin::bucket_head head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_reservedA = SWAP32LE(head.m_reservedA); + head.m_reservedB = SWAP32LE(head.m_reservedB); +#endif if(head.m_signature != LEVIN_SIGNATURE) { LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message"); diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h index b3a75bedc..791766762 100644 --- a/contrib/epee/include/net/levin_protocol_handler.h +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -31,6 +31,7 @@ #include <boost/uuid/uuid_generators.hpp> #include "levin_base.h" +#include "int-util.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -103,7 +104,7 @@ namespace levin case conn_state_reading_head: if(m_cach_in_buffer.size() < sizeof(bucket_head)) { - if(m_cach_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE) + if(m_cach_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cach_in_buffer.data()) != SWAP64LE(LEVIN_SIGNATURE)) { LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; @@ -112,13 +113,23 @@ namespace levin break; } { - bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); - if(LEVIN_SIGNATURE != phead->m_signature) +#if BYTE_ORDER == LITTLE_ENDIAN + bucket_head &phead = *(bucket_head*)m_cach_in_buffer.data(); +#else + bucket_head phead = *(bucket_head*)m_cach_in_buffer.data(); + phead.m_signature = SWAP64LE(phead.m_signature); + phead.m_cb = SWAP64LE(phead.m_cb); + phead.m_command = SWAP32LE(phead.m_command); + phead.m_return_code = SWAP32LE(phead.m_return_code); + phead.m_reservedA = SWAP32LE(phead.m_reservedA); + phead.m_reservedB = SWAP32LE(phead.m_reservedB); +#endif + if(LEVIN_SIGNATURE != phead.m_signature) { LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; } - m_current_head = *phead; + m_current_head = phead; } m_cach_in_buffer.erase(0, sizeof(bucket_head)); m_state = conn_state_reading_body; diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 08aa1d468..6b1528caf 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -37,6 +37,7 @@ #include "misc_language.h" #include "syncobj.h" #include "misc_os_dependent.h" +#include "int-util.h" #include <random> #include <chrono> @@ -469,7 +470,18 @@ public: m_current_head.m_have_to_return_data = false; m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; m_current_head.m_flags = LEVIN_PACKET_RESPONSE; +#if BYTE_ORDER == LITTLE_ENDIAN std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); +#else + bucket_head2 head = m_current_head; + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_flags = SWAP32LE(head.m_flags); + head.m_protocol_version = SWAP32LE(head.m_protocol_version); + std::string send_buff((const char*)&head, sizeof(head)); +#endif send_buff += return_buff; CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size())) @@ -491,7 +503,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()) != LEVIN_SIGNATURE) + if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.data()) != SWAP64LE(LEVIN_SIGNATURE)) { MWARNING(m_connection_context << "Signature mismatch, connection will be closed"); return false; @@ -500,13 +512,23 @@ public: break; } - bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data(); - if(LEVIN_SIGNATURE != phead->m_signature) +#if BYTE_ORDER == LITTLE_ENDIAN + bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.data(); +#else + bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.data(); + phead.m_signature = SWAP64LE(phead.m_signature); + phead.m_cb = SWAP64LE(phead.m_cb); + phead.m_command = SWAP32LE(phead.m_command); + phead.m_return_code = SWAP32LE(phead.m_return_code); + phead.m_flags = SWAP32LE(phead.m_flags); + phead.m_protocol_version = SWAP32LE(phead.m_protocol_version); +#endif + if(LEVIN_SIGNATURE != phead.m_signature) { LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); return false; } - m_current_head = *phead; + m_current_head = phead; m_cache_in_buffer.erase(0, sizeof(bucket_head2)); m_state = stream_state_body; @@ -566,13 +588,13 @@ public: } bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_flags = LEVIN_PACKET_REQUEST; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_send_lock); @@ -624,13 +646,13 @@ public: return LEVIN_ERROR_CONNECTION_DESTROYED; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_flags = LEVIN_PACKET_REQUEST; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_send_lock); @@ -698,13 +720,13 @@ public: return LEVIN_ERROR_CONNECTION_DESTROYED; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_have_to_return_data = false; - head.m_cb = in_buff.size(); + head.m_cb = SWAP64LE(in_buff.size()); - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 174915ecf..b1296a0b7 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -109,6 +109,8 @@ namespace epee constexpr std::size_t size() const noexcept { return len; } constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); } + const T &operator[](size_t idx) const { return ptr[idx]; } + private: T* ptr; std::size_t len; diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index 2023e2f2a..0f0c6210f 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 "int-util.h" namespace epee { @@ -135,8 +136,8 @@ namespace epee TRY_ENTRY(); std::stringstream ss; storage_block_header sbh = AUTO_VAL_INIT(sbh); - sbh.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; - sbh.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; + sbh.m_signature_a = SWAP32LE(PORTABLE_STORAGE_SIGNATUREA); + sbh.m_signature_b = SWAP32LE(PORTABLE_STORAGE_SIGNATUREB); sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER; ss.write((const char*)&sbh, sizeof(storage_block_header)); pack_entry_to_buff(ss, m_root); @@ -154,8 +155,8 @@ namespace epee return false; } storage_block_header* pbuff = (storage_block_header*)source.data(); - if(pbuff->m_signature_a != PORTABLE_STORAGE_SIGNATUREA || - pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB + if(pbuff->m_signature_a != SWAP32LE(PORTABLE_STORAGE_SIGNATUREA) || + pbuff->m_signature_b != SWAP32LE(PORTABLE_STORAGE_SIGNATUREB) ) { LOG_ERROR("portable_storage: wrong binary format - signature mismatch"); diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index aba065cc7..6a063cc36 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -59,6 +59,26 @@ #pragma comment (lib, "Rpcrt4.lib") #endif +static const constexpr unsigned char isx[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + namespace epee { namespace string_tools @@ -98,30 +118,30 @@ namespace string_tools } //---------------------------------------------------------------------------- template<class CharT> - bool parse_hexstr_to_binbuff(const std::basic_string<CharT>& s, std::basic_string<CharT>& res, bool allow_partial_byte = false) + bool parse_hexstr_to_binbuff(const std::basic_string<CharT>& s, std::basic_string<CharT>& res) { res.clear(); - if (!allow_partial_byte && (s.size() & 1)) + if (s.size() & 1) return false; try { - long v = 0; - for(size_t i = 0; i < (s.size() + 1) / 2; i++) + res.resize(s.size() / 2); + unsigned char *dst = (unsigned char *)res.data(); + const unsigned char *src = (const unsigned char *)s.data(); + for(size_t i = 0; i < s.size(); i += 2) { - CharT byte_str[3]; - size_t copied = s.copy(byte_str, 2, 2 * i); - byte_str[copied] = CharT(0); - CharT* endptr; - v = strtoul(byte_str, &endptr, 16); - if (v < 0 || 0xFF < v || endptr != byte_str + copied) - { - return false; - } - res.push_back(static_cast<unsigned char>(v)); + int tmp = *src++; + tmp = isx[tmp]; + if (tmp == 0xff) return false; + int t2 = *src++; + t2 = isx[t2]; + if (t2 == 0xff) return false; + *dst++ = (tmp << 4) | t2; } return true; - }catch(...) + } + catch(...) { return false; } diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 9ab485839..7d145ee46 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -34,47 +34,15 @@ #include "net/connection_basic.hpp" -#include <boost/asio.hpp> -#include <string> -#include <vector> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <atomic> - -#include <boost/asio.hpp> -#include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/thread/thread.hpp> - -#include <memory> - -#include "syncobj.h" - #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include <boost/lambda/bind.hpp> -#include <boost/lambda/lambda.hpp> -#include <boost/uuid/random_generator.hpp> -#include <boost/chrono.hpp> -#include <boost/utility/value_init.hpp> -#include <boost/asio/deadline_timer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> -#include <boost/filesystem.hpp> #include "misc_language.h" #include "pragma_comp_defs.h" -#include <fstream> -#include <sstream> #include <iomanip> -#include <algorithm> -#include <mutex> #include <boost/asio/basic_socket.hpp> -#include <boost/asio/ip/unicast.hpp> -#include "net/abstract_tcp_server2.h" // TODO: #include "net/network_throttle-detail.hpp" @@ -161,7 +129,6 @@ connection_basic::connection_basic(boost::asio::io_service& io_service, std::ato try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; _note("Spawned connection p2p#"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_ref_sock_count); - //boost::filesystem::create_directories("log/dr-monero/net/"); } connection_basic::~connection_basic() noexcept(false) { diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index c3262e8f4..078a84ce3 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -38,6 +38,12 @@ #include "syncobj.h" #include "mlocker.h" +#include <atomic> + +// 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 }; + static size_t query_page_size() { #if defined HAVE_MLOCK @@ -47,7 +53,6 @@ static size_t query_page_size() MERROR("Failed to determine page size"); return 0; } - MINFO("Page size: " << ret); return ret; #else #warning Missing query_page_size implementation @@ -59,8 +64,8 @@ static void do_lock(void *ptr, size_t len) { #if defined HAVE_MLOCK int ret = mlock(ptr, len); - if (ret < 0) - MERROR("Error locking page at " << ptr << ": " << strerror(errno)); + if (ret < 0 && !previously_failed.exchange(true)) + MERROR("Error locking page at " << ptr << ": " << strerror(errno) << ", subsequent mlock errors will be silenced"); #else #warning Missing do_lock implementation #endif @@ -70,7 +75,10 @@ static void do_unlock(void *ptr, size_t len) { #if defined HAVE_MLOCK int ret = munlock(ptr, len); - if (ret < 0) + // check whether we previously failed, but don't set it, this is just + // to pacify the errors of mlock()ing failed, in which case unlocking + // is also not going to work of course + if (ret < 0 && !previously_failed.load()) MERROR("Error unlocking page at " << ptr << ": " << strerror(errno)); #else #warning Missing implementation of page size detection @@ -89,8 +97,8 @@ namespace epee } std::map<size_t, unsigned int> &mlocker::map() { - static std::map<size_t, unsigned int> vmap; - return vmap; + static std::map<size_t, unsigned int> *vmap = new std::map<size_t, unsigned int>(); + return *vmap; } size_t mlocker::get_page_size() @@ -108,11 +116,14 @@ namespace epee mlocker::~mlocker() { - unlock(ptr, len); + try { unlock(ptr, len); } + catch (...) { /* ignore and do not propagate through the dtor */ } } void mlocker::lock(void *ptr, size_t len) { + TRY_ENTRY(); + size_t page_size = get_page_size(); if (page_size == 0) return; @@ -123,10 +134,14 @@ namespace epee for (size_t page = first; page <= last; ++page) lock_page(page); ++num_locked_objects; + + CATCH_ENTRY_L1("mlocker::lock", void()); } void mlocker::unlock(void *ptr, size_t len) { + TRY_ENTRY(); + size_t page_size = get_page_size(); if (page_size == 0) return; @@ -136,6 +151,8 @@ namespace epee for (size_t page = first; page <= last; ++page) unlock_page(page); --num_locked_objects; + + CATCH_ENTRY_L1("mlocker::lock", void()); } size_t mlocker::get_num_locked_pages() diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 638155b6b..00d848388 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -40,6 +40,7 @@ #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include "string_tools.h" +#include "misc_os_dependent.h" #include "misc_log_ex.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -58,12 +59,7 @@ static std::string generate_log_filename(const char *base) char tmp[200]; struct tm tm; time_t now = time(NULL); - if -#ifdef WIN32 - (!gmtime_s(&tm, &now)) -#else - (!gmtime_r(&now, &tm)) -#endif + if (!epee::misc_utils::get_gmt_time(now, tm)) snprintf(tmp, sizeof(tmp), "part-%u", ++fallback_counter); else strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm); diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp index 2f4015e81..354c3d2c3 100644 --- a/contrib/epee/src/net_utils_base.cpp +++ b/contrib/epee/src/net_utils_base.cpp @@ -2,8 +2,6 @@ #include "net/net_utils_base.h" #include "string_tools.h" -#include <cstring> -#include <typeindex> #include "net/local_ip.h" namespace epee { namespace net_utils diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 6f727a1cf..d2e776df0 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -32,20 +32,11 @@ /* rfree: implementation for throttle details */ -#include <boost/asio.hpp> #include <string> #include <vector> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> #include <atomic> #include <boost/asio.hpp> -#include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/thread/thread.hpp> #include <memory> @@ -53,14 +44,7 @@ #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include <boost/lambda/bind.hpp> -#include <boost/lambda/lambda.hpp> -#include <boost/uuid/random_generator.hpp> #include <boost/chrono.hpp> -#include <boost/utility/value_init.hpp> -#include <boost/asio/deadline_timer.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/thread/thread.hpp> #include "misc_language.h" #include "pragma_comp_defs.h" #include <sstream> diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp index da264471f..c5949da0a 100644 --- a/contrib/epee/src/readline_buffer.cpp +++ b/contrib/epee/src/readline_buffer.cpp @@ -1,9 +1,9 @@ #include "readline_buffer.h" #include <readline/readline.h> #include <readline/history.h> -#include <unistd.h> #include <iostream> -#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/lock_guard.hpp> #include <boost/algorithm/string.hpp> static void install_line_handler(); diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h index 04fef089c..51b1f1ec6 100644 --- a/contrib/epee/tests/src/net/test_net.h +++ b/contrib/epee/tests/src/net/test_net.h @@ -29,7 +29,9 @@ #include <boost/thread.hpp> #include <boost/bind.hpp> -#include "net/levin_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "net/levin_protocol_handler.h" +#include "net/levin_protocol_handler_async.h" #include "storages/abstract_invoke.h" namespace epee diff --git a/contrib/valgrind/monero.supp b/contrib/valgrind/monero.supp index 16e34e82f..015b05a1c 100644 --- a/contrib/valgrind/monero.supp +++ b/contrib/valgrind/monero.supp @@ -17,3 +17,12 @@ fun:maybe_unlock_and_signal_one<boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex> > ... } + +{ + we leak the logger, for performance reasons in on-the-fly init + Memcheck:Leak + match-leak-kinds: definite + fun:_Znwm + fun:_ZN2el4base7Storage7getELPPEv + ... +} diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index d57f3f3a0..aec4a150d 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2191,17 +2191,13 @@ void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) # define ELPP_DEFAULT_LOGGING_FLAGS 0x0 #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) // Storage -el::base::type::StoragePointer getresetELPP(bool reset) +el::base::type::StoragePointer &el::base::Storage::getELPP() { - static el::base::type::StoragePointer p(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))); - if (reset) - p = NULL; - return p; -} -el::base::type::StoragePointer el::base::Storage::getELPP() -{ - return getresetELPP(false); + if (!el::base::elStorage) + el::base::elStorage = new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())); + return el::base::elStorage; } +static struct EnsureELPP { EnsureELPP() { el::base::Storage::getELPP(); } } ensureELPP; #if ELPP_ASYNC_LOGGING Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : #else @@ -2250,7 +2246,6 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : Storage::~Storage(void) { ELPP_INTERNAL_INFO(4, "Destroying storage"); - getresetELPP(true); #if ELPP_ASYNC_LOGGING ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 046252a5b..acf2a7674 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -552,7 +552,7 @@ typedef std::ostream ostream_t; typedef unsigned int EnumType; typedef unsigned short VerboseLevel; typedef unsigned long int LineNumber; -typedef std::shared_ptr<base::Storage> StoragePointer; +typedef base::Storage *StoragePointer; typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr; typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr; typedef std::shared_ptr<LoggerRegistrationCallback> LoggerRegistrationCallbackPtr; @@ -2734,7 +2734,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return it->second; } - static el::base::type::StoragePointer getELPP(); + static el::base::type::StoragePointer &getELPP(); private: base::RegisteredHitCounters* m_registeredHitCounters; @@ -4613,9 +4613,10 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ } #if ELPP_ASYNC_LOGGING -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) #else -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) #endif // ELPP_ASYNC_LOGGING #define INITIALIZE_NULL_EASYLOGGINGPP \ namespace el {\ diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 7118b0881..f13aa0cae 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -30,7 +30,6 @@ #pragma once -#include <list> #include <string> #include <exception> #include <boost/program_options.hpp> @@ -1307,11 +1306,11 @@ public: * get_output_data(const uint64_t& amount, const uint64_t& index) * but for a list of outputs rather than just one. * - * @param amount an output amount + * @param amounts an output amount, or as many as offsets * @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 uint64_t &amount, 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) = 0; /* * FIXME: Need to check with git blame and ask what this does to @@ -1406,6 +1405,13 @@ public: virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0; /** + * @brief prune output data for the given amount + * + * @param amount the amount for which to prune data + */ + virtual void prune_outputs(uint64_t amount) = 0; + + /** * @brief runs a function over all txpool transactions * * The subclass should run the passed function for each txpool tx it has diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index ea3638a85..4bfc80863 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -29,10 +29,8 @@ #include <boost/filesystem.hpp> #include <boost/format.hpp> -#include <boost/current_function.hpp> #include <memory> // std::unique_ptr #include <cstring> // memcpy -#include <random> #include "string_tools.h" #include "file_io_utils.h" @@ -1077,6 +1075,60 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str())); } +void BlockchainLMDB::prune_outputs(uint64_t amount) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + CURSOR(output_amounts); + CURSOR(output_txs); + + MINFO("Pruning outputs for amount " << amount); + + MDB_val v; + MDB_val_set(k, amount); + int result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return; + if (result) + throw0(DB_ERROR(lmdb_error("Error looking up outputs: ", result).c_str())); + + // gather output ids + mdb_size_t num_elems; + mdb_cursor_count(m_cur_output_amounts, &num_elems); + MINFO(num_elems << " outputs found"); + std::vector<uint64_t> output_ids; + output_ids.reserve(num_elems); + while (1) + { + const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data; + output_ids.push_back(okp->output_id); + MDEBUG("output id " << okp->output_id); + result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP); + if (result == MDB_NOTFOUND) + break; + if (result) + throw0(DB_ERROR(lmdb_error("Error counting outputs: ", result).c_str())); + } + if (output_ids.size() != num_elems) + throw0(DB_ERROR("Unexpected number of outputs")); + + result = mdb_cursor_del(m_cur_output_amounts, MDB_NODUPDATA); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting outputs: ", result).c_str())); + + for (uint64_t output_id: output_ids) + { + MDB_val_set(v, output_id); + result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (result) + throw0(DB_ERROR(lmdb_error("Error looking up output: ", result).c_str())); + result = mdb_cursor_del(m_cur_output_txs, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting output: ", result).c_str())); + } +} + void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -1350,6 +1402,15 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) #if VERSION > 0 else if (db_version < VERSION) { + if (mdb_flags & MDB_RDONLY) + { + txn.abort(); + mdb_env_close(m_env); + m_open = false; + MFATAL("Existing lmdb database needs to be converted, which cannot be done on a read-only database."); + MFATAL("Please run monerod once to convert the database."); + return; + } // Note that there was a schema change within version 0 as well. // See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10 // We don't handle the old format previous to that commit. @@ -2222,11 +2283,19 @@ uint64_t BlockchainLMDB::num_outputs() const TXN_PREFIX_RDONLY(); int result; - // get current height - MDB_stat db_stats; - if ((result = mdb_stat(m_txn, m_output_txs, &db_stats))) + RCURSOR(output_txs) + + uint64_t num = 0; + MDB_val k, v; + result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_LAST); + if (result == MDB_NOTFOUND) + num = 0; + else if (result == 0) + num = 1 + ((const outtx*)v.mv_data)->output_id; + else throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); - return db_stats.ms_entries; + + return num; } bool BlockchainLMDB::tx_exists(const crypto::hash& h) const @@ -3197,8 +3266,11 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 TXN_POSTFIX_RDONLY(); } -void BlockchainLMDB::get_output_key(const uint64_t &amount, 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) { + if (amounts.size() != 1 && amounts.size() != offsets.size()) + throw0(DB_ERROR("Invalid sizes of amounts and offets")); + LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); check_open(); @@ -3209,10 +3281,11 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui RCURSOR(output_amounts); - MDB_val_set(k, amount); - for (const uint64_t &index : offsets) + for (size_t i = 0; i < offsets.size(); ++i) { - MDB_val_set(v, index); + const uint64_t amount = amounts.size() == 1 ? amounts[0] : amounts[i]; + MDB_val_set(k, amount); + MDB_val_set(v, offsets[i]); auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH); if (get_result == MDB_NOTFOUND) @@ -3222,7 +3295,7 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui MDEBUG("Partial result: " << outputs.size() << "/" << offsets.size()); break; } - throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(index) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast<std::string>(height()) + ")").c_str())); + throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(offsets[i]) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast<std::string>(height()) + ")").c_str())); } else if (get_result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str())); @@ -4358,16 +4431,12 @@ void BlockchainLMDB::migrate_2_3() void BlockchainLMDB::migrate(const uint32_t oldversion) { - switch(oldversion) { - case 0: - migrate_0_1(); /* FALLTHRU */ - case 1: - migrate_1_2(); /* FALLTHRU */ - case 2: - migrate_2_3(); /* FALLTHRU */ - default: - ; - } + if (oldversion < 1) + migrate_0_1(); + if (oldversion < 2) + migrate_1_2(); + if (oldversion < 3) + migrate_2_3(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 7e76236a5..6db241240 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -243,7 +243,7 @@ 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 uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<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<output_data_t> &outputs, bool allow_partial = false); 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, @@ -345,6 +345,8 @@ private: void remove_output(const uint64_t amount, const uint64_t& out_index); + virtual void prune_outputs(uint64_t amount); + virtual void add_spent_key(const crypto::key_image& k_image); virtual void remove_spent_key(const crypto::key_image& k_image); diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index c9ad1cebe..ddf575c29 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -81,6 +81,17 @@ monero_private_headers(blockchain_usage +set(blockchain_prune_known_spent_data_sources + blockchain_prune_known_spent_data.cpp + ) + +set(blockchain_prune_known_spent_data_private_headers) + +monero_private_headers(blockchain_prune_known_spent_data + ${blockchain_prune_known_spent_data_private_headers}) + + + set(blockchain_ancestry_sources blockchain_ancestry.cpp ) @@ -265,3 +276,25 @@ set_property(TARGET blockchain_stats PROPERTY OUTPUT_NAME "monero-blockchain-stats") install(TARGETS blockchain_stats DESTINATION bin) + +monero_add_executable(blockchain_prune_known_spent_data + ${blockchain_prune_known_spent_data_sources} + ${blockchain_prune_known_spent_data_private_headers}) + +target_link_libraries(blockchain_prune_known_spent_data + PRIVATE + cryptonote_core + blockchain_db + p2p + version + epee + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +set_property(TARGET blockchain_prune_known_spent_data + PROPERTY + OUTPUT_NAME "monero-blockchain-prune-known-spent-data") +install(TARGETS blockchain_prune_known_spent_data DESTINATION bin) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index d2ce5cf76..73819bd25 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -59,6 +59,7 @@ static MDB_dbi dbi_relative_rings; static MDB_dbi dbi_outputs; static MDB_dbi dbi_processed_txidx; static MDB_dbi dbi_spent; +static MDB_dbi dbi_per_amount; static MDB_dbi dbi_ring_instances; static MDB_dbi dbi_stats; static MDB_env *env = NULL; @@ -238,7 +239,7 @@ static void init(std::string cache_filename) dbr = mdb_env_create(&env); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); - dbr = mdb_env_set_maxdbs(env, 6); + dbr = mdb_env_set_maxdbs(env, 7); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); const std::string actual_filename = get_cache_filename(cache_filename); dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664); @@ -265,6 +266,10 @@ static void init(std::string cache_filename) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); mdb_set_dupsort(txn, dbi_spent, compare_uint64); + dbr = mdb_dbi_open(txn, "per_amount", MDB_CREATE | MDB_INTEGERKEY, &dbi_per_amount); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_compare(txn, dbi_per_amount, compare_uint64); + dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); @@ -283,6 +288,7 @@ static void close() mdb_dbi_close(env, dbi_relative_rings); mdb_dbi_close(env, dbi_outputs); mdb_dbi_close(env, dbi_processed_txidx); + mdb_dbi_close(env, dbi_per_amount); mdb_dbi_close(env, dbi_spent); mdb_dbi_close(env, dbi_ring_instances); mdb_dbi_close(env, dbi_stats); @@ -585,6 +591,55 @@ static std::vector<output_data> get_spent_outputs(MDB_txn *txn) return outs; } +static void get_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t &total, uint64_t &spent) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + mdb_size_t count = 0; + k.mv_size = sizeof(uint64_t); + k.mv_data = (void*)&amount; + dbr = mdb_cursor_get(cur, &k, &v, MDB_SET); + if (dbr == MDB_NOTFOUND) + { + total = spent = 0; + } + else + { + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr))); + total = ((const uint64_t*)v.mv_data)[0]; + spent = ((const uint64_t*)v.mv_data)[1]; + } + mdb_cursor_close(cur); +} + +static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total, uint64_t spent) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + mdb_size_t count = 0; + k.mv_size = sizeof(uint64_t); + k.mv_data = (void*)&amount; + dbr = mdb_cursor_get(cur, &k, &v, MDB_SET); + if (dbr == 0) + { + total += ((const uint64_t*)v.mv_data)[0]; + spent += ((const uint64_t*)v.mv_data)[1]; + } + else + { + CHECK_AND_ASSERT_THROW_MES(dbr == MDB_NOTFOUND, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr))); + } + uint64_t data[2] = {total, spent}; + v.mv_size = 2 * sizeof(uint64_t); + v.mv_data = (void*)data; + dbr = mdb_cursor_put(cur, &k, &v, 0); + mdb_cursor_close(cur); +} + static uint64_t get_processed_txidx(const std::string &name) { MDB_txn *txn; @@ -1193,6 +1248,7 @@ int main(int argc, char* argv[]) for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool { std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen); for (const auto &in: tx.vin) { if (in.type() != typeid(txin_to_key)) @@ -1210,6 +1266,9 @@ int main(int argc, char* argv[]) std::vector<uint64_t> new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring); + uint64_t pa_total = 0, pa_spent = 0; + if (!opt_rct_only) + get_per_amount_outputs(txn, txin.amount, pa_total, pa_spent); if (n == 0 && ring_size == 1) { const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]); @@ -1237,6 +1296,21 @@ int main(int argc, char* argv[]) inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } + else if (n == 0 && !opt_rct_only && pa_spent + 1 == pa_total) + { + for (size_t o = 0; o < pa_total; ++o) + { + const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, o); + if (opt_verbose) + { + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to as many outputs of that amount being spent as exist so far"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } + blackballs.push_back(output); + if (add_spent_output(cur, output_data(txin.amount, o))) + inc_stat(txn, txin.amount ? "pre-rct-full-count" : "rct-full-count"); + } + } else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size()) { for (size_t o = 0; o < new_ring.size(); ++o) @@ -1299,9 +1373,28 @@ int main(int argc, char* argv[]) } } if (n == 0) + { set_relative_ring(txn, txin.k_image, new_ring); + if (!opt_rct_only) + inc_per_amount_outputs(txn, txin.amount, 0, 1); + } } set_processed_txidx(txn, canonical, start_idx+1); + if (!opt_rct_only) + { + for (const auto &out: tx.vout) + { + uint64_t amount = out.amount; + if (miner_tx && tx.version >= 2) + amount = 0; + + if (opt_rct_only && amount != 0) + continue; + if (out.target.type() != typeid(txout_to_key)) + continue; + inc_per_amount_outputs(txn, amount, 1, 0); + } + } ++records; if (records >= records_per_sync) @@ -1433,6 +1526,7 @@ skip_secondary_passes: { "pre-rct-ring-size-1", pre_rct }, { "rct-ring-size-1", rct }, { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, { "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct }, + { "pre-rct-full-count", pre_rct }, { "rct-full-count", rct }, { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, { "pre-rct-extra", pre_rct }, { "rct-ring-extra", rct }, { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index eae078ea2..2a8a6f494 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -764,7 +764,7 @@ int main(int argc, char* argv[]) #else const GetCheckpointsCallback& get_checkpoints = nullptr; #endif - if (!core.init(vm, nullptr, nullptr, get_checkpoints)) + if (!core.init(vm, nullptr, get_checkpoints)) { std::cerr << "Failed to initialize core" << ENDL; return 1; diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp new file mode 100644 index 000000000..f6136c1ba --- /dev/null +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -0,0 +1,305 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/algorithm/string.hpp> +#include "common/command_line.h" +#include "serialization/crypto.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/db_types.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" + +namespace po = boost::program_options; +using namespace epee; +using namespace cryptonote; + +static std::map<uint64_t, uint64_t> load_outputs(const std::string &filename) +{ + std::map<uint64_t, uint64_t> outputs; + uint64_t amount = std::numeric_limits<uint64_t>::max(); + FILE *f; + + f = fopen(filename.c_str(), "r"); + if (!f) + { + MERROR("Failed to load outputs from " << filename << ": " << strerror(errno)); + return {}; + } + while (1) + { + char s[256]; + if (!fgets(s, sizeof(s), f)) + break; + if (feof(f)) + break; + const size_t len = strlen(s); + if (len > 0 && s[len - 1] == '\n') + s[len - 1] = 0; + if (!s[0]) + continue; + std::pair<uint64_t, uint64_t> output; + uint64_t offset, num_offsets; + if (sscanf(s, "@%" PRIu64, &amount) == 1) + { + continue; + } + if (amount == std::numeric_limits<uint64_t>::max()) + { + MERROR("Bad format in " << filename); + continue; + } + if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits<uint64_t>::max() - offset) + { + outputs[amount] += num_offsets; + } + else if (sscanf(s, "%" PRIu64, &offset) == 1) + { + outputs[amount] += 1; + } + else + { + MERROR("Bad format in " << filename); + continue; + } + } + fclose(f); + return outputs; +} + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + std::string default_db_type = "lmdb"; + + std::string available_dbs = cryptonote::blockchain_db_types(", "); + available_dbs = "available: " + available_dbs; + + uint32_t log_level = 0; + + tools::on_startup(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor<std::string> arg_database = { + "database", available_dbs.c_str(), default_db_type + }; + const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output", false}; + const command_line::arg_descriptor<bool> arg_dry_run = {"dry-run", "Do not actually prune", false}; + const command_line::arg_descriptor<std::string> arg_input = {"input", "Path to the known spent outputs file"}; + + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_database); + command_line::add_arg(desc_cmd_sett, arg_verbose); + command_line::add_arg(desc_cmd_sett, arg_dry_run); + command_line::add_arg(desc_cmd_sett, arg_input); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + auto parser = po::command_line_parser(argc, argv).options(desc_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure(mlog_get_default_log_path("monero-blockchain-prune-known-spent-data.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 + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + + LOG_PRINT_L0("Starting..."); + + std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; + bool opt_verbose = command_line::get_arg(vm, arg_verbose); + bool opt_dry_run = command_line::get_arg(vm, arg_dry_run); + + std::string db_type = command_line::get_arg(vm, arg_database); + if (!cryptonote::blockchain_valid_db_type(db_type)) + { + std::cerr << "Invalid database type: " << db_type << std::endl; + return 1; + } + + const std::string input = command_line::get_arg(vm, arg_input); + + LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); + std::unique_ptr<Blockchain> core_storage; + tx_memory_pool m_mempool(*core_storage); + core_storage.reset(new Blockchain(m_mempool)); + BlockchainDB *db = new_db(db_type); + if (db == NULL) + { + LOG_ERROR("Attempted to use non-existent database type: " << db_type); + throw std::runtime_error("Attempting to use non-existent database type"); + } + LOG_PRINT_L0("database: " << db_type); + + const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string(); + LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); + + try + { + db->open(filename, 0); + } + catch (const std::exception& e) + { + LOG_PRINT_L0("Error opening database: " << e.what()); + return 1; + } + r = core_storage->init(db, net_type); + + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); + LOG_PRINT_L0("Source blockchain storage initialized OK"); + + std::map<uint64_t, uint64_t> known_spent_outputs; + if (input.empty()) + { + std::map<uint64_t, std::pair<uint64_t, uint64_t>> outputs; + + LOG_PRINT_L0("Scanning for known spent data..."); + db->for_all_transactions([&](const crypto::hash &txid, const cryptonote::transaction &tx){ + const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen); + for (const auto &in: tx.vin) + { + if (in.type() != typeid(txin_to_key)) + continue; + const auto &txin = boost::get<txin_to_key>(in); + if (txin.amount == 0) + continue; + + outputs[txin.amount].second++; + } + + for (const auto &out: tx.vout) + { + uint64_t amount = out.amount; + if (miner_tx && tx.version >= 2) + amount = 0; + if (amount == 0) + continue; + if (out.target.type() != typeid(txout_to_key)) + continue; + + outputs[amount].first++; + } + return true; + }, true); + + for (const auto &i: outputs) + { + known_spent_outputs[i.first] = i.second.second; + } + } + else + { + LOG_PRINT_L0("Loading known spent data..."); + known_spent_outputs = load_outputs(input); + } + + LOG_PRINT_L0("Pruning known spent data..."); + + bool stop_requested = false; + tools::signal_handler::install([&stop_requested](int type) { + stop_requested = true; + }); + + db->batch_start(); + + size_t num_total_outputs = 0, num_prunable_outputs = 0, num_known_spent_outputs = 0, num_eligible_outputs = 0, num_eligible_known_spent_outputs = 0; + for (auto i = known_spent_outputs.begin(); i != known_spent_outputs.end(); ++i) + { + uint64_t num_outputs = db->get_num_outputs(i->first); + num_total_outputs += num_outputs; + num_known_spent_outputs += i->second; + if (i->first == 0 || is_valid_decomposed_amount(i->first)) + { + if (opt_verbose) + MINFO("Ignoring output value " << i->first << ", with " << num_outputs << " outputs"); + continue; + } + num_eligible_outputs += num_outputs; + num_eligible_known_spent_outputs += i->second; + if (opt_verbose) + MINFO(i->first << ": " << i->second << "/" << num_outputs); + if (num_outputs > i->second) + continue; + if (num_outputs && num_outputs < i->second) + { + MERROR("More outputs are spent than known for amount " << i->first << ", not touching"); + continue; + } + if (opt_verbose) + MINFO("Pruning data for " << num_outputs << " outputs"); + if (!opt_dry_run) + db->prune_outputs(i->first); + num_prunable_outputs += i->second; + } + + db->batch_stop(); + + MINFO("Total outputs: " << num_total_outputs); + MINFO("Known spent outputs: " << num_known_spent_outputs); + MINFO("Eligible outputs: " << num_eligible_outputs); + MINFO("Eligible known spent outputs: " << num_eligible_known_spent_outputs); + MINFO("Prunable outputs: " << num_prunable_outputs); + + LOG_PRINT_L0("Blockchain known spent data pruned OK"); + core_storage->deinit(); + return 0; + + CATCH_ENTRY("Error", 1); +} diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 716b33cae..aae8f333b 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -234,7 +234,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' } time_t tt = blk.timestamp; char timebuf[64]; - gmtime_r(&tt, &currtm); + epee::misc_utils::get_gmt_time(tt, currtm); if (!prevtm.tm_year) prevtm = currtm; // catch change of day diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 6251fcc91..1807d44d9 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -28,17 +28,15 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "include_base_utils.h" - -using namespace epee; - #include "checkpoints.h" #include "common/dns_utils.h" -#include "include_base_utils.h" #include "string_tools.h" #include "storages/portable_storage_template_helper.h" // epee json include #include "serialization/keyvalue_serialization.h" +#include <vector> + +using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "checkpoints" diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index 61be2c27a..ad2b44d1a 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -30,7 +30,6 @@ #pragma once #include <map> -#include <vector> #include "misc_log_ex.h" #include "crypto/hash.h" #include "cryptonote_config.h" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aed9bfee7..5da23c944 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -43,7 +43,8 @@ set(common_sources spawn.cpp threadpool.cpp updates.cpp - aligned.c) + aligned.c + combinator.cpp) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -62,7 +63,6 @@ set(common_private_headers error.h expect.h http_connection.h - int-util.h notify.h pod-class.h rpc_client.h @@ -77,7 +77,8 @@ set(common_private_headers stack_trace.h threadpool.h updates.h - aligned.h) + aligned.h + combinator.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/base58.cpp b/src/common/base58.cpp index b28a04f20..3562af486 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -36,7 +36,6 @@ #include "crypto/hash.h" #include "int-util.h" -#include "util.h" #include "varint.h" namespace tools @@ -235,7 +234,7 @@ namespace tools return encode(buf); } - bool decode_addr(std::string addr, uint64_t& tag, std::string& data) + bool decode_addr(const std::string &addr, uint64_t& tag, std::string& data) { std::string addr_data; bool r = decode(addr, addr_data); diff --git a/src/common/base58.h b/src/common/base58.h index 02ca96956..69611859d 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -41,6 +41,6 @@ namespace tools bool decode(const std::string& enc, std::string& data); std::string encode_addr(uint64_t tag, const std::string& data); - bool decode_addr(std::string addr, uint64_t& tag, std::string& data); + bool decode_addr(const std::string &addr, uint64_t& tag, std::string& data); } } diff --git a/src/common/combinator.cpp b/src/common/combinator.cpp new file mode 100644 index 000000000..cb4fbc908 --- /dev/null +++ b/src/common/combinator.cpp @@ -0,0 +1,50 @@ +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "combinator.h" + +namespace tools { + +uint64_t combinations_count(uint32_t k, uint32_t n) +{ + if (k > n) { + throw std::runtime_error("k must not be greater than n"); + } + + uint64_t c = 1; + for (uint64_t i = 1; i <= k; ++i) { + c *= n--; + c /= i; + } + + return c; +} + +} diff --git a/src/common/combinator.h b/src/common/combinator.h new file mode 100644 index 000000000..72c6800d5 --- /dev/null +++ b/src/common/combinator.h @@ -0,0 +1,96 @@ +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include <iostream> +#include <vector> + +namespace tools { + +uint64_t combinations_count(uint32_t k, uint32_t n); + +template<typename T> +class Combinator { +public: + Combinator(const std::vector<T>& v) : origin(v) { } + + std::vector<std::vector<T>> combine(size_t k); + +private: + void doCombine(size_t from, size_t k); + + std::vector<T> origin; + std::vector<std::vector<T>> combinations; + std::vector<size_t> current; +}; + +template<typename T> +std::vector<std::vector<T>> Combinator<T>::combine(size_t k) +{ + if (k > origin.size()) + { + throw std::runtime_error("k must be smaller than elements number"); + } + + if (k == 0) + { + throw std::runtime_error("k must be greater than zero"); + } + + combinations.clear(); + doCombine(0, k); + return combinations; +} + +template<typename T> +void Combinator<T>::doCombine(size_t from, size_t k) +{ + current.push_back(0); + + for (size_t i = from; i <= origin.size() - k; ++i) + { + current.back() = i; + + if (k > 1) { + doCombine(i + 1, k - 1); + } else { + std::vector<T> comb; + for (auto ind: current) { + comb.push_back(origin[ind]); + } + combinations.push_back(comb); + } + } + + current.pop_back(); +} + +} //namespace tools diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 7980b381f..35135ea18 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -31,10 +31,7 @@ #include "command_line.h" #include <boost/algorithm/string/compare.hpp> #include <boost/algorithm/string/predicate.hpp> -#include <unordered_set> #include "common/i18n.h" -#include "cryptonote_config.h" -#include "string_tools.h" namespace command_line { diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 606a2c7b7..417b5b4ac 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -33,13 +33,11 @@ #include <stdlib.h> #include "include_base_utils.h" #include <random> -#include <boost/filesystem/fstream.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/optional.hpp> using namespace epee; -namespace bf = boost::filesystem; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.dns" @@ -305,7 +303,7 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result)) { - dnssec_available = (result->secure || (!result->secure && result->bogus)); + dnssec_available = (result->secure || result->bogus); dnssec_valid = result->secure && !result->bogus; if (result->havedata) { diff --git a/src/common/download.cpp b/src/common/download.cpp index 6698a5abf..58ce0595f 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -29,10 +29,7 @@ #include <string> #include <atomic> #include <boost/filesystem.hpp> -#include <boost/asio.hpp> #include <boost/thread/thread.hpp> -#include "cryptonote_config.h" -#include "include_base_utils.h" #include "file_io_utils.h" #include "net/http_client.h" #include "download.h" diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 9fc6be261..554dd832b 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -55,7 +55,8 @@ public: { if (m_ok) { - mp_http_client->disconnect(); + try { mp_http_client->disconnect(); } + catch (...) { /* do not propagate through dtor */ } } } diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 4a89876fb..ffe8d8b52 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -31,9 +31,7 @@ #include <ctype.h> #include <string> #include <map> -#include "include_base_utils.h" #include "file_io_utils.h" -#include "common/util.h" #include "common/i18n.h" #include "translation_files.h" diff --git a/src/common/password.cpp b/src/common/password.cpp index b3c51128f..5f5cb800a 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -31,7 +31,6 @@ #include "password.h" #include <iostream> -#include <memory.h> #include <stdio.h> #if defined(_WIN32) @@ -42,8 +41,6 @@ #include <unistd.h> #endif -#include "memwipe.h" - #define EOT 0x4 namespace diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index d887a13c9..42f439ad8 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -101,13 +101,13 @@ public: MCLOG_FILE(m_log_level, "msgwriter", m_oss.str()); + PAUSE_READLINE(); if (epee::console_color_default == m_color) { std::cout << m_oss.str(); } else { - PAUSE_READLINE(); set_console_color(m_color, m_bright); std::cout << m_oss.str(); epee::reset_console_color(); diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 37825e31d..cbf7163c5 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -28,10 +28,6 @@ #include "misc_log_ex.h" #include "common/threadpool.h" -#include <cassert> -#include <limits> -#include <stdexcept> - #include "cryptonote_config.h" #include "common/util.h" diff --git a/src/common/util.cpp b/src/common/util.cpp index 58b0d8210..448f792ff 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -58,6 +58,7 @@ #include "include_base_utils.h" #include "file_io_utils.h" #include "wipeable_string.h" +#include "misc_os_dependent.h" using namespace epee; #include "crypto/crypto.h" @@ -1025,4 +1026,15 @@ std::string get_nix_version_display_string() #endif } + std::string get_human_readable_timestamp(uint64_t ts) + { + char buffer[64]; + if (ts < 1234567890) + return "<unknown>"; + time_t tt = ts; + struct tm tm; + misc_utils::get_gmt_time(tt, tm); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); + return std::string(buffer); + } } diff --git a/src/common/util.h b/src/common/util.h index 1c5c5f4e7..d5aca15d1 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -242,4 +242,6 @@ namespace tools #endif void closefrom(int fd); + + std::string get_human_readable_timestamp(uint64_t ts); } diff --git a/src/crypto/aesb.c b/src/crypto/aesb.c index 8a22a4b93..efdeef8d1 100644 --- a/src/crypto/aesb.c +++ b/src/crypto/aesb.c @@ -19,7 +19,7 @@ Issue Date: 20/12/2007 */ #include <stdint.h> -#include "common/int-util.h" +#include "int-util.h" #if defined(__cplusplus) extern "C" diff --git a/src/crypto/chacha.c b/src/crypto/chacha.c index 5d3edb98d..d734e8b1b 100644 --- a/src/crypto/chacha.c +++ b/src/crypto/chacha.c @@ -11,7 +11,7 @@ Public domain. #endif #include "chacha.h" -#include "common/int-util.h" +#include "int-util.h" #include "warnings.h" /* diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index ad7721cf0..ddf072f68 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -34,7 +34,6 @@ #include <cstdint> #include <cstdlib> #include <cstring> -#include <memory> #include <boost/thread/mutex.hpp> #include <boost/thread/lock_guard.hpp> #include <boost/shared_ptr.hpp> diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 33cc0a25a..f22df1230 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -32,14 +32,11 @@ #include <cstddef> #include <iostream> -#include <boost/thread/mutex.hpp> -#include <boost/thread/lock_guard.hpp> #include <boost/optional.hpp> #include <type_traits> #include <vector> #include "common/pod-class.h" -#include "common/util.h" #include "memwipe.h" #include "mlocker.h" #include "generic-ops.h" diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index 53594c569..12472dced 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -29,7 +29,7 @@ #ifndef __tables_h #define __tables_h -#include "common/int-util.h" +#include "int-util.h" #if BYTE_ORDER == LITTLE_ENDIAN diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d77d55cf3..77b52e2d4 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -37,7 +37,7 @@ #include <stddef.h> #include <stdint.h> -#include "common/int-util.h" +#include "int-util.h" #include "warnings.h" static inline void *padd(void *p, size_t i) { diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index b095b5ce2..170911262 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -5,7 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include "common/int-util.h" +#include "int-util.h" #include "hash-ops.h" #include "keccak.h" diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index a50a28e6b..8a1640e57 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -114,7 +114,7 @@ typedef uint64_t u64b_t; /* 64-bit unsigned integer */ #ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */ -#include "common/int-util.h" +#include "int-util.h" #define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ #define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index ed61e1017..ae0bd4e98 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -35,7 +35,7 @@ #include <stdio.h> #include <unistd.h> -#include "common/int-util.h" +#include "int-util.h" #include "hash-ops.h" #include "oaes_lib.h" #include "variant2_int_sqrt.h" @@ -109,8 +109,8 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \ xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \ xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \ - division_result = state.hs.w[12]; \ - sqrt_result = state.hs.w[13]; \ + division_result = SWAP64LE(state.hs.w[12]); \ + sqrt_result = SWAP64LE(state.hs.w[13]); \ } while (0) #define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \ @@ -145,30 +145,31 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \ \ uint64_t b1[2]; \ - memcpy(b1, b + 16, 16); \ - chunk1[0] = chunk3[0] + b1[0]; \ - chunk1[1] = chunk3[1] + b1[1]; \ + memcpy_swap64le(b1, b + 16, 2); \ + chunk1[0] = SWAP64LE(SWAP64LE(chunk3[0]) + b1[0]); \ + chunk1[1] = SWAP64LE(SWAP64LE(chunk3[1]) + b1[1]); \ \ uint64_t a0[2]; \ - memcpy(a0, a, 16); \ - chunk3[0] = chunk2[0] + a0[0]; \ - chunk3[1] = chunk2[1] + a0[1]; \ + memcpy_swap64le(a0, a, 2); \ + chunk3[0] = SWAP64LE(SWAP64LE(chunk2[0]) + a0[0]); \ + chunk3[1] = SWAP64LE(SWAP64LE(chunk2[1]) + a0[1]); \ \ uint64_t b0[2]; \ - memcpy(b0, b, 16); \ - chunk2[0] = chunk1_old[0] + b0[0]; \ - chunk2[1] = chunk1_old[1] + b0[1]; \ + memcpy_swap64le(b0, b, 2); \ + chunk2[0] = SWAP64LE(SWAP64LE(chunk1_old[0]) + b0[0]); \ + chunk2[1] = SWAP64LE(SWAP64LE(chunk1_old[1]) + b0[1]); \ } while (0) #define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ - ((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \ + uint64_t tmpx = division_result ^ (sqrt_result << 32); \ + ((uint64_t*)(b))[0] ^= SWAP64LE(tmpx); \ { \ - const uint64_t dividend = ((uint64_t*)(ptr))[1]; \ - const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ + const uint64_t dividend = SWAP64LE(((uint64_t*)(ptr))[1]); \ + const uint32_t divisor = (SWAP64LE(((uint64_t*)(ptr))[0]) + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ division_result = ((uint32_t)(dividend / divisor)) + \ (((uint64_t)(dividend % divisor)) << 32); \ } \ - const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result + const uint64_t sqrt_input = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result #define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ do if (variant >= 2) \ @@ -207,10 +208,10 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #define VARIANT2_2() \ do if (variant >= 2) \ { \ - *U64(hp_state + (j ^ 0x10)) ^= hi; \ - *(U64(hp_state + (j ^ 0x10)) + 1) ^= lo; \ - hi ^= *U64(hp_state + (j ^ 0x20)); \ - lo ^= *(U64(hp_state + (j ^ 0x20)) + 1); \ + *U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ + *(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ + hi ^= SWAP64LE(*U64(hp_state + (j ^ 0x20))); \ + lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ } while (0) @@ -1408,7 +1409,7 @@ static void (*const extra_hashes[4])(const void *, size_t, char *) = { hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; -static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } +static size_t e2i(const uint8_t* a, size_t count) { return (SWAP64LE(*((uint64_t*)a)) / AES_BLOCK_SIZE) & (count - 1); } static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { uint64_t a0, b0; diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index b0eabb0aa..645e99f91 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -175,7 +175,15 @@ namespace cryptonote END_SERIALIZE() public: - transaction_prefix(){} + transaction_prefix(){ set_null(); } + void set_null() + { + version = 1; + unlock_time = 0; + vin.clear(); + vout.clear(); + extra.clear(); + } }; class transaction: public transaction_prefix @@ -302,17 +310,12 @@ namespace cryptonote inline transaction::~transaction() { - //set_null(); } inline void transaction::set_null() { - version = 1; - unlock_time = 0; - vin.clear(); - vout.clear(); - extra.clear(); + transaction_prefix::set_null(); signatures.clear(); rct_signatures.type = rct::RCTTypeNull; set_hash_valid(false); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index c4e10851e..23a5bd5bd 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -40,7 +40,7 @@ using namespace epee; #include "misc_language.h" #include "common/base58.h" #include "crypto/hash.h" -#include "common/int-util.h" +#include "int-util.h" #include "common/dns_utils.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -322,7 +322,7 @@ namespace cryptonote { } //-------------------------------------------------------------------------------- -bool parse_hash256(const std::string str_hash, crypto::hash& hash) +bool parse_hash256(const std::string &str_hash, crypto::hash& hash) { std::string buf; bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index c804a88fa..0b8131a7a 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -124,5 +124,5 @@ namespace cryptonote { bool operator ==(const cryptonote::block& a, const cryptonote::block& b); } -bool parse_hash256(const std::string str_hash, crypto::hash& hash); +bool parse_hash256(const std::string &str_hash, crypto::hash& hash); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index e26aac76b..55d7d23f8 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -28,9 +28,6 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "include_base_utils.h" -using namespace epee; - #include <atomic> #include <boost/algorithm/string.hpp> #include "wipeable_string.h" @@ -42,6 +39,8 @@ using namespace epee; #include "crypto/hash.h" #include "ringct/rctSigs.h" +using namespace epee; + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" @@ -883,7 +882,7 @@ namespace cryptonote { if (decimal_point == (unsigned int)-1) decimal_point = default_decimal_point; - switch (std::atomic_load(&default_decimal_point)) + switch (decimal_point) { case 12: return "monero"; @@ -896,7 +895,7 @@ namespace cryptonote case 0: return "piconero"; default: - ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); } } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index cb2a00a12..55e3e93b3 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -34,7 +34,7 @@ #include <cstdint> #include <vector> -#include "common/int-util.h" +#include "int-util.h" #include "crypto/hash.h" #include "cryptonote_config.h" #include "difficulty.h" diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index d8ca2dd35..f4de2ed7e 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -33,8 +33,6 @@ #include <boost/utility/value_init.hpp> #include <boost/interprocess/detail/atomic.hpp> #include <boost/algorithm/string.hpp> -#include <boost/limits.hpp> -#include "include_base_utils.h" #include "misc_language.h" #include "syncobj.h" #include "cryptonote_basic_impl.h" @@ -54,19 +52,22 @@ #include <mach/mach_host.h> #include <AvailabilityMacros.h> #include <TargetConditionals.h> -#endif - -#ifdef __FreeBSD__ -#include <devstat.h> -#include <errno.h> -#include <fcntl.h> -#include <machine/apm_bios.h> -#include <stdio.h> -#include <sys/resource.h> -#include <sys/sysctl.h> -#include <sys/times.h> -#include <sys/types.h> -#include <unistd.h> +#elif defined(__linux__) + #include <unistd.h> + #include <sys/resource.h> + #include <sys/times.h> + #include <time.h> +#elif defined(__FreeBSD__) + #include <devstat.h> + #include <errno.h> + #include <fcntl.h> + #include <machine/apm_bios.h> + #include <stdio.h> + #include <sys/resource.h> + #include <sys/sysctl.h> + #include <sys/times.h> + #include <sys/types.h> + #include <unistd.h> #endif #undef MONERO_DEFAULT_LOG_CATEGORY @@ -146,7 +147,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- bool miner::request_block_template() { - block bl = AUTO_VAL_INIT(bl); + block bl; difficulty_type di = AUTO_VAL_INIT(di); uint64_t height = AUTO_VAL_INIT(height); uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 2bff784c7..e16d9f3b8 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -38,11 +38,6 @@ #include "math_helper.h" #ifdef _WIN32 #include <windows.h> -#elif defined(__linux__) -#include <unistd.h> -#include <sys/resource.h> -#include <sys/times.h> -#include <time.h> #endif namespace cryptonote diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f3105114e..c6a3c6180 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -44,7 +44,7 @@ #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" -#include "common/int-util.h" +#include "int-util.h" #include "common/threadpool.h" #include "common/boost_serialization_helper.h" #include "warnings.h" @@ -229,7 +229,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke { try { - m_db->get_output_key(tx_in_to_key.amount, absolute_offsets, outputs, true); + m_db->get_output_key(epee::span<const uint64_t>(&tx_in_to_key.amount, 1), absolute_offsets, outputs, true); if (absolute_offsets.size() != outputs.size()) { MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount); @@ -255,7 +255,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke add_offsets.push_back(absolute_offsets[i]); try { - m_db->get_output_key(tx_in_to_key.amount, add_offsets, add_outputs, true); + m_db->get_output_key(epee::span<const uint64_t>(&tx_in_to_key.amount, 1), add_offsets, add_outputs, true); if (add_offsets.size() != add_outputs.size()) { MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount); @@ -404,7 +404,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if(!m_db->height()) { MINFO("Blockchain not loaded, generating genesis block."); - block bl = boost::value_initialized<block>(); + block bl; block_verification_context bvc = boost::value_initialized<block_verification_context>(); generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); add_new_block(bl, bvc); @@ -576,6 +576,38 @@ bool Blockchain::deinit() return true; } //------------------------------------------------------------------ +// This function removes blocks from the top of blockchain. +// It starts a batch and calls private method pop_block_from_blockchain(). +void Blockchain::pop_blocks(uint64_t nblocks) +{ + uint64_t i; + CRITICAL_REGION_LOCAL(m_tx_pool); + CRITICAL_REGION_LOCAL1(m_blockchain_lock); + + while (!m_db->batch_start()) + { + m_blockchain_lock.unlock(); + m_tx_pool.unlock(); + epee::misc_utils::sleep_no_w(1000); + m_tx_pool.lock(); + m_blockchain_lock.lock(); + } + + try + { + for (i=0; i < nblocks; ++i) + { + pop_block_from_blockchain(); + } + } + catch (const std::exception& e) + { + LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what()); + } + + m_db->batch_stop(); +} +//------------------------------------------------------------------ // This function tells BlockchainDB to remove the top block from the // blockchain and then returns all transactions (except the miner tx, of course) // from it to the tx_pool @@ -929,7 +961,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, m_hardfork->reorganize_from_chain_height(rollback_height); MINFO("Rollback to height " << rollback_height << " was successful."); - if (original_chain.size()) + if (!original_chain.empty()) { MINFO("Restoration to previous blockchain successful as well."); } @@ -1484,7 +1516,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // if block to be added connects to known blocks that aren't part of the // main chain -- that is, if we're adding on to an alternate chain - if(alt_chain.size()) + if(!alt_chain.empty()) { // make sure alt chain doesn't somehow start past the end of the main chain CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height"); @@ -1767,16 +1799,34 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA res.outs.clear(); res.outs.reserve(req.outputs.size()); + + std::vector<cryptonote::output_data_t> data; try { + std::vector<uint64_t> amounts, offsets; + amounts.reserve(req.outputs.size()); + offsets.reserve(req.outputs.size()); for (const auto &i: req.outputs) { - // get tx_hash, tx_out_index from DB - const output_data_t od = m_db->get_output_key(i.amount, i.index); - tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); - bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); + amounts.push_back(i.amount); + offsets.push_back(i.index); + } + m_db->get_output_key(epee::span<const uint64_t>(amounts.data(), amounts.size()), offsets, data); + if (data.size() != req.outputs.size()) + { + MERROR("Unexpected output data size: expected " << req.outputs.size() << ", got " << data.size()); + return false; + } + for (const auto &t: data) + res.outs.push_back({t.pubkey, t.commitment, is_tx_spendtime_unlocked(t.unlock_time), t.height, crypto::null_hash}); - res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first}); + if (req.get_txid) + { + for (size_t i = 0; i < req.outputs.size(); ++i) + { + tx_out_index toi = m_db->get_output_tx_and_index(req.outputs[i].amount, req.outputs[i].index); + res.outs[i].txid = toi.first; + } } } catch (const std::exception &e) @@ -1805,6 +1855,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, case STAGENET: start_height = stagenet_hard_forks[3].height; break; case TESTNET: start_height = testnet_hard_forks[3].height; break; case MAINNET: start_height = mainnet_hard_forks[3].height; break; + case FAKECHAIN: start_height = 0; break; default: return false; } } @@ -1823,18 +1874,21 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t db_height = m_db->height(); if (db_height == 0) return false; - if (to_height == 0) - to_height = db_height - 1; if (start_height >= db_height || to_height >= db_height) return false; if (amount == 0) { std::vector<uint64_t> heights; heights.reserve(to_height + 1 - start_height); - for (uint64_t h = start_height; h <= to_height; ++h) + uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + for (uint64_t h = real_start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); - base = 0; + if (start_height > 0) + { + base = distribution[0]; + distribution.erase(distribution.begin()); + } return true; } else @@ -1853,7 +1907,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc // make sure the request includes at least the genesis block, otherwise // how can we expect to sync from the client that the block list came from? - if(!qblock_ids.size()) + if(qblock_ids.empty()) { MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << ", dropping connection"); return false; @@ -3701,7 +3755,7 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints) } //------------------------------------------------------------------ -void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const +void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const { TIME_MEASURE_START(t); slow_hash_allocate_state(); @@ -3787,11 +3841,33 @@ 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 +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 { try { - m_db->get_output_key(amount, offsets, outputs, true); + 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) { @@ -3906,6 +3982,34 @@ 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__); @@ -3952,42 +4056,40 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete m_blockchain_lock.lock(); } - if ((m_db->height() + blocks_entry.size()) < m_blocks_hash_check.size()) + const uint64_t height = m_db->height(); + if ((height + blocks_entry.size()) < m_blocks_hash_check.size()) return true; bool blocks_exist = false; tools::threadpool& tpool = tools::threadpool::getInstance(); - uint64_t threads = tpool.get_max_concurrency(); + unsigned threads = tpool.get_max_concurrency(); + std::vector<block> blocks; + blocks.resize(blocks_entry.size()); - if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1) + if (1) { // limit threads, default limit = 4 if(threads > m_max_prepare_blocks_threads) threads = m_max_prepare_blocks_threads; - uint64_t height = m_db->height(); - int batches = blocks_entry.size() / threads; - int extra = blocks_entry.size() % threads; + unsigned int batches = blocks_entry.size() / threads; + unsigned int extra = blocks_entry.size() % threads; MDEBUG("block_batches: " << batches); std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads); - std::vector < std::vector < block >> blocks(threads); auto it = blocks_entry.begin(); + unsigned blockidx = 0; - for (uint64_t i = 0; i < threads; i++) + for (unsigned i = 0; i < threads; i++) { - blocks[i].reserve(batches + 1); - for (int j = 0; j < batches; j++) + for (unsigned int j = 0; j < batches; j++, ++blockidx) { - block block; + block &block = blocks[blockidx]; if (!parse_and_validate_block_from_blob(it->block, block)) - { - std::advance(it, 1); - continue; - } + return false; // check first block and skip all blocks if its not chained properly - if (i == 0 && j == 0) + if (blockidx == 0) { crypto::hash tophash = m_db->top_block_hash(); if (block.prev_id != tophash) @@ -4002,20 +4104,16 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete break; } - blocks[i].push_back(std::move(block)); std::advance(it, 1); } } - for (int i = 0; i < extra && !blocks_exist; i++) + for (unsigned i = 0; i < extra && !blocks_exist; i++, blockidx++) { - block block; + block &block = blocks[blockidx]; if (!parse_and_validate_block_from_blob(it->block, block)) - { - std::advance(it, 1); - continue; - } + return false; if (have_block(get_block_hash(block))) { @@ -4023,7 +4121,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete break; } - blocks[i].push_back(std::move(block)); std::advance(it, 1); } @@ -4032,10 +4129,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete m_blocks_longhash_table.clear(); uint64_t thread_height = height; tools::threadpool::waiter waiter; - for (uint64_t i = 0; i < threads; i++) + for (unsigned int i = 0; i < threads; i++) { - tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])), true); - thread_height += blocks[i].size(); + unsigned nblocks = batches; + if (i < extra) + ++nblocks; + tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, epee::span<const block>(&blocks[i], nblocks), std::ref(maps[i])), true); + thread_height += nblocks; } waiter.wait(&tpool); @@ -4078,7 +4178,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; + std::map<uint64_t, std::vector<output_data_t>> tx_map, extra_tx_map; std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs); #define SCAN_TABLE_QUIT(m) \ @@ -4089,12 +4189,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete } while(0); \ // generate sorted tables for all amounts and absolute offsets - size_t tx_index = 0; + size_t tx_index = 0, block_index = 0; for (const auto &entry : blocks_entry) { 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()) @@ -4153,7 +4255,10 @@ 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; } // sort and remove duplicate absolute_offsets in offset_map @@ -4176,7 +4281,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])), true); + 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); } waiter.wait(&tpool); } @@ -4185,7 +4290,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]); + output_scan_worker(amount, offset_map[amount], tx_map[amount], extra_tx_map[amount]); } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index dfe833fb4..877828f81 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -918,7 +918,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> &outputs, const std::vector<output_data_t> &extra_tx_map) const; /** * @brief computes the "short" and "long" hashes for a set of blocks @@ -927,7 +927,7 @@ namespace cryptonote * @param blocks the blocks to be hashed * @param map return-by-reference the hashes for each block */ - void block_longhash_worker(uint64_t height, const std::vector<block> &blocks, + void block_longhash_worker(uint64_t height, const epee::span<const block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const; /** @@ -967,6 +967,13 @@ namespace cryptonote */ std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const; + /** + * @brief removes blocks from the top of the blockchain + * + * @param nblocks number of blocks to be removed + */ + void pop_blocks(uint64_t nblocks); + private: // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 10ab3fe65..f3249ea92 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -30,13 +30,11 @@ #include <boost/algorithm/string.hpp> -#include "include_base_utils.h" #include "string_tools.h" using namespace epee; #include <unordered_set> #include "cryptonote_core.h" -#include "common/command_line.h" #include "common/util.h" #include "common/updates.h" #include "common/download.h" @@ -45,7 +43,6 @@ using namespace epee; #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" -#include "cryptonote_tx_utils.h" #include "misc_language.h" #include "file_io_utils.h" #include <csignal> @@ -163,6 +160,11 @@ namespace cryptonote , "Relay blocks as normal blocks" , false }; + static const command_line::arg_descriptor<bool> arg_pad_transactions = { + "pad-transactions" + , "Pad relayed transactions to help defend against traffic volume analysis" + , false + }; static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = { "max-txpool-weight" , "Set maximum txpool weight in bytes." @@ -188,7 +190,8 @@ namespace cryptonote m_disable_dns_checkpoints(false), m_update_download(0), m_nettype(UNDEFINED), - m_update_available(false) + m_update_available(false), + m_pad_transactions(false) { m_checkpoints_updating.clear(); set_cryptonote_protocol(pprotocol); @@ -247,6 +250,7 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core::stop() { + m_miner.stop(); m_blockchain_storage.cancel(); tools::download_async_handle handle; @@ -282,6 +286,7 @@ namespace cryptonote command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_max_txpool_weight); + command_line::add_arg(desc, arg_pad_transactions); command_line::add_arg(desc, arg_block_notify); miner::init_options(desc); @@ -320,6 +325,7 @@ namespace cryptonote set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks); + m_pad_transactions = get_arg(vm, arg_pad_transactions); m_offline = get_arg(vm, arg_offline); m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints); if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks)) @@ -389,7 +395,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) + bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) { start_time = std::time(nullptr); @@ -399,10 +405,6 @@ namespace cryptonote m_nettype = FAKECHAIN; } bool r = handle_command_line(vm); - std::string m_config_folder_mempool = m_config_folder; - - if (config_subdir) - m_config_folder_mempool = m_config_folder_mempool + "/" + config_subdir; std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); @@ -834,7 +836,7 @@ namespace cryptonote TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); - struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; }; + struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; }; std::vector<result> results(tx_blobs.size()); tvc.resize(tx_blobs.size()); @@ -900,13 +902,15 @@ namespace cryptonote bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { - if (already_have[i]) - continue; if (!results[i].res) { ok = false; continue; } + if (keeped_by_block) + get_blockchain_storage().on_new_tx_from_block(results[i].tx); + if (already_have[i]) + continue; const size_t weight = get_transaction_weight(results[i].tx, it->size()); ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay); @@ -1137,9 +1141,6 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - if (keeped_by_block) - get_blockchain_storage().on_new_tx_from_block(tx); - if(m_mempool.have_tx(tx_hash)) { LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 2eb6c842b..cc53fce58 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -34,7 +34,6 @@ #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> -#include <boost/interprocess/sync/file_lock.hpp> #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" @@ -242,13 +241,12 @@ namespace cryptonote * a miner instance with parameters given on the command line (or defaults) * * @param vm command line parameters - * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); + bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @copydoc Blockchain::reset_and_set_genesis_block @@ -757,6 +755,13 @@ namespace cryptonote bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; } /** + * @brief get whether transaction relay should be padded + * + * @return whether transaction relay should be padded + */ + bool pad_transactions() const { return m_pad_transactions; } + + /** * @brief check a set of hashes against the precompiled hash set * * @return number of usable blocks @@ -1015,6 +1020,7 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; + bool m_pad_transactions; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4fc2736a6..f443d638c 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -198,7 +198,7 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) { hw::device &hwdev = sender_account_keys.get_device(); @@ -610,7 +610,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout) { hw::device &hwdev = sender_account_keys.get_device(); hwdev.open_tx(tx_key); @@ -633,7 +633,7 @@ namespace cryptonote return r; } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time) { std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index f2cf7b6ca..87edafe9d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -89,9 +89,9 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr); - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); bool generate_genesis_block( block& bl diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e2900916b..d4b4e4d34 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -40,7 +40,7 @@ #include "blockchain.h" #include "blockchain_db/blockchain_db.h" #include "common/boost_serialization_helper.h" -#include "common/int-util.h" +#include "int-util.h" #include "misc_language.h" #include "warnings.h" #include "common/perf_timer.h" diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index db159f0f4..d5bb50930 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -146,9 +146,11 @@ namespace cryptonote struct request { std::vector<blobdata> txs; + std::string _; // padding BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs) + KV_SERIALIZE(_) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index c9fd40d88..6d9ad9028 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -30,20 +30,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include <boost/asio.hpp> #include <string> #include <vector> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <atomic> - -#include <boost/asio.hpp> -#include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/thread/thread.hpp> #include <memory> @@ -51,24 +39,14 @@ #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include <boost/lambda/bind.hpp> -#include <boost/lambda/lambda.hpp> -#include <boost/uuid/random_generator.hpp> #include <boost/chrono.hpp> -#include <boost/utility/value_init.hpp> -#include <boost/asio/deadline_timer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> #include "misc_language.h" #include "pragma_comp_defs.h" -#include <sstream> -#include <iomanip> #include <algorithm> -#include <boost/asio/basic_socket.hpp> -#include <boost/asio/ip/unicast.hpp> - #include "cryptonote_protocol_handler.h" #include "net/network_throttle.hpp" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c2c660e8c..1de0cde07 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1077,8 +1077,10 @@ skip: if(tvc[i].m_verifivation_failed) { if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + cryptonote::transaction tx; + parse_and_validate_tx_from_blob(*it, tx); // must succeed if we got here LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " - << epee::string_tools::pod_to_hex(get_blob_hash(*it)) << ", dropping connection"); + << epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(tx)) << ", dropping connection"); drop_connection(context, false, true); return 1; })) @@ -1724,8 +1726,39 @@ skip: bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) { // no check for success, so tell core they're relayed unconditionally + const bool pad_transactions = m_core.pad_transactions(); + size_t bytes = pad_transactions ? 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(arg.txs.size()).size() : 0; for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end(); ++tx_blob_it) + { m_core.on_transaction_relayed(*tx_blob_it); + if (pad_transactions) + bytes += tools::get_varint_data(tx_blob_it->size()).size() + tx_blob_it->size(); + } + + if (pad_transactions) + { + // stuff some dummy bytes in to stay safe from traffic volume analysis + static constexpr size_t granularity = 1024; + size_t padding = granularity - bytes % granularity; + const size_t overhead = 2 /* 1 + '_' */ + tools::get_varint_data(padding).size(); + if (overhead > padding) + padding = 0; + else + padding -= overhead; + arg._ = std::string(padding, ' '); + + std::string arg_buff; + epee::serialization::store_t_to_binary(arg, arg_buff); + + // we probably lowballed the payload size a bit, so added a but too much. Fix this now. + size_t remove = arg_buff.size() % granularity; + if (remove > arg._.size()) + arg._.clear(); + else + arg._.resize(arg._.size() - remove); + // if the size of _ moved enough, we might lose byte in size encoding, we don't care + } + return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context); } //------------------------------------------------------------------------------------------------------------------------ @@ -1738,9 +1771,9 @@ skip: if (add_fail) m_p2p->add_host_fail(context.m_remote_address); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + + m_p2p->drop_connection(context); } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 1638cf505..853cde9c3 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -674,6 +674,31 @@ bool t_command_parser_executor::sync_info(const std::vector<std::string>& args) return m_executor.sync_info(); } +bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args) +{ + if (args.size() != 1) + { + std::cout << "Exactly one parameter is needed" << std::endl; + return false; + } + + try + { + uint64_t nblocks = boost::lexical_cast<uint64_t>(args[0]); + if (nblocks < 1) + { + std::cout << "number of blocks must be greater than 0" << std::endl; + return false; + } + return m_executor.pop_blocks(nblocks); + } + catch (const boost::bad_lexical_cast&) + { + std::cout << "number of blocks must be a number greater than 0" << std::endl; + } + return false; +} + bool t_command_parser_executor::version(const std::vector<std::string>& args) { std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index a70070171..e2844e8b7 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -139,6 +139,8 @@ public: bool sync_info(const std::vector<std::string>& args); + bool pop_blocks(const std::vector<std::string>& args); + bool version(const std::vector<std::string>& args); }; diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 35504f2c9..527ed2cf1 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -282,6 +282,12 @@ t_command_server::t_command_server( , "Print information about the blockchain sync state." ); m_command_lookup.set_handler( + "pop_blocks" + , std::bind(&t_command_parser_executor::pop_blocks, &m_parser, p::_1) + , "pop_blocks <nblocks>" + , "Remove blocks from end of blockchain" + ); + m_command_lookup.set_handler( "version" , std::bind(&t_command_parser_executor::version, &m_parser, p::_1) , "Print version information." diff --git a/src/daemon/core.h b/src/daemon/core.h index d1defd573..c15d8d236 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -67,31 +67,16 @@ public: m_core.set_cryptonote_protocol(&protocol); } - std::string get_config_subdir() const - { - bool testnet = command_line::get_arg(m_vm_HACK, cryptonote::arg_testnet_on); - bool stagenet = command_line::get_arg(m_vm_HACK, cryptonote::arg_stagenet_on); - bool mainnet = !testnet && !stagenet; - std::string port = command_line::get_arg(m_vm_HACK, nodetool::arg_p2p_bind_port); - if ((mainnet && port != std::to_string(::config::P2P_DEFAULT_PORT)) - || (testnet && port != std::to_string(::config::testnet::P2P_DEFAULT_PORT)) - || (stagenet && port != std::to_string(::config::stagenet::P2P_DEFAULT_PORT))) { - return port; - } - return std::string(); - } - bool run() { //initialize core here MGINFO("Initializing core..."); - std::string config_subdir = get_config_subdir(); #if defined(PER_BLOCK_CHECKPOINT) const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData; #else const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr; #endif - if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints)) + if (!m_core.init(m_vm_HACK, nullptr, get_checkpoints)) { return false; } diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 49d6d49cf..88fba8372 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -75,18 +75,15 @@ public: protocol.set_p2p_endpoint(p2p.get()); core.set_protocol(protocol.get()); - const auto testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); - const auto stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); - const auto regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on); const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc); const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); - rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : regtest ? cryptonote::FAKECHAIN : cryptonote::MAINNET, main_rpc_port, "core"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"}); auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg)) { auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); - rpcs.emplace_back(new t_rpc{vm, core, p2p, true, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, restricted_rpc_port, "restricted"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"}); } } }; @@ -198,7 +195,6 @@ bool t_daemon::run(bool interactive) for(auto& rpc : mp_internals->rpcs) rpc->stop(); - mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); return true; } @@ -220,7 +216,6 @@ void t_daemon::stop() { throw std::runtime_error{"Can't stop stopped daemon"}; } - mp_internals->core.get().get_miner().stop(); mp_internals->p2p.stop(); for(auto& rpc : mp_internals->rpcs) rpc->stop(); diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index f483ba6c9..35017d9ef 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -216,6 +216,16 @@ int main(int argc, char const * argv[]) // after logs initialized tools::create_directories_if_necessary(data_dir.string()); +#ifdef STACK_TRACE + tools::set_stack_trace_log(log_file_path.filename().string()); +#endif // STACK_TRACE + + if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency)) + tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); + + // logging is now set up + MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); + // If there are positional options, we're running a daemon command { auto command = command_line::get_arg(vm, daemon_args::arg_command); @@ -276,16 +286,6 @@ int main(int argc, char const * argv[]) } } -#ifdef STACK_TRACE - tools::set_stack_trace_log(log_file_path.filename().string()); -#endif // STACK_TRACE - - if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency)) - tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); - - // logging is now set up - MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); - MINFO("Moving from main() into the daemonize now."); return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm) ? 0 : 1; diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 9621b0d01..37dffc097 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -54,7 +54,6 @@ public: , t_core & core , t_p2p & p2p , const bool restricted - , const cryptonote::network_type nettype , const std::string & port , const std::string & description ) @@ -62,7 +61,7 @@ public: { MGINFO("Initializing " << m_description << " RPC server..."); - if (!m_server.init(vm, restricted, nettype, port)) + if (!m_server.init(vm, restricted, port)) { throw std::runtime_error("Failed to initialize " + m_description + " RPC server."); } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5ae9851a7..015e1e1f9 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1967,4 +1967,31 @@ bool t_rpc_command_executor::sync_info() return true; } +bool t_rpc_command_executor::pop_blocks(uint64_t num_blocks) +{ + cryptonote::COMMAND_RPC_POP_BLOCKS::request req; + cryptonote::COMMAND_RPC_POP_BLOCKS::response res; + std::string fail_message = "pop_blocks failed"; + + req.nblocks = num_blocks; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/pop_blocks", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_pop_blocks(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + tools::success_msg_writer() << "new height: " << res.height; + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 9e6010c5b..592584a5f 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -152,6 +152,8 @@ public: bool relay_tx(const std::string &txid); bool sync_info(); + + bool pop_blocks(uint64_t num_blocks); }; } // namespace daemonize diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index 3cbee9c51..5af4e1a4a 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -12,7 +12,6 @@ #include <unistd.h> #include <stdexcept> #include <string> -#include <sys/stat.h> #ifndef TMPDIR #define TMPDIR "/tmp" diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index b344e1a4b..1302fa578 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -70,8 +70,9 @@ namespace { } else { - return std::string{p_error_text}; + std::string ret{p_error_text}; LocalFree(p_error_text); + return ret; } } diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 3e2552230..83422083b 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -169,6 +169,7 @@ int main(int argc, char* argv[]) return 1; } + bool full; cryptonote::block block; cryptonote::transaction tx; std::vector<cryptonote::tx_extra_field> fields; @@ -200,9 +201,9 @@ int main(int argc, char* argv[]) std::cout << "No fields were found in tx_extra" << std::endl; } } - else if (cryptonote::parse_tx_extra(std::vector<uint8_t>(blob.begin(), blob.end()), fields) && !fields.empty()) + else if (((full = cryptonote::parse_tx_extra(std::vector<uint8_t>(blob.begin(), blob.end()), fields)) || true) && !fields.empty()) { - std::cout << "Parsed tx_extra:" << std::endl; + std::cout << "Parsed" << (full ? "" : " partial") << " tx_extra:" << std::endl; print_extra_fields(fields); } else diff --git a/src/device/device.hpp b/src/device/device.hpp index dd9ad4332..399648f01 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -80,6 +80,14 @@ namespace hw { return false; } + class i_device_callback { + public: + virtual void on_button_request() {} + virtual void on_pin_request(epee::wipeable_string & pin) {} + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} + virtual ~i_device_callback() = default; + }; + class device { protected: std::string name; @@ -129,6 +137,8 @@ namespace hw { virtual device_type get_type() const = 0; virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; }; + virtual void set_callback(i_device_callback * callback) {}; + virtual void set_derivation_path(const std::string &derivation_path) {}; /* ======================================================================= */ /* LOCKER */ diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 1e3d80949..2286998a4 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -31,7 +31,7 @@ #include "device_default.hpp" -#include "common/int-util.h" +#include "int-util.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" #include "ringct/rctOps.h" diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 0a86e6987..bfb41bbe4 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -789,8 +789,6 @@ namespace hw { } #ifdef DEBUG_HWDEVICE - bool recover_x = recover; - const crypto::secret_key recovery_key_x = recovery_key; crypto::public_key pub_x; crypto::secret_key sec_x; #endif diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index c555e9fcd..b27c843b6 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -63,37 +63,20 @@ set(trezor_sources set(trezor_private_headers) -include(FindProtobuf) -find_package(Protobuf) # REQUIRED - -# Test for HAVE_PROTOBUF from the parent -if(Protobuf_FOUND AND HAVE_PROTOBUF) - if ("$ENV{PYTHON3}" STREQUAL "") - set(PYTHON3 "python3") - else() - set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable") - endif() - - execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) - if(RET) - message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." - "OUT: ${OUT}, ERR: ${ERR}." - "Please read src/device_trezor/trezor/tools/README.md") - else() - message(STATUS "Trezor protobuf messages regenerated ${OUT}") - set(TREZOR_PROTOBUF_GENERATED 1) - endif() -endif() - - -if(TREZOR_PROTOBUF_GENERATED) +# Protobuf and LibUSB processed by CheckTrezor +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} - ${PROTOBUF_INCLUDE_DIR}) + ${device_private_headers}) monero_add_library(device_trezor ${trezor_sources} @@ -109,14 +92,13 @@ if(TREZOR_PROTOBUF_GENERATED) common ${SODIUM_LIBRARY} ${Boost_CHRONO_LIBRARY} - ${PROTOBUF_LIBRARY} + ${Protobuf_LIBRARY} + ${TREZOR_LIBUSB_LIBRARIES} PRIVATE ${EXTRA_LIBRARIES}) - # set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE) - # add_definitions(-DWITH_DEVICE_TREZOR=1) - else() + message(STATUS "Trezor support disabled") monero_private_headers(device_trezor) monero_add_library(device_trezor device_trezor.cpp) target_link_libraries(device_trezor PUBLIC cncrypto) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index f55cbb15d..8868fb995 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -32,13 +32,12 @@ namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" #define HW_TREZOR_NAME "Trezor" -#define HW_TREZOR_NAME_LITE "TrezorLite" static device_trezor *trezor_device = nullptr; static device_trezor *ensure_trezor_device(){ @@ -122,7 +121,8 @@ namespace trezor { const boost::optional<cryptonote::network_type> & network_type){ AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); auto req = std::make_shared<messages::monero::MoneroGetAddress>(); this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type); @@ -137,7 +137,8 @@ namespace trezor { const boost::optional<cryptonote::network_type> & network_type){ AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); auto req = std::make_shared<messages::monero::MoneroGetWatchKey>(); this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type); @@ -153,7 +154,8 @@ namespace trezor { { AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req; @@ -239,12 +241,11 @@ namespace trezor { cpend.construction_data = cdata.tx_data; // Transaction check - cryptonote::blobdata tx_blob; - cryptonote::transaction tx_deserialized; - bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob); - CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed"); - r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized); - CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed"); + try { + transaction_check(cdata, aux_data); + } catch(const std::exception &e){ + throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what()); + } std::string key_images; bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool @@ -284,7 +285,8 @@ namespace trezor { { AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index"); signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data); @@ -295,6 +297,7 @@ namespace trezor { // Step: Init auto init_msg = signer->step_init(); this->set_msg_addr(init_msg.get()); + transaction_pre_check(init_msg); auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg); signer->step_init_ack(response); @@ -352,6 +355,59 @@ namespace trezor { signer->step_final_ack(ack_final); } + void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg) + { + CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty"); + CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data"); + CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features + const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0; + + if (nonce_required){ + // Versions 2.0.9 and lower do not support payment ID + CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); + const uint32_t vma = m_features->major_version(); + const uint32_t vmi = m_features->minor_version(); + const uint32_t vpa = m_features->patch_version(); + if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) { + throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update."); + } + } + } + + void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data) + { + // Simple serialization check + cryptonote::blobdata tx_blob; + cryptonote::transaction tx_deserialized; + bool r = cryptonote::t_serializable_object_to_blob(tdata.tx, tx_blob); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed"); + r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed"); + + // Extras check + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + cryptonote::tx_extra_nonce nonce; + + r = cryptonote::parse_tx_extra(tdata.tx.extra, tx_extra_fields); + CHECK_AND_ASSERT_THROW_MES(r, "tx.extra parsing failed"); + + const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0; + const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce); + CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent"); + + if (nonce_required){ + const std::string & payment_id = tdata.tsx_data.payment_id(); + if (payment_id.size() == 32){ + crypto::hash payment_id_long{}; + CHECK_AND_ASSERT_THROW_MES(cryptonote::get_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_long), "Long payment ID not present"); + + } else if (payment_id.size() == 8){ + crypto::hash8 payment_id_short{}; + CHECK_AND_ASSERT_THROW_MES(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_short), "Short payment ID not present"); + } + } + } + #else //WITH_DEVICE_TREZOR void register_all(std::map<std::string, std::unique_ptr<device>> ®istry) { diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 765c9b82c..1f08be887 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -49,7 +49,7 @@ namespace trezor { void register_all(); void register_all(std::map<std::string, std::unique_ptr<device>> ®istry); -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR class device_trezor; /** @@ -57,9 +57,8 @@ namespace trezor { */ class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold { protected: - // To speed up blockchain parsing the view key maybe handle here. - crypto::secret_key viewkey; - bool has_view_key; + void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg); + void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data); public: device_trezor(); diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 3a98bba5a..5071932ee 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -28,50 +28,22 @@ // #include "device_trezor_base.hpp" +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/regex.hpp> namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" +#define TREZOR_BIP44_HARDENED_ZERO 0x80000000 - std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){ - MDEBUG("on_button_request"); - device.on_button_request(); - return std::make_shared<messages::common::ButtonAck>(); - } + const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080}; - std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){ - MDEBUG("on_pin_request"); - epee::wipeable_string pin; - device.on_pin_request(pin); - auto resp = std::make_shared<messages::common::PinMatrixAck>(); - resp->set_pin(pin.data(), pin.size()); - return resp; - } - - std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){ - MDEBUG("on_passhprase_request"); - epee::wipeable_string passphrase; - device.on_passphrase_request(msg->on_device(), passphrase); - auto resp = std::make_shared<messages::common::PassphraseAck>(); - if (!msg->on_device()){ - resp->set_passphrase(passphrase.data(), passphrase.size()); - } - return resp; - } - - std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){ - MDEBUG("on_passhprase_state_request"); - device.on_passphrase_state_request(msg->state()); - return std::make_shared<messages::common::PassphraseStateAck>(); - } - - const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000}; - - device_trezor_base::device_trezor_base() { + device_trezor_base::device_trezor_base(): m_callback(nullptr) { } @@ -93,7 +65,7 @@ namespace trezor { } bool device_trezor_base::set_name(const std::string & name) { - this->full_name = name; + this->m_full_name = name; this->name = ""; auto delim = name.find(':'); @@ -105,10 +77,10 @@ namespace trezor { } const std::string device_trezor_base::get_name() const { - if (this->full_name.empty()) { + if (this->m_full_name.empty()) { return std::string("<disconnected:").append(this->name).append(">"); } - return this->full_name; + return this->m_full_name; } bool device_trezor_base::init() { @@ -117,9 +89,6 @@ namespace trezor { return false; } - if (!m_protocol_callback){ - m_protocol_callback = std::make_shared<trezor_protocol_callback>(*this); - } return true; } @@ -170,6 +139,9 @@ namespace trezor { } bool device_trezor_base::disconnect() { + m_device_state.clear(); + m_features.reset(); + if (m_transport){ try { m_transport->close(); @@ -224,6 +196,25 @@ namespace trezor { } } + void device_trezor_base::require_initialized(){ + if (!m_features){ + throw exc::TrezorException("Device state not initialized"); + } + + if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){ + throw exc::TrezorException("Device is in the bootloader mode"); + } + + if (m_features->has_firmware_present() && !m_features->firmware_present()){ + throw exc::TrezorException("Device has no firmware loaded"); + } + + // Hard requirement on initialized field, has to be there. + if (!m_features->has_initialized() || !m_features->initialized()){ + throw exc::TrezorException("Device is not initialized"); + } + } + void device_trezor_base::call_ping_unsafe(){ auto pingMsg = std::make_shared<messages::management::Ping>(); pingMsg->set_message("PING"); @@ -245,6 +236,82 @@ namespace trezor { } } + void device_trezor_base::write_raw(const google::protobuf::Message * msg){ + require_connected(); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + this->get_transport()->write(*msg); + } + + GenericMessage device_trezor_base::read_raw(){ + require_connected(); + std::shared_ptr<google::protobuf::Message> msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + + this->get_transport()->read(msg_resp, &msg_resp_type); + return GenericMessage(msg_resp_type, msg_resp); + } + + GenericMessage device_trezor_base::call_raw(const google::protobuf::Message * msg) { + write_raw(msg); + return read_raw(); + } + + bool device_trezor_base::message_handler(GenericMessage & input){ + // Later if needed this generic message handler can be replaced by a pointer to + // a protocol message handler which by default points to the device class which implements + // the default handler. + switch(input.m_type){ + case messages::MessageType_ButtonRequest: + on_button_request(input, dynamic_cast<const messages::common::ButtonRequest*>(input.m_msg.get())); + return true; + case messages::MessageType_PassphraseRequest: + on_passphrase_request(input, dynamic_cast<const messages::common::PassphraseRequest*>(input.m_msg.get())); + return true; + case messages::MessageType_PassphraseStateRequest: + on_passphrase_state_request(input, dynamic_cast<const messages::common::PassphraseStateRequest*>(input.m_msg.get())); + return true; + case messages::MessageType_PinMatrixRequest: + on_pin_request(input, dynamic_cast<const messages::common::PinMatrixRequest*>(input.m_msg.get())); + return true; + default: + return false; + } + } + + void device_trezor_base::ensure_derivation_path() noexcept { + if (m_wallet_deriv_path.empty()){ + m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO); // default 0' + } + } + + void device_trezor_base::set_derivation_path(const std::string &deriv_path){ + this->m_wallet_deriv_path.clear(); + + if (deriv_path.empty() || deriv_path == "-"){ + ensure_derivation_path(); + return; + } + + CHECK_AND_ASSERT_THROW_MES(deriv_path.size() <= 255, "Derivation path is too long"); + + std::vector<std::string> fields; + boost::split(fields, deriv_path, boost::is_any_of("/")); + CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long"); + + boost::regex rgx("^([0-9]+)'?$"); + boost::cmatch match; + + this->m_wallet_deriv_path.reserve(fields.size()); + for(const std::string & cur : fields){ + const bool ok = boost::regex_match(cur.c_str(), match, rgx); + CHECK_AND_ASSERT_THROW_MES(ok, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur); + CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur); + + const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO; + this->m_wallet_deriv_path.push_back((unsigned int)cidx); + } + } + /* ======================================================================= */ /* TREZOR PROTOCOL */ /* ======================================================================= */ @@ -269,32 +336,89 @@ namespace trezor { return false; } - void device_trezor_base::on_button_request() + void device_trezor_base::device_state_reset_unsafe() + { + require_connected(); + auto initMsg = std::make_shared<messages::management::Initialize>(); + + if(!m_device_state.empty()) { + initMsg->set_allocated_state(&m_device_state); + } + + m_features = this->client_exchange<messages::management::Features>(initMsg); + initMsg->release_state(); + } + + void device_trezor_base::device_state_reset() + { + AUTO_LOCK_CMD(); + device_state_reset_unsafe(); + } + + void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg) { + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + MDEBUG("on_button_request, code: " << msg->code()); + + messages::common::ButtonAck ack; + write_raw(&ack); + if (m_callback){ m_callback->on_button_request(); } + + resp = read_raw(); } - void device_trezor_base::on_pin_request(epee::wipeable_string & pin) + void device_trezor_base::on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg) { + MDEBUG("on_pin_request"); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + + epee::wipeable_string pin; + if (m_callback){ m_callback->on_pin_request(pin); } + + // TODO: remove PIN from memory + messages::common::PinMatrixAck m; + m.set_pin(pin.data(), pin.size()); + resp = call_raw(&m); } - void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) + void device_trezor_base::on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg) { + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + MDEBUG("on_passhprase_request, on device: " << msg->on_device()); + epee::wipeable_string passphrase; + if (m_callback){ - m_callback->on_passphrase_request(on_device, passphrase); + m_callback->on_passphrase_request(msg->on_device(), passphrase); } + + messages::common::PassphraseAck m; + if (!msg->on_device()){ + // TODO: remove passphrase from memory + m.set_passphrase(passphrase.data(), passphrase.size()); + } + + if (!m_device_state.empty()){ + m.set_allocated_state(&m_device_state); + } + + resp = call_raw(&m); + m.release_state(); } - void device_trezor_base::on_passphrase_state_request(const std::string & state) + void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg) { - if (m_callback){ - m_callback->on_passphrase_state_request(state); - } + MDEBUG("on_passhprase_state_request"); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + + m_device_state = msg->state(); + messages::common::PassphraseStateAck m; + resp = call_raw(&m); } #endif //WITH_DEVICE_TREZOR diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 644a49332..3c35e8aca 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -54,56 +54,10 @@ namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR class device_trezor_base; /** - * Trezor device callbacks - */ - class trezor_callback { - public: - virtual void on_button_request() {}; - virtual void on_pin_request(epee::wipeable_string & pin) {}; - virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}; - virtual void on_passphrase_state_request(const std::string & state) {}; - }; - - /** - * Default Trezor protocol client callback - */ - class trezor_protocol_callback { - protected: - device_trezor_base & device; - - public: - explicit trezor_protocol_callback(device_trezor_base & device): device(device) {} - - std::shared_ptr<google::protobuf::Message> on_button_request(const messages::common::ButtonRequest * msg); - std::shared_ptr<google::protobuf::Message> on_pin_matrix_request(const messages::common::PinMatrixRequest * msg); - std::shared_ptr<google::protobuf::Message> on_passphrase_request(const messages::common::PassphraseRequest * msg); - std::shared_ptr<google::protobuf::Message> on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg); - - std::shared_ptr<google::protobuf::Message> on_message(const google::protobuf::Message * msg, messages::MessageType message_type){ - MDEBUG("on_general_message"); - return on_message_dispatch(msg, message_type); - } - - std::shared_ptr<google::protobuf::Message> on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){ - if (message_type == messages::MessageType_ButtonRequest){ - return on_button_request(dynamic_cast<const messages::common::ButtonRequest*>(msg)); - } else if (message_type == messages::MessageType_PassphraseRequest) { - return on_passphrase_request(dynamic_cast<const messages::common::PassphraseRequest*>(msg)); - } else if (message_type == messages::MessageType_PassphraseStateRequest) { - return on_passphrase_state_request(dynamic_cast<const messages::common::PassphraseStateRequest*>(msg)); - } else if (message_type == messages::MessageType_PinMatrixRequest) { - return on_pin_matrix_request(dynamic_cast<const messages::common::PinMatrixRequest*>(msg)); - } else { - return nullptr; - } - } - }; - - /** * TREZOR device template with basic functions */ class device_trezor_base : public hw::core::device_default { @@ -114,10 +68,12 @@ namespace trezor { mutable boost::mutex command_locker; std::shared_ptr<Transport> m_transport; - std::shared_ptr<trezor_protocol_callback> m_protocol_callback; - std::shared_ptr<trezor_callback> m_callback; + i_device_callback * m_callback; - std::string full_name; + std::string m_full_name; + std::vector<unsigned int> m_wallet_deriv_path; + std::string m_device_state; // returned after passphrase entry, session + std::shared_ptr<messages::management::Features> m_features; // features from the last device reset cryptonote::network_type network_type; @@ -126,8 +82,20 @@ namespace trezor { // void require_connected(); + void require_initialized(); void call_ping_unsafe(); void test_ping(); + void device_state_reset_unsafe(); + void ensure_derivation_path() noexcept; + + // Communication methods + + void write_raw(const google::protobuf::Message * msg); + GenericMessage read_raw(); + GenericMessage call_raw(const google::protobuf::Message * msg); + + // Trezor message protocol handler. Handles specific signalling messages. + bool message_handler(GenericMessage & input); /** * Client communication wrapper, handles specific Trezor protocol. @@ -141,8 +109,7 @@ namespace trezor { const boost::optional<messages::MessageType> & resp_type = boost::none, const boost::optional<std::vector<messages::MessageType>> & resp_types = boost::none, const boost::optional<messages::MessageType*> & resp_type_ptr = boost::none, - bool open_session = false, - unsigned depth=0) + bool open_session = false) { // Require strictly protocol buffers response in the template. BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); @@ -151,8 +118,12 @@ namespace trezor { throw std::invalid_argument("Cannot specify list of accepted types and not using generic response"); } + // Determine type of expected message response + const messages::MessageType required_type = accepting_base ? messages::MessageType_Success : + (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>()); + // Open session if required - if (open_session && depth == 0){ + if (open_session){ try { m_transport->open(); } catch (const std::exception& e) { @@ -162,47 +133,37 @@ namespace trezor { // Scoped session closer BOOST_SCOPE_EXIT_ALL(&, this) { - if (open_session && depth == 0){ - this->getTransport()->close(); + if (open_session){ + this->get_transport()->close(); } }; - // Write the request + // Write/read the request CHECK_AND_ASSERT_THROW_MES(req, "Request is null"); - this->getTransport()->write(*req); + auto msg_resp = call_raw(req.get()); - // Read the response - std::shared_ptr<google::protobuf::Message> msg_resp; - hw::trezor::messages::MessageType msg_resp_type; + bool processed = false; + do { + processed = message_handler(msg_resp); + } while(processed); - // We may have several roundtrips with the handler - this->getTransport()->read(msg_resp, &msg_resp_type); + // Response section if (resp_type_ptr){ - *(resp_type_ptr.get()) = msg_resp_type; + *(resp_type_ptr.get()) = msg_resp.m_type; } - // Determine type of expected message response - messages::MessageType required_type = accepting_base ? messages::MessageType_Success : - (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>()); + if (msg_resp.m_type == messages::MessageType_Failure) { + throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.m_msg.get())); - if (msg_resp_type == messages::MessageType_Failure) { - throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.get())); + } else if (!accepting_base && msg_resp.m_type == required_type) { + return message_ptr_retype<t_message>(msg_resp.m_msg); - } else if (!accepting_base && msg_resp_type == required_type) { - return message_ptr_retype<t_message>(msg_resp); + } else if (accepting_base && (!resp_types || + std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp.m_type) != resp_types.get().end())) { + return message_ptr_retype<t_message>(msg_resp.m_msg); } else { - auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type); - if (resp) { - return this->client_exchange<t_message>(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1); - - } else if (accepting_base && (!resp_types || - std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) { - return message_ptr_retype<t_message>(msg_resp); - - } else { - throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); - } + throw exc::UnexpectedMessageException(msg_resp.m_type, msg_resp.m_msg); } } @@ -221,9 +182,13 @@ namespace trezor { msg->add_address_n(x); } } else { + ensure_derivation_path(); for (unsigned int i : DEFAULT_BIP44_PATH) { msg->add_address_n(i); } + for (unsigned int i : m_wallet_deriv_path) { + msg->add_address_n(i); + } } if (network_type){ @@ -246,20 +211,26 @@ namespace trezor { bool reset(); // Default derivation path for Monero - static const uint32_t DEFAULT_BIP44_PATH[3]; + static const uint32_t DEFAULT_BIP44_PATH[2]; - std::shared_ptr<Transport> getTransport(){ + std::shared_ptr<Transport> get_transport(){ return m_transport; } - std::shared_ptr<trezor_protocol_callback> getProtocolCallback(){ - return m_protocol_callback; + void set_callback(i_device_callback * callback) override { + m_callback = callback; } - std::shared_ptr<trezor_callback> getCallback(){ + i_device_callback * get_callback(){ return m_callback; } + std::shared_ptr<messages::management::Features> & get_features() { + return m_features; + } + + void set_derivation_path(const std::string &deriv_path) override; + /* ======================================================================= */ /* SETUP/TEARDOWN */ /* ======================================================================= */ @@ -287,11 +258,16 @@ namespace trezor { */ bool ping(); + /** + * Performs Initialize call to the Trezor, resets to known state. + */ + void device_state_reset(); + // Protocol callbacks - void on_button_request(); - void on_pin_request(epee::wipeable_string & pin); - void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); - void on_passphrase_state_request(const std::string & state); + void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg); + void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); + void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); + void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg); }; #endif diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 8abdd2c18..97dc0a957 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -32,7 +32,7 @@ #include "trezor/trezor_defs.hpp" -#ifdef HAVE_PROTOBUF +#ifdef WITH_DEVICE_TREZOR #include "trezor/transport.hpp" #include "trezor/messages/messages.pb.h" #include "trezor/messages/messages-common.pb.h" diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md index 91a8fb3f0..0802e734a 100644 --- a/src/device_trezor/trezor/tools/README.md +++ b/src/device_trezor/trezor/tools/README.md @@ -2,33 +2,42 @@ ## Messages rebuild -Install `protoc` for your distribution. +Install `protoc` for your distribution. Requirements: - `protobuf-compiler` - `libprotobuf-dev` -- `libprotoc-dev` -- `python-protobuf` +- `python` -Python 3 is required. If you don't have python 3 quite an easy way is -to use [pyenv]. -It is also advised to create own python virtual environment so dependencies -are installed in this project-related virtual environment. +Soft requirement: Python 3, can be easily installed with [pyenv]. +If Python 3 is used there are no additional python dependencies. -```bash -python -m venv / -``` +Since Cmake 3.12 the `FindPython` module is used to locate the Python +interpreter in your system. It preferably searches for Python 3, if none +is found, it searches for Python 2. -Make sure your python has `protobuf` package installed +Lower version of the cmake uses another module which does not guarantee +ordering. If you want to override the selected python you can do it in +the following way: ```bash -pip install protobuf -``` +export TREZOR_PYTHON=`which python3` +``` + -Regenerate messages: +### Python 2.7+ -``` -./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py +Python 3 has `tempfile.TemporaryDirectory` available but Python 2 lacks +this class so the message generation code uses `backports.tempfile` package +bundled in the repository. + +The minimal Python versions are 2.7 and 3.4 + +### Regenerate messages + +```bash +cd src/device_trezor/trezor +python tools/build_protob.py ``` The messages regeneration is done also automatically via cmake. diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py index eaa8a90ed..3e0318ea5 100644 --- a/src/device_trezor/trezor/tools/pb2cpp.py +++ b/src/device_trezor/trezor/tools/pb2cpp.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # Converts Google's protobuf python definitions of TREZOR wire messages # to plain-python objects as used in TREZOR Core and python-trezor @@ -8,11 +8,25 @@ import os import re import shutil import subprocess -import sys import glob -import tempfile import hashlib +try: + from tempfile import TemporaryDirectory +except: + # Py2 backward compatibility, using bundled sources. + # Original source: pip install backports.tempfile + try: + # Try bundled python version + import sys + sys.path.append(os.path.dirname(__file__)) + from py2backports.tempfile import TemporaryDirectory + + except: + raise EnvironmentError('Python 2.7+ or 3.4+ is required. ' + 'TemporaryDirectory is not available in Python 2.' + 'Try to specify python to use, e.g.: "export TREZOR_PYTHON=`which python3`"') + AUTO_HEADER = "# Automatically generated by pb2cpp\n" @@ -23,6 +37,9 @@ UNDEF_STATEMENT = """ #endif """ +PROTOC = None +PROTOC_INCLUDE = None + def which(pgm): path = os.getenv('PATH') @@ -32,15 +49,6 @@ def which(pgm): return p -PROTOC = which("protoc") -if not PROTOC: - print("protoc command not found") - sys.exit(1) - -PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC)) -PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include") - - def namespace_file(fpath, package): """Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later""" with open(fpath) as fh: @@ -82,9 +90,10 @@ def protoc(files, out_dir, additional_includes=(), package=None, force=False): include_dirs = set() include_dirs.add(PROTOC_INCLUDE) - include_dirs.update(additional_includes) + if additional_includes: + include_dirs.update(additional_includes) - with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out: + with TemporaryDirectory() as tmpdir_protob, TemporaryDirectory() as tmpdir_out: include_dirs.add(tmpdir_protob) new_files = [] @@ -125,9 +134,9 @@ def update_message_files(tmpdir_out, out_dir, force=False): dest_file = os.path.join(out_dir, bname) if not force and os.path.exists(dest_file): data = open(fname, 'rb').read() - data_hash = hashlib.sha3_256(data).digest() + data_hash = hashlib.sha256(data).digest() data_dest = open(dest_file, 'rb').read() - data_dest_hash = hashlib.sha3_256(data_dest).digest() + data_dest_hash = hashlib.sha256(data_dest).digest() if data_hash == data_dest_hash: continue @@ -163,7 +172,8 @@ def strip_leader(s, prefix): return s -if __name__ == "__main__": +def main(): + global PROTOC, PROTOC_INCLUDE logging.basicConfig(level=logging.DEBUG) parser = argparse.ArgumentParser() @@ -179,8 +189,31 @@ if __name__ == "__main__": protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),) + PROTOBUF_INCLUDE_DIRS = os.getenv("PROTOBUF_INCLUDE_DIRS", None) + PROTOBUF_PROTOC_EXECUTABLE = os.getenv("PROTOBUF_PROTOC_EXECUTABLE", None) + + if PROTOBUF_PROTOC_EXECUTABLE and not os.path.exists(PROTOBUF_PROTOC_EXECUTABLE): + raise ValueError("PROTOBUF_PROTOC_EXECUTABLE set but not found: %s" % PROTOBUF_PROTOC_EXECUTABLE) + + elif PROTOBUF_PROTOC_EXECUTABLE: + PROTOC = PROTOBUF_PROTOC_EXECUTABLE + + else: + if os.name == "nt": + PROTOC = which("protoc.exe") + else: + PROTOC = which("protoc") + + if not PROTOC: + raise ValueError("protoc command not found. Set PROTOBUF_PROTOC_EXECUTABLE env var to the protoc binary and optionally PROTOBUF_INCLUDE_DIRS") + + PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC)) + PROTOC_INCLUDE = PROTOBUF_INCLUDE_DIRS if PROTOBUF_INCLUDE_DIRS else os.path.join(PROTOC_PREFIX, "include") + protoc( args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force ) +if __name__ == "__main__": + main() diff --git a/src/device_trezor/trezor/tools/py2backports/__init__.py b/src/device_trezor/trezor/tools/py2backports/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/device_trezor/trezor/tools/py2backports/__init__.py diff --git a/src/device_trezor/trezor/tools/py2backports/tempfile.py b/src/device_trezor/trezor/tools/py2backports/tempfile.py new file mode 100644 index 000000000..e259dea3f --- /dev/null +++ b/src/device_trezor/trezor/tools/py2backports/tempfile.py @@ -0,0 +1,72 @@ +""" +https://github.com/pjdelport/backports.tempfile/blob/master/src/backports/tempfile.py +Partial backport of Python 3.5's tempfile module: + TemporaryDirectory +Backport modifications are marked with marked with "XXX backport". +""" +from __future__ import absolute_import + +import sys +import warnings as _warnings +from shutil import rmtree as _rmtree + +from py2backports.weakref import finalize + + +# XXX backport: Rather than backporting all of mkdtemp(), we just create a +# thin wrapper implementing its Python 3.5 signature. +if sys.version_info < (3, 5): + from tempfile import mkdtemp as old_mkdtemp + + def mkdtemp(suffix=None, prefix=None, dir=None): + """ + Wrap `tempfile.mkdtemp()` to make the suffix and prefix optional (like Python 3.5). + """ + kwargs = {k: v for (k, v) in + dict(suffix=suffix, prefix=prefix, dir=dir).items() + if v is not None} + return old_mkdtemp(**kwargs) + +else: + from tempfile import mkdtemp + + +# XXX backport: ResourceWarning was added in Python 3.2. +# For earlier versions, fall back to RuntimeWarning instead. +_ResourceWarning = RuntimeWarning if sys.version_info < (3, 2) else ResourceWarning + + +class TemporaryDirectory(object): + """Create and return a temporary directory. This has the same + behavior as mkdtemp but can be used as a context manager. For + example: + with TemporaryDirectory() as tmpdir: + ... + Upon exiting the context, the directory and everything contained + in it are removed. + """ + + def __init__(self, suffix=None, prefix=None, dir=None): + self.name = mkdtemp(suffix, prefix, dir) + self._finalizer = finalize( + self, self._cleanup, self.name, + warn_message="Implicitly cleaning up {!r}".format(self)) + + @classmethod + def _cleanup(cls, name, warn_message): + _rmtree(name) + _warnings.warn(warn_message, _ResourceWarning) + + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self._finalizer.detach(): + _rmtree(self.name) diff --git a/src/device_trezor/trezor/tools/py2backports/weakref.py b/src/device_trezor/trezor/tools/py2backports/weakref.py new file mode 100644 index 000000000..eb646812b --- /dev/null +++ b/src/device_trezor/trezor/tools/py2backports/weakref.py @@ -0,0 +1,148 @@ +""" +https://github.com/pjdelport/backports.weakref/blob/master/src/backports/weakref.py +Partial backport of Python 3.6's weakref module: + finalize (new in Python 3.4) +Backport modifications are marked with "XXX backport". +""" +from __future__ import absolute_import + +import itertools +import sys +from weakref import ref + +__all__ = ['finalize'] + + +class finalize(object): + """Class for finalization of weakrefable objects + finalize(obj, func, *args, **kwargs) returns a callable finalizer + object which will be called when obj is garbage collected. The + first time the finalizer is called it evaluates func(*arg, **kwargs) + and returns the result. After this the finalizer is dead, and + calling it just returns None. + When the program exits any remaining finalizers for which the + atexit attribute is true will be run in reverse order of creation. + By default atexit is true. + """ + + # Finalizer objects don't have any state of their own. They are + # just used as keys to lookup _Info objects in the registry. This + # ensures that they cannot be part of a ref-cycle. + + __slots__ = () + _registry = {} + _shutdown = False + _index_iter = itertools.count() + _dirty = False + _registered_with_atexit = False + + class _Info(object): + __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") + + def __init__(self, obj, func, *args, **kwargs): + if not self._registered_with_atexit: + # We may register the exit function more than once because + # of a thread race, but that is harmless + import atexit + atexit.register(self._exitfunc) + finalize._registered_with_atexit = True + info = self._Info() + info.weakref = ref(obj, self) + info.func = func + info.args = args + info.kwargs = kwargs or None + info.atexit = True + info.index = next(self._index_iter) + self._registry[self] = info + finalize._dirty = True + + def __call__(self, _=None): + """If alive then mark as dead and return func(*args, **kwargs); + otherwise return None""" + info = self._registry.pop(self, None) + if info and not self._shutdown: + return info.func(*info.args, **(info.kwargs or {})) + + def detach(self): + """If alive then mark as dead and return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None and self._registry.pop(self, None): + return (obj, info.func, info.args, info.kwargs or {}) + + def peek(self): + """If alive then return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None: + return (obj, info.func, info.args, info.kwargs or {}) + + @property + def alive(self): + """Whether finalizer is alive""" + return self in self._registry + + @property + def atexit(self): + """Whether finalizer should be called at exit""" + info = self._registry.get(self) + return bool(info) and info.atexit + + @atexit.setter + def atexit(self, value): + info = self._registry.get(self) + if info: + info.atexit = bool(value) + + def __repr__(self): + info = self._registry.get(self) + obj = info and info.weakref() + if obj is None: + return '<%s object at %#x; dead>' % (type(self).__name__, id(self)) + else: + return '<%s object at %#x; for %r at %#x>' % \ + (type(self).__name__, id(self), type(obj).__name__, id(obj)) + + @classmethod + def _select_for_exit(cls): + # Return live finalizers marked for exit, oldest first + L = [(f,i) for (f,i) in cls._registry.items() if i.atexit] + L.sort(key=lambda item:item[1].index) + return [f for (f,i) in L] + + @classmethod + def _exitfunc(cls): + # At shutdown invoke finalizers for which atexit is true. + # This is called once all other non-daemonic threads have been + # joined. + reenable_gc = False + try: + if cls._registry: + import gc + if gc.isenabled(): + reenable_gc = True + gc.disable() + pending = None + while True: + if pending is None or finalize._dirty: + pending = cls._select_for_exit() + finalize._dirty = False + if not pending: + break + f = pending.pop() + try: + # gc is disabled, so (assuming no daemonic + # threads) the following is the only line in + # this function which might trigger creation + # of a new finalizer + f() + except Exception: + sys.excepthook(*sys.exc_info()) + assert f not in cls._registry + finally: + # prevent any more finalizers from executing during shutdown + finalize._shutdown = True + if reenable_gc: + gc.enable() diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index fc86177e1..cd66e59e8 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -27,13 +27,21 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#ifdef WITH_DEVICE_TREZOR_WEBUSB +#include <libusb.h> +#endif + #include <boost/endian/conversion.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/ip/udp.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/format.hpp> #include "transport.hpp" #include "messages/messages-common.pb.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor.transport" + using namespace std; using json = rapidjson::Document; @@ -581,12 +589,400 @@ namespace trezor{ << ">"; } +#ifdef WITH_DEVICE_TREZOR_WEBUSB + + static bool is_trezor1(libusb_device_descriptor * info){ + return info->idVendor == 0x534C && info->idProduct == 0x0001; + } + + static bool is_trezor2(libusb_device_descriptor * info){ + return info->idVendor == 0x1209 && info->idProduct == 0x53C1; + } + + static bool is_trezor2_bl(libusb_device_descriptor * info){ + return info->idVendor == 0x1209 && info->idProduct == 0x53C0; + } + + static uint8_t get_trezor_dev_mask(libusb_device_descriptor * info){ + uint8_t mask = 0; + CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor"); + mask |= is_trezor1(info) ? 1 : 0; + mask |= is_trezor2(info) ? 2 : 0; + mask |= is_trezor2_bl(info) ? 4 : 0; + return mask; + } + + static void set_libusb_log(libusb_context *ctx){ + CHECK_AND_ASSERT_THROW_MES(ctx, "Null libusb context"); + + // http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106) +# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) +#else +# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_debug(ctx, level) +#endif + + if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 3); + else if (ELPP->vRegistry()->allowed(el::Level::Warning, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 2); + else if (ELPP->vRegistry()->allowed(el::Level::Error, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 1); + +#undef TREZOR_LIBUSB_SET_DEBUG + } + + static int get_libusb_ports(libusb_device *dev, std::vector<uint8_t> &path){ + uint8_t tmp_path[16]; + int r = libusb_get_port_numbers(dev, tmp_path, sizeof(tmp_path)); + CHECK_AND_ASSERT_MES(r != LIBUSB_ERROR_OVERFLOW, -1, "Libusb path array too small"); + CHECK_AND_ASSERT_MES(r >= 0, -1, "Libusb path array error"); + + path.resize(r); + for (int i = 0; i < r; i++){ + path[i] = tmp_path[i]; + } + + return 0; + } + + static std::string get_usb_path(uint8_t bus_id, const std::vector<uint8_t> &path){ + std::stringstream ss; + ss << WebUsbTransport::PATH_PREFIX << (boost::format("%03d") % ((int)bus_id)); + for(uint8_t port : path){ + ss << ":" << ((int) port); + } + return ss.str(); + } + + const char * WebUsbTransport::PATH_PREFIX = "webusb:"; + + WebUsbTransport::WebUsbTransport( + boost::optional<libusb_device_descriptor*> descriptor, + boost::optional<std::shared_ptr<Protocol>> proto + ): m_conn_count(0), + m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), + m_bus_id(-1), m_device_addr(-1) + { + if (descriptor){ + libusb_device_descriptor * desc = new libusb_device_descriptor; + memcpy(desc, descriptor.get(), sizeof(libusb_device_descriptor)); + this->m_usb_device_desc.reset(desc); + } + + m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>(); + +#ifdef WITH_TREZOR_DEBUG + m_debug_mode = false; +#endif + } + + WebUsbTransport::~WebUsbTransport(){ + if (m_usb_device){ + close(); + } + + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; + } + } + + void WebUsbTransport::require_device() const{ + if (!m_usb_device_desc){ + throw std::runtime_error("No USB device specified"); + } + } + + void WebUsbTransport::require_connected() const{ + require_device(); + if (!m_usb_device_handle){ + throw std::runtime_error("USB Device not opened"); + } + } + + void WebUsbTransport::enumerate(t_transport_vect & res) { + int r; + libusb_device **devs; + libusb_context *ctx = nullptr; + + r = libusb_init(&ctx); + CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb"); + + set_libusb_log(ctx); + + ssize_t cnt = libusb_get_device_list(ctx, &devs); + if (cnt < 0){ + libusb_exit(ctx); + throw std::runtime_error("Unable to enumerate libusb devices"); + } + + MTRACE("Libusb devices: " << cnt); + + for(ssize_t i = 0; i < cnt; i++) { + libusb_device_descriptor desc{}; + r = libusb_get_device_descriptor(devs[i], &desc); + if (r < 0){ + MERROR("Unable to get libusb device descriptor " << i); + continue; + } + + const auto trezor_mask = get_trezor_dev_mask(&desc); + if (!trezor_mask){ + continue; + } + + MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " mask " << (int)trezor_mask); + + auto t = std::make_shared<WebUsbTransport>(boost::make_optional(&desc)); + t->m_bus_id = libusb_get_bus_number(devs[i]); + t->m_device_addr = libusb_get_device_address(devs[i]); + + // Port resolution may fail. Non-critical error, just addressing precision is decreased. + get_libusb_ports(devs[i], t->m_port_numbers); + + res.push_back(t); + } + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); + } + + std::string WebUsbTransport::get_path() const { + if (!m_usb_device_desc){ + return ""; + } + + return get_usb_path(static_cast<uint8_t>(m_bus_id), m_port_numbers); + }; + + void WebUsbTransport::open() { + const int interface = get_interface(); + if (m_conn_count > 0){ + MTRACE("Already opened, count: " << m_conn_count); + m_conn_count += 1; + return; + } + +#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0) + + int r; + libusb_device **devs = nullptr; + + if (m_usb_session) { + TREZOR_DESTROY_SESSION(); + } + + r = libusb_init(&m_usb_session); + CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb"); + set_libusb_log(m_usb_session); + + bool found = false; + int open_res = 0; + + ssize_t cnt = libusb_get_device_list(m_usb_session, &devs); + if (cnt < 0){ + TREZOR_DESTROY_SESSION(); + throw std::runtime_error("Unable to enumerate libusb devices"); + } + + for (ssize_t i = 0; i < cnt; i++) { + libusb_device_descriptor desc{}; + r = libusb_get_device_descriptor(devs[i], &desc); + if (r < 0){ + MERROR("Unable to get libusb device descriptor " << i); + continue; + } + + const auto trezor_mask = get_trezor_dev_mask(&desc); + if (!trezor_mask) { + continue; + } + + auto bus_id = libusb_get_bus_number(devs[i]); + std::vector<uint8_t> path; + + // Port resolution may fail. Non-critical error, just addressing precision is decreased. + get_libusb_ports(devs[i], path); + + MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct + << ", mask: " << (int)trezor_mask + << ". path: " << get_usb_path(bus_id, path)); + + if (bus_id == m_bus_id && path == m_port_numbers) { + found = true; + m_usb_device = devs[i]; + open_res = libusb_open(m_usb_device, &m_usb_device_handle); + break; + } + } + + libusb_free_device_list(devs, 1); + + if (!found){ + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Device not found"); + + } else if (found && open_res != 0) { + m_usb_device_handle = nullptr; + m_usb_device = nullptr; + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Unable to open libusb device"); + } + + r = libusb_claim_interface(m_usb_device_handle, interface); + + if (r != 0){ + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + m_usb_device = nullptr; + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Unable to claim libusb device"); + } + + m_conn_count = 1; + m_proto->session_begin(*this); + +#undef TREZOR_DESTROY_SESSION + }; + + void WebUsbTransport::close() { + m_conn_count -= 1; + + if (m_conn_count < 0){ + MERROR("Close counter is negative: " << m_conn_count); + + } else if (m_conn_count == 0){ + MTRACE("Closing webusb device"); + + m_proto->session_end(*this); + + int r = libusb_release_interface(m_usb_device_handle, get_interface()); + if (r != 0){ + MERROR("Could not release libusb interface: " << r); + } + + m_usb_device = nullptr; + if (m_usb_device_handle) { + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + } + + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; + } + } + }; + + + int WebUsbTransport::get_interface() const{ + const int INTERFACE_NORMAL = 0; +#ifdef WITH_TREZOR_DEBUG + const int INTERFACE_DEBUG = 1; + return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL; +#else + return INTERFACE_NORMAL; +#endif + } + + unsigned char WebUsbTransport::get_endpoint() const{ + const unsigned char ENDPOINT_NORMAL = 1; +#ifdef WITH_TREZOR_DEBUG + const unsigned char ENDPOINT_DEBUG = 2; + return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL; +#else + return ENDPOINT_NORMAL; +#endif + } + + void WebUsbTransport::write(const google::protobuf::Message &req) { + m_proto->write(*this, req); + }; + + void WebUsbTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) { + m_proto->read(*this, msg, msg_type); + }; + + void WebUsbTransport::write_chunk(const void * buff, size_t size) { + require_connected(); + if (size != REPLEN){ + throw exc::CommunicationException("Invalid chunk size: "); + } + + unsigned char endpoint = get_endpoint(); + endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_OUT; + + int transferred = 0; + int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0); + CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r); + if (transferred != (int)size){ + throw exc::CommunicationException("Could not transfer chunk"); + } + }; + + size_t WebUsbTransport::read_chunk(void * buff, size_t size) { + require_connected(); + unsigned char endpoint = get_endpoint(); + endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN; + + int transferred = 0; + int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0); + CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r); + if (transferred != (int)size){ + throw exc::CommunicationException("Could not read the chunk"); + } + + return transferred; + }; + + std::ostream& WebUsbTransport::dump(std::ostream& o) const { + o << "WebUsbTransport<path=" << get_path() + << ", vendorId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idVendor) : "?") + << ", productId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idProduct) : "?") + << ", deviceType="; + + if (m_usb_device_desc){ + if (is_trezor1(m_usb_device_desc.get())) + o << "TrezorOne"; + else if (is_trezor2(m_usb_device_desc.get())) + o << "TrezorT"; + else if (is_trezor2_bl(m_usb_device_desc.get())) + o << "TrezorT BL"; + } else { + o << "?"; + } + + return o << ">"; + }; + +#endif // WITH_DEVICE_TREZOR_WEBUSB + void enumerate(t_transport_vect & res){ BridgeTransport bt; - bt.enumerate(res); + try{ + bt.enumerate(res); + } catch (const std::exception & e){ + MERROR("BridgeTransport enumeration failed:" << e.what()); + } + +#ifdef WITH_DEVICE_TREZOR_WEBUSB + hw::trezor::WebUsbTransport btw; + try{ + btw.enumerate(res); + } catch (const std::exception & e){ + MERROR("WebUsbTransport enumeration failed:" << e.what()); + } +#endif +#ifdef WITH_DEVICE_TREZOR_UDP hw::trezor::UdpTransport btu; - btu.enumerate(res); + try{ + btu.enumerate(res); + } catch (const std::exception & e){ + MERROR("UdpTransport enumeration failed:" << e.what()); + } +#endif } std::shared_ptr<Transport> transport(const std::string & path){ @@ -633,6 +1029,9 @@ namespace trezor{ } } + GenericMessage::GenericMessage(messages::MessageType m_type, const shared_ptr<google::protobuf::Message> &m_msg) + : m_type(m_type), m_msg(m_msg), m_empty(false) {} + std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){ return t.dump(o); } diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 7b82fd06f..50c31cf73 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -239,6 +239,59 @@ namespace trezor { udp::endpoint m_endpoint; }; +#ifdef WITH_DEVICE_TREZOR_WEBUSB +#include <libusb.h> + + class WebUsbTransport : public Transport { + public: + + explicit WebUsbTransport( + boost::optional<libusb_device_descriptor*> descriptor = boost::none, + boost::optional<std::shared_ptr<Protocol>> proto = boost::none + ); + + virtual ~WebUsbTransport(); + + static const char * PATH_PREFIX; + + std::string get_path() const override; + void enumerate(t_transport_vect & res) override; + + void open() override; + void close() override; + + void write(const google::protobuf::Message &req) override; + void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override; + + void write_chunk(const void * buff, size_t size) override; + size_t read_chunk(void * buff, size_t size) override; + + std::ostream& dump(std::ostream& o) const override; + + private: + void require_device() const; + void require_connected() const; + int get_interface() const; + unsigned char get_endpoint() const; + + int m_conn_count; + std::shared_ptr<Protocol> m_proto; + + libusb_context *m_usb_session; + libusb_device *m_usb_device; + libusb_device_handle *m_usb_device_handle; + std::unique_ptr<libusb_device_descriptor> m_usb_device_desc; + std::vector<uint8_t> m_port_numbers; + int m_bus_id; + int m_device_addr; + +#ifdef WITH_TREZOR_DEBUG + bool m_debug_mode; +#endif + }; + +#endif + // // General helpers // @@ -290,6 +343,20 @@ namespace trezor { [[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure); /** + * Generic message holder, type + obj + */ + class GenericMessage { + public: + GenericMessage(): m_empty(true) {} + GenericMessage(messages::MessageType m_type, const std::shared_ptr<google::protobuf::Message> &m_msg); + bool empty() const { return m_empty; } + + hw::trezor::messages::MessageType m_type; + std::shared_ptr<google::protobuf::Message> m_msg; + bool m_empty; + }; + + /** * Simple wrapper for write-read message exchange with expected message response type. * * @throws UnexpectedMessageException if the response message type is different than expected. diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index 951a8f802..30e76eadc 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -27,14 +27,41 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR) - #define WITH_DEVICE_TREZOR 1 -#else - #define WITH_DEVICE_TREZOR 0 +#ifndef USE_DEVICE_TREZOR +#define USE_DEVICE_TREZOR 1 #endif -#ifndef WITH_DEVICE_TREZOR_LITE -#define WITH_DEVICE_TREZOR_LITE 0 +// HAVE_TREZOR_READY set by cmake after protobuf messages +// were generated successfully and all minimal dependencies are met. +#ifndef DEVICE_TREZOR_READY +#undef USE_DEVICE_TREZOR +#define USE_DEVICE_TREZOR 0 +#endif + +#if USE_DEVICE_TREZOR +#define WITH_DEVICE_TREZOR 1 +#endif + +#ifndef WITH_DEVICE_TREZOR +#undef WITH_DEVICE_TREZOR_LITE +#endif + +#if defined(HAVE_TREZOR_LIBUSB) && USE_DEVICE_TREZOR +#define WITH_DEVICE_TREZOR_WEBUSB 1 +#endif + +// Enable / disable UDP in the enumeration +#ifndef USE_DEVICE_TREZOR_UDP +#define USE_DEVICE_TREZOR_UDP 1 +#endif + +// Enable / disable UDP in the enumeration for release +#ifndef USE_DEVICE_TREZOR_UDP_RELEASE +#define USE_DEVICE_TREZOR_UDP_RELEASE 0 +#endif + +#if USE_DEVICE_TREZOR_UDP && (USE_DEVICE_TREZOR_UDP_RELEASE || defined(TREZOR_DEBUG)) +#define WITH_DEVICE_TREZOR_UDP 1 #endif // Avoids protobuf undefined macro warning diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 94071c23c..b1e3bdcd5 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -37,22 +37,14 @@ */ #include <string> -#include <cassert> -#include <map> #include <cstdint> #include <vector> #include <unordered_map> -#include <boost/algorithm/string.hpp> #include "wipeable_string.h" #include "misc_language.h" -#include "crypto/crypto.h" // for declaration of crypto::secret_key -#include <fstream> -#include "common/int-util.h" +#include "int-util.h" #include "mnemonics/electrum-words.h" -#include <stdexcept> -#include <boost/filesystem.hpp> #include <boost/crc.hpp> -#include <boost/algorithm/string/join.hpp> #include "chinese_simplified.h" #include "english.h" diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 5401b9779..60d2c5f15 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -41,7 +41,6 @@ #include <string> #include <cstdint> -#include <map> #include "crypto/crypto.h" // for declaration of crypto::secret_key namespace epee { class wipeable_string; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 808945393..8930418bd 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -38,7 +38,9 @@ #include "cryptonote_config.h" #include "warnings.h" -#include "net/levin_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "net/levin_protocol_handler.h" +#include "net/levin_protocol_handler_async.h" #include "p2p_protocol_defs.h" #include "storages/levin_abstract_invoke2.h" #include "net_peerlist.h" diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 1592e74e4..d485fb748 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -30,7 +30,9 @@ #include <stdlib.h> #include <boost/thread/mutex.hpp> +#include <boost/thread/lock_guard.hpp> #include "misc_log_ex.h" +#include "span.h" #include "common/perf_timer.h" #include "cryptonote_config.h" extern "C" @@ -218,7 +220,7 @@ static rct::key vector_power_sum(const rct::key &x, size_t n) } /* Given two scalar arrays, construct the inner product */ -static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +static rct::key inner_product(const epee::span<const rct::key> &a, const epee::span<const rct::key> &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); rct::key res = rct::zero(); @@ -229,6 +231,11 @@ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) return res; } +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +{ + return inner_product(epee::span<const rct::key>(a.data(), a.size()), epee::span<const rct::key>(b.data(), b.size())); +} + /* Given two scalar arrays, construct the Hadamard product */ static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) { @@ -294,7 +301,7 @@ static rct::keyV vector_subtract(const rct::keyV &a, const rct::key &b) } /* Multiply a scalar and a vector */ -static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +static rct::keyV vector_scalar(const epee::span<const rct::key> &a, const rct::key &x) { rct::keyV res(a.size()); for (size_t i = 0; i < a.size(); ++i) @@ -304,6 +311,11 @@ static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) return res; } +static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +{ + return vector_scalar(epee::span<const rct::key>(a.data(), a.size()), x); +} + /* Create a vector from copies of a single value */ static rct::keyV vector_dup(const rct::key &x, size_t N) { @@ -401,17 +413,12 @@ static rct::keyV invert(rct::keyV x) } /* Compute the slice of a vector */ -static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) +static epee::span<const rct::key> slice(const rct::keyV &a, size_t start, size_t stop) { CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); - rct::keyV res(stop - start); - for (size_t i = start; i < stop; ++i) - { - res[i - start] = a[i]; - } - return res; + return epee::span<const rct::key>(&a[start], stop - start); } static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 4db543f64..0ec654af6 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -42,179 +42,179 @@ using namespace std; struct zero_commitment { uint64_t amount; rct::key commitment; }; static const zero_commitment zero_commitments[] = { - { (uint64_t)0ull, {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66} }, - { (uint64_t)1ull, {0x17, 0x38, 0xeb, 0x7a, 0x67, 0x7c, 0x61, 0x49, 0x22, 0x8a, 0x2b, 0xea, 0xa2, 0x1b, 0xea, 0x9e, 0x33, 0x70, 0x80, 0x2d, 0x72, 0xa3, 0xee, 0xc7, 0x90, 0x11, 0x95, 0x80, 0xe0, 0x2b, 0xd5, 0x22} }, - { (uint64_t)2ull, {0x76, 0x24, 0x84, 0x63, 0xa, 0x6, 0x17, 0x17, 0x8d, 0xe, 0x33, 0xf3, 0x2e, 0xe, 0x11, 0x3e, 0xa8, 0x46, 0x86, 0x9d, 0x46, 0x4b, 0xb, 0x6f, 0xf1, 0x3b, 0x29, 0x97, 0x4, 0x9c, 0xda, 0x7d} }, - { (uint64_t)3ull, {0xcf, 0xf7, 0x7b, 0x56, 0x62, 0x1c, 0x4f, 0xef, 0x74, 0xcf, 0x37, 0xc1, 0x78, 0xd4, 0xb5, 0x8a, 0xf4, 0xad, 0x8c, 0xd4, 0x35, 0xfc, 0xb9, 0x62, 0x76, 0xbc, 0x15, 0x9c, 0x7c, 0x6a, 0x28, 0x8c} }, - { (uint64_t)4ull, {0x9a, 0xb8, 0x6c, 0x31, 0xf4, 0x22, 0xd8, 0x21, 0xb5, 0x22, 0x57, 0x30, 0xd1, 0xbf, 0x73, 0xa, 0x9b, 0x91, 0xd2, 0xee, 0xe3, 0x14, 0xb8, 0x4e, 0xbd, 0x4b, 0x93, 0xa6, 0x81, 0x61, 0x82, 0x66} }, - { (uint64_t)5ull, {0x32, 0xee, 0x2f, 0x65, 0x9a, 0xf6, 0x38, 0x58, 0xc2, 0xf7, 0xdc, 0x11, 0x1b, 0x3b, 0xb8, 0xfe, 0xc0, 0x2c, 0xac, 0x42, 0x38, 0x3b, 0xb7, 0x36, 0xde, 0x1, 0x8, 0x6f, 0x38, 0xf0, 0x12, 0x3c} }, - { (uint64_t)6ull, {0x47, 0x26, 0x2b, 0x1e, 0xa6, 0x43, 0x1, 0x6e, 0x38, 0x24, 0x17, 0x53, 0xa4, 0xfb, 0x39, 0x92, 0x9e, 0x31, 0xea, 0x9b, 0xd3, 0x41, 0x1a, 0xb1, 0x7f, 0x16, 0x6e, 0x61, 0xf6, 0xc, 0xe5, 0xa7} }, - { (uint64_t)7ull, {0xc6, 0x32, 0x93, 0x68, 0x79, 0x9a, 0xd, 0xed, 0x4c, 0x20, 0x25, 0x6b, 0xff, 0xe6, 0x45, 0x47, 0xf1, 0x7b, 0xc4, 0x23, 0x95, 0x4, 0xbe, 0x82, 0x4d, 0xff, 0x8a, 0x2b, 0xe1, 0xaf, 0xe3, 0xcd} }, - { (uint64_t)8ull, {0xd5, 0xf1, 0x50, 0x74, 0x33, 0x46, 0x19, 0xf, 0x84, 0x2b, 0x6, 0xb8, 0xfa, 0xe1, 0x20, 0xeb, 0x85, 0x24, 0x7e, 0x9f, 0x6d, 0xec, 0x88, 0xff, 0xa2, 0x23, 0xbf, 0x69, 0x94, 0xe9, 0xc8, 0xc2} }, - { (uint64_t)9ull, {0x56, 00, 0x23, 0x32, 0x9e, 0xc0, 0xfa, 0xf3, 0x3b, 0x5e, 0x3a, 0x5c, 0xb4, 0xea, 0xef, 0xee, 0x38, 0xf8, 0x96, 0x1c, 0x88, 0xb6, 0x6a, 0x2f, 0x19, 0xd4, 0x59, 0x51, 0x96, 0x9c, 0x6d, 0x1f} }, - { (uint64_t)10ull, {0x3, 0x80, 0xdc, 0x24, 0xcc, 0x97, 0xcc, 0xe6, 0x58, 0xc3, 0xa9, 0x47, 0xc5, 0x10, 0x25, 0xde, 0x1a, 0x69, 0x80, 0x3b, 0xdb, 0x50, 0x5, 0xe3, 0xb7, 0xdd, 0xa9, 0xd, 0x68, 0x59, 0xb0, 0x1c} }, - { (uint64_t)20ull, {0x9, 0x3, 0xf6, 0x2e, 0x97, 0x76, 0x47, 0x58, 0xfe, 0xf8, 0x9e, 0x5b, 0xec, 0x29, 0xef, 0x4f, 0xc5, 0xe6, 0x45, 0x4b, 0x2d, 0x47, 0x44, 0x47, 0x36, 0x4, 0x4c, 0x25, 0x2e, 0xe2, 0x8e, 0xba} }, - { (uint64_t)30ull, {0xa2, 0x8b, 0x89, 0xe0, 0xb, 0xed, 0x62, 0x31, 0x68, 0x5b, 0xf9, 0x74, 0x36, 0xf2, 0xba, 0x51, 0xa2, 0x51, 0x55, 0x7f, 0x8d, 0x17, 0xa, 0x78, 0xe3, 0x12, 0xd6, 0x24, 0xbf, 0x60, 0xff, 0xfe} }, - { (uint64_t)40ull, {0xb5, 0xc6, 0x95, 0x55, 0x6a, 0x28, 0x47, 0xb2, 0xe, 0x1c, 0xbb, 0x26, 0xe6, 0xa9, 0xc6, 0x8a, 0x61, 0xc5, 0x50, 0xce, 0xb7, 0xc3, 0x4, 0xfe, 0x92, 0x28, 0x3d, 0x29, 0xa9, 0xb2, 0x43, 0xcb} }, - { (uint64_t)50ull, {0x12, 0x8e, 0xc6, 0xcd, 0xc0, 0x6b, 0x43, 0xc5, 0xd0, 0x9c, 0x3f, 0x65, 0x2a, 0xe3, 0x44, 0x7f, 0x9b, 0x3f, 0x2c, 0x30, 0x91, 0x2d, 0xf0, 0x80, 0x37, 00, 0x85, 0xbc, 0xc, 0x9, 0xef, 0x78} }, - { (uint64_t)60ull, {0x1f, 0x9f, 0x40, 0x3a, 0xae, 0xa7, 0x16, 0xfb, 0xe2, 0x98, 0xa8, 0x14, 0xf1, 0xee, 0xbc, 0x1b, 0x73, 0x16, 0x8c, 0x37, 0xfa, 0xe3, 0x16, 0xeb, 0x65, 0x5, 0x81, 0x6f, 0xc2, 0x20, 0xeb, 0xfb} }, - { (uint64_t)70ull, {0x10, 0xa2, 0x38, 0xc5, 0xe4, 0x8e, 0x4b, 0x93, 0x99, 0xdb, 0xa6, 0xcb, 0xd9, 0x8e, 0x63, 0x54, 0x41, 0x59, 0xe9, 0x8c, 0x93, 0x5a, 0xc0, 0x60, 0x3d, 0x72, 0xde, 0xf, 0xff, 0x31, 0x53, 0xbb} }, - { (uint64_t)80ull, {0x75, 0xab, 0x78, 0xc7, 0x28, 0x1f, 0x69, 0x28, 0xf0, 0x94, 0x86, 0x5, 0x7a, 0x63, 0x64, 0x18, 0x27, 0xc5, 0x74, 0x84, 0xe3, 0xe9, 0x9a, 0x39, 0xf3, 0x12, 0xa4, 0x3a, 0x51, 0x9b, 0xda, 0x8} }, - { (uint64_t)90ull, {0xe9, 0x56, 0x7b, 0xa7, 0x88, 0xb8, 0x5b, 0x82, 0xc8, 0x65, 0x7a, 0x15, 0xa5, 0x48, 0x99, 0x5c, 0xf6, 0xb0, 0xbd, 0xd1, 0xc6, 0x2a, 0xda, 0x77, 0x55, 0xf2, 0x32, 0x3a, 0xd8, 0xa4, 0x8, 0x51} }, - { (uint64_t)100ull, {0xb6, 0x17, 0x36, 0xd5, 0xf2, 0x8d, 0xef, 0x28, 0x61, 0x6a, 0xfc, 0x47, 0x93, 0xe9, 0x9b, 0x27, 0xcd, 0x3e, 0x89, 0xfb, 0x91, 0xc1, 0x13, 0xd4, 0x30, 0x73, 0x65, 0xfb, 0x75, 0xde, 0xdf, 0x88} }, - { (uint64_t)200ull, {0x71, 0x3, 0xeb, 0x72, 0x19, 0x28, 0xd7, 0x91, 0x99, 0x87, 0xf3, 0x50, 0xca, 0xa5, 0x7a, 0xe7, 0xb0, 0x81, 0x57, 0x15, 0x3b, 0x4c, 0x43, 0xd, 0x3e, 0xde, 0xc0, 0xc2, 0x3, 0x7, 0x97, 0x44} }, - { (uint64_t)300ull, {0x24, 0x40, 0x9e, 0x92, 0x2e, 0xce, 0xd1, 0xa0, 0x5e, 0x4e, 0xac, 0xa3, 0xdf, 0x91, 0x19, 0xc3, 0x8a, 0x92, 0x2e, 0xb, 0x66, 0xd0, 0x2d, 0x9d, 0xd2, 0xfb, 0x1d, 0xcc, 0x20, 0xb9, 0xaf, 0xc7} }, - { (uint64_t)400ull, {0xa7, 0x72, 0x9f, 0xa9, 0x32, 0x81, 0x82, 0x99, 0x34, 0x11, 0x5d, 0x47, 0x5a, 0x67, 0x86, 0xa, 0x14, 0x12, 0xc5, 0xe5, 0x95, 0x12, 0x20, 0xd9, 0x60, 0xc2, 0x41, 0xa0, 0x19, 0x1a, 0x9e, 0x65} }, - { (uint64_t)500ull, {0x2e, 0x53, 0xc, 0x6, 0x1c, 0x6d, 0x9e, 0x97, 0xab, 0xaf, 0x46, 0x8c, 0x32, 0xb0, 0xad, 0xa7, 0x49, 0x22, 0x57, 0x72, 0xfc, 0xd1, 0x17, 0x41, 0xcb, 0x5c, 0x3, 0x5c, 0xdd, 0x26, 0x14, 0xe} }, - { (uint64_t)600ull, {0xa5, 0xb, 0x91, 0x9, 0x9d, 0xf1, 0xb1, 0x69, 0x4f, 0x30, 0xb5, 0x8f, 0xe6, 0x77, 0x68, 0x50, 0xdb, 0xdb, 0xf4, 0x6c, 0xed, 0x99, 0x7f, 0x52, 0x62, 0xa8, 0x51, 0x59, 0x40, 0x74, 0xa5, 0x9d} }, - { (uint64_t)700ull, {0x51, 0x2c, 0xf, 0xae, 0xcc, 0xbe, 0xf2, 0xfe, 0xe5, 0x75, 0x4c, 0x6a, 0x45, 0xfd, 0xc0, 0x75, 0x2d, 0x4f, 0x15, 0x22, 0xe7, 0x7f, 0xf0, 0xc4, 0x8d, 0xcb, 0x19, 0x91, 0x8a, 0x68, 0x84, 0xe0} }, - { (uint64_t)800ull, {0xda, 0xa9, 0xf9, 0xa5, 0xb9, 0x71, 0x33, 0x33, 0xe9, 0x8c, 0x5, 0xac, 0xe7, 0x27, 0xcc, 0xe, 0x7d, 0xc3, 0xf1, 0x59, 0x49, 0xe1, 0xef, 0x4d, 0x94, 0xfa, 0x47, 0xd6, 0x8a, 0x34, 0xc6, 0x75} }, - { (uint64_t)900ull, {0xc7, 0x2b, 0x18, 0xc9, 0x17, 0xcd, 0x43, 0xee, 0x78, 0x40, 0x5e, 0x39, 0x83, 0x98, 0xb8, 0x3a, 0xc0, 0x97, 0x7b, 0x25, 0x19, 0x90, 0xd8, 0x13, 0xc, 0x38, 0xba, 0x53, 0xb6, 0x3d, 0xb4, 0xf7} }, - { (uint64_t)1000ull, {0x1, 0xdf, 0x60, 0x91, 0xeb, 0x6a, 0x48, 0xe9, 0xe4, 0x22, 0x25, 0xb, 0xe3, 0x83, 0x88, 0xc8, 0x61, 0xb6, 0x55, 0x55, 0xa7, 0x20, 0xad, 0x15, 0x35, 0x86, 0xfe, 0x2b, 0xd2, 0x2f, 0xa2, 0x3d} }, - { (uint64_t)2000ull, {0x24, 0xf5, 0xb1, 0x34, 0x78, 0x46, 0xaf, 0x22, 0xb5, 0x6f, 0x41, 0x25, 0xb3, 0xe7, 0x67, 0x8c, 0xf8, 0x4b, 0x4f, 0xd2, 0xf9, 0x2e, 0x1c, 0x40, 0xaa, 0x3a, 0x1b, 0xe0, 0xc7, 0x4d, 0x95, 0xe6} }, - { (uint64_t)3000ull, {0xa7, 0x1c, 0x9a, 0x8f, 0x40, 0xc1, 0x25, 0x9c, 0x36, 0x26, 0x27, 0x73, 0xe0, 0x8, 0x20, 0x18, 0x3e, 0x6b, 0x59, 0xe0, 0x71, 0xc9, 0x9b, 0x34, 0x9b, 0xef, 0x8f, 0x7e, 0xd2, 0xc6, 0xad, 0xb9} }, - { (uint64_t)4000ull, {0x98, 0xdc, 0x74, 0xaf, 0x19, 0x89, 0xd3, 0x4b, 0x64, 0x2e, 0xb3, 0x6, 0x2d, 0xbc, 0x9d, 0xca, 0xd8, 0x1, 0xc5, 0x65, 0x27, 0x6, 0x93, 0x99, 0xe7, 0xc4, 0x11, 0xad, 0x14, 0x28, 0x82, 0xf6} }, - { (uint64_t)5000ull, {0x61, 0x76, 0xac, 0x4a, 0xc0, 0x6, 0x5e, 0x49, 0xd6, 0xc4, 0x41, 0xcf, 0x40, 0x4f, 0xad, 0xda, 0xad, 0x44, 0x93, 0xe, 0xf0, 0x3c, 0x68, 0x9, 0xad, 0xd7, 0x77, 0xe4, 0x2f, 0xee, 0x7f, 0x10} }, - { (uint64_t)6000ull, {0x78, 0x79, 0x4, 0x65, 0xf6, 0x60, 0x5b, 0x5a, 0x84, 0x77, 0x36, 0x5a, 0xa6, 0xc2, 0xa4, 0xa5, 0x84, 0x91, 0xc, 0x23, 0x95, 0x2, 0x92, 0x97, 0x52, 0x49, 0xa1, 0xad, 0x7d, 0xf0, 0xf7, 0xe8} }, - { (uint64_t)7000ull, {0x20, 0xa5, 0x60, 0x6b, 0x60, 0x23, 0x95, 0xd6, 0x8e, 0x2f, 0xad, 0x8e, 0xc6, 0x7f, 0x92, 0xde, 0x89, 0xc6, 0x3e, 0x1e, 0x7f, 0xc1, 0xdd, 0x7f, 0x92, 0xff, 0xed, 0xb8, 0xf6, 0x55, 0xfb, 0xd} }, - { (uint64_t)8000ull, {0x9a, 0x78, 0x97, 0x43, 0x98, 0x65, 0x17, 0xd9, 0x5f, 0x4e, 0x80, 0x8b, 0xeb, 0xe6, 0x52, 0xd, 0xe6, 0xcf, 0x8c, 0x51, 0x35, 0xab, 0x36, 0x8, 0x7e, 0x87, 0xe2, 0x76, 0xac, 0x6a, 0x34, 0x1} }, - { (uint64_t)9000ull, {0x5f, 0xc7, 0xaa, 0x48, 0xbb, 0x19, 0x13, 0x58, 0xc7, 0xe3, 0x4d, 0x24, 0xcf, 0x9c, 0x31, 0x16, 0x74, 0x12, 0x7a, 0xb2, 0x45, 0xd0, 0x8f, 0x4e, 0x2c, 0xfd, 0xbf, 0x8f, 0x5, 0xc9, 0x5b, 0xf5} }, - { (uint64_t)10000ull, {0x61, 0x20, 0xe7, 0x76, 0xe9, 0x12, 0xab, 0x10, 0x5a, 0x49, 0xf9, 0xda, 0x2, 0xa6, 0x75, 0x17, 0xc0, 0xa9, 0xb, 0x2b, 0x3e, 0x2d, 0xa3, 0xd, 0xff, 0x34, 0x39, 0x93, 0xdb, 0xec, 0x95, 0x97} }, - { (uint64_t)20000ull, {0x77, 0xbf, 0xb5, 0x37, 0xac, 0xa, 0xbc, 0x41, 0xaa, 0x21, 0xd0, 0xec, 0xd9, 0x18, 0x13, 0x34, 0xd8, 0x6b, 0xa7, 0x86, 0x5a, 0x94, 0x47, 0xf5, 0xc1, 0x58, 0x9a, 0x81, 0xd7, 0xef, 0xb3, 0xbb} }, - { (uint64_t)30000ull, {0x35, 0xf4, 0x5, 0xa9, 0x5f, 0x75, 0x19, 0x2a, 0xe9, 0xc0, 0xd4, 0xf5, 0x88, 0x84, 0x47, 0x14, 0xf6, 0x85, 0x1b, 0x97, 0xce, 0xbd, 0x9f, 0x7c, 0x2, 0xc5, 0xdd, 0xd7, 0xbf, 0x58, 0xff, 0x31} }, - { (uint64_t)40000ull, {0x77, 0x55, 0xbb, 0x3f, 0x38, 0x7c, 0x21, 0xb8, 0xa0, 0xf4, 0x48, 0x1f, 0xbf, 0xa8, 0x8a, 0xbe, 0xee, 0xce, 0xc7, 0x56, 0x53, 0xfc, 0xa1, 0x89, 0x58, 0x39, 0xc1, 0xba, 0x6, 0x47, 0x9f, 0x96} }, - { (uint64_t)50000ull, {0x8b, 0x7e, 0x84, 0xa3, 0x37, 0xb7, 0xb9, 0xcd, 0x5d, 0xb3, 0x63, 0x33, 0x8, 0xad, 0x51, 0x86, 0xa3, 0x59, 0xd, 0xff, 0xb8, 0x23, 0x1e, 0x2f, 0x31, 0xfd, 0x20, 0x42, 0x54, 0x9f, 0xfb, 0xe2} }, - { (uint64_t)60000ull, {0xef, 0xfd, 0xa6, 0x25, 0x15, 0xea, 0xb1, 0xbc, 0x1e, 0xbd, 0x74, 0x92, 0x94, 0x9b, 0x1, 0x22, 0xc3, 0x9f, 0x71, 0xa, 0x65, 0x16, 0xec, 0x66, 0x8c, 0x37, 0x61, 0xe6, 0xcc, 0x36, 0x1f, 0x25} }, - { (uint64_t)70000ull, {0x16, 0xba, 0x89, 00, 0xf3, 0x6f, 0xf, 0x6c, 0x46, 0x1c, 0xb, 0xe7, 0x64, 0xae, 0xee, 0x48, 0x86, 0x6, 0xb0, 0x53, 0xed, 0xdc, 0x10, 0xb5, 0x9a, 0x3e, 0xde, 0xcd, 0x23, 0xd4, 0x4f, 0xc0} }, - { (uint64_t)80000ull, {0x4d, 0xd4, 0x70, 0x3b, 0x7b, 0x7f, 0xcf, 0xe7, 0x2a, 0x2e, 0x4f, 0x31, 0xa4, 0x34, 0x17, 0xf9, 0xc0, 0xda, 0x64, 0x2f, 0xd0, 0xa9, 0x29, 0xb8, 0xf5, 0xed, 0xd8, 0x3, 0x7f, 0x93, 0xc5, 0xb3} }, - { (uint64_t)90000ull, {0x8e, 0xfc, 0x3, 0x20, 0x40, 0xbd, 0x90, 0x41, 0xda, 0x3d, 0xb0, 0x9b, 0xa1, 0x3d, 0xa2, 0xa5, 0xd1, 0xb8, 0x12, 0x3, 0xa, 0x5a, 0x36, 0x7c, 0x58, 0x94, 0xbd, 0x54, 0x11, 0x9, 0xe7, 0x30} }, - { (uint64_t)100000ull, {0xbd, 0x2e, 0xb1, 0x97, 0x83, 0x57, 0x1c, 0xf2, 0x22, 0x2c, 0x81, 0xb, 0x69, 0xf, 0xc7, 0x66, 0x64, 0x57, 0xae, 0x20, 0x92, 0x5b, 0x90, 0x5, 0xce, 0xe6, 0x1d, 0xf2, 0x66, 0x6f, 0xdc, 0xb7} }, - { (uint64_t)200000ull, {0x83, 0xd4, 0xcd, 0xdd, 0xc1, 0x44, 0x87, 0x32, 0xf2, 0x97, 0x7c, 0x41, 0xaa, 0xa7, 0x1f, 0xe6, 0xde, 0x9c, 0x17, 0x6d, 0xa8, 0x99, 0xee, 0xbf, 0xfc, 0x1b, 0xb, 0xa9, 0xea, 0x92, 0x97, 0x90} }, - { (uint64_t)300000ull, {0xcc, 0xc0, 0x6b, 0x44, 0xc3, 0x1, 0x38, 0x6, 0x30, 0x45, 0xed, 0x1, 0xd2, 0x45, 0xd8, 0x14, 0x3, 0xb6, 0x36, 0x52, 0xeb, 0xc4, 0xf9, 0x96, 0x7f, 0xd, 0x7f, 0x38, 0x69, 0x7f, 0x46, 0x16} }, - { (uint64_t)400000ull, {0x1b, 0xbf, 0xe7, 0xe, 0xca, 0xf1, 0xdd, 0xd7, 0xf1, 0x2, 0x36, 0xf6, 0x8a, 0x41, 00, 0xb, 0x5d, 0xab, 0x2d, 0x47, 0x5c, 0xb9, 0x2f, 0x62, 0xc2, 0xd6, 0x84, 0xcf, 0x57, 0x69, 0xfb, 0x84} }, - { (uint64_t)500000ull, {0xf1, 0xb1, 0xcd, 0xaa, 0x78, 0x14, 0x95, 0x36, 0xf, 0x53, 0x31, 0x81, 0xaa, 0x58, 0xc8, 0xbd, 0xae, 0x6a, 0x77, 0x98, 0xd0, 0x2d, 0xab, 0x6d, 0x56, 0x26, 0x81, 0x27, 0x67, 0x9, 0xe7, 0x1} }, - { (uint64_t)600000ull, {0xd5, 0x26, 0x7d, 0x60, 0xd4, 0xfe, 0x9b, 0xc5, 0xfe, 0xfa, 0x7d, 0x3f, 0xe0, 0x7c, 0xd1, 0xfa, 0xd4, 0x55, 0x73, 0xd5, 0xae, 0x19, 0x10, 0xda, 0x7, 0x3e, 0x6d, 0x2d, 0xf9, 0xe2, 0x4, 0x39} }, - { (uint64_t)700000ull, {0xb, 0x58, 0x11, 0x25, 0xc2, 0xc4, 0x83, 0xc9, 0xa3, 0xd8, 0xbc, 0x8, 0x32, 0x2f, 0x26, 0xaa, 0x1f, 0xc5, 0xe, 0x41, 0x53, 0x2c, 0x1b, 0x9d, 0xf6, 0x26, 0xb0, 0x9, 0xd7, 0x88, 0x67, 0xcf} }, - { (uint64_t)800000ull, {0xf5, 0xb3, 0xd1, 0x8f, 0x66, 0xd0, 0xf9, 0x17, 0x5c, 0x30, 0x83, 0xb5, 0xf8, 0x7, 0x8e, 0xaf, 0xa8, 0x9e, 0xf8, 0x1d, 0xe7, 0x15, 0x8, 0xbc, 0x25, 0x1f, 0x5c, 0x5f, 0xe7, 0x25, 0x2e, 0x6} }, - { (uint64_t)900000ull, {0x1, 0xde, 0x40, 0x2c, 0x4b, 00, 0x43, 0x4, 0x2e, 0xae, 0x9e, 0xde, 0xa1, 0x49, 0x2b, 0x9d, 0x82, 0xb7, 0xbc, 0x36, 0x68, 0xe9, 0xb5, 0x84, 0xb0, 0x31, 0x3d, 0x44, 0x50, 0x53, 0x40, 0x74} }, - { (uint64_t)1000000ull, {0x53, 0x4b, 0x85, 0xc7, 0x89, 0x3f, 0x66, 0xf0, 0x26, 0xb6, 0x5e, 0xd7, 0xe7, 0xa4, 0xb8, 0xc9, 0xf4, 0xb, 0xe3, 0x1b, 0xcd, 0xa, 0x3d, 0xcd, 0x27, 0xc4, 0x71, 0x2, 0x56, 0x51, 0x65, 0x3} }, - { (uint64_t)2000000ull, {0xcd, 0xb5, 0xda, 0xfa, 0x53, 0x10, 0xf5, 0x26, 0x2f, 0xfc, 0x9, 0x26, 0xd0, 0xdf, 0x6e, 0xeb, 0xee, 0x2d, 0x52, 0xa9, 0x8d, 0xc6, 0x9f, 0xd, 0xc5, 0xe4, 0xeb, 0xf0, 0xc1, 0xa8, 0x77, 0x2e} }, - { (uint64_t)3000000ull, {0x9e, 0x75, 0x63, 0xf0, 0x33, 0x59, 0xea, 0x31, 0xe2, 0x91, 0xe7, 0xf0, 0xb8, 0x74, 0x17, 0xbc, 0xf5, 0xb2, 0x34, 0xee, 0x8b, 0x7e, 0x5b, 0x4, 0x41, 0x73, 0xbf, 00, 0x46, 0x86, 0x7c, 0x57} }, - { (uint64_t)4000000ull, {0xfd, 0x2a, 0xeb, 0xd, 0x5e, 0xe5, 0x3b, 0x77, 0xf2, 0xb1, 0xe3, 0xac, 0x75, 0x2d, 0x19, 0x38, 0x9f, 0xc5, 0xba, 0xa0, 0xf8, 0xd7, 0x64, 0x48, 0xa5, 0x9f, 0x99, 0x85, 0xa4, 0x8d, 0xa, 0x25} }, - { (uint64_t)5000000ull, {0xc0, 0xbe, 0x4f, 0xb8, 0x77, 0xb9, 0xce, 0x50, 0x87, 0x71, 0x32, 0x3b, 0xcf, 0x1f, 0xb9, 0x48, 0x47, 0x10, 0xee, 0x23, 0x2, 00, 0x6, 0xc3, 0xe8, 0xca, 0xac, 0x6e, 0x4f, 0x2, 0xfa, 0xbf} }, - { (uint64_t)6000000ull, {0xfc, 0x44, 0x5c, 0xa3, 0x84, 0xf3, 0x3e, 0x55, 0x8d, 0xc1, 0x56, 0x44, 0x9d, 0x3f, 0xba, 0x6a, 0xfd, 0x54, 0xc3, 0x42, 0xe6, 0x35, 0x11, 0xf, 0xe7, 0x9c, 0x16, 0xc7, 0x17, 0xf7, 0xd4, 0xf7} }, - { (uint64_t)7000000ull, {0xd8, 0x9, 0x2b, 0x8d, 0x45, 0xdb, 0x54, 0xa5, 0x6d, 0x64, 0xe8, 0x9, 0x4a, 0x6, 0x22, 0xe2, 0x6e, 0x8a, 0x2e, 0xec, 0xb9, 0x3, 0xb2, 0xe1, 0xf7, 0x5a, 0x83, 0x7b, 0x3a, 0xd8, 0x55, 0x4a} }, - { (uint64_t)8000000ull, {0x10, 0x4, 0x5c, 0x91, 0xdb, 0xad, 0x8a, 0x6a, 0x81, 0x62, 0x4a, 0xe0, 0xcf, 0x20, 0x5d, 0xb9, 0x97, 0x3e, 0xe8, 0x42, 0x3e, 0x97, 0xaf, 0x58, 0xa6, 0x1c, 0xfa, 0x7a, 0x78, 0x66, 0xf4, 0x1} }, - { (uint64_t)9000000ull, {0x11, 0x5c, 0x20, 0x9e, 0xe1, 0xde, 0xf3, 0x10, 0xce, 0xc9, 0xa6, 0xd1, 0x6c, 0xe6, 0x27, 0xec, 0xbd, 0xb9, 0xff, 0x2c, 0x23, 0x9, 0x3c, 0x24, 0xc8, 0x6c, 0x1b, 0xf2, 0x50, 0xd4, 0xb5, 0x85} }, - { (uint64_t)10000000ull, {0x2f, 0x99, 0xd9, 0x74, 0x44, 0x18, 0x66, 0x9, 0x49, 0xba, 0x43, 0x35, 0x61, 0xc6, 0x5, 0xb6, 0xf7, 0xbe, 0x8f, 0x82, 0xa, 0x93, 0xcb, 0x2a, 0xed, 0xa9, 0x7c, 0x87, 0x32, 0x92, 0x56, 0x49} }, - { (uint64_t)20000000ull, {0xc6, 0x77, 0x1f, 0xab, 0x14, 0xb, 0x75, 0xf4, 0xef, 0xd0, 0x97, 0xfc, 0xe1, 0x82, 0x6b, 0x80, 0xba, 0xe3, 0x16, 0xbc, 0xec, 0x28, 0x86, 0x9b, 0x3a, 0x1b, 0xf1, 0xbc, 0x6e, 0x4d, 0x20, 0x43} }, - { (uint64_t)30000000ull, {0xa1, 0xe9, 0xed, 0x3e, 0xf6, 0x5a, 0x9d, 0x52, 0x6c, 0xc2, 0x62, 0x5, 0x88, 0x12, 0x1, 0xd8, 0xa8, 0xf2, 0xc4, 0x40, 0x9f, 0xa3, 0x64, 0x10, 0x72, 0x96, 0xb9, 0xf9, 0x6a, 0x61, 0xb3, 0x58} }, - { (uint64_t)40000000ull, {0xb5, 0x31, 0x2d, 0xc7, 0x72, 0x94, 0xab, 0x9b, 0xc8, 0xbf, 0xd1, 0x39, 0x1e, 0x9a, 0xca, 0x92, 0x45, 0xe2, 0x28, 0xf7, 0x4b, 0x49, 0x74, 0xfc, 0x29, 0xad, 0x1c, 0x31, 0xcb, 0xe3, 0xe6, 0xa3} }, - { (uint64_t)50000000ull, {0xb8, 0xab, 0xc9, 0xff, 0xf6, 0x84, 0x1d, 0x2e, 0xa0, 0x13, 0x5a, 0x21, 0x72, 0xd3, 0xa7, 0xb, 0xfc, 0x2b, 0x70, 0x22, 0x8, 0xcd, 0x4a, 0x43, 0xc6, 0x30, 0xbe, 0xb1, 0xb8, 0xa0, 0x32, 0x8b} }, - { (uint64_t)60000000ull, {0x63, 0x90, 0xe1, 0xdb, 0x81, 0xb0, 0xea, 0x5c, 0xe2, 0x73, 0x94, 0x14, 0xe5, 0x2b, 0x7, 0x98, 0xd8, 0x2e, 0xb8, 0xe9, 0xae, 0xc5, 0x6d, 0xfe, 0x7e, 0x2c, 0x64, 0x11, 0xab, 0x79, 0x41, 0x87} }, - { (uint64_t)70000000ull, {0x7e, 0x51, 0xaf, 0xee, 0x5b, 0xc9, 0x71, 0x52, 0x9d, 0x64, 0x4d, 0xcd, 0x7f, 0x2a, 0x2a, 0xb0, 0x26, 0x69, 0xce, 0x2c, 0xb5, 0x7, 0xa6, 0x2d, 0xfc, 0x93, 0x17, 0x6c, 0xb6, 0xdf, 0x41, 0x38} }, - { (uint64_t)80000000ull, {0xf3, 0x7b, 0x94, 0x6b, 0x8b, 0x24, 0x88, 0xeb, 0xee, 0x1c, 0x6, 0xc1, 0x27, 0xfb, 0xe5, 0xfa, 0x5e, 0xfd, 0x62, 0x36, 0x9d, 0xd5, 0xaa, 0xda, 0xed, 0xd8, 0x88, 0x50, 0x1d, 0x3b, 0x7e, 0x3b} }, - { (uint64_t)90000000ull, {0x46, 0xcb, 0x76, 0x57, 0xf6, 0x1c, 0x83, 0x7c, 0xec, 0x80, 0x74, 0xbb, 0xb0, 0xf5, 0x2e, 0x7f, 0xc5, 0x9a, 0xd, 0x94, 0xe0, 0x17, 00, 0x9a, 0xbe, 0x25, 0x65, 0x2e, 0x4a, 0xd2, 0xe5, 0x3d} }, - { (uint64_t)100000000ull, {0x66, 0x7b, 0x8e, 0x6f, 0x6a, 0x4b, 0x91, 0x89, 0x76, 0xd9, 0x73, 0x5a, 0x43, 0x36, 0x7d, 0xc7, 0x59, 0x2c, 0x87, 0xd0, 0xa1, 0xf8, 0x15, 0xc6, 0xe8, 0x7d, 0xf1, 0x1a, 0x13, 0x50, 0x9f, 0xb2} }, - { (uint64_t)200000000ull, {0x3b, 0xcb, 0x51, 0x48, 0x1, 0x64, 0x1b, 0x62, 0x55, 0x93, 0x8c, 0xc5, 0x3, 0x76, 0x2d, 0x35, 0xce, 0x6, 0xd7, 0x5f, 0xe9, 0x50, 0x95, 0x9a, 0x1a, 0xab, 0x21, 0x4b, 0x50, 0x9b, 0x10, 0xb} }, - { (uint64_t)300000000ull, {0xa5, 0x92, 0x6f, 0x3, 0x1e, 0x6b, 0x15, 0xeb, 0x86, 0x23, 0x51, 0x8, 0xab, 0xb1, 0xaf, 0x90, 0xc5, 0xb1, 0x62, 0xc3, 0x99, 0x8c, 0x8b, 0xbb, 0x3f, 0xfb, 0xb0, 0x72, 0x9d, 0xa9, 0x45, 0x7b} }, - { (uint64_t)400000000ull, {0xfe, 0x35, 0xb6, 0x99, 0x44, 0x41, 0xe, 0xaf, 0x81, 0x5b, 0xdc, 0xd0, 0xa4, 0xd7, 0x1e, 0xf9, 0xfc, 0x66, 0x86, 0x48, 0xad, 0x43, 0x74, 0x3b, 0x3, 0x5a, 0xed, 0x2c, 0x17, 0xc1, 0x38, 0x7a} }, - { (uint64_t)500000000ull, {0x22, 0x22, 0xd6, 0x70, 0xb8, 0x7d, 0x9b, 0x47, 0xb8, 0xb9, 0x5c, 0x8c, 0x39, 0x7b, 0xc5, 0x2e, 0x2b, 0x46, 0xa6, 0x48, 0xb0, 0x2, 0xa0, 0x48, 0x5a, 0x37, 0x5c, 0xd8, 0x1f, 0x4a, 0x54, 0x5f} }, - { (uint64_t)600000000ull, {0xd3, 0x23, 0x8a, 0x4a, 0x8b, 0x71, 0xab, 0x46, 0xd1, 0x53, 0x4, 0xac, 0xfa, 0x2f, 0x40, 0xbf, 0x5e, 0xa6, 0x3b, 0x3d, 0x86, 0x4a, 0x79, 0xfa, 0x84, 0x25, 0xd2, 0x65, 0x5a, 0xe7, 0x7, 0x6f} }, - { (uint64_t)700000000ull, {0xa8, 0xff, 0x28, 0x3f, 0xcf, 0xf0, 0x53, 0xd3, 0x44, 0xc8, 0xf7, 0x56, 0x4f, 0x40, 0x24, 0xb6, 0x6b, 0xfa, 0x45, 0x9f, 0x47, 0x6f, 0xd, 0x73, 0xc, 0x91, 0x39, 0x90, 0x8b, 0x2d, 0x64, 0x7e} }, - { (uint64_t)800000000ull, {0xf2, 0xda, 0xf8, 0x88, 0xc4, 0x46, 0x57, 0x1, 0xc0, 0xe6, 0x1e, 0x12, 0xc3, 0xfb, 0xd4, 0xea, 0x79, 0xc7, 0xec, 0xb4, 0xf0, 0xc4, 0xb1, 0x54, 0xc5, 0x1a, 0x24, 0xd1, 0xe9, 0x21, 0x28, 0xba} }, - { (uint64_t)900000000ull, {0x11, 0x6a, 0xe5, 0xd2, 0x9c, 0xec, 0x72, 0xaa, 0xc5, 0x57, 0xcb, 0x14, 0xe2, 0xcd, 0xd5, 0x53, 0xe5, 0x88, 0xff, 0x8b, 0x81, 0x78, 0x26, 0x1, 0x99, 0xc4, 0xc, 0xae, 0xa2, 0x12, 0xcb, 0x63} }, - { (uint64_t)1000000000ull, {0x8c, 0xe6, 0x48, 0x33, 0xce, 0xc9, 00, 0xcb, 0x6d, 0x5a, 0xc4, 0x6f, 0xc0, 0x23, 0x7d, 0x8f, 0x24, 0x39, 0xc3, 0xdf, 0xa2, 0x38, 0xba, 0xf9, 0xcc, 0x94, 0x16, 0x6a, 0xd2, 0xe8, 0x98, 0x87} }, - { (uint64_t)2000000000ull, {0x37, 0x8d, 0x3c, 0x5d, 0xbb, 0xa4, 0x82, 0x3d, 0x33, 0x12, 0xbb, 0x61, 0xfc, 0x6, 0x75, 0xa1, 0xbb, 0x39, 0x89, 0xf3, 0x97, 0x1, 0xeb, 0xd, 0x5c, 0xe4, 0xde, 0x5b, 0xd, 0x90, 0x74, 0x72} }, - { (uint64_t)3000000000ull, {0x7f, 0xa2, 0xd0, 0xa5, 0x99, 0xe7, 0x97, 0x2e, 0x74, 0xcb, 0x75, 0xf9, 0x8a, 0xf4, 0x84, 0xfc, 0x85, 0x19, 0xcb, 0x7e, 0x25, 0xb9, 0x84, 0xa7, 0x6d, 0x8b, 0xc2, 0xba, 0x8d, 0xaf, 0xde, 0xd8} }, - { (uint64_t)4000000000ull, {0xda, 0x3a, 0xcb, 00, 0xab, 0x2d, 0x8d, 0xcc, 0xac, 0xec, 0x8f, 0x77, 0x59, 0x21, 0xc4, 0xe, 0x26, 0xb1, 0xff, 0xbe, 0xca, 0x9e, 0xb7, 0xe6, 0x57, 0x25, 0x6f, 0x59, 0x68, 0xf2, 0x34, 0x1c} }, - { (uint64_t)5000000000ull, {0x34, 0x6, 0xd7, 0x9a, 0x50, 0xd8, 0x14, 0xa9, 0xcc, 0xed, 0x3b, 0x24, 0x4, 0xed, 0x3e, 0x1b, 0x8d, 0xa6, 0x21, 0x98, 0x8c, 0x43, 0xb1, 0x93, 0x69, 0x42, 0xf4, 0x94, 0xa, 0xc5, 0xbf, 0x6a} }, - { (uint64_t)6000000000ull, {0xf8, 0x3e, 0xe8, 0xc1, 0x62, 0xfc, 0x52, 0xa0, 0x8, 0x9f, 0x46, 0xe8, 0x29, 0xc2, 0xea, 0xf6, 0xa1, 0x9f, 0xd5, 0x96, 0xcd, 0x12, 0xb3, 0xe8, 0x19, 0xd5, 0x67, 0x69, 0x44, 0xf, 0x7b, 0x4e} }, - { (uint64_t)7000000000ull, {0x8c, 0x72, 0x7d, 0x24, 0x57, 0xf3, 0x4b, 0x2f, 0xdb, 0x6a, 0xdf, 0x69, 0x1a, 0xb3, 0x5f, 0xaa, 0xe4, 0xff, 0x23, 0x4c, 0x28, 0xb4, 0x4e, 0x9f, 0xd3, 0x71, 0x8e, 0xef, 0xec, 0x41, 0x75, 0x80} }, - { (uint64_t)8000000000ull, {0x4a, 0x2e, 0x2f, 0x76, 0xe3, 0x5d, 0xcb, 0xa8, 0x97, 0xa3, 0xae, 0x72, 0xc4, 0x27, 0xd, 0x9c, 0x13, 0x17, 0x14, 0xed, 0x19, 0x1b, 0x55, 0x5c, 0x5e, 0x1, 0xe4, 0x75, 0x7c, 0xba, 0xe7, 0x2c} }, - { (uint64_t)9000000000ull, {0x3f, 0x9f, 0xc, 0x4, 0xc0, 0xb9, 0xec, 0x9b, 0x4d, 0x11, 0x7c, 0x5f, 0xc9, 0xf1, 0x8a, 0x20, 0xf2, 0xb3, 0xfa, 0xcc, 0xa4, 0xc8, 0xae, 0x41, 0xaf, 0x7c, 0x8, 0xe9, 0xe0, 0xef, 0xb9, 0x81} }, - { (uint64_t)10000000000ull, {0x97, 0xc9, 0x2a, 0x29, 0x1, 0x5e, 0xcb, 0x49, 0xf8, 0x9, 0x5, 0x45, 0xe0, 0x1f, 0xf9, 0x78, 0x6c, 0xae, 0x40, 0x57, 0x73, 0x47, 0x61, 0x18, 0x24, 0xf4, 0xb6, 0x59, 0x9f, 0xf5, 0xd3, 0x64} }, - { (uint64_t)20000000000ull, {0x9, 0xba, 0xed, 0x9a, 0x3c, 0x44, 0xb2, 0x22, 0x85, 0xa0, 0xae, 0xa4, 0x14, 0x8c, 0xa7, 0xde, 0x9b, 0xea, 0x96, 0x3c, 0xf6, 0x96, 0x23, 0xb6, 0x83, 0x44, 0x5c, 0xa, 0x10, 0xa5, 0x86, 0x77} }, - { (uint64_t)30000000000ull, {0x45, 0xac, 0xaf, 0x1d, 0xe2, 0x89, 0x6d, 0xe8, 0x72, 0x84, 0xff, 0xed, 0x57, 0x8b, 0x77, 0x14, 0xf5, 0x18, 0xa6, 0x18, 0xe2, 0xae, 0x6f, 0x90, 0xae, 0x4f, 0x70, 0x13, 0xa2, 0x8e, 0x99, 0xe0} }, - { (uint64_t)40000000000ull, {0x8, 0xb8, 0x47, 0x36, 0x42, 0x24, 0xe2, 0x9c, 0xe3, 0x36, 0x63, 0x93, 0xc2, 0xe1, 0x1e, 0xfc, 0x75, 0x55, 0xde, 0xe1, 0xa0, 0x5f, 0x91, 0xa7, 0x2e, 0x61, 0x11, 0x76, 0x84, 0xdd, 0xbe, 0x29} }, - { (uint64_t)50000000000ull, {0x6c, 0x8e, 0xe, 0x4a, 0x63, 0x4f, 0x85, 0x9a, 0x31, 0xab, 0x2f, 0x7a, 0x78, 0xc0, 0xc4, 0xa5, 0x93, 0x8c, 0xb7, 0x7f, 0x3, 0x35, 0x50, 0xa4, 0x7d, 0x7e, 0x31, 0x81, 0xb6, 0xb2, 0x6e, 0xc0} }, - { (uint64_t)60000000000ull, {0x66, 0xc2, 0xa0, 0x9, 0x65, 0xf9, 0xbf, 0xcb, 0xb1, 0x1e, 0xa0, 0x3c, 0xf1, 0xd6, 0x31, 0xb0, 0xe, 0x8a, 0x1e, 0xf7, 0xa6, 0xb, 0x1b, 0xe4, 0xa5, 0xac, 0x9, 0x23, 0xb, 0xf8, 0x17, 0x3f} }, - { (uint64_t)70000000000ull, {0x63, 0x51, 0xd7, 0x74, 0xc0, 0x2c, 0x5a, 0x9d, 0xee, 0xcf, 0xdb, 0xab, 0x70, 0x96, 0x68, 0x59, 0x8c, 0x47, 0xe4, 0xb1, 0x78, 0x2c, 0xe5, 0xae, 0x31, 0x6a, 0xf7, 0x40, 0xa6, 0x6f, 0x7e, 0x30} }, - { (uint64_t)80000000000ull, {0x5a, 0xcc, 0xfd, 0x16, 0x22, 0x79, 0xa5, 0x1c, 0x8b, 0x3b, 0xd5, 0xd3, 0x67, 0x9e, 0x91, 0x89, 0x67, 0xa2, 0x64, 0xea, 0x6, 0x3d, 0x37, 0xdf, 0xf5, 0xe3, 0x45, 0x7e, 0xc3, 0x7, 0xd4, 0x57} }, - { (uint64_t)90000000000ull, {0xb7, 0x47, 0xfc, 0x1, 0xc6, 0xf0, 0xc7, 0x49, 0x67, 0x3a, 0x29, 0x10, 0x25, 0xc, 0x2e, 0x23, 0xcb, 0x38, 0x27, 0x4d, 0x63, 0xb4, 0x2f, 0x52, 0x1b, 0x84, 0x63, 0x56, 0xe4, 0x13, 0x61, 0x8f} }, - { (uint64_t)100000000000ull, {0x9, 0x42, 0x84, 0x3d, 0x6f, 0x69, 0xe1, 0xcf, 0x3d, 0x99, 0xc9, 0x9f, 0xc, 0x97, 0xc0, 0xe6, 0xe5, 0x78, 0x93, 0x5a, 0xf6, 0xa8, 0xbd, 0xb8, 0xf8, 0x1d, 0x5b, 0x90, 0xbd, 0xe7, 0xcc, 0x10} }, - { (uint64_t)200000000000ull, {0x56, 0x4c, 0x64, 0xea, 0x50, 0xe4, 0xbd, 0x20, 0xdb, 0x58, 0x5d, 0xb5, 0x87, 0xb1, 0xf7, 0x64, 0xa2, 0x62, 0xd8, 0x46, 0xa6, 0xb0, 0xa2, 0x4b, 0x43, 0x27, 0x60, 0xd2, 0xf9, 0xde, 0x66, 0x5b} }, - { (uint64_t)300000000000ull, {0xac, 0x65, 0x83, 0x41, 0x5b, 0xd6, 0x4c, 0x3, 0x35, 0x97, 0xf9, 0x28, 0xa4, 0xb5, 0xd4, 0xf4, 0x78, 0x9e, 0xa8, 0xb2, 0x87, 0x82, 0x73, 0x89, 0xa8, 0x1e, 0xb6, 0x62, 0x9e, 0xc5, 0xb8, 0x50} }, - { (uint64_t)400000000000ull, {0x52, 0xf4, 0x9d, 0x89, 0xcf, 0x74, 0x13, 0x2f, 0xc7, 0x43, 0x2e, 0x6a, 0x6b, 0xef, 0xcf, 0xf3, 0xfd, 0x13, 0xd6, 0x3b, 0x51, 0x60, 0xab, 0x1c, 0xe6, 0x4a, 0xb0, 0xd1, 0x21, 0xcd, 0xa9, 0x9a} }, - { (uint64_t)500000000000ull, {0xe9, 0xaa, 0x7c, 0x81, 0xcd, 0xb5, 0xb3, 0x14, 0x8f, 0xb7, 0x62, 0x80, 0x63, 0xcd, 0x7a, 0x7, 0xd1, 0xad, 0xd1, 0x64, 0x3c, 0xed, 0xd3, 0xfa, 0x34, 0x47, 0x9d, 0x85, 0x9c, 0xc5, 0x62, 0x65} }, - { (uint64_t)600000000000ull, {0x98, 0x27, 0xae, 0x31, 0xe5, 0xc2, 0xa7, 0x78, 0x39, 0xf6, 0xb, 0x83, 0xab, 0x45, 0x78, 0xe2, 0xa0, 0x1e, 0xfa, 0x4b, 0x3b, 0x14, 0xcc, 0x72, 0x73, 0x14, 0xff, 0xd7, 0x15, 0x53, 0x63, 0xbf} }, - { (uint64_t)700000000000ull, {0x72, 0x91, 0x6a, 0x79, 0x27, 0xff, 0x13, 0x24, 0xd4, 0x98, 0x40, 0xec, 0xc0, 0x98, 0x68, 0xb8, 0xf3, 0x15, 0xe4, 0xf1, 0xf6, 0xd4, 0x45, 0x8d, 0x37, 0x5e, 0xc7, 0x45, 0xfc, 0x2e, 0x63, 0x53} }, - { (uint64_t)800000000000ull, {0x66, 0x76, 0xe0, 0x4, 0xf, 0xa4, 0xb8, 0x22, 0x9c, 0x61, 0x69, 0xc, 0x71, 0x32, 0x22, 0xcf, 0x3d, 0x37, 0xb9, 0x49, 0x3b, 0x49, 0x6, 0x80, 0xbb, 0x48, 0xd8, 0xd5, 0x1a, 0xde, 0x95, 0xf2} }, - { (uint64_t)900000000000ull, {0x41, 0x54, 0xb3, 0x46, 0x5a, 0x43, 0x72, 0x67, 0x1e, 0xa9, 0xe0, 0x64, 0xa7, 0xca, 0xa6, 0x6e, 0x14, 0xb4, 0x98, 0x6a, 0x46, 0x68, 0x91, 0x8a, 0xfa, 0x57, 0x9b, 0xf1, 0xed, 0x25, 0x6, 0xdd} }, - { (uint64_t)1000000000000ull, {0xbb, 0x6f, 0x70, 0x62, 0xca, 0x30, 0x6d, 0x67, 0x2a, 0x73, 0xe, 0x2a, 0x2f, 0x21, 0x9b, 0xdb, 0xe4, 0xc, 0x9f, 0xb3, 0xfe, 0x4d, 0x60, 0x13, 0x69, 0x2a, 0xf9, 0x3c, 0xdb, 0x2e, 0xc, 0xd1} }, - { (uint64_t)2000000000000ull, {0xbc, 0xe, 0xae, 0x5b, 0x9c, 0x6a, 0xd6, 0x38, 0x7a, 0x41, 0x19, 0x3c, 0x46, 0xf3, 0xc1, 0xd0, 0x71, 0x6d, 0x77, 0xd6, 0x4e, 0x22, 0xb2, 0xe0, 0x7b, 0x4b, 0xce, 0x75, 0x67, 0x65, 0xa2, 0xb} }, - { (uint64_t)3000000000000ull, {0x10, 0xc, 0x6f, 0x13, 0x42, 0xb7, 0x1b, 0x73, 0xed, 0xdd, 0xc5, 0x49, 0x2b, 0xe9, 0x23, 0x18, 0x2f, 00, 0xa6, 0x83, 0x48, 0x8e, 0xc3, 0xa2, 0xa1, 0xc7, 0xa9, 0x49, 0xcb, 0xe5, 0x77, 0x68} }, - { (uint64_t)4000000000000ull, {0x41, 0x9f, 0x7c, 0x94, 0x91, 0x4, 0x34, 0xf, 0xd3, 0xce, 0x85, 0x94, 0x8d, 0x2e, 0xf9, 0xf0, 0xdd, 0x4b, 0xb3, 0xd9, 0x2f, 0x5a, 0x78, 0x2c, 0x5f, 0x78, 0x4, 0xb7, 0x52, 0x9a, 0x13, 0xc6} }, - { (uint64_t)5000000000000ull, {0x40, 0x65, 0x34, 0x98, 0xbe, 0xa0, 0x22, 0xe3, 0x36, 0x5a, 0x3, 0xe5, 0x75, 0x25, 0xba, 0x65, 0x96, 0x53, 0x76, 0x24, 0x4f, 0xff, 0x10, 0x73, 0xe, 0xd9, 0x7a, 0x73, 0xb7, 0x53, 0x1, 0x91} }, - { (uint64_t)6000000000000ull, {0xdb, 0x1c, 0x7c, 0xf6, 0x8, 0x91, 0xf9, 0x65, 0xeb, 0xa9, 0xc6, 0x2, 0x24, 00, 0x63, 0xe, 00, 0x47, 0x95, 0x34, 0xe6, 0xf5, 0xb5, 0x33, 0xdc, 0xfc, 0x83, 0x19, 0x38, 0x52, 0x2c, 0x78} }, - { (uint64_t)7000000000000ull, {0x59, 0xa0, 0x3a, 0x31, 0x53, 0xa9, 0x94, 0xd7, 0x23, 0x27, 0xe4, 0xd9, 0x24, 0x21, 0xd3, 0xe3, 0x29, 0x1b, 0x1f, 0xa1, 0xb2, 0x40, 0xde, 0x44, 0xb9, 0x2d, 0x7f, 0x62, 0xec, 0x1, 0x28, 0xf1} }, - { (uint64_t)8000000000000ull, {0xb2, 0x80, 0xb9, 0x3b, 0x1e, 0x43, 0x88, 00, 0x73, 0xea, 0x4a, 0xa0, 0xef, 0x11, 0x4, 0xf8, 0x24, 0xbd, 0x12, 0x7a, 0x4a, 0x3d, 0xa2, 0x13, 0x92, 0x65, 0xf, 0xe8, 0xc6, 0x55, 0xb6, 0xc5} }, - { (uint64_t)9000000000000ull, {0xda, 0xf0, 0xd3, 0xe9, 0x32, 0x17, 0xd8, 0xe9, 0x5a, 0xbf, 0xdd, 0xf1, 0x3b, 0x7f, 0xd4, 0x8e, 0x34, 0x47, 0xad, 0x9, 0x23, 0x26, 0xb8, 0x99, 0xed, 0x58, 0x1f, 0xd5, 0xf8, 0x6, 0xc5, 0x6} }, - { (uint64_t)10000000000000ull, {0x16, 0x3d, 0xd6, 0x82, 0xec, 0x97, 0x7c, 0xdd, 0xa5, 0x95, 0x31, 0xda, 0x3f, 0xfa, 0x72, 0x99, 0x8a, 0x6f, 0x88, 0x37, 0xab, 0xad, 0xc6, 0x36, 0xaa, 0xed, 0xc8, 0xbe, 0x19, 0xb2, 0xd7, 0xc7} }, - { (uint64_t)20000000000000ull, {0x2, 0xfa, 0x35, 0x3a, 0xa8, 0x4e, 0xa8, 0xc4, 0x4c, 0x80, 0x23, 0x6, 0x5d, 0x79, 0x41, 0x60, 0x6b, 0x1f, 0xa5, 0xc2, 0x64, 0xdc, 0xcf, 0x46, 0xdc, 0x64, 0x94, 0xeb, 0xe9, 0x60, 0x6f, 0x20} }, - { (uint64_t)30000000000000ull, {0x87, 0x5, 0xd, 0xab, 0xf5, 0xb2, 0x3e, 0x8b, 0x79, 0x81, 0x3f, 0x4e, 0xd7, 0x6a, 0xa4, 0xad, 0xd2, 0x25, 0xdd, 0x2a, 0x50, 0x89, 0xaf, 0x6, 0x7d, 0xa7, 0x7c, 0xcb, 0x6e, 0xc5, 0x59, 0x46} }, - { (uint64_t)40000000000000ull, {0xaa, 0xe6, 0xb2, 0xc8, 0xa2, 0x9e, 0x4d, 0xbc, 0x63, 0x76, 0xc1, 0x72, 0x5, 0xfb, 0x2, 0x85, 0xe7, 0xd7, 0xd3, 0x25, 0x32, 0x3c, 0xd5, 0x26, 0xf, 0x98, 0xad, 0xff, 0xf7, 0xd4, 0xd4, 0xfb} }, - { (uint64_t)50000000000000ull, {0x9d, 0x79, 0x28, 0x82, 0x12, 0xa1, 0xe2, 0x3c, 0x9, 0x9f, 0xb2, 0xd8, 0xf0, 0xd0, 0xdb, 0xd3, 0xc2, 0xec, 0xd7, 0x58, 0xb9, 0xe6, 0xb5, 0xb4, 0xf2, 0x90, 0x60, 0x7, 0x9f, 0x19, 0x66, 0x9f} }, - { (uint64_t)60000000000000ull, {0x18, 0x90, 0x10, 0x6f, 0x1b, 0x97, 0xbc, 0x2d, 0xa, 0xe3, 0x96, 0xe5, 0xe5, 0x5e, 0xbf, 0xcc, 0x8e, 0xf6, 0x91, 0x7f, 0xb1, 0x96, 0xcb, 0x2b, 0x1e, 0x80, 0x25, 0x5d, 0x54, 0xb6, 0x87, 0x10} }, - { (uint64_t)70000000000000ull, {0x57, 0xd3, 0x4e, 0xf7, 0x54, 0x3b, 0xe4, 0x7b, 0x7b, 0xf4, 0x97, 0xce, 0x4a, 0x17, 0x6e, 0x78, 0xc6, 0xd6, 0x5c, 0xd3, 0x27, 0xf6, 0x4b, 0xa7, 0x5c, 0x27, 0xd1, 0x57, 0xb3, 0x37, 0x12, 0x5d} }, - { (uint64_t)80000000000000ull, {0x5e, 0xcb, 0x10, 0x15, 0x4b, 0x96, 0xca, 0xb5, 0x5e, 0x9, 0x46, 0x83, 0xf8, 0xdb, 0xff, 0x7f, 0x56, 0x63, 0x5f, 0xa6, 0x64, 0x97, 0xee, 0x9e, 0x24, 0xe, 0x83, 0x63, 0x7c, 0x7c, 0x87, 0x72} }, - { (uint64_t)90000000000000ull, {0x42, 0x32, 0x69, 0x98, 0x51, 0x30, 0xf1, 0x66, 0x51, 0x6a, 0x5b, 0xa8, 0x61, 0x9, 0x6d, 0x72, 0xec, 0xcc, 0x67, 0xad, 0xab, 0xa4, 0x5e, 0xb3, 0x73, 0x9a, 0xe, 0xbc, 0x61, 0xa3, 0x20, 0xae} }, - { (uint64_t)100000000000000ull, {0xa8, 0xd1, 0x60, 0x95, 0x91, 0x49, 0x8f, 0xa7, 0xc2, 0x94, 0x27, 0xad, 0x89, 0x31, 0xaf, 0x36, 0xc5, 0x2d, 0xc9, 0x7b, 0x4a, 0x11, 0xe7, 0x47, 0xa9, 0x56, 0xc2, 0x8c, 0x42, 0x54, 0xcf, 0xd4} }, - { (uint64_t)200000000000000ull, {0x23, 0x14, 0x49, 00, 0xa8, 0x66, 0xe8, 0xc1, 0xbf, 0x40, 0x98, 0xda, 0xa9, 0x48, 0xb9, 0x86, 0xf3, 0x84, 0xe, 0x5a, 0x7d, 0x21, 0x5e, 0xf0, 0xd5, 0x64, 0xef, 0xd8, 0xbe, 0xc6, 0x83, 0x15} }, - { (uint64_t)300000000000000ull, {0x6a, 0x51, 0x47, 0x3c, 0x86, 0xed, 0xad, 0x53, 0x51, 0x4b, 0x3f, 0x95, 0x97, 0xed, 0x21, 0xae, 00, 0x81, 0x51, 0xa0, 0x9e, 0x43, 0xad, 0xdd, 0x45, 0xd1, 0x74, 0x63, 0xc5, 0x34, 0x3, 0x97} }, - { (uint64_t)400000000000000ull, {0x8, 0xbd, 0xd4, 0xc3, 0xe4, 0x53, 0x1b, 0x29, 0x7a, 0x70, 00, 0x1e, 0xb8, 0xa4, 0xf1, 0x98, 0xdc, 0x3b, 0xd4, 0xf1, 0xf5, 0x60, 0x9a, 0xda, 0x98, 0xf6, 0xd9, 0x5f, 0x9a, 0x1a, 0x30, 0x2e} }, - { (uint64_t)500000000000000ull, {0x97, 0x55, 0x70, 0xea, 0x12, 0xde, 0x5a, 0xf5, 0xc5, 0x36, 0xbd, 0xb6, 0x83, 0x54, 0xfb, 0xc8, 0x32, 0x21, 0x50, 0xfc, 0x56, 0x83, 0x7c, 0x4b, 0x78, 0xa9, 0x85, 0x76, 0x5d, 0x2a, 0x70, 0x99} }, - { (uint64_t)600000000000000ull, {0xa7, 0xa6, 0x39, 0x93, 0x41, 0xcb, 0x4d, 0x67, 0x76, 0xcd, 0x94, 0xd, 0x1d, 0x6a, 0xb0, 0xac, 0xa, 0xbf, 0x56, 0x93, 0x6a, 0x35, 0x31, 0xdf, 0xe9, 0x6c, 0x23, 0x69, 0x97, 0x8e, 0x49, 0xfa} }, - { (uint64_t)700000000000000ull, {0x55, 0x9, 0x3e, 0x5e, 0xeb, 0xca, 0x3, 0x88, 0x48, 0xdc, 0x99, 0x7e, 0x31, 0x95, 0xec, 0xc5, 0x8f, 0xb4, 0xa5, 0x71, 0xb9, 0x52, 0x56, 0xc0, 0xff, 0x49, 0xbe, 0xd0, 0xf1, 0x65, 0x22, 0xbd} }, - { (uint64_t)800000000000000ull, {0xbb, 0xc6, 0x18, 0x2, 0x24, 0xaf, 0xd3, 0x38, 0xa6, 0xf4, 0xa0, 0x6b, 0x11, 0x98, 0x40, 0x68, 0xeb, 0x36, 0x35, 0xe7, 0xe5, 0x47, 0x66, 0x69, 0x78, 0x83, 0xaf, 0xbd, 0xce, 0xad, 0x2f, 0x31} }, - { (uint64_t)900000000000000ull, {0x61, 0x3a, 0xa1, 0x2c, 0xc0, 0xa1, 0x9b, 0xc8, 0x43, 0x63, 0x50, 0xbb, 0xc0, 0xf6, 0x16, 0x32, 0x6e, 0x64, 0x85, 0x83, 0x33, 0x4a, 0x32, 0x65, 0x16, 0x29, 0xe9, 0x5, 0xc5, 0x20, 0x62, 0x69} }, - { (uint64_t)1000000000000000ull, {0x52, 0xdd, 0xf8, 0x81, 0x13, 0xa0, 0xfc, 0xf2, 0x12, 0x90, 0x95, 0xc6, 0x18, 0x91, 0xbe, 0x88, 0x5c, 0x9, 0x30, 0x8, 0xeb, 0xc4, 0x65, 0xc, 0xb0, 0xee, 0xa5, 0x60, 0xcd, 0x4d, 0x75, 0x1b} }, - { (uint64_t)2000000000000000ull, {0x75, 0xbd, 0xfc, 0x35, 0xa6, 0xdf, 0x76, 0xe5, 0x98, 0x8e, 0xd9, 0xe3, 0x10, 0xa5, 0x89, 0x16, 0xae, 0xf0, 0xc5, 0xf0, 0x5b, 0x89, 0x22, 0xea, 0xae, 0x2c, 0xf9, 0x8f, 0x58, 0x42, 0x3c, 0xe3} }, - { (uint64_t)3000000000000000ull, {0x88, 0x98, 0x93, 0xe8, 0x7d, 0x56, 0x9f, 0x14, 0xb2, 0x48, 0xd1, 0xed, 0x93, 0xe8, 0xce, 0x60, 0xbb, 0xe3, 0x73, 0x69, 0xb0, 0xd6, 0xc7, 0xa1, 0x86, 0x89, 0x33, 0xd3, 0xc3, 0xda, 0x9a, 0x72} }, - { (uint64_t)4000000000000000ull, {0x88, 0x3e, 0xf3, 0x4b, 0xa2, 0xc1, 0x91, 0xf4, 0x9d, 0x3c, 0xc6, 0xad, 0xa0, 0xaf, 0xf1, 0xcf, 0xb1, 0x77, 0xbd, 0x9e, 0xd4, 0xb3, 0xa5, 0x37, 0x84, 0xb7, 0xf1, 0x62, 0x9b, 0xed, 0x17, 0x41} }, - { (uint64_t)5000000000000000ull, {0xa2, 0x90, 0x7c, 0x39, 0x84, 0xb1, 0x4a, 0xb1, 0xf4, 0xda, 0x58, 0xc2, 0xc8, 0x2d, 0x6b, 0x24, 0xf1, 0x29, 0x49, 0x9, 0x75, 0xfc, 0x4a, 0x33, 0x3d, 0x25, 0xa1, 0xf9, 0x2b, 0xc4, 0x32, 0xb6} }, - { (uint64_t)6000000000000000ull, {0xa0, 0x7d, 0x9f, 0x18, 0x95, 0x1f, 0xf2, 0x32, 0xcf, 0x4e, 0xc0, 0xee, 0x2f, 0xbc, 0xc3, 0xe1, 0x1b, 0x2c, 0xaf, 0xc9, 0x57, 0x65, 0x82, 0x10, 0x38, 0x1e, 0x3e, 0xe4, 0xed, 0xec, 0x2e, 0x7a} }, - { (uint64_t)7000000000000000ull, {0x66, 0x80, 0x21, 0xd5, 0xde, 0x8c, 0xa4, 0xc1, 0x8f, 0x5a, 0x74, 0xf2, 0x78, 0x69, 0xc4, 0xd6, 0xd4, 0x93, 0xa3, 0x30, 0x39, 0x3c, 0xf0, 0x26, 0x41, 0xff, 0xa8, 0x56, 0x7b, 0xa5, 0x36, 0x20} }, - { (uint64_t)8000000000000000ull, {0xe0, 0x48, 0x7a, 0xc4, 0x5a, 0x82, 0x59, 0xe3, 0xe5, 0xf2, 0xd9, 0xb8, 0xf6, 0xb8, 0xfa, 0x26, 0x9a, 0x63, 0x49, 0x71, 0xa2, 0xf7, 0xc2, 0x1a, 0x54, 0x17, 0x76, 0x81, 0xeb, 0x2, 0xbd, 0x4a} }, - { (uint64_t)9000000000000000ull, {0x98, 0x92, 0x6a, 0x3a, 0xf0, 0x5b, 0xf4, 0xa9, 0x8d, 0xf9, 0xf6, 0x4a, 0xe7, 0xb9, 0xda, 0x45, 0xa7, 0x6, 0xc3, 0xf8, 0x39, 0x5e, 0x47, 0x1f, 0x96, 0xed, 0x3c, 0x6, 0x6, 0xbe, 0xbb, 0x71} }, - { (uint64_t)10000000000000000ull, {0x80, 0xad, 0xb7, 0xd, 0x46, 0xf6, 0x3a, 0x75, 0x64, 0xa3, 0xf6, 0x71, 0xd9, 0xba, 0x95, 0x71, 0xb7, 0xf7, 0x95, 0xa9, 0x63, 0x38, 0x2a, 0x4d, 0x9f, 0xaf, 0x2d, 0x54, 0xf6, 0xc6, 0x84, 0x29} }, - { (uint64_t)20000000000000000ull, {0xae, 0xbd, 0x97, 0x42, 0x1f, 0x3f, 0xca, 0xe8, 0x95, 0x18, 0x60, 0xe6, 0xd9, 0xd1, 0xf3, 0xec, 0x59, 0x73, 0xa2, 0xf7, 0x66, 0x88, 0x4b, 0xfe, 0x17, 0x50, 0x79, 0x51, 0xe4, 0x62, 0xc6, 0x63} }, - { (uint64_t)30000000000000000ull, {0x61, 0x2, 0x6c, 0x84, 0x2a, 0x6a, 0x22, 0x25, 0x74, 0x6b, 0x19, 0x6b, 0x56, 0x89, 0xe1, 0x18, 0xf5, 0x41, 0x34, 0x15, 0x98, 0x1d, 0x7, 0x73, 0x62, 0xb2, 0xe7, 0xb9, 0xac, 0xa5, 0x28, 0x16} }, - { (uint64_t)40000000000000000ull, {0x52, 0x54, 0xb5, 0x78, 0xe8, 0x57, 0x9a, 0x27, 0x3b, 0x89, 0x8e, 0x65, 0x9d, 0xd3, 0xe1, 0xa1, 0xcf, 0xba, 0x12, 0x47, 0x26, 0x64, 0xbd, 0x4e, 0x7f, 0x9a, 0x13, 0xb1, 0xfc, 0xee, 0x2, 0x93} }, - { (uint64_t)50000000000000000ull, {0x7b, 0x2a, 0xb, 00, 0xcf, 0xdc, 0xa9, 0x51, 0x46, 0xcf, 0x80, 0x95, 0xdd, 0x2b, 0x82, 0x90, 0x91, 0xb0, 0xf2, 0xd5, 0xbb, 0xc, 0x33, 0x82, 0x2d, 0x8b, 0x43, 0x42, 0x69, 0xcd, 0x2a, 0x42} }, - { (uint64_t)60000000000000000ull, {0x21, 0x57, 0x4f, 0xed, 0x15, 0x1a, 0x2f, 0x9f, 0x64, 0xa4, 0x5b, 0xe2, 0x8a, 0x3a, 0xf5, 0x88, 0xe9, 0xf2, 0xd1, 0x71, 0x35, 0xa3, 0x53, 0x7f, 0x7, 0xfd, 0x6a, 0xef, 0xa2, 0x9f, 0x2, 0xaf} }, - { (uint64_t)70000000000000000ull, {0x1a, 0xf2, 0x41, 0xe1, 0x38, 0x27, 0x98, 0x29, 0xac, 0x6a, 0xe6, 0x2f, 0xf, 0x33, 0x20, 0x4b, 0xb2, 0x8a, 0xfd, 0x6, 0x5c, 0x42, 0x59, 0x3b, 0xdc, 0x79, 0x14, 0x85, 0x97, 0x5b, 0x26, 0x95} }, - { (uint64_t)80000000000000000ull, {0xa8, 0xc8, 0xb8, 0x7b, 0x51, 0x2d, 0xef, 0x9b, 0x5e, 0x50, 0xe, 0xb4, 0x98, 0xaf, 0x86, 0xaa, 0xd2, 0x46, 0x4a, 0xea, 0xe7, 0x6d, 0xb1, 0xf6, 0x5d, 0x23, 0x26, 0xce, 0x90, 0x26, 0xec, 0x69} }, - { (uint64_t)90000000000000000ull, {0x3d, 0x78, 0x73, 0x63, 0x95, 0xf1, 0xd7, 0xde, 0x8e, 0x16, 0xc0, 0xb5, 0xa9, 0x9f, 0x4d, 0xc4, 0xeb, 0x8f, 0x22, 0xac, 0xc1, 0x5b, 0x21, 0x42, 0x44, 0x1d, 0xbd, 0x8d, 0x2c, 0x31, 0xb9, 0xce} }, - { (uint64_t)100000000000000000ull, {0x27, 0x27, 0xd4, 0x93, 0x2f, 0x98, 0x39, 0xe4, 0x3b, 0x6b, 0xf5, 0xfb, 0x29, 0xa3, 0xbe, 0x4c, 0x9, 0xb, 0x6e, 0xb9, 0x31, 00, 0xbb, 0x92, 0x58, 0x1a, 0xdb, 0x8d, 0xd2, 0xb6, 0x61, 0x54} }, - { (uint64_t)200000000000000000ull, {0xae, 0x96, 0x78, 0x2e, 0xf2, 0xc4, 0xdf, 0x7d, 0x2e, 0x4, 0xcc, 0xf9, 0xef, 0x76, 0x23, 0x7f, 0x17, 0xc, 0x97, 0x3, 0xb4, 0x92, 0xc0, 0x78, 0x52, 0x6e, 0xb1, 0xf6, 0x85, 0x3d, 0xb1, 0x33} }, - { (uint64_t)300000000000000000ull, {0x17, 0x43, 0xfe, 0xab, 0x12, 0xad, 0xe5, 0xfe, 0x12, 0x53, 0x22, 0x27, 0x2f, 0xd1, 0x40, 0x6b, 0x74, 0xe8, 0x19, 0x70, 0x32, 0x68, 0x46, 0x22, 0xee, 0x79, 0xab, 0xcd, 0x94, 0x93, 0x66, 0x4c} }, - { (uint64_t)400000000000000000ull, {0x7, 0x9b, 0xf2, 0xa9, 0x6e, 0x16, 0x6e, 0xf9, 0xe6, 0xb2, 0x23, 0x1d, 0xb9, 0x85, 0x8b, 0x99, 0x98, 0x7f, 0x49, 0x33, 0x87, 0xde, 0xeb, 0xd5, 0x17, 0x48, 0x54, 0x9a, 0xd, 0xf7, 0xdc, 0x44} }, - { (uint64_t)500000000000000000ull, {0xca, 0xba, 0x97, 0x98, 0x51, 0x6d, 0xad, 0x3, 0x38, 0xd0, 0x6e, 0x10, 0x6d, 0x76, 0xa2, 0x1, 0x93, 0x7a, 0xce, 0x4c, 0x91, 0x53, 0x9e, 0x61, 0x7d, 0x89, 0x28, 0x73, 0x6, 0xa3, 0x92, 0xb1} }, - { (uint64_t)600000000000000000ull, {0x6b, 0x8, 0x7f, 0x48, 0xb3, 0xd7, 0xaa, 0xc9, 0x57, 0xc4, 0x52, 0xe5, 0x1a, 0x18, 0xd7, 0x26, 0xb, 0xf8, 0xc8, 0x56, 0xc4, 0xc7, 0x1e, 0x48, 0xf6, 0x49, 0xae, 00, 0x4a, 0xf6, 0x8f, 0x13} }, - { (uint64_t)700000000000000000ull, {0x9e, 0xed, 0x8b, 0x23, 0x1f, 0x79, 0x4c, 0x46, 0x5c, 0xbe, 0x88, 0x40, 0xd0, 0xf1, 0x6f, 0x7b, 0x9f, 0x9c, 0x6e, 0xb4, 0x9c, 0x20, 0x7d, 0xe9, 0xd8, 0x55, 0x11, 0x83, 0xd0, 0xc7, 0x6e, 0x43} }, - { (uint64_t)800000000000000000ull, {0x58, 0x4a, 0x78, 0x93, 0x13, 0x7e, 0xbd, 0x2, 0x8b, 0xa7, 0x59, 0x82, 0xc3, 0x39, 0xb7, 0x66, 0xaa, 0xda, 0xad, 0xf9, 0x14, 0x50, 0xf9, 0x40, 0x7d, 0x2a, 0x97, 0xd7, 0xf6, 0xb1, 0x93, 0x5e} }, - { (uint64_t)900000000000000000ull, {0x7, 0xce, 0x54, 0xb1, 0x18, 0x26, 0xa1, 0x75, 0x23, 0x13, 0x55, 0x1a, 00, 0x20, 0xfd, 0x79, 0x8a, 00, 0x9e, 0x20, 0xcd, 0xb2, 0x40, 0x1d, 0x52, 0x51, 0xc1, 0x55, 0x8e, 0xea, 0xd2, 0x6c} }, - { (uint64_t)1000000000000000000ull, {0x39, 0x80, 0x7f, 0x3d, 0xce, 0xb0, 0xa6, 0xfe, 0x34, 0xa7, 0xa1, 0xed, 0xc6, 0x9b, 0x78, 0xff, 0xbe, 0xd5, 0xa7, 0x8c, 0x6c, 0x87, 0x5d, 0xda, 0x96, 0x69, 0xdb, 0xb2, 0x95, 0x70, 0xf0, 0xf4} }, - { (uint64_t)2000000000000000000ull, {0xda, 0x74, 00, 0x86, 0xf1, 0x5c, 0xe8, 0x21, 0xe9, 0xd, 0x50, 0xaf, 0xcf, 0x80, 0x9c, 0x7e, 0x18, 0x51, 0x90, 0x1b, 0xa3, 0x5f, 0x9f, 0x63, 0x78, 0xd6, 0x40, 0x7c, 0xb9, 0xc7, 0xa2, 0x75} }, - { (uint64_t)3000000000000000000ull, {0x7, 0xa1, 0x75, 0x63, 0xae, 0xf5, 0xcf, 0xd0, 0x36, 0xfa, 0x64, 0xd4, 0xb1, 0x97, 0xa9, 0x51, 0xc0, 0xd2, 0x87, 0x2b, 0xd, 0xb6, 0xf9, 0xbe, 0x47, 0xe6, 0x7c, 0xa6, 0xb5, 0x35, 0xe2, 0x6e} }, - { (uint64_t)4000000000000000000ull, {0xe3, 0x49, 0xf7, 0xeb, 0xe5, 0x11, 0x39, 0xfe, 0xd5, 0x69, 0x40, 0x37, 0xd1, 0x14, 0xb7, 0xbd, 0x45, 0xdd, 0xa, 0x6a, 0xf0, 0x4b, 0x62, 0xec, 0xa4, 0xd8, 0xcd, 0x55, 0x2a, 0x14, 0xe3, 0xfb} }, - { (uint64_t)5000000000000000000ull, {0x8d, 0x59, 0x7e, 0xa9, 0xf5, 0x79, 0x9a, 0x4d, 0x15, 0x3d, 0x82, 0xd6, 0xf7, 0xbe, 0xa0, 0x2e, 0x52, 0x40, 0xa2, 0xc8, 0x9b, 0x4, 0x1e, 0x6, 0x2f, 0x37, 0xbc, 0x7b, 0x82, 0xa0, 0xac, 0x55} }, - { (uint64_t)6000000000000000000ull, {0xa3, 0x43, 0xa7, 0xe1, 0x14, 0x4d, 0x33, 0x50, 0xf, 0x3e, 0xfd, 0x38, 0x15, 0x82, 0xdd, 0xc5, 0xd0, 0x18, 0x3e, 0x5d, 0xcf, 0x8a, 0xfa, 0x64, 0xbb, 0x67, 0x6c, 0x97, 0x3e, 0x3d, 0x1a, 0xb1} }, - { (uint64_t)7000000000000000000ull, {0x89, 0xe9, 0x3e, 0xe9, 0xf2, 0x4d, 0x72, 0x61, 0xe5, 0x44, 0xca, 0x8f, 0x9, 0xa7, 0x40, 0x4e, 0xe3, 0xa9, 0xe, 0xe2, 0x50, 0x7d, 0xda, 0xcf, 0x41, 0x2a, 0x58, 0xc, 0x9, 0x65, 0x1c, 0x53} }, - { (uint64_t)8000000000000000000ull, {0xc5, 0x94, 0x10, 0x81, 0x54, 0x69, 0xf4, 0x59, 0xd1, 0x5a, 0x6f, 0xe3, 0xf2, 0xa1, 0x1b, 0xa6, 0x31, 0x12, 0xfa, 0xaa, 0xc5, 0x3d, 0xbc, 0x52, 0x5d, 0x3c, 0xfa, 0xb1, 0xfa, 0x9c, 0x3d, 0xdb} }, - { (uint64_t)9000000000000000000ull, {0x9d, 0xe7, 0xcb, 0xb, 0x8d, 0x7b, 0xac, 0x47, 0xff, 0xd3, 0x93, 0x1b, 0xcd, 0x82, 0xcd, 0xd5, 0x35, 0xc, 0x29, 0x34, 0xb1, 0x6e, 0xb, 0x64, 0x32, 0xab, 0xf7, 0xcb, 0x4b, 0x5c, 0x37, 0x6d} }, - { (uint64_t)10000000000000000000ull, {0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3} }, + { (uint64_t)0ull, {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}} }, + { (uint64_t)1ull, {{0x17, 0x38, 0xeb, 0x7a, 0x67, 0x7c, 0x61, 0x49, 0x22, 0x8a, 0x2b, 0xea, 0xa2, 0x1b, 0xea, 0x9e, 0x33, 0x70, 0x80, 0x2d, 0x72, 0xa3, 0xee, 0xc7, 0x90, 0x11, 0x95, 0x80, 0xe0, 0x2b, 0xd5, 0x22}} }, + { (uint64_t)2ull, {{0x76, 0x24, 0x84, 0x63, 0xa, 0x6, 0x17, 0x17, 0x8d, 0xe, 0x33, 0xf3, 0x2e, 0xe, 0x11, 0x3e, 0xa8, 0x46, 0x86, 0x9d, 0x46, 0x4b, 0xb, 0x6f, 0xf1, 0x3b, 0x29, 0x97, 0x4, 0x9c, 0xda, 0x7d}} }, + { (uint64_t)3ull, {{0xcf, 0xf7, 0x7b, 0x56, 0x62, 0x1c, 0x4f, 0xef, 0x74, 0xcf, 0x37, 0xc1, 0x78, 0xd4, 0xb5, 0x8a, 0xf4, 0xad, 0x8c, 0xd4, 0x35, 0xfc, 0xb9, 0x62, 0x76, 0xbc, 0x15, 0x9c, 0x7c, 0x6a, 0x28, 0x8c}} }, + { (uint64_t)4ull, {{0x9a, 0xb8, 0x6c, 0x31, 0xf4, 0x22, 0xd8, 0x21, 0xb5, 0x22, 0x57, 0x30, 0xd1, 0xbf, 0x73, 0xa, 0x9b, 0x91, 0xd2, 0xee, 0xe3, 0x14, 0xb8, 0x4e, 0xbd, 0x4b, 0x93, 0xa6, 0x81, 0x61, 0x82, 0x66}} }, + { (uint64_t)5ull, {{0x32, 0xee, 0x2f, 0x65, 0x9a, 0xf6, 0x38, 0x58, 0xc2, 0xf7, 0xdc, 0x11, 0x1b, 0x3b, 0xb8, 0xfe, 0xc0, 0x2c, 0xac, 0x42, 0x38, 0x3b, 0xb7, 0x36, 0xde, 0x1, 0x8, 0x6f, 0x38, 0xf0, 0x12, 0x3c}} }, + { (uint64_t)6ull, {{0x47, 0x26, 0x2b, 0x1e, 0xa6, 0x43, 0x1, 0x6e, 0x38, 0x24, 0x17, 0x53, 0xa4, 0xfb, 0x39, 0x92, 0x9e, 0x31, 0xea, 0x9b, 0xd3, 0x41, 0x1a, 0xb1, 0x7f, 0x16, 0x6e, 0x61, 0xf6, 0xc, 0xe5, 0xa7}} }, + { (uint64_t)7ull, {{0xc6, 0x32, 0x93, 0x68, 0x79, 0x9a, 0xd, 0xed, 0x4c, 0x20, 0x25, 0x6b, 0xff, 0xe6, 0x45, 0x47, 0xf1, 0x7b, 0xc4, 0x23, 0x95, 0x4, 0xbe, 0x82, 0x4d, 0xff, 0x8a, 0x2b, 0xe1, 0xaf, 0xe3, 0xcd}} }, + { (uint64_t)8ull, {{0xd5, 0xf1, 0x50, 0x74, 0x33, 0x46, 0x19, 0xf, 0x84, 0x2b, 0x6, 0xb8, 0xfa, 0xe1, 0x20, 0xeb, 0x85, 0x24, 0x7e, 0x9f, 0x6d, 0xec, 0x88, 0xff, 0xa2, 0x23, 0xbf, 0x69, 0x94, 0xe9, 0xc8, 0xc2}} }, + { (uint64_t)9ull, {{0x56, 00, 0x23, 0x32, 0x9e, 0xc0, 0xfa, 0xf3, 0x3b, 0x5e, 0x3a, 0x5c, 0xb4, 0xea, 0xef, 0xee, 0x38, 0xf8, 0x96, 0x1c, 0x88, 0xb6, 0x6a, 0x2f, 0x19, 0xd4, 0x59, 0x51, 0x96, 0x9c, 0x6d, 0x1f}} }, + { (uint64_t)10ull, {{0x3, 0x80, 0xdc, 0x24, 0xcc, 0x97, 0xcc, 0xe6, 0x58, 0xc3, 0xa9, 0x47, 0xc5, 0x10, 0x25, 0xde, 0x1a, 0x69, 0x80, 0x3b, 0xdb, 0x50, 0x5, 0xe3, 0xb7, 0xdd, 0xa9, 0xd, 0x68, 0x59, 0xb0, 0x1c}} }, + { (uint64_t)20ull, {{0x9, 0x3, 0xf6, 0x2e, 0x97, 0x76, 0x47, 0x58, 0xfe, 0xf8, 0x9e, 0x5b, 0xec, 0x29, 0xef, 0x4f, 0xc5, 0xe6, 0x45, 0x4b, 0x2d, 0x47, 0x44, 0x47, 0x36, 0x4, 0x4c, 0x25, 0x2e, 0xe2, 0x8e, 0xba}} }, + { (uint64_t)30ull, {{0xa2, 0x8b, 0x89, 0xe0, 0xb, 0xed, 0x62, 0x31, 0x68, 0x5b, 0xf9, 0x74, 0x36, 0xf2, 0xba, 0x51, 0xa2, 0x51, 0x55, 0x7f, 0x8d, 0x17, 0xa, 0x78, 0xe3, 0x12, 0xd6, 0x24, 0xbf, 0x60, 0xff, 0xfe}} }, + { (uint64_t)40ull, {{0xb5, 0xc6, 0x95, 0x55, 0x6a, 0x28, 0x47, 0xb2, 0xe, 0x1c, 0xbb, 0x26, 0xe6, 0xa9, 0xc6, 0x8a, 0x61, 0xc5, 0x50, 0xce, 0xb7, 0xc3, 0x4, 0xfe, 0x92, 0x28, 0x3d, 0x29, 0xa9, 0xb2, 0x43, 0xcb}} }, + { (uint64_t)50ull, {{0x12, 0x8e, 0xc6, 0xcd, 0xc0, 0x6b, 0x43, 0xc5, 0xd0, 0x9c, 0x3f, 0x65, 0x2a, 0xe3, 0x44, 0x7f, 0x9b, 0x3f, 0x2c, 0x30, 0x91, 0x2d, 0xf0, 0x80, 0x37, 00, 0x85, 0xbc, 0xc, 0x9, 0xef, 0x78}} }, + { (uint64_t)60ull, {{0x1f, 0x9f, 0x40, 0x3a, 0xae, 0xa7, 0x16, 0xfb, 0xe2, 0x98, 0xa8, 0x14, 0xf1, 0xee, 0xbc, 0x1b, 0x73, 0x16, 0x8c, 0x37, 0xfa, 0xe3, 0x16, 0xeb, 0x65, 0x5, 0x81, 0x6f, 0xc2, 0x20, 0xeb, 0xfb}} }, + { (uint64_t)70ull, {{0x10, 0xa2, 0x38, 0xc5, 0xe4, 0x8e, 0x4b, 0x93, 0x99, 0xdb, 0xa6, 0xcb, 0xd9, 0x8e, 0x63, 0x54, 0x41, 0x59, 0xe9, 0x8c, 0x93, 0x5a, 0xc0, 0x60, 0x3d, 0x72, 0xde, 0xf, 0xff, 0x31, 0x53, 0xbb}} }, + { (uint64_t)80ull, {{0x75, 0xab, 0x78, 0xc7, 0x28, 0x1f, 0x69, 0x28, 0xf0, 0x94, 0x86, 0x5, 0x7a, 0x63, 0x64, 0x18, 0x27, 0xc5, 0x74, 0x84, 0xe3, 0xe9, 0x9a, 0x39, 0xf3, 0x12, 0xa4, 0x3a, 0x51, 0x9b, 0xda, 0x8}} }, + { (uint64_t)90ull, {{0xe9, 0x56, 0x7b, 0xa7, 0x88, 0xb8, 0x5b, 0x82, 0xc8, 0x65, 0x7a, 0x15, 0xa5, 0x48, 0x99, 0x5c, 0xf6, 0xb0, 0xbd, 0xd1, 0xc6, 0x2a, 0xda, 0x77, 0x55, 0xf2, 0x32, 0x3a, 0xd8, 0xa4, 0x8, 0x51}} }, + { (uint64_t)100ull, {{0xb6, 0x17, 0x36, 0xd5, 0xf2, 0x8d, 0xef, 0x28, 0x61, 0x6a, 0xfc, 0x47, 0x93, 0xe9, 0x9b, 0x27, 0xcd, 0x3e, 0x89, 0xfb, 0x91, 0xc1, 0x13, 0xd4, 0x30, 0x73, 0x65, 0xfb, 0x75, 0xde, 0xdf, 0x88}} }, + { (uint64_t)200ull, {{0x71, 0x3, 0xeb, 0x72, 0x19, 0x28, 0xd7, 0x91, 0x99, 0x87, 0xf3, 0x50, 0xca, 0xa5, 0x7a, 0xe7, 0xb0, 0x81, 0x57, 0x15, 0x3b, 0x4c, 0x43, 0xd, 0x3e, 0xde, 0xc0, 0xc2, 0x3, 0x7, 0x97, 0x44}} }, + { (uint64_t)300ull, {{0x24, 0x40, 0x9e, 0x92, 0x2e, 0xce, 0xd1, 0xa0, 0x5e, 0x4e, 0xac, 0xa3, 0xdf, 0x91, 0x19, 0xc3, 0x8a, 0x92, 0x2e, 0xb, 0x66, 0xd0, 0x2d, 0x9d, 0xd2, 0xfb, 0x1d, 0xcc, 0x20, 0xb9, 0xaf, 0xc7}} }, + { (uint64_t)400ull, {{0xa7, 0x72, 0x9f, 0xa9, 0x32, 0x81, 0x82, 0x99, 0x34, 0x11, 0x5d, 0x47, 0x5a, 0x67, 0x86, 0xa, 0x14, 0x12, 0xc5, 0xe5, 0x95, 0x12, 0x20, 0xd9, 0x60, 0xc2, 0x41, 0xa0, 0x19, 0x1a, 0x9e, 0x65}} }, + { (uint64_t)500ull, {{0x2e, 0x53, 0xc, 0x6, 0x1c, 0x6d, 0x9e, 0x97, 0xab, 0xaf, 0x46, 0x8c, 0x32, 0xb0, 0xad, 0xa7, 0x49, 0x22, 0x57, 0x72, 0xfc, 0xd1, 0x17, 0x41, 0xcb, 0x5c, 0x3, 0x5c, 0xdd, 0x26, 0x14, 0xe}} }, + { (uint64_t)600ull, {{0xa5, 0xb, 0x91, 0x9, 0x9d, 0xf1, 0xb1, 0x69, 0x4f, 0x30, 0xb5, 0x8f, 0xe6, 0x77, 0x68, 0x50, 0xdb, 0xdb, 0xf4, 0x6c, 0xed, 0x99, 0x7f, 0x52, 0x62, 0xa8, 0x51, 0x59, 0x40, 0x74, 0xa5, 0x9d}} }, + { (uint64_t)700ull, {{0x51, 0x2c, 0xf, 0xae, 0xcc, 0xbe, 0xf2, 0xfe, 0xe5, 0x75, 0x4c, 0x6a, 0x45, 0xfd, 0xc0, 0x75, 0x2d, 0x4f, 0x15, 0x22, 0xe7, 0x7f, 0xf0, 0xc4, 0x8d, 0xcb, 0x19, 0x91, 0x8a, 0x68, 0x84, 0xe0}} }, + { (uint64_t)800ull, {{0xda, 0xa9, 0xf9, 0xa5, 0xb9, 0x71, 0x33, 0x33, 0xe9, 0x8c, 0x5, 0xac, 0xe7, 0x27, 0xcc, 0xe, 0x7d, 0xc3, 0xf1, 0x59, 0x49, 0xe1, 0xef, 0x4d, 0x94, 0xfa, 0x47, 0xd6, 0x8a, 0x34, 0xc6, 0x75}} }, + { (uint64_t)900ull, {{0xc7, 0x2b, 0x18, 0xc9, 0x17, 0xcd, 0x43, 0xee, 0x78, 0x40, 0x5e, 0x39, 0x83, 0x98, 0xb8, 0x3a, 0xc0, 0x97, 0x7b, 0x25, 0x19, 0x90, 0xd8, 0x13, 0xc, 0x38, 0xba, 0x53, 0xb6, 0x3d, 0xb4, 0xf7}} }, + { (uint64_t)1000ull, {{0x1, 0xdf, 0x60, 0x91, 0xeb, 0x6a, 0x48, 0xe9, 0xe4, 0x22, 0x25, 0xb, 0xe3, 0x83, 0x88, 0xc8, 0x61, 0xb6, 0x55, 0x55, 0xa7, 0x20, 0xad, 0x15, 0x35, 0x86, 0xfe, 0x2b, 0xd2, 0x2f, 0xa2, 0x3d}} }, + { (uint64_t)2000ull, {{0x24, 0xf5, 0xb1, 0x34, 0x78, 0x46, 0xaf, 0x22, 0xb5, 0x6f, 0x41, 0x25, 0xb3, 0xe7, 0x67, 0x8c, 0xf8, 0x4b, 0x4f, 0xd2, 0xf9, 0x2e, 0x1c, 0x40, 0xaa, 0x3a, 0x1b, 0xe0, 0xc7, 0x4d, 0x95, 0xe6}} }, + { (uint64_t)3000ull, {{0xa7, 0x1c, 0x9a, 0x8f, 0x40, 0xc1, 0x25, 0x9c, 0x36, 0x26, 0x27, 0x73, 0xe0, 0x8, 0x20, 0x18, 0x3e, 0x6b, 0x59, 0xe0, 0x71, 0xc9, 0x9b, 0x34, 0x9b, 0xef, 0x8f, 0x7e, 0xd2, 0xc6, 0xad, 0xb9}} }, + { (uint64_t)4000ull, {{0x98, 0xdc, 0x74, 0xaf, 0x19, 0x89, 0xd3, 0x4b, 0x64, 0x2e, 0xb3, 0x6, 0x2d, 0xbc, 0x9d, 0xca, 0xd8, 0x1, 0xc5, 0x65, 0x27, 0x6, 0x93, 0x99, 0xe7, 0xc4, 0x11, 0xad, 0x14, 0x28, 0x82, 0xf6}} }, + { (uint64_t)5000ull, {{0x61, 0x76, 0xac, 0x4a, 0xc0, 0x6, 0x5e, 0x49, 0xd6, 0xc4, 0x41, 0xcf, 0x40, 0x4f, 0xad, 0xda, 0xad, 0x44, 0x93, 0xe, 0xf0, 0x3c, 0x68, 0x9, 0xad, 0xd7, 0x77, 0xe4, 0x2f, 0xee, 0x7f, 0x10}} }, + { (uint64_t)6000ull, {{0x78, 0x79, 0x4, 0x65, 0xf6, 0x60, 0x5b, 0x5a, 0x84, 0x77, 0x36, 0x5a, 0xa6, 0xc2, 0xa4, 0xa5, 0x84, 0x91, 0xc, 0x23, 0x95, 0x2, 0x92, 0x97, 0x52, 0x49, 0xa1, 0xad, 0x7d, 0xf0, 0xf7, 0xe8}} }, + { (uint64_t)7000ull, {{0x20, 0xa5, 0x60, 0x6b, 0x60, 0x23, 0x95, 0xd6, 0x8e, 0x2f, 0xad, 0x8e, 0xc6, 0x7f, 0x92, 0xde, 0x89, 0xc6, 0x3e, 0x1e, 0x7f, 0xc1, 0xdd, 0x7f, 0x92, 0xff, 0xed, 0xb8, 0xf6, 0x55, 0xfb, 0xd}} }, + { (uint64_t)8000ull, {{0x9a, 0x78, 0x97, 0x43, 0x98, 0x65, 0x17, 0xd9, 0x5f, 0x4e, 0x80, 0x8b, 0xeb, 0xe6, 0x52, 0xd, 0xe6, 0xcf, 0x8c, 0x51, 0x35, 0xab, 0x36, 0x8, 0x7e, 0x87, 0xe2, 0x76, 0xac, 0x6a, 0x34, 0x1}} }, + { (uint64_t)9000ull, {{0x5f, 0xc7, 0xaa, 0x48, 0xbb, 0x19, 0x13, 0x58, 0xc7, 0xe3, 0x4d, 0x24, 0xcf, 0x9c, 0x31, 0x16, 0x74, 0x12, 0x7a, 0xb2, 0x45, 0xd0, 0x8f, 0x4e, 0x2c, 0xfd, 0xbf, 0x8f, 0x5, 0xc9, 0x5b, 0xf5}} }, + { (uint64_t)10000ull, {{0x61, 0x20, 0xe7, 0x76, 0xe9, 0x12, 0xab, 0x10, 0x5a, 0x49, 0xf9, 0xda, 0x2, 0xa6, 0x75, 0x17, 0xc0, 0xa9, 0xb, 0x2b, 0x3e, 0x2d, 0xa3, 0xd, 0xff, 0x34, 0x39, 0x93, 0xdb, 0xec, 0x95, 0x97}} }, + { (uint64_t)20000ull, {{0x77, 0xbf, 0xb5, 0x37, 0xac, 0xa, 0xbc, 0x41, 0xaa, 0x21, 0xd0, 0xec, 0xd9, 0x18, 0x13, 0x34, 0xd8, 0x6b, 0xa7, 0x86, 0x5a, 0x94, 0x47, 0xf5, 0xc1, 0x58, 0x9a, 0x81, 0xd7, 0xef, 0xb3, 0xbb}} }, + { (uint64_t)30000ull, {{0x35, 0xf4, 0x5, 0xa9, 0x5f, 0x75, 0x19, 0x2a, 0xe9, 0xc0, 0xd4, 0xf5, 0x88, 0x84, 0x47, 0x14, 0xf6, 0x85, 0x1b, 0x97, 0xce, 0xbd, 0x9f, 0x7c, 0x2, 0xc5, 0xdd, 0xd7, 0xbf, 0x58, 0xff, 0x31}} }, + { (uint64_t)40000ull, {{0x77, 0x55, 0xbb, 0x3f, 0x38, 0x7c, 0x21, 0xb8, 0xa0, 0xf4, 0x48, 0x1f, 0xbf, 0xa8, 0x8a, 0xbe, 0xee, 0xce, 0xc7, 0x56, 0x53, 0xfc, 0xa1, 0x89, 0x58, 0x39, 0xc1, 0xba, 0x6, 0x47, 0x9f, 0x96}} }, + { (uint64_t)50000ull, {{0x8b, 0x7e, 0x84, 0xa3, 0x37, 0xb7, 0xb9, 0xcd, 0x5d, 0xb3, 0x63, 0x33, 0x8, 0xad, 0x51, 0x86, 0xa3, 0x59, 0xd, 0xff, 0xb8, 0x23, 0x1e, 0x2f, 0x31, 0xfd, 0x20, 0x42, 0x54, 0x9f, 0xfb, 0xe2}} }, + { (uint64_t)60000ull, {{0xef, 0xfd, 0xa6, 0x25, 0x15, 0xea, 0xb1, 0xbc, 0x1e, 0xbd, 0x74, 0x92, 0x94, 0x9b, 0x1, 0x22, 0xc3, 0x9f, 0x71, 0xa, 0x65, 0x16, 0xec, 0x66, 0x8c, 0x37, 0x61, 0xe6, 0xcc, 0x36, 0x1f, 0x25}} }, + { (uint64_t)70000ull, {{0x16, 0xba, 0x89, 00, 0xf3, 0x6f, 0xf, 0x6c, 0x46, 0x1c, 0xb, 0xe7, 0x64, 0xae, 0xee, 0x48, 0x86, 0x6, 0xb0, 0x53, 0xed, 0xdc, 0x10, 0xb5, 0x9a, 0x3e, 0xde, 0xcd, 0x23, 0xd4, 0x4f, 0xc0}} }, + { (uint64_t)80000ull, {{0x4d, 0xd4, 0x70, 0x3b, 0x7b, 0x7f, 0xcf, 0xe7, 0x2a, 0x2e, 0x4f, 0x31, 0xa4, 0x34, 0x17, 0xf9, 0xc0, 0xda, 0x64, 0x2f, 0xd0, 0xa9, 0x29, 0xb8, 0xf5, 0xed, 0xd8, 0x3, 0x7f, 0x93, 0xc5, 0xb3}} }, + { (uint64_t)90000ull, {{0x8e, 0xfc, 0x3, 0x20, 0x40, 0xbd, 0x90, 0x41, 0xda, 0x3d, 0xb0, 0x9b, 0xa1, 0x3d, 0xa2, 0xa5, 0xd1, 0xb8, 0x12, 0x3, 0xa, 0x5a, 0x36, 0x7c, 0x58, 0x94, 0xbd, 0x54, 0x11, 0x9, 0xe7, 0x30}} }, + { (uint64_t)100000ull, {{0xbd, 0x2e, 0xb1, 0x97, 0x83, 0x57, 0x1c, 0xf2, 0x22, 0x2c, 0x81, 0xb, 0x69, 0xf, 0xc7, 0x66, 0x64, 0x57, 0xae, 0x20, 0x92, 0x5b, 0x90, 0x5, 0xce, 0xe6, 0x1d, 0xf2, 0x66, 0x6f, 0xdc, 0xb7}} }, + { (uint64_t)200000ull, {{0x83, 0xd4, 0xcd, 0xdd, 0xc1, 0x44, 0x87, 0x32, 0xf2, 0x97, 0x7c, 0x41, 0xaa, 0xa7, 0x1f, 0xe6, 0xde, 0x9c, 0x17, 0x6d, 0xa8, 0x99, 0xee, 0xbf, 0xfc, 0x1b, 0xb, 0xa9, 0xea, 0x92, 0x97, 0x90}} }, + { (uint64_t)300000ull, {{0xcc, 0xc0, 0x6b, 0x44, 0xc3, 0x1, 0x38, 0x6, 0x30, 0x45, 0xed, 0x1, 0xd2, 0x45, 0xd8, 0x14, 0x3, 0xb6, 0x36, 0x52, 0xeb, 0xc4, 0xf9, 0x96, 0x7f, 0xd, 0x7f, 0x38, 0x69, 0x7f, 0x46, 0x16}} }, + { (uint64_t)400000ull, {{0x1b, 0xbf, 0xe7, 0xe, 0xca, 0xf1, 0xdd, 0xd7, 0xf1, 0x2, 0x36, 0xf6, 0x8a, 0x41, 00, 0xb, 0x5d, 0xab, 0x2d, 0x47, 0x5c, 0xb9, 0x2f, 0x62, 0xc2, 0xd6, 0x84, 0xcf, 0x57, 0x69, 0xfb, 0x84}} }, + { (uint64_t)500000ull, {{0xf1, 0xb1, 0xcd, 0xaa, 0x78, 0x14, 0x95, 0x36, 0xf, 0x53, 0x31, 0x81, 0xaa, 0x58, 0xc8, 0xbd, 0xae, 0x6a, 0x77, 0x98, 0xd0, 0x2d, 0xab, 0x6d, 0x56, 0x26, 0x81, 0x27, 0x67, 0x9, 0xe7, 0x1}} }, + { (uint64_t)600000ull, {{0xd5, 0x26, 0x7d, 0x60, 0xd4, 0xfe, 0x9b, 0xc5, 0xfe, 0xfa, 0x7d, 0x3f, 0xe0, 0x7c, 0xd1, 0xfa, 0xd4, 0x55, 0x73, 0xd5, 0xae, 0x19, 0x10, 0xda, 0x7, 0x3e, 0x6d, 0x2d, 0xf9, 0xe2, 0x4, 0x39}} }, + { (uint64_t)700000ull, {{0xb, 0x58, 0x11, 0x25, 0xc2, 0xc4, 0x83, 0xc9, 0xa3, 0xd8, 0xbc, 0x8, 0x32, 0x2f, 0x26, 0xaa, 0x1f, 0xc5, 0xe, 0x41, 0x53, 0x2c, 0x1b, 0x9d, 0xf6, 0x26, 0xb0, 0x9, 0xd7, 0x88, 0x67, 0xcf}} }, + { (uint64_t)800000ull, {{0xf5, 0xb3, 0xd1, 0x8f, 0x66, 0xd0, 0xf9, 0x17, 0x5c, 0x30, 0x83, 0xb5, 0xf8, 0x7, 0x8e, 0xaf, 0xa8, 0x9e, 0xf8, 0x1d, 0xe7, 0x15, 0x8, 0xbc, 0x25, 0x1f, 0x5c, 0x5f, 0xe7, 0x25, 0x2e, 0x6}} }, + { (uint64_t)900000ull, {{0x1, 0xde, 0x40, 0x2c, 0x4b, 00, 0x43, 0x4, 0x2e, 0xae, 0x9e, 0xde, 0xa1, 0x49, 0x2b, 0x9d, 0x82, 0xb7, 0xbc, 0x36, 0x68, 0xe9, 0xb5, 0x84, 0xb0, 0x31, 0x3d, 0x44, 0x50, 0x53, 0x40, 0x74}} }, + { (uint64_t)1000000ull, {{0x53, 0x4b, 0x85, 0xc7, 0x89, 0x3f, 0x66, 0xf0, 0x26, 0xb6, 0x5e, 0xd7, 0xe7, 0xa4, 0xb8, 0xc9, 0xf4, 0xb, 0xe3, 0x1b, 0xcd, 0xa, 0x3d, 0xcd, 0x27, 0xc4, 0x71, 0x2, 0x56, 0x51, 0x65, 0x3}} }, + { (uint64_t)2000000ull, {{0xcd, 0xb5, 0xda, 0xfa, 0x53, 0x10, 0xf5, 0x26, 0x2f, 0xfc, 0x9, 0x26, 0xd0, 0xdf, 0x6e, 0xeb, 0xee, 0x2d, 0x52, 0xa9, 0x8d, 0xc6, 0x9f, 0xd, 0xc5, 0xe4, 0xeb, 0xf0, 0xc1, 0xa8, 0x77, 0x2e}} }, + { (uint64_t)3000000ull, {{0x9e, 0x75, 0x63, 0xf0, 0x33, 0x59, 0xea, 0x31, 0xe2, 0x91, 0xe7, 0xf0, 0xb8, 0x74, 0x17, 0xbc, 0xf5, 0xb2, 0x34, 0xee, 0x8b, 0x7e, 0x5b, 0x4, 0x41, 0x73, 0xbf, 00, 0x46, 0x86, 0x7c, 0x57}} }, + { (uint64_t)4000000ull, {{0xfd, 0x2a, 0xeb, 0xd, 0x5e, 0xe5, 0x3b, 0x77, 0xf2, 0xb1, 0xe3, 0xac, 0x75, 0x2d, 0x19, 0x38, 0x9f, 0xc5, 0xba, 0xa0, 0xf8, 0xd7, 0x64, 0x48, 0xa5, 0x9f, 0x99, 0x85, 0xa4, 0x8d, 0xa, 0x25}} }, + { (uint64_t)5000000ull, {{0xc0, 0xbe, 0x4f, 0xb8, 0x77, 0xb9, 0xce, 0x50, 0x87, 0x71, 0x32, 0x3b, 0xcf, 0x1f, 0xb9, 0x48, 0x47, 0x10, 0xee, 0x23, 0x2, 00, 0x6, 0xc3, 0xe8, 0xca, 0xac, 0x6e, 0x4f, 0x2, 0xfa, 0xbf}} }, + { (uint64_t)6000000ull, {{0xfc, 0x44, 0x5c, 0xa3, 0x84, 0xf3, 0x3e, 0x55, 0x8d, 0xc1, 0x56, 0x44, 0x9d, 0x3f, 0xba, 0x6a, 0xfd, 0x54, 0xc3, 0x42, 0xe6, 0x35, 0x11, 0xf, 0xe7, 0x9c, 0x16, 0xc7, 0x17, 0xf7, 0xd4, 0xf7}} }, + { (uint64_t)7000000ull, {{0xd8, 0x9, 0x2b, 0x8d, 0x45, 0xdb, 0x54, 0xa5, 0x6d, 0x64, 0xe8, 0x9, 0x4a, 0x6, 0x22, 0xe2, 0x6e, 0x8a, 0x2e, 0xec, 0xb9, 0x3, 0xb2, 0xe1, 0xf7, 0x5a, 0x83, 0x7b, 0x3a, 0xd8, 0x55, 0x4a}} }, + { (uint64_t)8000000ull, {{0x10, 0x4, 0x5c, 0x91, 0xdb, 0xad, 0x8a, 0x6a, 0x81, 0x62, 0x4a, 0xe0, 0xcf, 0x20, 0x5d, 0xb9, 0x97, 0x3e, 0xe8, 0x42, 0x3e, 0x97, 0xaf, 0x58, 0xa6, 0x1c, 0xfa, 0x7a, 0x78, 0x66, 0xf4, 0x1}} }, + { (uint64_t)9000000ull, {{0x11, 0x5c, 0x20, 0x9e, 0xe1, 0xde, 0xf3, 0x10, 0xce, 0xc9, 0xa6, 0xd1, 0x6c, 0xe6, 0x27, 0xec, 0xbd, 0xb9, 0xff, 0x2c, 0x23, 0x9, 0x3c, 0x24, 0xc8, 0x6c, 0x1b, 0xf2, 0x50, 0xd4, 0xb5, 0x85}} }, + { (uint64_t)10000000ull, {{0x2f, 0x99, 0xd9, 0x74, 0x44, 0x18, 0x66, 0x9, 0x49, 0xba, 0x43, 0x35, 0x61, 0xc6, 0x5, 0xb6, 0xf7, 0xbe, 0x8f, 0x82, 0xa, 0x93, 0xcb, 0x2a, 0xed, 0xa9, 0x7c, 0x87, 0x32, 0x92, 0x56, 0x49}} }, + { (uint64_t)20000000ull, {{0xc6, 0x77, 0x1f, 0xab, 0x14, 0xb, 0x75, 0xf4, 0xef, 0xd0, 0x97, 0xfc, 0xe1, 0x82, 0x6b, 0x80, 0xba, 0xe3, 0x16, 0xbc, 0xec, 0x28, 0x86, 0x9b, 0x3a, 0x1b, 0xf1, 0xbc, 0x6e, 0x4d, 0x20, 0x43}} }, + { (uint64_t)30000000ull, {{0xa1, 0xe9, 0xed, 0x3e, 0xf6, 0x5a, 0x9d, 0x52, 0x6c, 0xc2, 0x62, 0x5, 0x88, 0x12, 0x1, 0xd8, 0xa8, 0xf2, 0xc4, 0x40, 0x9f, 0xa3, 0x64, 0x10, 0x72, 0x96, 0xb9, 0xf9, 0x6a, 0x61, 0xb3, 0x58}} }, + { (uint64_t)40000000ull, {{0xb5, 0x31, 0x2d, 0xc7, 0x72, 0x94, 0xab, 0x9b, 0xc8, 0xbf, 0xd1, 0x39, 0x1e, 0x9a, 0xca, 0x92, 0x45, 0xe2, 0x28, 0xf7, 0x4b, 0x49, 0x74, 0xfc, 0x29, 0xad, 0x1c, 0x31, 0xcb, 0xe3, 0xe6, 0xa3}} }, + { (uint64_t)50000000ull, {{0xb8, 0xab, 0xc9, 0xff, 0xf6, 0x84, 0x1d, 0x2e, 0xa0, 0x13, 0x5a, 0x21, 0x72, 0xd3, 0xa7, 0xb, 0xfc, 0x2b, 0x70, 0x22, 0x8, 0xcd, 0x4a, 0x43, 0xc6, 0x30, 0xbe, 0xb1, 0xb8, 0xa0, 0x32, 0x8b}} }, + { (uint64_t)60000000ull, {{0x63, 0x90, 0xe1, 0xdb, 0x81, 0xb0, 0xea, 0x5c, 0xe2, 0x73, 0x94, 0x14, 0xe5, 0x2b, 0x7, 0x98, 0xd8, 0x2e, 0xb8, 0xe9, 0xae, 0xc5, 0x6d, 0xfe, 0x7e, 0x2c, 0x64, 0x11, 0xab, 0x79, 0x41, 0x87}} }, + { (uint64_t)70000000ull, {{0x7e, 0x51, 0xaf, 0xee, 0x5b, 0xc9, 0x71, 0x52, 0x9d, 0x64, 0x4d, 0xcd, 0x7f, 0x2a, 0x2a, 0xb0, 0x26, 0x69, 0xce, 0x2c, 0xb5, 0x7, 0xa6, 0x2d, 0xfc, 0x93, 0x17, 0x6c, 0xb6, 0xdf, 0x41, 0x38}} }, + { (uint64_t)80000000ull, {{0xf3, 0x7b, 0x94, 0x6b, 0x8b, 0x24, 0x88, 0xeb, 0xee, 0x1c, 0x6, 0xc1, 0x27, 0xfb, 0xe5, 0xfa, 0x5e, 0xfd, 0x62, 0x36, 0x9d, 0xd5, 0xaa, 0xda, 0xed, 0xd8, 0x88, 0x50, 0x1d, 0x3b, 0x7e, 0x3b}} }, + { (uint64_t)90000000ull, {{0x46, 0xcb, 0x76, 0x57, 0xf6, 0x1c, 0x83, 0x7c, 0xec, 0x80, 0x74, 0xbb, 0xb0, 0xf5, 0x2e, 0x7f, 0xc5, 0x9a, 0xd, 0x94, 0xe0, 0x17, 00, 0x9a, 0xbe, 0x25, 0x65, 0x2e, 0x4a, 0xd2, 0xe5, 0x3d}} }, + { (uint64_t)100000000ull, {{0x66, 0x7b, 0x8e, 0x6f, 0x6a, 0x4b, 0x91, 0x89, 0x76, 0xd9, 0x73, 0x5a, 0x43, 0x36, 0x7d, 0xc7, 0x59, 0x2c, 0x87, 0xd0, 0xa1, 0xf8, 0x15, 0xc6, 0xe8, 0x7d, 0xf1, 0x1a, 0x13, 0x50, 0x9f, 0xb2}} }, + { (uint64_t)200000000ull, {{0x3b, 0xcb, 0x51, 0x48, 0x1, 0x64, 0x1b, 0x62, 0x55, 0x93, 0x8c, 0xc5, 0x3, 0x76, 0x2d, 0x35, 0xce, 0x6, 0xd7, 0x5f, 0xe9, 0x50, 0x95, 0x9a, 0x1a, 0xab, 0x21, 0x4b, 0x50, 0x9b, 0x10, 0xb}} }, + { (uint64_t)300000000ull, {{0xa5, 0x92, 0x6f, 0x3, 0x1e, 0x6b, 0x15, 0xeb, 0x86, 0x23, 0x51, 0x8, 0xab, 0xb1, 0xaf, 0x90, 0xc5, 0xb1, 0x62, 0xc3, 0x99, 0x8c, 0x8b, 0xbb, 0x3f, 0xfb, 0xb0, 0x72, 0x9d, 0xa9, 0x45, 0x7b}} }, + { (uint64_t)400000000ull, {{0xfe, 0x35, 0xb6, 0x99, 0x44, 0x41, 0xe, 0xaf, 0x81, 0x5b, 0xdc, 0xd0, 0xa4, 0xd7, 0x1e, 0xf9, 0xfc, 0x66, 0x86, 0x48, 0xad, 0x43, 0x74, 0x3b, 0x3, 0x5a, 0xed, 0x2c, 0x17, 0xc1, 0x38, 0x7a}} }, + { (uint64_t)500000000ull, {{0x22, 0x22, 0xd6, 0x70, 0xb8, 0x7d, 0x9b, 0x47, 0xb8, 0xb9, 0x5c, 0x8c, 0x39, 0x7b, 0xc5, 0x2e, 0x2b, 0x46, 0xa6, 0x48, 0xb0, 0x2, 0xa0, 0x48, 0x5a, 0x37, 0x5c, 0xd8, 0x1f, 0x4a, 0x54, 0x5f}} }, + { (uint64_t)600000000ull, {{0xd3, 0x23, 0x8a, 0x4a, 0x8b, 0x71, 0xab, 0x46, 0xd1, 0x53, 0x4, 0xac, 0xfa, 0x2f, 0x40, 0xbf, 0x5e, 0xa6, 0x3b, 0x3d, 0x86, 0x4a, 0x79, 0xfa, 0x84, 0x25, 0xd2, 0x65, 0x5a, 0xe7, 0x7, 0x6f}} }, + { (uint64_t)700000000ull, {{0xa8, 0xff, 0x28, 0x3f, 0xcf, 0xf0, 0x53, 0xd3, 0x44, 0xc8, 0xf7, 0x56, 0x4f, 0x40, 0x24, 0xb6, 0x6b, 0xfa, 0x45, 0x9f, 0x47, 0x6f, 0xd, 0x73, 0xc, 0x91, 0x39, 0x90, 0x8b, 0x2d, 0x64, 0x7e}} }, + { (uint64_t)800000000ull, {{0xf2, 0xda, 0xf8, 0x88, 0xc4, 0x46, 0x57, 0x1, 0xc0, 0xe6, 0x1e, 0x12, 0xc3, 0xfb, 0xd4, 0xea, 0x79, 0xc7, 0xec, 0xb4, 0xf0, 0xc4, 0xb1, 0x54, 0xc5, 0x1a, 0x24, 0xd1, 0xe9, 0x21, 0x28, 0xba}} }, + { (uint64_t)900000000ull, {{0x11, 0x6a, 0xe5, 0xd2, 0x9c, 0xec, 0x72, 0xaa, 0xc5, 0x57, 0xcb, 0x14, 0xe2, 0xcd, 0xd5, 0x53, 0xe5, 0x88, 0xff, 0x8b, 0x81, 0x78, 0x26, 0x1, 0x99, 0xc4, 0xc, 0xae, 0xa2, 0x12, 0xcb, 0x63}} }, + { (uint64_t)1000000000ull, {{0x8c, 0xe6, 0x48, 0x33, 0xce, 0xc9, 00, 0xcb, 0x6d, 0x5a, 0xc4, 0x6f, 0xc0, 0x23, 0x7d, 0x8f, 0x24, 0x39, 0xc3, 0xdf, 0xa2, 0x38, 0xba, 0xf9, 0xcc, 0x94, 0x16, 0x6a, 0xd2, 0xe8, 0x98, 0x87}} }, + { (uint64_t)2000000000ull, {{0x37, 0x8d, 0x3c, 0x5d, 0xbb, 0xa4, 0x82, 0x3d, 0x33, 0x12, 0xbb, 0x61, 0xfc, 0x6, 0x75, 0xa1, 0xbb, 0x39, 0x89, 0xf3, 0x97, 0x1, 0xeb, 0xd, 0x5c, 0xe4, 0xde, 0x5b, 0xd, 0x90, 0x74, 0x72}} }, + { (uint64_t)3000000000ull, {{0x7f, 0xa2, 0xd0, 0xa5, 0x99, 0xe7, 0x97, 0x2e, 0x74, 0xcb, 0x75, 0xf9, 0x8a, 0xf4, 0x84, 0xfc, 0x85, 0x19, 0xcb, 0x7e, 0x25, 0xb9, 0x84, 0xa7, 0x6d, 0x8b, 0xc2, 0xba, 0x8d, 0xaf, 0xde, 0xd8}} }, + { (uint64_t)4000000000ull, {{0xda, 0x3a, 0xcb, 00, 0xab, 0x2d, 0x8d, 0xcc, 0xac, 0xec, 0x8f, 0x77, 0x59, 0x21, 0xc4, 0xe, 0x26, 0xb1, 0xff, 0xbe, 0xca, 0x9e, 0xb7, 0xe6, 0x57, 0x25, 0x6f, 0x59, 0x68, 0xf2, 0x34, 0x1c}} }, + { (uint64_t)5000000000ull, {{0x34, 0x6, 0xd7, 0x9a, 0x50, 0xd8, 0x14, 0xa9, 0xcc, 0xed, 0x3b, 0x24, 0x4, 0xed, 0x3e, 0x1b, 0x8d, 0xa6, 0x21, 0x98, 0x8c, 0x43, 0xb1, 0x93, 0x69, 0x42, 0xf4, 0x94, 0xa, 0xc5, 0xbf, 0x6a}} }, + { (uint64_t)6000000000ull, {{0xf8, 0x3e, 0xe8, 0xc1, 0x62, 0xfc, 0x52, 0xa0, 0x8, 0x9f, 0x46, 0xe8, 0x29, 0xc2, 0xea, 0xf6, 0xa1, 0x9f, 0xd5, 0x96, 0xcd, 0x12, 0xb3, 0xe8, 0x19, 0xd5, 0x67, 0x69, 0x44, 0xf, 0x7b, 0x4e}} }, + { (uint64_t)7000000000ull, {{0x8c, 0x72, 0x7d, 0x24, 0x57, 0xf3, 0x4b, 0x2f, 0xdb, 0x6a, 0xdf, 0x69, 0x1a, 0xb3, 0x5f, 0xaa, 0xe4, 0xff, 0x23, 0x4c, 0x28, 0xb4, 0x4e, 0x9f, 0xd3, 0x71, 0x8e, 0xef, 0xec, 0x41, 0x75, 0x80}} }, + { (uint64_t)8000000000ull, {{0x4a, 0x2e, 0x2f, 0x76, 0xe3, 0x5d, 0xcb, 0xa8, 0x97, 0xa3, 0xae, 0x72, 0xc4, 0x27, 0xd, 0x9c, 0x13, 0x17, 0x14, 0xed, 0x19, 0x1b, 0x55, 0x5c, 0x5e, 0x1, 0xe4, 0x75, 0x7c, 0xba, 0xe7, 0x2c}} }, + { (uint64_t)9000000000ull, {{0x3f, 0x9f, 0xc, 0x4, 0xc0, 0xb9, 0xec, 0x9b, 0x4d, 0x11, 0x7c, 0x5f, 0xc9, 0xf1, 0x8a, 0x20, 0xf2, 0xb3, 0xfa, 0xcc, 0xa4, 0xc8, 0xae, 0x41, 0xaf, 0x7c, 0x8, 0xe9, 0xe0, 0xef, 0xb9, 0x81}} }, + { (uint64_t)10000000000ull, {{0x97, 0xc9, 0x2a, 0x29, 0x1, 0x5e, 0xcb, 0x49, 0xf8, 0x9, 0x5, 0x45, 0xe0, 0x1f, 0xf9, 0x78, 0x6c, 0xae, 0x40, 0x57, 0x73, 0x47, 0x61, 0x18, 0x24, 0xf4, 0xb6, 0x59, 0x9f, 0xf5, 0xd3, 0x64}} }, + { (uint64_t)20000000000ull, {{0x9, 0xba, 0xed, 0x9a, 0x3c, 0x44, 0xb2, 0x22, 0x85, 0xa0, 0xae, 0xa4, 0x14, 0x8c, 0xa7, 0xde, 0x9b, 0xea, 0x96, 0x3c, 0xf6, 0x96, 0x23, 0xb6, 0x83, 0x44, 0x5c, 0xa, 0x10, 0xa5, 0x86, 0x77}} }, + { (uint64_t)30000000000ull, {{0x45, 0xac, 0xaf, 0x1d, 0xe2, 0x89, 0x6d, 0xe8, 0x72, 0x84, 0xff, 0xed, 0x57, 0x8b, 0x77, 0x14, 0xf5, 0x18, 0xa6, 0x18, 0xe2, 0xae, 0x6f, 0x90, 0xae, 0x4f, 0x70, 0x13, 0xa2, 0x8e, 0x99, 0xe0}} }, + { (uint64_t)40000000000ull, {{0x8, 0xb8, 0x47, 0x36, 0x42, 0x24, 0xe2, 0x9c, 0xe3, 0x36, 0x63, 0x93, 0xc2, 0xe1, 0x1e, 0xfc, 0x75, 0x55, 0xde, 0xe1, 0xa0, 0x5f, 0x91, 0xa7, 0x2e, 0x61, 0x11, 0x76, 0x84, 0xdd, 0xbe, 0x29}} }, + { (uint64_t)50000000000ull, {{0x6c, 0x8e, 0xe, 0x4a, 0x63, 0x4f, 0x85, 0x9a, 0x31, 0xab, 0x2f, 0x7a, 0x78, 0xc0, 0xc4, 0xa5, 0x93, 0x8c, 0xb7, 0x7f, 0x3, 0x35, 0x50, 0xa4, 0x7d, 0x7e, 0x31, 0x81, 0xb6, 0xb2, 0x6e, 0xc0}} }, + { (uint64_t)60000000000ull, {{0x66, 0xc2, 0xa0, 0x9, 0x65, 0xf9, 0xbf, 0xcb, 0xb1, 0x1e, 0xa0, 0x3c, 0xf1, 0xd6, 0x31, 0xb0, 0xe, 0x8a, 0x1e, 0xf7, 0xa6, 0xb, 0x1b, 0xe4, 0xa5, 0xac, 0x9, 0x23, 0xb, 0xf8, 0x17, 0x3f}} }, + { (uint64_t)70000000000ull, {{0x63, 0x51, 0xd7, 0x74, 0xc0, 0x2c, 0x5a, 0x9d, 0xee, 0xcf, 0xdb, 0xab, 0x70, 0x96, 0x68, 0x59, 0x8c, 0x47, 0xe4, 0xb1, 0x78, 0x2c, 0xe5, 0xae, 0x31, 0x6a, 0xf7, 0x40, 0xa6, 0x6f, 0x7e, 0x30}} }, + { (uint64_t)80000000000ull, {{0x5a, 0xcc, 0xfd, 0x16, 0x22, 0x79, 0xa5, 0x1c, 0x8b, 0x3b, 0xd5, 0xd3, 0x67, 0x9e, 0x91, 0x89, 0x67, 0xa2, 0x64, 0xea, 0x6, 0x3d, 0x37, 0xdf, 0xf5, 0xe3, 0x45, 0x7e, 0xc3, 0x7, 0xd4, 0x57}} }, + { (uint64_t)90000000000ull, {{0xb7, 0x47, 0xfc, 0x1, 0xc6, 0xf0, 0xc7, 0x49, 0x67, 0x3a, 0x29, 0x10, 0x25, 0xc, 0x2e, 0x23, 0xcb, 0x38, 0x27, 0x4d, 0x63, 0xb4, 0x2f, 0x52, 0x1b, 0x84, 0x63, 0x56, 0xe4, 0x13, 0x61, 0x8f}} }, + { (uint64_t)100000000000ull, {{0x9, 0x42, 0x84, 0x3d, 0x6f, 0x69, 0xe1, 0xcf, 0x3d, 0x99, 0xc9, 0x9f, 0xc, 0x97, 0xc0, 0xe6, 0xe5, 0x78, 0x93, 0x5a, 0xf6, 0xa8, 0xbd, 0xb8, 0xf8, 0x1d, 0x5b, 0x90, 0xbd, 0xe7, 0xcc, 0x10}} }, + { (uint64_t)200000000000ull, {{0x56, 0x4c, 0x64, 0xea, 0x50, 0xe4, 0xbd, 0x20, 0xdb, 0x58, 0x5d, 0xb5, 0x87, 0xb1, 0xf7, 0x64, 0xa2, 0x62, 0xd8, 0x46, 0xa6, 0xb0, 0xa2, 0x4b, 0x43, 0x27, 0x60, 0xd2, 0xf9, 0xde, 0x66, 0x5b}} }, + { (uint64_t)300000000000ull, {{0xac, 0x65, 0x83, 0x41, 0x5b, 0xd6, 0x4c, 0x3, 0x35, 0x97, 0xf9, 0x28, 0xa4, 0xb5, 0xd4, 0xf4, 0x78, 0x9e, 0xa8, 0xb2, 0x87, 0x82, 0x73, 0x89, 0xa8, 0x1e, 0xb6, 0x62, 0x9e, 0xc5, 0xb8, 0x50}} }, + { (uint64_t)400000000000ull, {{0x52, 0xf4, 0x9d, 0x89, 0xcf, 0x74, 0x13, 0x2f, 0xc7, 0x43, 0x2e, 0x6a, 0x6b, 0xef, 0xcf, 0xf3, 0xfd, 0x13, 0xd6, 0x3b, 0x51, 0x60, 0xab, 0x1c, 0xe6, 0x4a, 0xb0, 0xd1, 0x21, 0xcd, 0xa9, 0x9a}} }, + { (uint64_t)500000000000ull, {{0xe9, 0xaa, 0x7c, 0x81, 0xcd, 0xb5, 0xb3, 0x14, 0x8f, 0xb7, 0x62, 0x80, 0x63, 0xcd, 0x7a, 0x7, 0xd1, 0xad, 0xd1, 0x64, 0x3c, 0xed, 0xd3, 0xfa, 0x34, 0x47, 0x9d, 0x85, 0x9c, 0xc5, 0x62, 0x65}} }, + { (uint64_t)600000000000ull, {{0x98, 0x27, 0xae, 0x31, 0xe5, 0xc2, 0xa7, 0x78, 0x39, 0xf6, 0xb, 0x83, 0xab, 0x45, 0x78, 0xe2, 0xa0, 0x1e, 0xfa, 0x4b, 0x3b, 0x14, 0xcc, 0x72, 0x73, 0x14, 0xff, 0xd7, 0x15, 0x53, 0x63, 0xbf}} }, + { (uint64_t)700000000000ull, {{0x72, 0x91, 0x6a, 0x79, 0x27, 0xff, 0x13, 0x24, 0xd4, 0x98, 0x40, 0xec, 0xc0, 0x98, 0x68, 0xb8, 0xf3, 0x15, 0xe4, 0xf1, 0xf6, 0xd4, 0x45, 0x8d, 0x37, 0x5e, 0xc7, 0x45, 0xfc, 0x2e, 0x63, 0x53}} }, + { (uint64_t)800000000000ull, {{0x66, 0x76, 0xe0, 0x4, 0xf, 0xa4, 0xb8, 0x22, 0x9c, 0x61, 0x69, 0xc, 0x71, 0x32, 0x22, 0xcf, 0x3d, 0x37, 0xb9, 0x49, 0x3b, 0x49, 0x6, 0x80, 0xbb, 0x48, 0xd8, 0xd5, 0x1a, 0xde, 0x95, 0xf2}} }, + { (uint64_t)900000000000ull, {{0x41, 0x54, 0xb3, 0x46, 0x5a, 0x43, 0x72, 0x67, 0x1e, 0xa9, 0xe0, 0x64, 0xa7, 0xca, 0xa6, 0x6e, 0x14, 0xb4, 0x98, 0x6a, 0x46, 0x68, 0x91, 0x8a, 0xfa, 0x57, 0x9b, 0xf1, 0xed, 0x25, 0x6, 0xdd}} }, + { (uint64_t)1000000000000ull, {{0xbb, 0x6f, 0x70, 0x62, 0xca, 0x30, 0x6d, 0x67, 0x2a, 0x73, 0xe, 0x2a, 0x2f, 0x21, 0x9b, 0xdb, 0xe4, 0xc, 0x9f, 0xb3, 0xfe, 0x4d, 0x60, 0x13, 0x69, 0x2a, 0xf9, 0x3c, 0xdb, 0x2e, 0xc, 0xd1}} }, + { (uint64_t)2000000000000ull, {{0xbc, 0xe, 0xae, 0x5b, 0x9c, 0x6a, 0xd6, 0x38, 0x7a, 0x41, 0x19, 0x3c, 0x46, 0xf3, 0xc1, 0xd0, 0x71, 0x6d, 0x77, 0xd6, 0x4e, 0x22, 0xb2, 0xe0, 0x7b, 0x4b, 0xce, 0x75, 0x67, 0x65, 0xa2, 0xb}} }, + { (uint64_t)3000000000000ull, {{0x10, 0xc, 0x6f, 0x13, 0x42, 0xb7, 0x1b, 0x73, 0xed, 0xdd, 0xc5, 0x49, 0x2b, 0xe9, 0x23, 0x18, 0x2f, 00, 0xa6, 0x83, 0x48, 0x8e, 0xc3, 0xa2, 0xa1, 0xc7, 0xa9, 0x49, 0xcb, 0xe5, 0x77, 0x68}} }, + { (uint64_t)4000000000000ull, {{0x41, 0x9f, 0x7c, 0x94, 0x91, 0x4, 0x34, 0xf, 0xd3, 0xce, 0x85, 0x94, 0x8d, 0x2e, 0xf9, 0xf0, 0xdd, 0x4b, 0xb3, 0xd9, 0x2f, 0x5a, 0x78, 0x2c, 0x5f, 0x78, 0x4, 0xb7, 0x52, 0x9a, 0x13, 0xc6}} }, + { (uint64_t)5000000000000ull, {{0x40, 0x65, 0x34, 0x98, 0xbe, 0xa0, 0x22, 0xe3, 0x36, 0x5a, 0x3, 0xe5, 0x75, 0x25, 0xba, 0x65, 0x96, 0x53, 0x76, 0x24, 0x4f, 0xff, 0x10, 0x73, 0xe, 0xd9, 0x7a, 0x73, 0xb7, 0x53, 0x1, 0x91}} }, + { (uint64_t)6000000000000ull, {{0xdb, 0x1c, 0x7c, 0xf6, 0x8, 0x91, 0xf9, 0x65, 0xeb, 0xa9, 0xc6, 0x2, 0x24, 00, 0x63, 0xe, 00, 0x47, 0x95, 0x34, 0xe6, 0xf5, 0xb5, 0x33, 0xdc, 0xfc, 0x83, 0x19, 0x38, 0x52, 0x2c, 0x78}} }, + { (uint64_t)7000000000000ull, {{0x59, 0xa0, 0x3a, 0x31, 0x53, 0xa9, 0x94, 0xd7, 0x23, 0x27, 0xe4, 0xd9, 0x24, 0x21, 0xd3, 0xe3, 0x29, 0x1b, 0x1f, 0xa1, 0xb2, 0x40, 0xde, 0x44, 0xb9, 0x2d, 0x7f, 0x62, 0xec, 0x1, 0x28, 0xf1}} }, + { (uint64_t)8000000000000ull, {{0xb2, 0x80, 0xb9, 0x3b, 0x1e, 0x43, 0x88, 00, 0x73, 0xea, 0x4a, 0xa0, 0xef, 0x11, 0x4, 0xf8, 0x24, 0xbd, 0x12, 0x7a, 0x4a, 0x3d, 0xa2, 0x13, 0x92, 0x65, 0xf, 0xe8, 0xc6, 0x55, 0xb6, 0xc5}} }, + { (uint64_t)9000000000000ull, {{0xda, 0xf0, 0xd3, 0xe9, 0x32, 0x17, 0xd8, 0xe9, 0x5a, 0xbf, 0xdd, 0xf1, 0x3b, 0x7f, 0xd4, 0x8e, 0x34, 0x47, 0xad, 0x9, 0x23, 0x26, 0xb8, 0x99, 0xed, 0x58, 0x1f, 0xd5, 0xf8, 0x6, 0xc5, 0x6}} }, + { (uint64_t)10000000000000ull, {{0x16, 0x3d, 0xd6, 0x82, 0xec, 0x97, 0x7c, 0xdd, 0xa5, 0x95, 0x31, 0xda, 0x3f, 0xfa, 0x72, 0x99, 0x8a, 0x6f, 0x88, 0x37, 0xab, 0xad, 0xc6, 0x36, 0xaa, 0xed, 0xc8, 0xbe, 0x19, 0xb2, 0xd7, 0xc7}} }, + { (uint64_t)20000000000000ull, {{0x2, 0xfa, 0x35, 0x3a, 0xa8, 0x4e, 0xa8, 0xc4, 0x4c, 0x80, 0x23, 0x6, 0x5d, 0x79, 0x41, 0x60, 0x6b, 0x1f, 0xa5, 0xc2, 0x64, 0xdc, 0xcf, 0x46, 0xdc, 0x64, 0x94, 0xeb, 0xe9, 0x60, 0x6f, 0x20}} }, + { (uint64_t)30000000000000ull, {{0x87, 0x5, 0xd, 0xab, 0xf5, 0xb2, 0x3e, 0x8b, 0x79, 0x81, 0x3f, 0x4e, 0xd7, 0x6a, 0xa4, 0xad, 0xd2, 0x25, 0xdd, 0x2a, 0x50, 0x89, 0xaf, 0x6, 0x7d, 0xa7, 0x7c, 0xcb, 0x6e, 0xc5, 0x59, 0x46}} }, + { (uint64_t)40000000000000ull, {{0xaa, 0xe6, 0xb2, 0xc8, 0xa2, 0x9e, 0x4d, 0xbc, 0x63, 0x76, 0xc1, 0x72, 0x5, 0xfb, 0x2, 0x85, 0xe7, 0xd7, 0xd3, 0x25, 0x32, 0x3c, 0xd5, 0x26, 0xf, 0x98, 0xad, 0xff, 0xf7, 0xd4, 0xd4, 0xfb}} }, + { (uint64_t)50000000000000ull, {{0x9d, 0x79, 0x28, 0x82, 0x12, 0xa1, 0xe2, 0x3c, 0x9, 0x9f, 0xb2, 0xd8, 0xf0, 0xd0, 0xdb, 0xd3, 0xc2, 0xec, 0xd7, 0x58, 0xb9, 0xe6, 0xb5, 0xb4, 0xf2, 0x90, 0x60, 0x7, 0x9f, 0x19, 0x66, 0x9f}} }, + { (uint64_t)60000000000000ull, {{0x18, 0x90, 0x10, 0x6f, 0x1b, 0x97, 0xbc, 0x2d, 0xa, 0xe3, 0x96, 0xe5, 0xe5, 0x5e, 0xbf, 0xcc, 0x8e, 0xf6, 0x91, 0x7f, 0xb1, 0x96, 0xcb, 0x2b, 0x1e, 0x80, 0x25, 0x5d, 0x54, 0xb6, 0x87, 0x10}} }, + { (uint64_t)70000000000000ull, {{0x57, 0xd3, 0x4e, 0xf7, 0x54, 0x3b, 0xe4, 0x7b, 0x7b, 0xf4, 0x97, 0xce, 0x4a, 0x17, 0x6e, 0x78, 0xc6, 0xd6, 0x5c, 0xd3, 0x27, 0xf6, 0x4b, 0xa7, 0x5c, 0x27, 0xd1, 0x57, 0xb3, 0x37, 0x12, 0x5d}} }, + { (uint64_t)80000000000000ull, {{0x5e, 0xcb, 0x10, 0x15, 0x4b, 0x96, 0xca, 0xb5, 0x5e, 0x9, 0x46, 0x83, 0xf8, 0xdb, 0xff, 0x7f, 0x56, 0x63, 0x5f, 0xa6, 0x64, 0x97, 0xee, 0x9e, 0x24, 0xe, 0x83, 0x63, 0x7c, 0x7c, 0x87, 0x72}} }, + { (uint64_t)90000000000000ull, {{0x42, 0x32, 0x69, 0x98, 0x51, 0x30, 0xf1, 0x66, 0x51, 0x6a, 0x5b, 0xa8, 0x61, 0x9, 0x6d, 0x72, 0xec, 0xcc, 0x67, 0xad, 0xab, 0xa4, 0x5e, 0xb3, 0x73, 0x9a, 0xe, 0xbc, 0x61, 0xa3, 0x20, 0xae}} }, + { (uint64_t)100000000000000ull, {{0xa8, 0xd1, 0x60, 0x95, 0x91, 0x49, 0x8f, 0xa7, 0xc2, 0x94, 0x27, 0xad, 0x89, 0x31, 0xaf, 0x36, 0xc5, 0x2d, 0xc9, 0x7b, 0x4a, 0x11, 0xe7, 0x47, 0xa9, 0x56, 0xc2, 0x8c, 0x42, 0x54, 0xcf, 0xd4}} }, + { (uint64_t)200000000000000ull, {{0x23, 0x14, 0x49, 00, 0xa8, 0x66, 0xe8, 0xc1, 0xbf, 0x40, 0x98, 0xda, 0xa9, 0x48, 0xb9, 0x86, 0xf3, 0x84, 0xe, 0x5a, 0x7d, 0x21, 0x5e, 0xf0, 0xd5, 0x64, 0xef, 0xd8, 0xbe, 0xc6, 0x83, 0x15}} }, + { (uint64_t)300000000000000ull, {{0x6a, 0x51, 0x47, 0x3c, 0x86, 0xed, 0xad, 0x53, 0x51, 0x4b, 0x3f, 0x95, 0x97, 0xed, 0x21, 0xae, 00, 0x81, 0x51, 0xa0, 0x9e, 0x43, 0xad, 0xdd, 0x45, 0xd1, 0x74, 0x63, 0xc5, 0x34, 0x3, 0x97}} }, + { (uint64_t)400000000000000ull, {{0x8, 0xbd, 0xd4, 0xc3, 0xe4, 0x53, 0x1b, 0x29, 0x7a, 0x70, 00, 0x1e, 0xb8, 0xa4, 0xf1, 0x98, 0xdc, 0x3b, 0xd4, 0xf1, 0xf5, 0x60, 0x9a, 0xda, 0x98, 0xf6, 0xd9, 0x5f, 0x9a, 0x1a, 0x30, 0x2e}} }, + { (uint64_t)500000000000000ull, {{0x97, 0x55, 0x70, 0xea, 0x12, 0xde, 0x5a, 0xf5, 0xc5, 0x36, 0xbd, 0xb6, 0x83, 0x54, 0xfb, 0xc8, 0x32, 0x21, 0x50, 0xfc, 0x56, 0x83, 0x7c, 0x4b, 0x78, 0xa9, 0x85, 0x76, 0x5d, 0x2a, 0x70, 0x99}} }, + { (uint64_t)600000000000000ull, {{0xa7, 0xa6, 0x39, 0x93, 0x41, 0xcb, 0x4d, 0x67, 0x76, 0xcd, 0x94, 0xd, 0x1d, 0x6a, 0xb0, 0xac, 0xa, 0xbf, 0x56, 0x93, 0x6a, 0x35, 0x31, 0xdf, 0xe9, 0x6c, 0x23, 0x69, 0x97, 0x8e, 0x49, 0xfa}} }, + { (uint64_t)700000000000000ull, {{0x55, 0x9, 0x3e, 0x5e, 0xeb, 0xca, 0x3, 0x88, 0x48, 0xdc, 0x99, 0x7e, 0x31, 0x95, 0xec, 0xc5, 0x8f, 0xb4, 0xa5, 0x71, 0xb9, 0x52, 0x56, 0xc0, 0xff, 0x49, 0xbe, 0xd0, 0xf1, 0x65, 0x22, 0xbd}} }, + { (uint64_t)800000000000000ull, {{0xbb, 0xc6, 0x18, 0x2, 0x24, 0xaf, 0xd3, 0x38, 0xa6, 0xf4, 0xa0, 0x6b, 0x11, 0x98, 0x40, 0x68, 0xeb, 0x36, 0x35, 0xe7, 0xe5, 0x47, 0x66, 0x69, 0x78, 0x83, 0xaf, 0xbd, 0xce, 0xad, 0x2f, 0x31}} }, + { (uint64_t)900000000000000ull, {{0x61, 0x3a, 0xa1, 0x2c, 0xc0, 0xa1, 0x9b, 0xc8, 0x43, 0x63, 0x50, 0xbb, 0xc0, 0xf6, 0x16, 0x32, 0x6e, 0x64, 0x85, 0x83, 0x33, 0x4a, 0x32, 0x65, 0x16, 0x29, 0xe9, 0x5, 0xc5, 0x20, 0x62, 0x69}} }, + { (uint64_t)1000000000000000ull, {{0x52, 0xdd, 0xf8, 0x81, 0x13, 0xa0, 0xfc, 0xf2, 0x12, 0x90, 0x95, 0xc6, 0x18, 0x91, 0xbe, 0x88, 0x5c, 0x9, 0x30, 0x8, 0xeb, 0xc4, 0x65, 0xc, 0xb0, 0xee, 0xa5, 0x60, 0xcd, 0x4d, 0x75, 0x1b}} }, + { (uint64_t)2000000000000000ull, {{0x75, 0xbd, 0xfc, 0x35, 0xa6, 0xdf, 0x76, 0xe5, 0x98, 0x8e, 0xd9, 0xe3, 0x10, 0xa5, 0x89, 0x16, 0xae, 0xf0, 0xc5, 0xf0, 0x5b, 0x89, 0x22, 0xea, 0xae, 0x2c, 0xf9, 0x8f, 0x58, 0x42, 0x3c, 0xe3}} }, + { (uint64_t)3000000000000000ull, {{0x88, 0x98, 0x93, 0xe8, 0x7d, 0x56, 0x9f, 0x14, 0xb2, 0x48, 0xd1, 0xed, 0x93, 0xe8, 0xce, 0x60, 0xbb, 0xe3, 0x73, 0x69, 0xb0, 0xd6, 0xc7, 0xa1, 0x86, 0x89, 0x33, 0xd3, 0xc3, 0xda, 0x9a, 0x72}} }, + { (uint64_t)4000000000000000ull, {{0x88, 0x3e, 0xf3, 0x4b, 0xa2, 0xc1, 0x91, 0xf4, 0x9d, 0x3c, 0xc6, 0xad, 0xa0, 0xaf, 0xf1, 0xcf, 0xb1, 0x77, 0xbd, 0x9e, 0xd4, 0xb3, 0xa5, 0x37, 0x84, 0xb7, 0xf1, 0x62, 0x9b, 0xed, 0x17, 0x41}} }, + { (uint64_t)5000000000000000ull, {{0xa2, 0x90, 0x7c, 0x39, 0x84, 0xb1, 0x4a, 0xb1, 0xf4, 0xda, 0x58, 0xc2, 0xc8, 0x2d, 0x6b, 0x24, 0xf1, 0x29, 0x49, 0x9, 0x75, 0xfc, 0x4a, 0x33, 0x3d, 0x25, 0xa1, 0xf9, 0x2b, 0xc4, 0x32, 0xb6}} }, + { (uint64_t)6000000000000000ull, {{0xa0, 0x7d, 0x9f, 0x18, 0x95, 0x1f, 0xf2, 0x32, 0xcf, 0x4e, 0xc0, 0xee, 0x2f, 0xbc, 0xc3, 0xe1, 0x1b, 0x2c, 0xaf, 0xc9, 0x57, 0x65, 0x82, 0x10, 0x38, 0x1e, 0x3e, 0xe4, 0xed, 0xec, 0x2e, 0x7a}} }, + { (uint64_t)7000000000000000ull, {{0x66, 0x80, 0x21, 0xd5, 0xde, 0x8c, 0xa4, 0xc1, 0x8f, 0x5a, 0x74, 0xf2, 0x78, 0x69, 0xc4, 0xd6, 0xd4, 0x93, 0xa3, 0x30, 0x39, 0x3c, 0xf0, 0x26, 0x41, 0xff, 0xa8, 0x56, 0x7b, 0xa5, 0x36, 0x20}} }, + { (uint64_t)8000000000000000ull, {{0xe0, 0x48, 0x7a, 0xc4, 0x5a, 0x82, 0x59, 0xe3, 0xe5, 0xf2, 0xd9, 0xb8, 0xf6, 0xb8, 0xfa, 0x26, 0x9a, 0x63, 0x49, 0x71, 0xa2, 0xf7, 0xc2, 0x1a, 0x54, 0x17, 0x76, 0x81, 0xeb, 0x2, 0xbd, 0x4a}} }, + { (uint64_t)9000000000000000ull, {{0x98, 0x92, 0x6a, 0x3a, 0xf0, 0x5b, 0xf4, 0xa9, 0x8d, 0xf9, 0xf6, 0x4a, 0xe7, 0xb9, 0xda, 0x45, 0xa7, 0x6, 0xc3, 0xf8, 0x39, 0x5e, 0x47, 0x1f, 0x96, 0xed, 0x3c, 0x6, 0x6, 0xbe, 0xbb, 0x71}} }, + { (uint64_t)10000000000000000ull, {{0x80, 0xad, 0xb7, 0xd, 0x46, 0xf6, 0x3a, 0x75, 0x64, 0xa3, 0xf6, 0x71, 0xd9, 0xba, 0x95, 0x71, 0xb7, 0xf7, 0x95, 0xa9, 0x63, 0x38, 0x2a, 0x4d, 0x9f, 0xaf, 0x2d, 0x54, 0xf6, 0xc6, 0x84, 0x29}} }, + { (uint64_t)20000000000000000ull, {{0xae, 0xbd, 0x97, 0x42, 0x1f, 0x3f, 0xca, 0xe8, 0x95, 0x18, 0x60, 0xe6, 0xd9, 0xd1, 0xf3, 0xec, 0x59, 0x73, 0xa2, 0xf7, 0x66, 0x88, 0x4b, 0xfe, 0x17, 0x50, 0x79, 0x51, 0xe4, 0x62, 0xc6, 0x63}} }, + { (uint64_t)30000000000000000ull, {{0x61, 0x2, 0x6c, 0x84, 0x2a, 0x6a, 0x22, 0x25, 0x74, 0x6b, 0x19, 0x6b, 0x56, 0x89, 0xe1, 0x18, 0xf5, 0x41, 0x34, 0x15, 0x98, 0x1d, 0x7, 0x73, 0x62, 0xb2, 0xe7, 0xb9, 0xac, 0xa5, 0x28, 0x16}} }, + { (uint64_t)40000000000000000ull, {{0x52, 0x54, 0xb5, 0x78, 0xe8, 0x57, 0x9a, 0x27, 0x3b, 0x89, 0x8e, 0x65, 0x9d, 0xd3, 0xe1, 0xa1, 0xcf, 0xba, 0x12, 0x47, 0x26, 0x64, 0xbd, 0x4e, 0x7f, 0x9a, 0x13, 0xb1, 0xfc, 0xee, 0x2, 0x93}} }, + { (uint64_t)50000000000000000ull, {{0x7b, 0x2a, 0xb, 00, 0xcf, 0xdc, 0xa9, 0x51, 0x46, 0xcf, 0x80, 0x95, 0xdd, 0x2b, 0x82, 0x90, 0x91, 0xb0, 0xf2, 0xd5, 0xbb, 0xc, 0x33, 0x82, 0x2d, 0x8b, 0x43, 0x42, 0x69, 0xcd, 0x2a, 0x42}} }, + { (uint64_t)60000000000000000ull, {{0x21, 0x57, 0x4f, 0xed, 0x15, 0x1a, 0x2f, 0x9f, 0x64, 0xa4, 0x5b, 0xe2, 0x8a, 0x3a, 0xf5, 0x88, 0xe9, 0xf2, 0xd1, 0x71, 0x35, 0xa3, 0x53, 0x7f, 0x7, 0xfd, 0x6a, 0xef, 0xa2, 0x9f, 0x2, 0xaf}} }, + { (uint64_t)70000000000000000ull, {{0x1a, 0xf2, 0x41, 0xe1, 0x38, 0x27, 0x98, 0x29, 0xac, 0x6a, 0xe6, 0x2f, 0xf, 0x33, 0x20, 0x4b, 0xb2, 0x8a, 0xfd, 0x6, 0x5c, 0x42, 0x59, 0x3b, 0xdc, 0x79, 0x14, 0x85, 0x97, 0x5b, 0x26, 0x95}} }, + { (uint64_t)80000000000000000ull, {{0xa8, 0xc8, 0xb8, 0x7b, 0x51, 0x2d, 0xef, 0x9b, 0x5e, 0x50, 0xe, 0xb4, 0x98, 0xaf, 0x86, 0xaa, 0xd2, 0x46, 0x4a, 0xea, 0xe7, 0x6d, 0xb1, 0xf6, 0x5d, 0x23, 0x26, 0xce, 0x90, 0x26, 0xec, 0x69}} }, + { (uint64_t)90000000000000000ull, {{0x3d, 0x78, 0x73, 0x63, 0x95, 0xf1, 0xd7, 0xde, 0x8e, 0x16, 0xc0, 0xb5, 0xa9, 0x9f, 0x4d, 0xc4, 0xeb, 0x8f, 0x22, 0xac, 0xc1, 0x5b, 0x21, 0x42, 0x44, 0x1d, 0xbd, 0x8d, 0x2c, 0x31, 0xb9, 0xce}} }, + { (uint64_t)100000000000000000ull, {{0x27, 0x27, 0xd4, 0x93, 0x2f, 0x98, 0x39, 0xe4, 0x3b, 0x6b, 0xf5, 0xfb, 0x29, 0xa3, 0xbe, 0x4c, 0x9, 0xb, 0x6e, 0xb9, 0x31, 00, 0xbb, 0x92, 0x58, 0x1a, 0xdb, 0x8d, 0xd2, 0xb6, 0x61, 0x54}} }, + { (uint64_t)200000000000000000ull, {{0xae, 0x96, 0x78, 0x2e, 0xf2, 0xc4, 0xdf, 0x7d, 0x2e, 0x4, 0xcc, 0xf9, 0xef, 0x76, 0x23, 0x7f, 0x17, 0xc, 0x97, 0x3, 0xb4, 0x92, 0xc0, 0x78, 0x52, 0x6e, 0xb1, 0xf6, 0x85, 0x3d, 0xb1, 0x33}} }, + { (uint64_t)300000000000000000ull, {{0x17, 0x43, 0xfe, 0xab, 0x12, 0xad, 0xe5, 0xfe, 0x12, 0x53, 0x22, 0x27, 0x2f, 0xd1, 0x40, 0x6b, 0x74, 0xe8, 0x19, 0x70, 0x32, 0x68, 0x46, 0x22, 0xee, 0x79, 0xab, 0xcd, 0x94, 0x93, 0x66, 0x4c}} }, + { (uint64_t)400000000000000000ull, {{0x7, 0x9b, 0xf2, 0xa9, 0x6e, 0x16, 0x6e, 0xf9, 0xe6, 0xb2, 0x23, 0x1d, 0xb9, 0x85, 0x8b, 0x99, 0x98, 0x7f, 0x49, 0x33, 0x87, 0xde, 0xeb, 0xd5, 0x17, 0x48, 0x54, 0x9a, 0xd, 0xf7, 0xdc, 0x44}} }, + { (uint64_t)500000000000000000ull, {{0xca, 0xba, 0x97, 0x98, 0x51, 0x6d, 0xad, 0x3, 0x38, 0xd0, 0x6e, 0x10, 0x6d, 0x76, 0xa2, 0x1, 0x93, 0x7a, 0xce, 0x4c, 0x91, 0x53, 0x9e, 0x61, 0x7d, 0x89, 0x28, 0x73, 0x6, 0xa3, 0x92, 0xb1}} }, + { (uint64_t)600000000000000000ull, {{0x6b, 0x8, 0x7f, 0x48, 0xb3, 0xd7, 0xaa, 0xc9, 0x57, 0xc4, 0x52, 0xe5, 0x1a, 0x18, 0xd7, 0x26, 0xb, 0xf8, 0xc8, 0x56, 0xc4, 0xc7, 0x1e, 0x48, 0xf6, 0x49, 0xae, 00, 0x4a, 0xf6, 0x8f, 0x13}} }, + { (uint64_t)700000000000000000ull, {{0x9e, 0xed, 0x8b, 0x23, 0x1f, 0x79, 0x4c, 0x46, 0x5c, 0xbe, 0x88, 0x40, 0xd0, 0xf1, 0x6f, 0x7b, 0x9f, 0x9c, 0x6e, 0xb4, 0x9c, 0x20, 0x7d, 0xe9, 0xd8, 0x55, 0x11, 0x83, 0xd0, 0xc7, 0x6e, 0x43}} }, + { (uint64_t)800000000000000000ull, {{0x58, 0x4a, 0x78, 0x93, 0x13, 0x7e, 0xbd, 0x2, 0x8b, 0xa7, 0x59, 0x82, 0xc3, 0x39, 0xb7, 0x66, 0xaa, 0xda, 0xad, 0xf9, 0x14, 0x50, 0xf9, 0x40, 0x7d, 0x2a, 0x97, 0xd7, 0xf6, 0xb1, 0x93, 0x5e}} }, + { (uint64_t)900000000000000000ull, {{0x7, 0xce, 0x54, 0xb1, 0x18, 0x26, 0xa1, 0x75, 0x23, 0x13, 0x55, 0x1a, 00, 0x20, 0xfd, 0x79, 0x8a, 00, 0x9e, 0x20, 0xcd, 0xb2, 0x40, 0x1d, 0x52, 0x51, 0xc1, 0x55, 0x8e, 0xea, 0xd2, 0x6c}} }, + { (uint64_t)1000000000000000000ull, {{0x39, 0x80, 0x7f, 0x3d, 0xce, 0xb0, 0xa6, 0xfe, 0x34, 0xa7, 0xa1, 0xed, 0xc6, 0x9b, 0x78, 0xff, 0xbe, 0xd5, 0xa7, 0x8c, 0x6c, 0x87, 0x5d, 0xda, 0x96, 0x69, 0xdb, 0xb2, 0x95, 0x70, 0xf0, 0xf4}} }, + { (uint64_t)2000000000000000000ull, {{0xda, 0x74, 00, 0x86, 0xf1, 0x5c, 0xe8, 0x21, 0xe9, 0xd, 0x50, 0xaf, 0xcf, 0x80, 0x9c, 0x7e, 0x18, 0x51, 0x90, 0x1b, 0xa3, 0x5f, 0x9f, 0x63, 0x78, 0xd6, 0x40, 0x7c, 0xb9, 0xc7, 0xa2, 0x75}} }, + { (uint64_t)3000000000000000000ull, {{0x7, 0xa1, 0x75, 0x63, 0xae, 0xf5, 0xcf, 0xd0, 0x36, 0xfa, 0x64, 0xd4, 0xb1, 0x97, 0xa9, 0x51, 0xc0, 0xd2, 0x87, 0x2b, 0xd, 0xb6, 0xf9, 0xbe, 0x47, 0xe6, 0x7c, 0xa6, 0xb5, 0x35, 0xe2, 0x6e}} }, + { (uint64_t)4000000000000000000ull, {{0xe3, 0x49, 0xf7, 0xeb, 0xe5, 0x11, 0x39, 0xfe, 0xd5, 0x69, 0x40, 0x37, 0xd1, 0x14, 0xb7, 0xbd, 0x45, 0xdd, 0xa, 0x6a, 0xf0, 0x4b, 0x62, 0xec, 0xa4, 0xd8, 0xcd, 0x55, 0x2a, 0x14, 0xe3, 0xfb}} }, + { (uint64_t)5000000000000000000ull, {{0x8d, 0x59, 0x7e, 0xa9, 0xf5, 0x79, 0x9a, 0x4d, 0x15, 0x3d, 0x82, 0xd6, 0xf7, 0xbe, 0xa0, 0x2e, 0x52, 0x40, 0xa2, 0xc8, 0x9b, 0x4, 0x1e, 0x6, 0x2f, 0x37, 0xbc, 0x7b, 0x82, 0xa0, 0xac, 0x55}} }, + { (uint64_t)6000000000000000000ull, {{0xa3, 0x43, 0xa7, 0xe1, 0x14, 0x4d, 0x33, 0x50, 0xf, 0x3e, 0xfd, 0x38, 0x15, 0x82, 0xdd, 0xc5, 0xd0, 0x18, 0x3e, 0x5d, 0xcf, 0x8a, 0xfa, 0x64, 0xbb, 0x67, 0x6c, 0x97, 0x3e, 0x3d, 0x1a, 0xb1}} }, + { (uint64_t)7000000000000000000ull, {{0x89, 0xe9, 0x3e, 0xe9, 0xf2, 0x4d, 0x72, 0x61, 0xe5, 0x44, 0xca, 0x8f, 0x9, 0xa7, 0x40, 0x4e, 0xe3, 0xa9, 0xe, 0xe2, 0x50, 0x7d, 0xda, 0xcf, 0x41, 0x2a, 0x58, 0xc, 0x9, 0x65, 0x1c, 0x53}} }, + { (uint64_t)8000000000000000000ull, {{0xc5, 0x94, 0x10, 0x81, 0x54, 0x69, 0xf4, 0x59, 0xd1, 0x5a, 0x6f, 0xe3, 0xf2, 0xa1, 0x1b, 0xa6, 0x31, 0x12, 0xfa, 0xaa, 0xc5, 0x3d, 0xbc, 0x52, 0x5d, 0x3c, 0xfa, 0xb1, 0xfa, 0x9c, 0x3d, 0xdb}} }, + { (uint64_t)9000000000000000000ull, {{0x9d, 0xe7, 0xcb, 0xb, 0x8d, 0x7b, 0xac, 0x47, 0xff, 0xd3, 0x93, 0x1b, 0xcd, 0x82, 0xcd, 0xd5, 0x35, 0xc, 0x29, 0x34, 0xb1, 0x6e, 0xb, 0x64, 0x32, 0xab, 0xf7, 0xcb, 0x4b, 0x5c, 0x37, 0x6d}} }, + { (uint64_t)10000000000000000000ull, {{0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3}} }, }; namespace rct { diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index dccd18867..baa649f82 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -46,13 +46,34 @@ using namespace std; namespace { - rct::Bulletproof make_dummy_bulletproof(size_t n_outs) + rct::Bulletproof make_dummy_bulletproof(const std::vector<uint64_t> &outamounts, rct::keyV &C, rct::keyV &masks) { + const size_t n_outs = outamounts.size(); const rct::key I = rct::identity(); size_t nrl = 0; while ((1u << nrl) < n_outs) ++nrl; nrl += 6; + + C.resize(n_outs); + masks.resize(n_outs); + for (size_t i = 0; i < n_outs; ++i) + { + masks[i] = I; + rct::key sv8, sv; + sv = rct::zero(); + sv.bytes[0] = outamounts[i] & 255; + sv.bytes[1] = (outamounts[i] >> 8) & 255; + sv.bytes[2] = (outamounts[i] >> 16) & 255; + sv.bytes[3] = (outamounts[i] >> 24) & 255; + sv.bytes[4] = (outamounts[i] >> 32) & 255; + sv.bytes[5] = (outamounts[i] >> 40) & 255; + sv.bytes[6] = (outamounts[i] >> 48) & 255; + sv.bytes[7] = (outamounts[i] >> 56) & 255; + sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes); + rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H); + } + return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I}; } } @@ -444,7 +465,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFeeKey, hw::device &hwdev) { + mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFeeKey, hw::device &hwdev) { mgSig mg; //setup vars size_t cols = pubs.size(); @@ -534,7 +555,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFeeKey, const key &message) { PERF_TIMER(verRctMG); //setup vars size_t cols = pubs.size(); @@ -769,9 +790,7 @@ namespace rct { if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts.size())); - C = rct::keyV(outamounts.size(), I); - masks = rct::keyV(outamounts.size(), I); + rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks)); } else { @@ -799,9 +818,7 @@ namespace rct { if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size())); - C = rct::keyV(batch_amounts.size(), I); - masks = rct::keyV(batch_amounts.size(), I); + rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks)); } else { diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index b67a0b992..459edc600 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -96,9 +96,9 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFee, const key &message, hw::device &hwdev); + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFee, const key &message, hw::device &hwdev); mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev); - bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message); + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFee, const key &message); bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C); //These functions get keys from blockchain diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 18290637b..487ea6f32 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -187,7 +187,8 @@ namespace rct { rct::keyV L, R; rct::key a, b, t; - Bulletproof() {} + Bulletproof(): + A({}), S({}), T1({}), T2({}), taux({}), mu({}), a({}), b({}), t({}) {} Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 8fc42b7e3..d2c4a33cb 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -112,6 +112,7 @@ target_link_libraries(rpc common cryptonote_core cryptonote_protocol + version ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE @@ -121,6 +122,7 @@ target_link_libraries(daemon_messages LINK_PRIVATE cryptonote_core cryptonote_protocol + version serialization ${EXTRA_LIBRARIES}) @@ -129,6 +131,7 @@ target_link_libraries(daemon_rpc_server rpc cryptonote_core cryptonote_protocol + version daemon_messages serialization ${Boost_CHRONO_LIBRARY} diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c2e71bef8..3851af3c8 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -91,12 +91,10 @@ namespace cryptonote bool core_rpc_server::init( const boost::program_options::variables_map& vm , const bool restricted - , const network_type nettype , const std::string& port ) { m_restricted = restricted; - m_nettype = nettype; m_net_server.set_threads_prefix("RPC"); auto rpc_config = cryptonote::rpc_args::process(vm); @@ -184,17 +182,19 @@ namespace cryptonote res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.rpc_connections_count = get_connections_count(); - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); - res.mainnet = m_nettype == MAINNET; - res.testnet = m_nettype == TESTNET; - res.stagenet = m_nettype == STAGENET; - res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; + res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count(); + res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count); + res.rpc_connections_count = m_restricted ? 0 : get_connections_count(); + res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count(); + + cryptonote::network_type net_type = nettype(); + res.mainnet = net_type == MAINNET; + res.testnet = net_type == TESTNET; + res.stagenet = net_type == STAGENET; + res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); @@ -202,14 +202,18 @@ namespace cryptonote res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); - res.bootstrap_daemon_address = m_bootstrap_daemon_address; - res.height_without_bootstrap = res.height; + res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address; + res.height_without_bootstrap = m_restricted ? 0 : res.height; + if (m_restricted) + res.was_bootstrap_ever_used = false; + else { boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - res.update_available = m_core.is_update_available(); + res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_restricted ? false : m_core.is_update_available(); + res.version = m_restricted ? "" : MONERO_VERSION; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -750,7 +754,7 @@ namespace cryptonote PERF_TIMER(on_start_mining); CHECK_CORE_READY(); cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_nettype, req.miner_address)) + if(!get_account_address_from_str(info, nettype(), req.miner_address)) { res.status = "Failed, wrong address"; LOG_PRINT_L0(res.status); @@ -831,7 +835,7 @@ namespace cryptonote res.speed = lMiner.get_speed(); res.threads_count = lMiner.get_threads_count(); const account_public_address& lMiningAdr = lMiner.get_mining_address(); - res.address = get_account_address_as_str(m_nettype, false, lMiningAdr); + res.address = get_account_address_as_str(nettype(), false, lMiningAdr); } res.status = CORE_RPC_STATUS_OK; @@ -1026,7 +1030,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ // equivalent of strstr, but with arbitrary bytes (ie, NULs) // This does not differentiate between "not found" and "found at offset 0" - uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen) + size_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen) { const void* buf = start_buff; const void* end=(const char*)buf+buflen; @@ -1064,7 +1068,7 @@ namespace cryptonote cryptonote::address_parse_info info; - if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_nettype, req.wallet_address)) + if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, nettype(), req.wallet_address)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; error_resp.message = "Failed to parse wallet address"; @@ -1077,7 +1081,7 @@ namespace cryptonote return false; } - block b = AUTO_VAL_INIT(b); + block b; cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve)) @@ -1148,7 +1152,7 @@ namespace cryptonote // Fixing of high orphan issue for most pools // Thanks Boolberry! - block b = AUTO_VAL_INIT(b); + block b; if(!parse_and_validate_block_from_blob(blockblob, b)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; @@ -1216,7 +1220,7 @@ namespace cryptonote error_resp.message = "Wrong block blob"; return false; } - block b = AUTO_VAL_INIT(b); + block b; if(!parse_and_validate_block_from_blob(blockblob, b)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; @@ -1583,32 +1587,39 @@ namespace cryptonote res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.rpc_connections_count = get_connections_count(); - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); - res.mainnet = m_nettype == MAINNET; - res.testnet = m_nettype == TESTNET; - res.stagenet = m_nettype == STAGENET; - res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; + res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count(); + res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count); + res.rpc_connections_count = m_restricted ? 0 : get_connections_count(); + res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count(); + + cryptonote::network_type net_type = nettype(); + res.mainnet = net_type == MAINNET; + res.testnet = net_type == TESTNET; + res.stagenet = net_type == STAGENET; + res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; + res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; - res.start_time = (uint64_t)m_core.get_start_time(); + res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); - res.bootstrap_daemon_address = m_bootstrap_daemon_address; - res.height_without_bootstrap = res.height; + res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address; + res.height_without_bootstrap = m_restricted ? 0 : res.height; + if (m_restricted) + res.was_bootstrap_ever_used = false; + else { boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - res.update_available = m_core.is_update_available(); + res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_restricted ? false : m_core.is_update_available(); + res.version = m_restricted ? "" : MONERO_VERSION; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2021,6 +2032,18 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res) + { + PERF_TIMER(on_pop_blocks); + + m_core.get_blockchain_storage().pop_blocks(req.nblocks); + + res.height = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_relay_tx); @@ -2125,7 +2148,7 @@ namespace cryptonote const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (uint64_t amount: req.amounts) { - auto data = rpc::RpcHandler::get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -2133,7 +2156,7 @@ namespace cryptonote return false; } - res.distributions.push_back({std::move(*data), amount, req.binary}); + res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); } } catch (const std::exception &e) @@ -2147,6 +2170,47 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res) + { + PERF_TIMER(on_get_output_distribution_bin); + + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r)) + return r; + + res.status = "Failed"; + + if (!req.binary) + { + res.status = "Binary only call"; + return false; + } + try + { + // 0 is placeholder for the whole chain + const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); + for (uint64_t amount: req.amounts) + { + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); + if (!data) + { + res.status = "Failed to get output distribution"; + return false; + } + + res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); + } + } + catch (const std::exception &e) + { + res.status = "Failed to get output distribution"; + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor<std::string, false, true, 2> core_rpc_server::arg_rpc_bind_port = { diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3ba882b23..081ccc25d 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -70,10 +70,9 @@ namespace cryptonote bool init( const boost::program_options::variables_map& vm, const bool restricted, - const network_type nettype, const std::string& port ); - network_type nettype() const { return m_nettype; } + network_type nettype() const { return m_core.get_nettype(); } CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map @@ -117,6 +116,8 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) + MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) + MAP_URI_AUTO_JON2_IF("/pop_blocks", on_pop_blocks, COMMAND_RPC_POP_BLOCKS, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -187,6 +188,8 @@ namespace cryptonote bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); + bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res); + bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8e8df7a52..0a07930ec 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -34,6 +34,40 @@ #include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" #include "rpc/rpc_handler.h" +#include "common/varint.h" +#include "common/perf_timer.h" + +namespace +{ + template<typename T> + std::string compress_integer_array(const std::vector<T> &v) + { + std::string s; + s.resize(v.size() * (sizeof(T) * 8 / 7 + 1)); + char *ptr = (char*)s.data(); + for (const T &t: v) + tools::write_varint(ptr, t); + s.resize(ptr - s.data()); + return s; + } + + template<typename T> + std::vector<T> decompress_integer_array(const std::string &s) + { + std::vector<T> v; + v.reserve(s.size()); + int read = 0; + const std::string::const_iterator end = s.end(); + for (std::string::const_iterator i = s.begin(); i != end; std::advance(i, read)) + { + T t; + read = tools::read_varint(std::string::const_iterator(i), s.end(), t); + CHECK_AND_ASSERT_THROW_MES(read > 0 && read <= 256, "Error decompressing data"); + v.push_back(t); + } + return v; + } +} namespace cryptonote { @@ -50,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 1 +#define CORE_RPC_VERSION_MINOR 2 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -697,9 +731,11 @@ namespace cryptonote struct request { std::vector<get_outputs_out> outputs; + bool get_txid; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outputs) + KV_SERIALIZE_OPT(get_txid, true) END_KV_SERIALIZE_MAP() }; @@ -892,6 +928,7 @@ namespace cryptonote bool was_bootstrap_ever_used; uint64_t database_size; bool update_available; + std::string version; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -926,6 +963,7 @@ namespace cryptonote KV_SERIALIZE(was_bootstrap_ever_used) KV_SERIALIZE(database_size) KV_SERIALIZE(update_available) + KV_SERIALIZE(version) END_KV_SERIALIZE_MAP() }; }; @@ -2222,6 +2260,7 @@ namespace cryptonote uint64_t to_height; bool cumulative; bool binary; + bool compress; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts) @@ -2229,6 +2268,7 @@ namespace cryptonote KV_SERIALIZE_OPT(to_height, (uint64_t)0) KV_SERIALIZE_OPT(cumulative, false) KV_SERIALIZE_OPT(binary, true) + KV_SERIALIZE_OPT(compress, false) END_KV_SERIALIZE_MAP() }; @@ -2236,14 +2276,38 @@ namespace cryptonote { rpc::output_distribution_data data; uint64_t amount; + std::string compressed_data; bool binary; + bool compress; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) KV_SERIALIZE_N(data.start_height, "start_height") KV_SERIALIZE(binary) + KV_SERIALIZE(compress) if (this_ref.binary) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + { + if (is_store) + { + if (this_ref.compress) + { + const_cast<std::string&>(this_ref.compressed_data) = compress_integer_array(this_ref.data.distribution); + KV_SERIALIZE(compressed_data) + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + else + { + if (this_ref.compress) + { + KV_SERIALIZE(compressed_data) + const_cast<std::vector<uint64_t>&>(this_ref.data.distribution) = decompress_integer_array<uint64_t>(this_ref.compressed_data); + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + } else KV_SERIALIZE_N(data.distribution, "distribution") KV_SERIALIZE_N(data.base, "base") @@ -2264,4 +2328,27 @@ namespace cryptonote }; }; + struct COMMAND_RPC_POP_BLOCKS + { + struct request + { + uint64_t nblocks; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(nblocks); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + } diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 8822bd378..e2885dbb5 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -34,6 +34,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/blobdatatype.h" #include "ringct/rctSigs.h" +#include "version.h" namespace cryptonote { @@ -437,6 +438,7 @@ namespace rpc res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.info.start_time = (uint64_t)m_core.get_start_time(); + res.info.version = MONERO_VERSION; res.status = Message::STATUS_OK; res.error_details = ""; @@ -751,7 +753,7 @@ namespace rpc const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (std::uint64_t amount : req.amounts) { - auto data = get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { res.distributions.clear(); diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index fa848cff4..7c7442014 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -177,8 +177,6 @@ rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes); return val; @@ -193,8 +191,6 @@ rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) con { rapidjson::Value val(rapidjson::kObjectType); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, txs, txs); INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes); @@ -212,8 +208,6 @@ rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); return val; @@ -228,8 +222,6 @@ rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status); return val; @@ -245,8 +237,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash); return val; @@ -261,8 +251,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); return val; @@ -277,8 +265,6 @@ rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx, tx); INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); @@ -295,8 +281,6 @@ rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed); return val; @@ -312,8 +296,6 @@ rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address); INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining); @@ -372,8 +354,6 @@ rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, active, active); INSERT_INTO_JSON_OBJECT(val, doc, speed, speed); INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); @@ -406,8 +386,6 @@ rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, info, info); return val; @@ -423,8 +401,6 @@ rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -436,8 +412,6 @@ rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -450,8 +424,6 @@ rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, height, height); return val; @@ -466,8 +438,6 @@ rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); return val; @@ -483,8 +453,6 @@ rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) c { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -496,8 +464,6 @@ rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -513,8 +479,6 @@ rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); return val; @@ -529,8 +493,6 @@ rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -546,8 +508,6 @@ rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& do { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, height, height); return val; @@ -562,8 +522,6 @@ rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& d { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -579,8 +537,6 @@ rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& d { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, heights, heights); return val; @@ -595,8 +551,6 @@ rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, headers, headers); return val; @@ -612,8 +566,6 @@ rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -625,8 +577,6 @@ rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list); INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list); @@ -679,8 +629,6 @@ rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions); INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); @@ -698,8 +646,6 @@ rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, version, version); return val; @@ -714,8 +660,6 @@ rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, info, info); return val; @@ -731,8 +675,6 @@ rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) c { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count); INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count); @@ -755,8 +697,6 @@ rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram); return val; @@ -772,8 +712,6 @@ rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs); return val; @@ -788,8 +726,6 @@ rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, keys, keys); return val; @@ -814,8 +750,6 @@ rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, version, version); return val; @@ -830,8 +764,6 @@ rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks); return val; @@ -846,8 +778,6 @@ rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee); INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask); INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale); @@ -867,7 +797,6 @@ void GetFeeEstimate::Response::fromJson(rapidjson::Value& val) rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height); @@ -888,7 +817,6 @@ void GetOutputDistribution::Request::fromJson(rapidjson::Value& val) rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); INSERT_INTO_JSON_OBJECT(val, doc, status, status); INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions); diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 3b56aff15..e09b6749e 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -191,6 +191,7 @@ namespace rpc uint64_t block_size_median; uint64_t block_weight_median; uint64_t start_time; + std::string version; }; struct output_distribution diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp index d4beb1928..e0a81c70f 100644 --- a/src/rpc/rpc_handler.cpp +++ b/src/rpc/rpc_handler.cpp @@ -26,7 +26,7 @@ namespace rpc } boost::optional<output_distribution_data> - RpcHandler::get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative) + RpcHandler::get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative) { static struct D { @@ -43,8 +43,25 @@ namespace rpc std::vector<std::uint64_t> distribution; std::uint64_t start_height, base; - if (!src.get_output_distribution(amount, from_height, to_height, start_height, distribution, base)) - return boost::none; + + // see if we can extend the cache - a common case + if (d.cached && amount == 0 && d.cached_from == from_height && to_height > d.cached_to) + { + std::vector<std::uint64_t> new_distribution; + if (!f(amount, d.cached_to + 1, to_height, start_height, new_distribution, base)) + return boost::none; + distribution = d.cached_distribution; + distribution.reserve(distribution.size() + new_distribution.size()); + for (const auto &e: new_distribution) + distribution.push_back(e); + start_height = d.cached_start_height; + base = d.cached_base; + } + else + { + if (!f(amount, from_height, to_height, start_height, distribution, base)) + return boost::none; + } if (to_height > 0 && to_height >= from_height) { diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index 3cccef78a..e0d520408 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -56,7 +56,7 @@ class RpcHandler virtual std::string handle(const std::string& request) = 0; static boost::optional<output_distribution_data> - get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative); + get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative); }; diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index edd3e6669..a2ff76668 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "zmq_server.h" -#include <boost/chrono/chrono.hpp> namespace cryptonote { diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index f906b5d3b..04436c21c 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -113,7 +113,7 @@ struct json_archive; template <> struct json_archive<true> : public json_archive_base<std::ostream, true> { - json_archive(stream_type &s, bool indent = false) : base_type(s, indent) { } + json_archive(stream_type &s, bool indent = false) : base_type(s, indent), inner_array_size_(0) { } template<typename T> static auto promote_to_printable_integer_type(T v) -> decltype(+v) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e..281702774 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -65,6 +65,7 @@ #include "wallet/wallet_args.h" #include "version.h" #include <stdexcept> +#include "wallet/message_store.h" #ifdef WIN32 #include <boost/locale.hpp> @@ -102,12 +103,14 @@ typedef cryptonote::simple_wallet sw; m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ }) -#define SCOPED_WALLET_UNLOCK() \ +#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ LOCK_IDLE_SCOPE(); \ boost::optional<tools::password_container> pwd_container = boost::none; \ - if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { return true; } \ + if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); +#define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) + enum TransferType { Transfer, TransferLocked, @@ -836,65 +839,83 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std: bool simple_wallet::prepare_multisig(const std::vector<std::string> &args) { + prepare_multisig_main(args, false); + return true; +} + +bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); - return true; + return false; } if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return true; + return false; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); - return true; + return false; } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string multisig_info = m_wallet->get_multisig_info(); success_msg_writer() << multisig_info; success_msg_writer() << tr("Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info"); success_msg_writer() << tr("This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants "); + + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::key_set, multisig_info); + } + return true; } bool simple_wallet::make_multisig(const std::vector<std::string> &args) { + make_multisig_main(args, false); + return true; +} + +bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); - return true; + return false; } if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return true; + return false; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); - return true; + return false; } if (args.size() < 2) { fail_msg_writer() << tr("usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...]"); - return true; + return false; } // parse threshold @@ -902,14 +923,14 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args) if (!string_tools::get_xtype_from_string(threshold, args[0])) { fail_msg_writer() << tr("Invalid threshold"); - return true; + return false; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return true; + return false; } LOCK_IDLE_SCOPE(); @@ -924,20 +945,24 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args) success_msg_writer() << tr("Another step is needed"); success_msg_writer() << multisig_extra_info; success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info"); + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info); + } return true; } } catch (const std::exception &e) { fail_msg_writer() << tr("Error creating multisig: ") << e.what(); - return true; + return false; } uint32_t total; if (!m_wallet->multisig(NULL, &threshold, &total)) { fail_msg_writer() << tr("Error creating multisig: new wallet is not multisig"); - return true; + return false; } success_msg_writer() << std::to_string(threshold) << "/" << total << tr(" multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); @@ -997,35 +1022,41 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args) return true; } -bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) { +bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) +{ + exchange_multisig_keys_main(args, false); + return true; +} + +bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms) { bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (ready) { fail_msg_writer() << tr("This wallet is already finalized"); - return true; + return false; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return true; + return false; } if (args.size() < 2) { fail_msg_writer() << tr("usage: exchange_multisig_keys <multisiginfo1> [<multisiginfo2>...]"); - return true; + return false; } try @@ -1036,6 +1067,10 @@ bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) message_writer() << tr("Another step is needed"); message_writer() << multisig_extra_info; message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info"); + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info); + } return true; } else { uint32_t threshold, total; @@ -1046,7 +1081,7 @@ bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) catch (const std::exception &e) { fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what(); - return true; + return false; } return true; @@ -1054,50 +1089,63 @@ bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) bool simple_wallet::export_multisig(const std::vector<std::string> &args) { + export_multisig_main(args, false); + return true; +} + +bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: export_multisig_info <filename>"); - return true; + return false; } const std::string filename = args[0]; - if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + if (!called_by_mms && m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); try { cryptonote::blobdata ciphertext = m_wallet->export_multisig(); - bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("failed to save file ") << filename; - return true; + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::multisig_sync_data, ciphertext); + } + else + { + bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); + if (!r) + { + fail_msg_writer() << tr("failed to save file ") << filename; + return false; + } } } catch (const std::exception &e) { LOG_ERROR("Error exporting multisig info: " << e.what()); fail_msg_writer() << tr("Error exporting multisig info: ") << e.what(); - return true; + return false; } success_msg_writer() << tr("Multisig info exported to ") << filename; @@ -1106,44 +1154,57 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args) bool simple_wallet::import_multisig(const std::vector<std::string> &args) { + import_multisig_main(args, false); + return true; +} + +bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ bool ready; uint32_t threshold, total; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready, &threshold, &total)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() < threshold - 1) { fail_msg_writer() << tr("usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant"); - return true; + return false; } std::vector<cryptonote::blobdata> info; for (size_t n = 0; n < args.size(); ++n) { - const std::string filename = args[n]; - std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("failed to read file ") << filename; - return true; + info.push_back(args[n]); + } + else + { + const std::string &filename = args[n]; + std::string data; + bool r = epee::file_io_utils::load_file_to_string(filename, data); + if (!r) + { + fail_msg_writer() << tr("failed to read file ") << filename; + return false; + } + info.push_back(std::move(data)); } - info.push_back(std::move(data)); } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); // all read and parsed, actually import try @@ -1158,7 +1219,7 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args) catch (const std::exception &e) { fail_msg_writer() << tr("Failed to import multisig info: ") << e.what(); - return true; + return false; } if (m_wallet->is_trusted_daemon()) { @@ -1169,11 +1230,13 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args) catch (const std::exception &e) { message_writer() << tr("Failed to update spent status after importing multisig info: ") << e.what(); + return false; } } else { message_writer() << tr("Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run \"rescan_spent\""); + return false; } return true; } @@ -1186,51 +1249,93 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs) bool simple_wallet::sign_multisig(const std::vector<std::string> &args) { + sign_multisig_main(args, false); + return true; +} + +bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if(!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This is not a multisig wallet"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: sign_multisig <filename>"); - return true; + return false; } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string filename = args[0]; std::vector<crypto::hash> txids; uint32_t signers = 0; try { - bool r = m_wallet->sign_multisig_tx_from_file(filename, txids, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("Failed to sign multisig transaction"); - return true; + tools::wallet2::multisig_tx_set exported_txs; + std::string ciphertext; + bool r = m_wallet->load_multisig_tx(args[0], exported_txs, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); + if (r) + { + r = m_wallet->sign_multisig_tx(exported_txs, txids); + } + if (r) + { + ciphertext = m_wallet->save_multisig_tx(exported_txs); + if (ciphertext.empty()) + { + r = false; + } + } + if (r) + { + mms::message_type message_type = mms::message_type::fully_signed_tx; + if (txids.empty()) + { + message_type = mms::message_type::partially_signed_tx; + } + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), message_type, ciphertext); + filename = "MMS"; // for the messages below + } + else + { + fail_msg_writer() << tr("Failed to sign multisig transaction"); + return false; + } + } + else + { + bool r = m_wallet->sign_multisig_tx_from_file(filename, txids, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to sign multisig transaction"); + return false; + } } } catch (const tools::error::multisig_export_needed& e) { fail_msg_writer() << tr("Multisig error: ") << e.what(); - return true; + return false; } catch (const std::exception &e) { fail_msg_writer() << tr("Failed to sign multisig transaction: ") << e.what(); - return true; + return false; } if (txids.empty()) @@ -1259,49 +1364,67 @@ bool simple_wallet::sign_multisig(const std::vector<std::string> &args) bool simple_wallet::submit_multisig(const std::vector<std::string> &args) { + submit_multisig_main(args, false); + return true; +} + +bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, bool called_by_mms) +{ bool ready; uint32_t threshold; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready, &threshold)) { fail_msg_writer() << tr("This is not a multisig wallet"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: submit_multisig <filename>"); - return true; + return false; } if (!try_connect_to_daemon()) - return true; + return false; - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string filename = args[0]; try { tools::wallet2::multisig_tx_set txs; - bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("Failed to load multisig transaction from file"); - return true; + bool r = m_wallet->load_multisig_tx(args[0], txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to load multisig transaction from MMS"); + return false; + } + } + else + { + bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to load multisig transaction from file"); + return false; + } } if (txs.m_signers.size() < threshold) { fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures")) % txs.m_signers.size() % (threshold - txs.m_signers.size())).str(); - return true; + return false; } // actually commit the transactions @@ -1320,6 +1443,7 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args) { LOG_ERROR("unknown error"); fail_msg_writer() << tr("unknown error"); + return false; } return true; @@ -1409,7 +1533,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>"); + fail_msg_writer() << tr("usage: print_ring <key_image> | <txid>"); return true; } @@ -2308,6 +2432,12 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st { success_msg_writer() << get_commands_str(); } + else if ((args.size() == 2) && (args.front() == "mms")) + { + // Little hack to be able to do "help mms <subcommand>" + std::vector<std::string> mms_args(1, args.front() + " " + args.back()); + success_msg_writer() << get_command_usage(mms_args); + } else { success_msg_writer() << get_command_usage(args); @@ -2348,7 +2478,9 @@ simple_wallet::simple_wallet() 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("Show the incoming transfers, all or filtered by availability and address index.")); + 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>]"), @@ -2653,10 +2785,95 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::export_raw_multisig, this, _1), tr("export_raw_multisig_tx <filename>"), 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("Interface with the MMS (Multisig Messaging System)\n" + "<subcommand> is one of:\n" + " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" + " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" + "Get help about a subcommand with: help 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("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("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("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("List all messages")); + m_cmd_binder.set_handler("mms next", + boost::bind(&simple_wallet::mms, this, _1), + tr("mms next [sync]"), + 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("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("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("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("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("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("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("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("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("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("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("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("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("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("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); + 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>...] )"), @@ -3786,6 +4003,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr { auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); + m_wallet->callback(this); if (!m_wallet) { return {}; @@ -3803,9 +4021,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr m_wallet->set_refresh_from_block_height(m_restore_height); auto device_desc = tools::wallet2::device_name_option(vm); + auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm); try { bool create_address_file = command_line::get_arg(vm, arg_create_address_file); + m_wallet->device_derivation_path(device_derivation_path); m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); @@ -3893,7 +4113,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) epee::wipeable_string password; try { - auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter); + auto rc = tools::wallet2::make_from_file(vm, false, "", password_prompter); m_wallet = std::move(rc.first); password = std::move(std::move(rc.second).password()); if (!m_wallet) @@ -3901,6 +4121,8 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) return false; } + m_wallet->callback(this); + m_wallet->load(m_wallet_file, password); std::string prefix; bool ready; uint32_t threshold, total; @@ -4304,6 +4526,61 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- +void simple_wallet::on_button_request() +{ + message_writer(console_color_white, false) << tr("Device requires attention"); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::on_pin_request(epee::wipeable_string & pin) +{ +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::string msg = tr("Enter device PIN"); + auto pwd_container = tools::password_container::prompt(false, msg.c_str()); + THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN")); + pin = pwd_container->password(); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (on_device){ + message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device"); + return; + } + +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::string msg = tr("Enter device passphrase"); + auto pwd_container = tools::password_container::prompt(false, msg.c_str()); + THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase")); + passphrase = pwd_container->password(); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money) +{ + // Key image sync after the first refresh + if (!m_wallet->get_account().get_device().has_tx_cold_sign()) { + return; + } + + if (!received_money || m_wallet->get_device_last_key_image_sync() != 0) { + return; + } + + // Finished first refresh for HW device and money received -> KI sync + message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received money. hw_key_images_sync is needed. "); + + std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): ")); + if (std::cin.eof() || !command_line::is_yes(accepted)) { + message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer."); + return; + } + + key_images_sync_intern(); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init) { if (!try_connect_to_daemon(is_init)) @@ -4321,13 +4598,14 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo message_writer() << tr("Starting refresh..."); uint64_t fetched_blocks = 0; + bool received_money = false; bool ok = false; std::ostringstream ss; try { m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks); + m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money); ok = true; // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -4335,6 +4613,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo if (is_init) print_accounts(); show_balance_unlocked(); + on_refresh_finished(start_height, fetched_blocks, is_init, received_money); } catch (const tools::error::daemon_busy&) { @@ -4440,7 +4719,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=<N>]"); + fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"); return true; } auto local_args = args; @@ -4482,7 +4761,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=<N>]"); + fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"); return true; } @@ -4777,11 +5056,11 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_) +bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" if (!try_connect_to_daemon()) - return true; + return false; std::vector<std::string> local_args = args_; @@ -4789,7 +5068,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { if (!parse_subaddress_indices(local_args[0], subaddr_indices)) - return true; + return false; local_args.erase(local_args.begin()); } @@ -4811,7 +5090,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri else if (ring_size == 0) { fail_msg_writer() << tr("Ring size must not be 0"); - return true; + return false; } else { @@ -4823,19 +5102,19 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (adjusted_fake_outs_count > fake_outs_count) { fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); - return true; + return false; } if (adjusted_fake_outs_count < fake_outs_count) { fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); - return true; + return false; } const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1; if(local_args.size() < min_args) { fail_msg_writer() << tr("wrong number of arguments"); - return true; + return false; } std::vector<uint8_t> extra; @@ -4870,7 +5149,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if(!r) { fail_msg_writer() << tr("payment id failed to encode"); - return true; + return false; } } @@ -4884,12 +5163,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri catch (const std::exception &e) { fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back(); - return true; + return false; } if (locked_blocks > 1000000) { fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); - return true; + return false; } local_args.pop_back(); } @@ -4917,7 +5196,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id)) { fail_msg_writer() << tr("failed to parse short payment ID from URI"); - return true; + return false; } info.has_payment_id = true; } @@ -4932,7 +5211,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max()); - return true; + return false; } i += 2; } @@ -4942,13 +5221,13 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri fail_msg_writer() << tr("Invalid last argument: ") << local_args.back() << ": " << error; else fail_msg_writer() << tr("Invalid last argument: ") << local_args.back(); - return true; + return false; } if (!r) { fail_msg_writer() << tr("failed to parse address"); - return true; + return false; } de.addr = info.address; de.is_subaddress = info.is_subaddress; @@ -4959,7 +5238,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (payment_id_seen) { fail_msg_writer() << tr("a single transaction cannot use more than one payment id"); - return true; + return false; } crypto::hash payment_id; @@ -4976,13 +5255,13 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri else { fail_msg_writer() << tr("failed to parse payment id, though it was detected"); - return true; + return false; } bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce); if(!r) { fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly"); - return true; + return false; } payment_id_seen = true; } @@ -4995,16 +5274,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) - return true; + return false; if (!command_line::is_yes(accepted)) { fail_msg_writer() << tr("transaction cancelled."); - return true; + return false; } } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); try { @@ -5019,7 +5298,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (!err.empty()) { fail_msg_writer() << tr("failed to get blockchain height: ") << err; - return true; + return false; } unlock_block = bc_height + locked_blocks; ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); @@ -5035,7 +5314,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (ptx_vector.empty()) { fail_msg_writer() << tr("No outputs found, or daemon is not ready"); - return true; + return false; } // if we need to check for backlog, check the worst case tx @@ -5075,12 +5354,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { std::string accepted = input_line(prompt_str); if (std::cin.eof()) - return true; + return false; if (!command_line::is_yes(accepted)) { fail_msg_writer() << tr("transaction cancelled."); - return true; + return false; } } } @@ -5140,7 +5419,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (m_wallet->print_ring_members()) { if (!print_ring_members(ptx_vector, prompt)) - return true; + return false; } bool default_ring_size = true; for (const auto &ptx: ptx_vector) @@ -5163,22 +5442,32 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri std::string accepted = input_line(prompt.str()); if (std::cin.eof()) - return true; + return false; if (!command_line::is_yes(accepted)) { fail_msg_writer() << tr("transaction cancelled."); - return true; + return false; } } // actually commit the transactions - if (m_wallet->multisig()) + if (m_wallet->multisig() && called_by_mms) + { + std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector); + if (!ciphertext.empty()) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::partially_signed_tx, ciphertext); + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to MMS"); + } + } + else if (m_wallet->multisig()) { bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); + return false; } else { @@ -5192,7 +5481,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri tools::wallet2::signed_tx_set signed_tx; if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){ fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet"); - return true; + return false; } commit_or_save(signed_tx.ptx, m_do_not_relay); @@ -5200,11 +5489,13 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri catch (const std::exception& e) { handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); + return false; } catch (...) { LOG_ERROR("Unknown error"); fail_msg_writer() << tr("unknown error"); + return false; } } else if (m_wallet->watch_only()) @@ -5213,6 +5504,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); + return false; } else { @@ -5227,11 +5519,13 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri catch (const std::exception &e) { handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); + return false; } catch (...) { LOG_ERROR("unknown error"); fail_msg_writer() << tr("unknown error"); + return false; } return true; @@ -5239,12 +5533,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector<std::string> &args_) { - return transfer_main(Transfer, args_); + transfer_main(Transfer, args_, false); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) { - return transfer_main(TransferLocked, args_); + transfer_main(TransferLocked, args_, false); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) @@ -5942,12 +6238,6 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) { - if(m_wallet->nettype() != cryptonote::MAINNET) - { - fail_msg_writer() << tr("donations are not enabled on the testnet or on the stagenet"); - return true; - } - std::vector<std::string> local_args = args_; if(local_args.empty() || local_args.size() > 5) { @@ -5969,11 +6259,30 @@ bool simple_wallet::donate(const std::vector<std::string> &args_) amount_str = local_args.back(); local_args.pop_back(); // push back address, amount, payment id - local_args.push_back(MONERO_DONATION_ADDR); + std::string address_str; + if (m_wallet->nettype() != cryptonote::MAINNET) + { + // if not mainnet, convert donation address string to the relevant network type + address_parse_info info; + if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, MONERO_DONATION_ADDR)) + { + fail_msg_writer() << tr("Failed to parse donation address: ") << MONERO_DONATION_ADDR; + return true; + } + address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address); + } + else + { + address_str = MONERO_DONATION_ADDR; + } + local_args.push_back(address_str); local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); + if (m_wallet->nettype() == cryptonote::MAINNET) + message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); + else + message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str(); transfer(local_args); return true; } @@ -6720,24 +7029,6 @@ bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- -static std::string get_human_readable_timestamp(uint64_t ts) -{ - char buffer[64]; - if (ts < 1234567890) - return "<unknown>"; - time_t tt = ts; - struct tm tm; -#ifdef WIN32 - gmtime_s(&tm, &tt); -#else - gmtime_r(&tt, &tm); -#endif - uint64_t now = time(NULL); - uint64_t diff = ts > now ? ts - now : now - ts; - strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); - return std::string(buffer); -} -//---------------------------------------------------------------------------------------------------- static std::string get_human_readable_timespan(std::chrono::seconds seconds) { uint64_t ts = seconds.count(); @@ -6844,7 +7135,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec std::string note = m_wallet->get_tx_note(pd.m_tx_hash); std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor}); const std::string type = pd.m_coinbase ? tr("block") : tr("in"); - const bool unlocked = m_wallet->is_tx_spendtime_unlocked(pd.m_unlock_time, pd.m_block_height); + const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); transfers.push_back({ pd.m_block_height, pd.m_timestamp, @@ -7022,7 +7313,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) % transfer.block % transfer.direction % transfer.unlocked - % get_human_readable_timestamp(transfer.timestamp) + % tools::get_human_readable_timestamp(transfer.timestamp) % print_money(transfer.amount) % string_tools::pod_to_hex(transfer.hash) % transfer.payment_id @@ -7086,7 +7377,7 @@ bool simple_wallet::export_transfers(const std::vector<std::string>& args_) % transfer.block % transfer.direction % transfer.unlocked - % get_human_readable_timestamp(transfer.timestamp) + % tools::get_human_readable_timestamp(transfer.timestamp) % print_money(transfer.amount) % print_money(running_balance) % string_tools::pod_to_hex(transfer.hash) @@ -7289,6 +7580,22 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_) return refresh_main(0, hard ? ResetHard : ResetSoft, true); } //---------------------------------------------------------------------------------------------------- +void simple_wallet::check_for_messages() +{ + try + { + std::vector<mms::message> new_messages; + bool new_message = get_message_store().check_for_messages(get_multisig_wallet_state(), new_messages); + if (new_message) + { + message_writer(console_color_magenta, true) << tr("MMS received new message"); + list_mms_messages(new_messages); + m_cmd_binder.print_prompt(); + } + } + catch(...) {} +} +//---------------------------------------------------------------------------------------------------- void simple_wallet::wallet_idle_thread() { while (true) @@ -7311,6 +7618,14 @@ void simple_wallet::wallet_idle_thread() m_auto_refresh_refreshing = false; } + // Check for new MMS messages; + // For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no + // separate thread either; thread syncing is tricky enough with only this one idle thread here + if (m_auto_refresh_enabled && get_message_store().get_active()) + { + check_for_messages(); + } + if (!m_idle_run.load(std::memory_order_relaxed)) break; m_idle_cond.wait_for(lock, boost::chrono::seconds(90)); @@ -8097,13 +8412,13 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args) fail_msg_writer() << tr("hw wallet does not support cold KI sync"); return true; } - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); - return true; - } LOCK_IDLE_SCOPE(); + key_images_sync_intern(); + return true; +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::key_images_sync_intern(){ try { message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device"); @@ -8112,19 +8427,23 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args) uint64_t height = m_wallet->cold_key_image_sync(spent, unspent); if (height > 0) { - success_msg_writer() << tr("Signed key images imported to height ") << height << ", " - << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent"); - } else { + success_msg_writer() << tr("Key images synchronized to height ") << height; + if (!m_wallet->is_trusted_daemon()) + { + message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent"); + } else + { + success_msg_writer() << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent"); + } + } + else { fail_msg_writer() << tr("Failed to import key images"); } } catch (const std::exception &e) { fail_msg_writer() << tr("Failed to import key images: ") << e.what(); - return true; } - - return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::hw_reconnect(const std::vector<std::string> &args) @@ -8258,7 +8577,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) success_msg_writer() << "Incoming transaction found"; success_msg_writer() << "txid: " << txid; success_msg_writer() << "Height: " << pd.m_block_height; - success_msg_writer() << "Timestamp: " << get_human_readable_timestamp(pd.m_timestamp); + success_msg_writer() << "Timestamp: " << tools::get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(pd.m_amount); success_msg_writer() << "Payment ID: " << payment_id; if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) @@ -8308,7 +8627,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) success_msg_writer() << "Outgoing transaction found"; success_msg_writer() << "txid: " << txid; success_msg_writer() << "Height: " << pd.m_block_height; - success_msg_writer() << "Timestamp: " << get_human_readable_timestamp(pd.m_timestamp); + success_msg_writer() << "Timestamp: " << tools::get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(pd.m_amount_in - change - fee); success_msg_writer() << "Payment ID: " << payment_id; success_msg_writer() << "Change: " << print_money(change); @@ -8333,7 +8652,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) payment_id = payment_id.substr(0,16); success_msg_writer() << "Unconfirmed incoming transaction found in the txpool"; success_msg_writer() << "txid: " << txid; - success_msg_writer() << "Timestamp: " << get_human_readable_timestamp(pd.m_timestamp); + success_msg_writer() << "Timestamp: " << tools::get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(pd.m_amount); success_msg_writer() << "Payment ID: " << payment_id; success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor; @@ -8364,7 +8683,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) success_msg_writer() << (is_failed ? "Failed" : "Pending") << " outgoing transaction found"; success_msg_writer() << "txid: " << txid; - success_msg_writer() << "Timestamp: " << get_human_readable_timestamp(pd.m_timestamp); + success_msg_writer() << "Timestamp: " << tools::get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(amount - pd.m_change - fee); success_msg_writer() << "Payment ID: " << payment_id; success_msg_writer() << "Change: " << print_money(pd.m_change); @@ -8524,3 +8843,1009 @@ int main(int argc, char* argv[]) return 0; CATCH_ENTRY_L0("main", 1); } + +// MMS --------------------------------------------------------------------------------------------------- + +// Access to the message store, or more exactly to the list of the messages that can be changed +// by the idle thread, is guarded by the same mutex-based mechanism as access to the wallet +// as a whole and thus e.g. uses the "LOCK_IDLE_SCOPE" macro. This is a little over-cautious, but +// simple and safe. Care has to be taken however where MMS methods call other simplewallet methods +// that use "LOCK_IDLE_SCOPE" as this cannot be nested! + +// Methods for commands like "export_multisig_info" usually read data from file(s) or write data +// to files. The MMS calls now those methods as well, to produce data for messages and to process data +// from messages. As it would be quite inconvenient for the MMS to write data for such methods to files +// first or get data out of result files after the call, those methods detect a call from the MMS and +// expect data as arguments instead of files and give back data by calling 'process_wallet_created_data'. + +bool simple_wallet::user_confirms(const std::string &question) +{ + std::string answer = input_line(question + tr(" (Y/Yes/N/No): ")); + return !std::cin.eof() && command_line::is_yes(answer); +} + +bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound) +{ + bool valid = false; + try + { + number = boost::lexical_cast<uint32_t>(arg); + valid = (number >= lower_bound) && (number <= upper_bound); + } + catch(const boost::bad_lexical_cast &) + { + } + return valid; +} + +bool simple_wallet::choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice) +{ + size_t choices = data_list.size(); + if (choices == 1) + { + choice = 0; + return true; + } + mms::message_store& ms = m_wallet->get_message_store(); + message_writer() << tr("Choose processing:"); + std::string text; + for (size_t i = 0; i < choices; ++i) + { + const mms::processing_data &data = data_list[i]; + text = std::to_string(i+1) + ": "; + switch (data.processing) + { + case mms::message_processing::sign_tx: + text += tr("Sign tx"); + break; + case mms::message_processing::send_tx: + { + mms::message m; + ms.get_message_by_id(data.message_ids[0], m); + if (m.type == mms::message_type::fully_signed_tx) + { + text += tr("Send the tx for submission to "); + } + else + { + text += tr("Send the tx for signing to "); + } + mms::authorized_signer signer = ms.get_signer(data.receiving_signer_index); + text += ms.signer_to_string(signer, 50); + break; + } + case mms::message_processing::submit_tx: + text += tr("Submit tx"); + break; + default: + text += tr("unknown"); + break; + } + message_writer() << text; + } + + std::string line = input_line(tr("Choice: ")); + if (std::cin.eof() || line.empty()) + { + return false; + } + bool choice_ok = get_number_from_arg(line, choice, 1, choices); + if (choice_ok) + { + choice--; + } + else + { + fail_msg_writer() << tr("Wrong choice"); + } + return choice_ok; +} + +void simple_wallet::list_mms_messages(const std::vector<mms::message> &messages) +{ + message_writer() << boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") % tr("Id") % tr("I/O") % tr("Authorized Signer") + % tr("Message Type") % tr("Height") % tr("R") % tr("Message State") % tr("Since"); + mms::message_store& ms = m_wallet->get_message_store(); + uint64_t now = (uint64_t)time(NULL); + for (size_t i = 0; i < messages.size(); ++i) + { + const mms::message &m = messages[i]; + const mms::authorized_signer &signer = ms.get_signer(m.signer_index); + bool highlight = (m.state == mms::message_state::ready_to_send) || (m.state == mms::message_state::waiting); + message_writer(m.direction == mms::message_direction::out ? console_color_green : console_color_magenta, highlight) << + boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") % + m.id % + ms.message_direction_to_string(m.direction) % + ms.signer_to_string(signer, 30) % + ms.message_type_to_string(m.type) % + m.wallet_height % + m.round % + ms.message_state_to_string(m.state) % + (tools::get_human_readable_timestamp(m.modified) + ", " + get_human_readable_timespan(std::chrono::seconds(now - m.modified)) + tr(" ago")); + } +} + +void simple_wallet::list_signers(const std::vector<mms::authorized_signer> &signers) +{ + message_writer() << boost::format("%2s %-20s %-s") % tr("#") % tr("Label") % tr("Transport Address"); + message_writer() << boost::format("%2s %-20s %-s") % "" % tr("Auto-Config Token") % tr("Monero Address"); + for (size_t i = 0; i < signers.size(); ++i) + { + const mms::authorized_signer &signer = signers[i]; + std::string label = signer.label.empty() ? tr("<not set>") : signer.label; + std::string monero_address; + if (signer.monero_address_known) + { + monero_address = get_account_address_as_str(m_wallet->nettype(), false, signer.monero_address); + } + else + { + monero_address = tr("<not set>"); + } + std::string transport_address = signer.transport_address.empty() ? tr("<not set>") : signer.transport_address; + message_writer() << boost::format("%2s %-20s %-s") % (i + 1) % label % transport_address; + message_writer() << boost::format("%2s %-20s %-s") % "" % signer.auto_config_token % monero_address; + message_writer() << ""; + } +} + +void simple_wallet::add_signer_config_messages() +{ + mms::message_store& ms = m_wallet->get_message_store(); + std::string signer_config; + ms.get_signer_config(signer_config); + + const std::vector<mms::authorized_signer> signers = ms.get_all_signers(); + mms::multisig_wallet_state state = get_multisig_wallet_state(); + uint32_t num_authorized_signers = ms.get_num_authorized_signers(); + for (uint32_t i = 1 /* without me */; i < num_authorized_signers; ++i) + { + ms.add_message(state, i, mms::message_type::signer_config, mms::message_direction::out, signer_config); + } +} + +void simple_wallet::show_message(const mms::message &m) +{ + mms::message_store& ms = m_wallet->get_message_store(); + const mms::authorized_signer &signer = ms.get_signer(m.signer_index); + bool display_content; + std::string sanitized_text; + switch (m.type) + { + case mms::message_type::key_set: + case mms::message_type::additional_key_set: + case mms::message_type::note: + display_content = true; + ms.get_sanitized_message_text(m, sanitized_text); + break; + default: + display_content = false; + } + uint64_t now = (uint64_t)time(NULL); + message_writer() << ""; + message_writer() << tr("Message ") << m.id; + message_writer() << tr("In/out: ") << ms.message_direction_to_string(m.direction); + message_writer() << tr("Type: ") << ms.message_type_to_string(m.type); + message_writer() << tr("State: ") << boost::format(tr("%s since %s, %s ago")) % + ms.message_state_to_string(m.state) % tools::get_human_readable_timestamp(m.modified) % get_human_readable_timespan(std::chrono::seconds(now - m.modified)); + if (m.sent == 0) + { + message_writer() << tr("Sent: Never"); + } + else + { + message_writer() << boost::format(tr("Sent: %s, %s ago")) % + tools::get_human_readable_timestamp(m.sent) % get_human_readable_timespan(std::chrono::seconds(now - m.sent)); + } + message_writer() << tr("Authorized signer: ") << ms.signer_to_string(signer, 100); + message_writer() << tr("Content size: ") << m.content.length() << tr(" bytes"); + message_writer() << tr("Content: ") << (display_content ? sanitized_text : tr("(binary data)")); + + if (m.type == mms::message_type::note) + { + // Showing a note and read its text is "processing" it: Set the state accordingly + // which will also delete it from Bitmessage as a side effect + // (Without this little "twist" it would never change the state, and never get deleted) + ms.set_message_processed_or_sent(m.id); + } +} + +void simple_wallet::ask_send_all_ready_messages() +{ + mms::message_store& ms = m_wallet->get_message_store(); + std::vector<mms::message> ready_messages; + const std::vector<mms::message> &messages = ms.get_all_messages(); + for (size_t i = 0; i < messages.size(); ++i) + { + const mms::message &m = messages[i]; + if (m.state == mms::message_state::ready_to_send) + { + ready_messages.push_back(m); + } + } + if (ready_messages.size() != 0) + { + list_mms_messages(ready_messages); + bool send = ms.get_auto_send(); + if (!send) + { + send = user_confirms(tr("Send these messages now?")); + } + if (send) + { + mms::multisig_wallet_state state = get_multisig_wallet_state(); + for (size_t i = 0; i < ready_messages.size(); ++i) + { + ms.send_message(state, ready_messages[i].id); + ms.set_message_processed_or_sent(ready_messages[i].id); + } + success_msg_writer() << tr("Queued for sending."); + } + } +} + +bool simple_wallet::get_message_from_arg(const std::string &arg, mms::message &m) +{ + mms::message_store& ms = m_wallet->get_message_store(); + bool valid_id = false; + uint32_t id; + try + { + id = (uint32_t)boost::lexical_cast<uint32_t>(arg); + valid_id = ms.get_message_by_id(id, m); + } + catch (const boost::bad_lexical_cast &) + { + } + if (!valid_id) + { + fail_msg_writer() << tr("Invalid message id"); + } + return valid_id; +} + +void simple_wallet::mms_init(const std::vector<std::string> &args) +{ + if (args.size() != 3) + { + fail_msg_writer() << tr("usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + if (ms.get_active()) + { + if (!user_confirms(tr("The MMS is already initialized. Re-initialize by deleting all signer info and messages?"))) + { + return; + } + } + uint32_t num_required_signers; + uint32_t num_authorized_signers; + const std::string &mn = args[0]; + std::vector<std::string> numbers; + boost::split(numbers, mn, boost::is_any_of("/")); + bool mn_ok = (numbers.size() == 2) + && get_number_from_arg(numbers[1], num_authorized_signers, 2, 100) + && get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers); + if (!mn_ok) + { + fail_msg_writer() << tr("Error in the number of required signers and/or authorized signers"); + return; + } + LOCK_IDLE_SCOPE(); + ms.init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers); +} + +void simple_wallet::mms_info(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + if (ms.get_active()) + { + message_writer() << boost::format("The MMS is active for %s/%s multisig.") + % ms.get_num_required_signers() % ms.get_num_authorized_signers(); + } + else + { + message_writer() << tr("The MMS is not active."); + } +} + +void simple_wallet::mms_signer(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + const std::vector<mms::authorized_signer> &signers = ms.get_all_signers(); + if (args.size() == 0) + { + // Without further parameters list all defined signers + list_signers(signers); + return; + } + + uint32_t index; + bool index_valid = get_number_from_arg(args[0], index, 1, ms.get_num_authorized_signers()); + if (index_valid) + { + index--; + } + else + { + fail_msg_writer() << tr("Invalid signer number ") + args[0]; + return; + } + if ((args.size() < 2) || (args.size() > 4)) + { + fail_msg_writer() << tr("mms signer [<number> <label> [<transport_address> [<monero_address>]]]"); + return; + } + + boost::optional<string> label = args[1]; + boost::optional<string> transport_address; + if (args.size() >= 3) + { + transport_address = args[2]; + } + boost::optional<cryptonote::account_public_address> monero_address; + LOCK_IDLE_SCOPE(); + mms::multisig_wallet_state state = get_multisig_wallet_state(); + if (args.size() == 4) + { + cryptonote::address_parse_info info; + bool ok = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[3], oa_prompter); + if (!ok) + { + fail_msg_writer() << tr("Invalid Monero address"); + return; + } + monero_address = info.address; + const std::vector<mms::message> &messages = ms.get_all_messages(); + if ((messages.size() > 0) || state.multisig) + { + fail_msg_writer() << tr("Wallet state does not allow changing Monero addresses anymore"); + return; + } + } + ms.set_signer(state, index, label, transport_address, monero_address); +} + +void simple_wallet::mms_list(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms list"); + return; + } + LOCK_IDLE_SCOPE(); + const std::vector<mms::message> &messages = ms.get_all_messages(); + list_mms_messages(messages); +} + +void simple_wallet::mms_next(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + if ((args.size() > 1) || ((args.size() == 1) && (args[0] != "sync"))) + { + fail_msg_writer() << tr("Usage: mms next [sync]"); + return; + } + bool avail = false; + std::vector<mms::processing_data> data_list; + bool force_sync = false; + uint32_t choice = 0; + { + LOCK_IDLE_SCOPE(); + if ((args.size() == 1) && (args[0] == "sync")) + { + // Force the MMS to process any waiting sync info although on its own it would just ignore + // those messages because no need to process them can be seen + force_sync = true; + } + string wait_reason; + { + avail = ms.get_processable_messages(get_multisig_wallet_state(), force_sync, data_list, wait_reason); + } + if (avail) + { + avail = choose_mms_processing(data_list, choice); + } + else if (!wait_reason.empty()) + { + message_writer() << tr("No next step: ") << wait_reason; + } + } + if (avail) + { + mms::processing_data data = data_list[choice]; + bool command_successful = false; + switch(data.processing) + { + case mms::message_processing::prepare_multisig: + message_writer() << tr("prepare_multisig"); + command_successful = prepare_multisig_main(std::vector<std::string>(), true); + break; + + case mms::message_processing::make_multisig: + { + message_writer() << tr("make_multisig"); + size_t number_of_key_sets = data.message_ids.size(); + std::vector<std::string> sig_args(number_of_key_sets + 1); + sig_args[0] = std::to_string(ms.get_num_required_signers()); + for (size_t i = 0; i < number_of_key_sets; ++i) + { + mms::message m = ms.get_message_by_id(data.message_ids[i]); + sig_args[i+1] = m.content; + } + command_successful = make_multisig_main(sig_args, true); + break; + } + + case mms::message_processing::exchange_multisig_keys: + { + message_writer() << tr("exchange_multisig_keys"); + size_t number_of_key_sets = data.message_ids.size(); + // Other than "make_multisig" only the key sets as parameters, no num_required_signers + std::vector<std::string> sig_args(number_of_key_sets); + for (size_t i = 0; i < number_of_key_sets; ++i) + { + mms::message m = ms.get_message_by_id(data.message_ids[i]); + sig_args[i] = m.content; + } + command_successful = exchange_multisig_keys_main(sig_args, true); + break; + } + + case mms::message_processing::create_sync_data: + { + message_writer() << tr("export_multisig_info"); + std::vector<std::string> export_args; + export_args.push_back("MMS"); // dummy filename + command_successful = export_multisig_main(export_args, true); + break; + } + + case mms::message_processing::process_sync_data: + { + message_writer() << tr("import_multisig_info"); + std::vector<std::string> import_args; + for (size_t i = 0; i < data.message_ids.size(); ++i) + { + mms::message m = ms.get_message_by_id(data.message_ids[i]); + import_args.push_back(m.content); + } + command_successful = import_multisig_main(import_args, true); + break; + } + + case mms::message_processing::sign_tx: + { + message_writer() << tr("sign_multisig"); + std::vector<std::string> sign_args; + mms::message m = ms.get_message_by_id(data.message_ids[0]); + sign_args.push_back(m.content); + command_successful = sign_multisig_main(sign_args, true); + break; + } + + case mms::message_processing::submit_tx: + { + message_writer() << tr("submit_multisig"); + std::vector<std::string> submit_args; + mms::message m = ms.get_message_by_id(data.message_ids[0]); + submit_args.push_back(m.content); + command_successful = submit_multisig_main(submit_args, true); + break; + } + + case mms::message_processing::send_tx: + { + message_writer() << tr("Send tx"); + mms::message m = ms.get_message_by_id(data.message_ids[0]); + LOCK_IDLE_SCOPE(); + ms.add_message(get_multisig_wallet_state(), data.receiving_signer_index, m.type, mms::message_direction::out, + m.content); + command_successful = true; + break; + } + + case mms::message_processing::process_signer_config: + { + message_writer() << tr("Process signer config"); + LOCK_IDLE_SCOPE(); + mms::message m = ms.get_message_by_id(data.message_ids[0]); + mms::authorized_signer me = ms.get_signer(0); + mms::multisig_wallet_state state = get_multisig_wallet_state(); + if (!me.auto_config_running) + { + // If no auto-config is running, the config sent may be unsolicited or problematic + // so show what arrived and ask for confirmation before taking it in + std::vector<mms::authorized_signer> signers; + ms.unpack_signer_config(state, m.content, signers); + list_signers(signers); + if (!user_confirms(tr("Replace current signer config with the one displayed above?"))) + { + break; + } + } + ms.process_signer_config(state, m.content); + ms.stop_auto_config(); + list_signers(ms.get_all_signers()); + command_successful = true; + break; + } + + case mms::message_processing::process_auto_config_data: + { + message_writer() << tr("Process auto config data"); + LOCK_IDLE_SCOPE(); + for (size_t i = 0; i < data.message_ids.size(); ++i) + { + ms.process_auto_config_data_message(data.message_ids[i]); + } + ms.stop_auto_config(); + list_signers(ms.get_all_signers()); + add_signer_config_messages(); + command_successful = true; + break; + } + + default: + message_writer() << tr("Nothing ready to process"); + break; + } + + if (command_successful) + { + { + LOCK_IDLE_SCOPE(); + ms.set_messages_processed(data); + ask_send_all_ready_messages(); + } + } + } +} + +void simple_wallet::mms_sync(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms sync"); + return; + } + // Force the start of a new sync round, for exceptional cases where something went wrong + // Can e.g. solve the problem "This signature was made with stale data" after trying to + // create 2 transactions in a row somehow + // Code is identical to the code for 'message_processing::create_sync_data' + message_writer() << tr("export_multisig_info"); + std::vector<std::string> export_args; + export_args.push_back("MMS"); // dummy filename + export_multisig_main(export_args, true); + ask_send_all_ready_messages(); +} + +void simple_wallet::mms_transfer(const std::vector<std::string> &args) +{ + // It's too complicated to check any arguments here, just let 'transfer_main' do the whole job + transfer_main(Transfer, args, true); +} + +void simple_wallet::mms_delete(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("Usage: mms delete (<message_id> | all)"); + return; + } + LOCK_IDLE_SCOPE(); + mms::message_store& ms = m_wallet->get_message_store(); + if (args[0] == "all") + { + if (user_confirms(tr("Delete all messages?"))) + { + ms.delete_all_messages(); + } + } + else + { + mms::message m; + bool valid_id = get_message_from_arg(args[0], m); + if (valid_id) + { + // If only a single message and not all delete even if unsent / unprocessed + ms.delete_message(m.id); + } + } +} + +void simple_wallet::mms_send(const std::vector<std::string> &args) +{ + if (args.size() == 0) + { + ask_send_all_ready_messages(); + return; + } + else if (args.size() != 1) + { + fail_msg_writer() << tr("Usage: mms send [<message_id>]"); + return; + } + LOCK_IDLE_SCOPE(); + mms::message_store& ms = m_wallet->get_message_store(); + mms::message m; + bool valid_id = get_message_from_arg(args[0], m); + if (valid_id) + { + ms.send_message(get_multisig_wallet_state(), m.id); + } +} + +void simple_wallet::mms_receive(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms receive"); + return; + } + std::vector<mms::message> new_messages; + LOCK_IDLE_SCOPE(); + mms::message_store& ms = m_wallet->get_message_store(); + bool avail = ms.check_for_messages(get_multisig_wallet_state(), new_messages); + if (avail) + { + list_mms_messages(new_messages); + } +} + +void simple_wallet::mms_export(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("Usage: mms export <message_id>"); + return; + } + LOCK_IDLE_SCOPE(); + mms::message_store& ms = m_wallet->get_message_store(); + mms::message m; + bool valid_id = get_message_from_arg(args[0], m); + if (valid_id) + { + const std::string filename = "mms_message_content"; + if (epee::file_io_utils::save_string_to_file(filename, m.content)) + { + success_msg_writer() << tr("Message content saved to: ") << filename; + } + else + { + fail_msg_writer() << tr("Failed to to save message content"); + } + } +} + +void simple_wallet::mms_note(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + if (args.size() == 0) + { + LOCK_IDLE_SCOPE(); + const std::vector<mms::message> &messages = ms.get_all_messages(); + for (size_t i = 0; i < messages.size(); ++i) + { + const mms::message &m = messages[i]; + if ((m.type == mms::message_type::note) && (m.state == mms::message_state::waiting)) + { + show_message(m); + } + } + return; + } + if (args.size() < 2) + { + fail_msg_writer() << tr("Usage: mms note [<label> <text>]"); + return; + } + uint32_t signer_index; + bool found = ms.get_signer_index_by_label(args[0], signer_index); + if (!found) + { + fail_msg_writer() << tr("No signer found with label ") << args[0]; + return; + } + std::string note = ""; + for (size_t n = 1; n < args.size(); ++n) + { + if (n > 1) + { + note += " "; + } + note += args[n]; + } + LOCK_IDLE_SCOPE(); + ms.add_message(get_multisig_wallet_state(), signer_index, mms::message_type::note, + mms::message_direction::out, note); + ask_send_all_ready_messages(); +} + +void simple_wallet::mms_show(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("Usage: mms show <message_id>"); + return; + } + LOCK_IDLE_SCOPE(); + mms::message_store& ms = m_wallet->get_message_store(); + mms::message m; + bool valid_id = get_message_from_arg(args[0], m); + if (valid_id) + { + show_message(m); + } +} + +void simple_wallet::mms_set(const std::vector<std::string> &args) +{ + bool set = args.size() == 2; + bool query = args.size() == 1; + if (!set && !query) + { + fail_msg_writer() << tr("Usage: mms set <option_name> [<option_value>]"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + LOCK_IDLE_SCOPE(); + if (args[0] == "auto-send") + { + if (set) + { + bool result; + bool ok = parse_bool(args[1], result); + if (ok) + { + ms.set_auto_send(result); + } + else + { + fail_msg_writer() << tr("Wrong option value"); + } + } + else + { + message_writer() << (ms.get_auto_send() ? tr("Auto-send is on") : tr("Auto-send is off")); + } + } + else + { + fail_msg_writer() << tr("Unknown option"); + } +} + +void simple_wallet::mms_help(const std::vector<std::string> &args) +{ + if (args.size() > 1) + { + fail_msg_writer() << tr("Usage: mms help [<subcommand>]"); + return; + } + std::vector<std::string> help_args; + help_args.push_back("mms"); + if (args.size() == 1) + { + help_args.push_back(args[0]); + } + help(help_args); +} + +void simple_wallet::mms_send_signer_config(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms send_signer_config"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + if (!ms.signer_config_complete()) + { + fail_msg_writer() << tr("Signer config not yet complete"); + return; + } + LOCK_IDLE_SCOPE(); + add_signer_config_messages(); + ask_send_all_ready_messages(); +} + +void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args) +{ + mms::message_store& ms = m_wallet->get_message_store(); + uint32_t other_signers = ms.get_num_authorized_signers() - 1; + size_t args_size = args.size(); + if ((args_size != 0) && (args_size != other_signers)) + { + fail_msg_writer() << tr("Usage: mms start_auto_config [<label> <label> ...]"); + return; + } + if ((args_size == 0) && !ms.signer_labels_complete()) + { + fail_msg_writer() << tr("There are signers without a label set. Complete labels before auto-config or specify them as parameters here."); + return; + } + mms::authorized_signer me = ms.get_signer(0); + if (me.auto_config_running) + { + if (!user_confirms(tr("Auto-config is already running. Cancel and restart?"))) + { + return; + } + } + LOCK_IDLE_SCOPE(); + mms::multisig_wallet_state state = get_multisig_wallet_state(); + if (args_size != 0) + { + // Set (or overwrite) all the labels except "me" from the arguments + for (uint32_t i = 1; i < (other_signers + 1); ++i) + { + ms.set_signer(state, i, args[i - 1], boost::none, boost::none); + } + } + ms.start_auto_config(state); + // List the signers to show the generated auto-config tokens + list_signers(ms.get_all_signers()); +} + +void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms stop_auto_config"); + return; + } + if (!user_confirms(tr("Delete any auto-config tokens and stop auto-config?"))) + { + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + LOCK_IDLE_SCOPE(); + ms.stop_auto_config(); +} + +void simple_wallet::mms_auto_config(const std::vector<std::string> &args) +{ + if (args.size() != 1) + { + fail_msg_writer() << tr("Usage: mms auto_config <auto_config_token>"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + std::string adjusted_token; + if (!ms.check_auto_config_token(args[0], adjusted_token)) + { + fail_msg_writer() << tr("Invalid auto-config token"); + return; + } + mms::authorized_signer me = ms.get_signer(0); + if (me.auto_config_running) + { + if (!user_confirms(tr("Auto-config already running. Cancel and restart?"))) + { + return; + } + } + LOCK_IDLE_SCOPE(); + ms.add_auto_config_data_message(get_multisig_wallet_state(), adjusted_token); + ask_send_all_ready_messages(); +} + +bool simple_wallet::mms(const std::vector<std::string> &args) +{ + try + { + mms::message_store& ms = m_wallet->get_message_store(); + if (args.size() == 0) + { + mms_info(args); + return true; + } + + const std::string &sub_command = args[0]; + std::vector<std::string> mms_args = args; + mms_args.erase(mms_args.begin()); + + if (sub_command == "init") + { + mms_init(mms_args); + return true; + } + if (!ms.get_active()) + { + fail_msg_writer() << tr("The MMS is not active. Activate using the \"mms init\" command"); + return true; + } + else if (sub_command == "info") + { + mms_info(mms_args); + } + else if (sub_command == "signer") + { + mms_signer(mms_args); + } + else if (sub_command == "list") + { + mms_list(mms_args); + } + else if (sub_command == "next") + { + mms_next(mms_args); + } + else if (sub_command == "sync") + { + mms_sync(mms_args); + } + else if (sub_command == "transfer") + { + mms_transfer(mms_args); + } + else if (sub_command == "delete") + { + mms_delete(mms_args); + } + else if (sub_command == "send") + { + mms_send(mms_args); + } + else if (sub_command == "receive") + { + mms_receive(mms_args); + } + else if (sub_command == "export") + { + mms_export(mms_args); + } + else if (sub_command == "note") + { + mms_note(mms_args); + } + else if (sub_command == "show") + { + mms_show(mms_args); + } + else if (sub_command == "set") + { + mms_set(mms_args); + } + else if (sub_command == "help") + { + mms_help(mms_args); + } + else if (sub_command == "send_signer_config") + { + mms_send_signer_config(mms_args); + } + else if (sub_command == "start_auto_config") + { + mms_start_auto_config(mms_args); + } + else if (sub_command == "stop_auto_config") + { + mms_stop_auto_config(mms_args); + } + else if (sub_command == "auto_config") + { + mms_auto_config(mms_args); + } + else + { + fail_msg_writer() << tr("Invalid MMS subcommand"); + } + } + catch (const tools::error::no_connection_to_daemon &e) + { + fail_msg_writer() << tr("Error in MMS command: ") << e.what() << " " << e.request(); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Error in MMS command: ") << e.what(); + return true; + } + return true; +} +// End MMS ------------------------------------------------------------------------------------------------ + diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 421afbeda..5010e3adc 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -154,7 +154,7 @@ namespace cryptonote bool show_incoming_transfers(const std::vector<std::string> &args); bool show_payments(const std::vector<std::string> &args); bool show_blockchain_height(const std::vector<std::string> &args); - bool transfer_main(int transfer_type, const std::vector<std::string> &args); + bool transfer_main(int transfer_type, const std::vector<std::string> &args, bool called_by_mms); bool transfer(const std::vector<std::string> &args); bool locked_transfer(const std::vector<std::string> &args); bool locked_sweep_all(const std::vector<std::string> &args); @@ -214,15 +214,23 @@ namespace cryptonote bool payment_id(const std::vector<std::string> &args); bool print_fee_info(const std::vector<std::string> &args); bool prepare_multisig(const std::vector<std::string>& args); + bool prepare_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool make_multisig(const std::vector<std::string>& args); + bool make_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool finalize_multisig(const std::vector<std::string> &args); bool exchange_multisig_keys(const std::vector<std::string> &args); + bool exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms); bool export_multisig(const std::vector<std::string>& args); + bool export_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool import_multisig(const std::vector<std::string>& args); + bool import_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs); bool sign_multisig(const std::vector<std::string>& args); + bool sign_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool submit_multisig(const std::vector<std::string>& args); + bool submit_multisig_main(const std::vector<std::string>& args, bool called_by_mms); bool export_raw_multisig(const std::vector<std::string>& args); + bool mms(const std::vector<std::string>& args); bool print_ring(const std::vector<std::string>& args); bool set_ring(const std::vector<std::string>& args); bool save_known_rings(const std::vector<std::string>& args); @@ -241,6 +249,8 @@ namespace cryptonote bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr); std::string get_prompt() const; 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); struct transfer_view { @@ -287,6 +297,9 @@ namespace cryptonote virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason); + virtual void on_button_request(); + virtual void on_pin_request(epee::wipeable_string & pin); + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -381,5 +394,40 @@ namespace cryptonote bool m_auto_refresh_refreshing; std::atomic<bool> m_in_manual_refresh; uint32_t m_current_subaddress_account; + + // MMS + mms::message_store& get_message_store() const { return m_wallet->get_message_store(); }; + mms::multisig_wallet_state get_multisig_wallet_state() const { return m_wallet->get_multisig_wallet_state(); }; + bool mms_active() const { return get_message_store().get_active(); }; + bool choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice); + void list_mms_messages(const std::vector<mms::message> &messages); + void list_signers(const std::vector<mms::authorized_signer> &signers); + void add_signer_config_messages(); + void show_message(const mms::message &m); + void ask_send_all_ready_messages(); + void check_for_messages(); + bool user_confirms(const std::string &question); + bool get_message_from_arg(const std::string &arg, mms::message &m); + bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound); + + void mms_init(const std::vector<std::string> &args); + void mms_info(const std::vector<std::string> &args); + void mms_signer(const std::vector<std::string> &args); + void mms_list(const std::vector<std::string> &args); + void mms_next(const std::vector<std::string> &args); + void mms_sync(const std::vector<std::string> &args); + void mms_transfer(const std::vector<std::string> &args); + void mms_delete(const std::vector<std::string> &args); + void mms_send(const std::vector<std::string> &args); + void mms_receive(const std::vector<std::string> &args); + void mms_export(const std::vector<std::string> &args); + void mms_note(const std::vector<std::string> &args); + void mms_show(const std::vector<std::string> &args); + void mms_set(const std::vector<std::string> &args); + void mms_help(const std::vector<std::string> &args); + void mms_send_signer_config(const std::vector<std::string> &args); + void mms_start_auto_config(const std::vector<std::string> &args); + void mms_stop_auto_config(const std::vector<std::string> &args); + void mms_auto_config(const std::vector<std::string> &args); }; } diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 4e3fb1ae5..8425e4a03 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -34,7 +34,10 @@ set(wallet_sources wallet2.cpp wallet_args.cpp ringdb.cpp - node_rpc_proxy.cpp) + node_rpc_proxy.cpp + message_store.cpp + message_transporter.cpp +) set(wallet_private_headers wallet2.h @@ -44,7 +47,9 @@ set(wallet_private_headers wallet_rpc_server_commands_defs.h wallet_rpc_server_error_codes.h ringdb.h - node_rpc_proxy.h) + node_rpc_proxy.h + message_store.h + message_transporter.h) monero_private_headers(wallet ${wallet_private_headers}) diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp new file mode 100644 index 000000000..ce6f4f52b --- /dev/null +++ b/src/wallet/message_store.cpp @@ -0,0 +1,1445 @@ +// 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 "message_store.h" +#include <boost/archive/portable_binary_oarchive.hpp> +#include <boost/archive/portable_binary_iarchive.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <fstream> +#include <sstream> +#include "file_io_utils.h" +#include "storages/http_abstract_invoke.h" +#include "wallet_errors.h" +#include "serialization/binary_utils.h" +#include "common/base58.h" +#include "common/util.h" +#include "string_tools.h" + + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms" + +namespace mms +{ + +message_store::message_store() +{ + m_active = false; + m_auto_send = false; + m_next_message_id = 1; + m_num_authorized_signers = 0; + m_num_required_signers = 0; + m_nettype = cryptonote::network_type::UNDEFINED; + m_run = true; +} + +namespace +{ + // MMS options handling mirrors what "wallet2" is doing for its options, on-demand init and all + // It's not very clean to initialize Bitmessage-specific options here, but going one level further + // down still into "message_transporter" for that is a little bit too much + struct options + { + const command_line::arg_descriptor<std::string> bitmessage_address = {"bitmessage-address", mms::message_store::tr("Use PyBitmessage instance at URL <arg>"), "http://localhost:8442/"}; + const command_line::arg_descriptor<std::string> bitmessage_login = {"bitmessage-login", mms::message_store::tr("Specify <arg> as username:password for PyBitmessage API"), "username:password"}; + }; +} + +void message_store::init_options(boost::program_options::options_description& desc_params) +{ + const options opts{}; + command_line::add_arg(desc_params, opts.bitmessage_address); + command_line::add_arg(desc_params, opts.bitmessage_login); +} + +void message_store::init(const multisig_wallet_state &state, const std::string &own_label, + const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers) +{ + m_num_authorized_signers = num_authorized_signers; + m_num_required_signers = num_required_signers; + m_signers.clear(); + m_messages.clear(); + m_next_message_id = 1; + + // The vector "m_signers" gets here once the required number of elements, one for each authorized signer, + // and is never changed again. The rest of the code relies on "size(m_signers) == m_num_authorized_signers" + // without further checks. + authorized_signer signer; + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + signer.me = signer.index == 0; // Strict convention: The very first signer is fixed as / must be "me" + m_signers.push_back(signer); + signer.index++; + } + + set_signer(state, 0, own_label, own_transport_address, state.address); + + m_nettype = state.nettype; + set_active(true); + m_filename = state.mms_file; + save(state); +} + +void message_store::set_options(const boost::program_options::variables_map& vm) +{ + const options opts{}; + std::string bitmessage_address = command_line::get_arg(vm, opts.bitmessage_address); + epee::wipeable_string bitmessage_login = command_line::get_arg(vm, opts.bitmessage_login); + set_options(bitmessage_address, bitmessage_login); +} + +void message_store::set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login) +{ + m_transporter.set_options(bitmessage_address, bitmessage_login); +} + +void message_store::set_signer(const multisig_wallet_state &state, + uint32_t index, + const boost::optional<std::string> &label, + 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); + authorized_signer &m = m_signers[index]; + if (label) + { + m.label = label.get(); + } + if (transport_address) + { + m.transport_address = transport_address.get(); + } + if (monero_address) + { + m.monero_address_known = true; + m.monero_address = monero_address.get(); + } + // Save to minimize the chance to loose that info (at least while in beta) + save(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); + return m_signers[index]; +} + +bool message_store::signer_config_complete() const +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.label.empty() || m.transport_address.empty() || !m.monero_address_known) + { + return false; + } + } + return true; +} + +// Check if all signers have a label set (as it's a requirement for starting auto-config +// by the "manager") +bool message_store::signer_labels_complete() const +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.label.empty()) + { + return false; + } + } + return true; +} + +void message_store::get_signer_config(std::string &signer_config) +{ + std::stringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + ar << m_signers; + signer_config = oss.str(); +} + +void message_store::unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, + std::vector<authorized_signer> &signers) +{ + try + { + std::stringstream iss; + iss << signer_config; + boost::archive::portable_binary_iarchive ar(iss); + ar >> signers; + } + catch (...) + { + 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); +} + +void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) +{ + // The signers in "signer_config" and the resident wallet signers are matched not by label, but + // by Monero address, and ALL labels will be set from "signer_config", even the "me" label. + // In the auto-config process as implemented now the auto-config manager is responsible for defining + // the labels, and right at the end of the process ALL wallets use the SAME labels. The idea behind this + // is preventing problems like duplicate labels and confusion (Bob choosing a label "IamAliceHonest"). + // (Of course signers are free to re-define any labels they don't like AFTER auto-config.) + // + // Usually this method will be called with only the "me" signer defined in the wallet, and may + // produce unexpected behaviour if that wallet contains additional signers that have nothing to do with + // those arriving in "signer_config". + std::vector<authorized_signer> signers; + unpack_signer_config(state, signer_config, signers); + + uint32_t new_index = 1; + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = signers[i]; + uint32_t index; + uint32_t take_index; + bool found = get_signer_index_by_monero_address(m.monero_address, index); + if (found) + { + // Redefine existing (probably "me", under usual circumstances) + take_index = index; + } + else + { + // Add new; neglect that we may erroneously overwrite already defined signers + // (but protect "me") + take_index = new_index; + if ((new_index + 1) < m_num_authorized_signers) + { + new_index++; + } + } + authorized_signer &modify = m_signers[take_index]; + modify.label = m.label; // ALWAYS set label, see comments above + if (!modify.me) + { + modify.transport_address = m.transport_address; + modify.monero_address_known = m.monero_address_known; + if (m.monero_address_known) + { + modify.monero_address = m.monero_address; + } + } + } + save(state); +} + +void message_store::start_auto_config(const multisig_wallet_state &state) +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + authorized_signer &m = m_signers[i]; + if (!m.me) + { + setup_signer_for_auto_config(i, create_auto_config_token(), true); + } + m.auto_config_running = true; + } + save(state); +} + +// Check auto-config token string and convert to standardized form; +// Try to make it as foolproof as possible, with built-in tolerance to make up for +// errors in transmission that still leave the token recognizable. +bool message_store::check_auto_config_token(const std::string &raw_token, + std::string &adjusted_token) const +{ + std::string prefix(AUTO_CONFIG_TOKEN_PREFIX); + uint32_t num_hex_digits = (AUTO_CONFIG_TOKEN_BYTES + 1) * 2; + uint32_t full_length = num_hex_digits + prefix.length(); + uint32_t raw_length = raw_token.length(); + std::string hex_digits; + + if (raw_length == full_length) + { + // Prefix must be there; accept it in any casing + std::string raw_prefix(raw_token.substr(0, 3)); + boost::algorithm::to_lower(raw_prefix); + if (raw_prefix != prefix) + { + return false; + } + hex_digits = raw_token.substr(3); + } + else if (raw_length == num_hex_digits) + { + // Accept the token without the prefix if it's otherwise ok + hex_digits = raw_token; + } + else + { + return false; + } + + // Convert to strict lowercase and correct any common misspellings + boost::algorithm::to_lower(hex_digits); + std::replace(hex_digits.begin(), hex_digits.end(), 'o', '0'); + std::replace(hex_digits.begin(), hex_digits.end(), 'i', '1'); + std::replace(hex_digits.begin(), hex_digits.end(), 'l', '1'); + + // Now it must be correct hex with correct checksum, no further tolerance possible + std::string token_bytes; + if (!epee::string_tools::parse_hexstr_to_binbuff(hex_digits, token_bytes)) + { + return false; + } + const crypto::hash &hash = crypto::cn_fast_hash(token_bytes.data(), token_bytes.size() - 1); + if (token_bytes[AUTO_CONFIG_TOKEN_BYTES] != hash.data[0]) + { + return false; + } + adjusted_token = prefix + hex_digits; + return true; +} + +// Create a new auto-config token with prefix, random 8-hex digits plus 2 checksum digits +std::string message_store::create_auto_config_token() +{ + unsigned char random[AUTO_CONFIG_TOKEN_BYTES]; + crypto::rand(AUTO_CONFIG_TOKEN_BYTES, random); + std::string token_bytes; + token_bytes.append((char *)random, AUTO_CONFIG_TOKEN_BYTES); + + // Add a checksum because technically ANY four bytes are a valid token, and without a checksum we would send + // auto-config messages "to nowhere" after the slightest typo without knowing it + const crypto::hash &hash = crypto::cn_fast_hash(token_bytes.data(), token_bytes.size()); + token_bytes += hash.data[0]; + std::string prefix(AUTO_CONFIG_TOKEN_PREFIX); + return prefix + epee::string_tools::buff_to_hex_nodelimer(token_bytes); +} + +// Add a message for sending "me" address data to the auto-config transport address +// that can be derived from the token and activate auto-config +size_t message_store::add_auto_config_data_message(const multisig_wallet_state &state, + const std::string &auto_config_token) +{ + authorized_signer &me = m_signers[0]; + me.auto_config_token = auto_config_token; + setup_signer_for_auto_config(0, auto_config_token, false); + me.auto_config_running = true; + + auto_config_data data; + data.label = me.label; + data.transport_address = me.transport_address; + data.monero_address = me.monero_address; + + std::stringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + ar << data; + + return add_message(state, 0, message_type::auto_config_data, message_direction::out, oss.str()); +} + +// Process a single message with auto-config data, destined for "message.signer_index" +void message_store::process_auto_config_data_message(uint32_t id) +{ + // "auto_config_data" contains the label that the auto-config data sender uses for "me", but that's + // more for completeness' sake, and right now it's not used. In general, the auto-config manager + // decides/defines the labels, and right after completing auto-config ALL wallets use the SAME labels. + + const message &m = get_message_ref_by_id(id); + + auto_config_data data; + try + { + std::stringstream iss; + iss << m.content; + boost::archive::portable_binary_iarchive ar(iss); + ar >> data; + } + catch (...) + { + THROW_WALLET_EXCEPTION_IF(true, tools::error::wallet_internal_error, "Invalid structure of auto config data"); + } + + authorized_signer &signer = m_signers[m.signer_index]; + // "signer.label" does NOT change, see comment above + signer.transport_address = data.transport_address; + signer.monero_address_known = true; + signer.monero_address = data.monero_address; + signer.auto_config_running = false; +} + +void message_store::stop_auto_config() +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + authorized_signer &m = m_signers[i]; + if (!m.me && !m.auto_config_transport_address.empty()) + { + // Try to delete those "unused API" addresses in PyBitmessage, especially since + // it seems it's not possible to delete them interactively, only to "disable" them + m_transporter.delete_transport_address(m.auto_config_transport_address); + } + m.auto_config_token.clear(); + m.auto_config_public_key = crypto::null_pkey; + m.auto_config_secret_key = crypto::null_skey; + m.auto_config_transport_address.clear(); + m.auto_config_running = false; + } +} + +void message_store::setup_signer_for_auto_config(uint32_t index, const std::string token, bool receiving) +{ + // It may be a little strange to hash the textual hex digits of the auto config token into + // 32 bytes and turn that into a Monero public/secret key pair, instead of doing something + // much less complicated like directly using the underlying random 40 bits as key for a + // symmetric cipher, but everything is there already for encrypting and decrypting messages + // with such key pairs, and furthermore it would be trivial to use tokens with a different + // number of bytes. + // + // In the wallet of the auto-config manager each signer except "me" gets set its own + // 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); + authorized_signer &m = m_signers[index]; + m.auto_config_token = token; + crypto::hash_to_scalar(token.data(), token.size(), m.auto_config_secret_key); + crypto::secret_key_to_public_key(m.auto_config_secret_key, m.auto_config_public_key); + if (receiving) + { + m.auto_config_transport_address = m_transporter.derive_and_receive_transport_address(m.auto_config_token); + } + else + { + m.auto_config_transport_address = m_transporter.derive_transport_address(m.auto_config_token); + } +} + +bool message_store::get_signer_index_by_monero_address(const cryptonote::account_public_address &monero_address, uint32_t &index) const +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.monero_address == monero_address) + { + index = m.index; + return true; + } + } + MWARNING("No authorized signer with Monero address " << account_address_to_string(monero_address)); + return false; +} + +bool message_store::get_signer_index_by_label(const std::string label, uint32_t &index) const +{ + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.label == label) + { + index = m.index; + return true; + } + } + MWARNING("No authorized signer with label " << label); + return false; +} + +void message_store::process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content) +{ + switch(type) + { + case message_type::key_set: + // Result of a "prepare_multisig" command in the wallet + // Send the key set to all other signers + case message_type::additional_key_set: + // Result of a "make_multisig" command or a "exchange_multisig_keys" in the wallet in case of M/N multisig + // Send the additional key set to all other signers + case message_type::multisig_sync_data: + // Result of a "export_multisig_info" command in the wallet + // Send the sync data to all other signers + for (uint32_t i = 1; i < m_num_authorized_signers; ++i) + { + add_message(state, i, type, message_direction::out, content); + } + break; + + case message_type::partially_signed_tx: + // Result of a "transfer" command in the wallet, or a "sign_multisig" command + // that did not yet result in the minimum number of signatures required + // Create a message "from me to me" as a container for the tx data + if (m_num_required_signers == 1) + { + // Probably rare, but possible: The 1 signature is already enough, correct the type + // Easier to correct here than asking all callers to detect this rare special case + type = message_type::fully_signed_tx; + } + add_message(state, 0, type, message_direction::in, content); + break; + + case message_type::fully_signed_tx: + add_message(state, 0, type, message_direction::in, content); + break; + + default: + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Illegal message type " + (uint32_t)type); + break; + } +} + +size_t message_store::add_message(const multisig_wallet_state &state, + uint32_t signer_index, message_type type, message_direction direction, + const std::string &content) +{ + message m; + m.id = m_next_message_id++; + m.type = type; + m.direction = direction; + m.content = content; + m.created = (uint64_t)time(NULL); + m.modified = m.created; + m.sent = 0; + m.signer_index = signer_index; + if (direction == message_direction::out) + { + m.state = message_state::ready_to_send; + } + else + { + m.state = message_state::waiting; + }; + m.wallet_height = (uint32_t)state.num_transfer_details; + if (m.type == message_type::additional_key_set) + { + m.round = state.multisig_rounds_passed; + } + else + { + m.round = 0; + } + m.signature_count = 0; // Future expansion for signature counting when signing txs + m.hash = crypto::null_hash; + m_messages.push_back(m); + + // Save for every new message right away (at least while in beta) + save(state); + + MINFO(boost::format("Added %s message %s for signer %s of type %s") + % message_direction_to_string(direction) % m.id % signer_index % message_type_to_string(type)); + return m_messages.size() - 1; +} + +// Get the index of the message with id "id", return false if not found +bool message_store::get_message_index_by_id(uint32_t id, size_t &index) const +{ + for (size_t i = 0; i < m_messages.size(); ++i) + { + if (m_messages[i].id == id) + { + index = i; + return true; + } + } + MWARNING("No message found with an id of " << id); + return false; +} + +// Get the index of the message with id "id" that must exist +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); + return index; +} + +// Get the modifiable message with id "id" that must exist; private/internal use! +message& message_store::get_message_ref_by_id(uint32_t id) +{ + return m_messages[get_message_index_by_id(id)]; +} + +// Get the message with id "id", return false if not found +// This version of the method allows to check whether id is valid without triggering an error +bool message_store::get_message_by_id(uint32_t id, message &m) const +{ + size_t index; + bool found = get_message_index_by_id(id, index); + if (found) + { + m = m_messages[index]; + } + return found; +} + +// Get the message with id "id" that must exist +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); + return m; +} + +bool message_store::any_message_of_type(message_type type, message_direction direction) const +{ + for (size_t i = 0; i < m_messages.size(); ++i) + { + if ((m_messages[i].type == type) && (m_messages[i].direction == direction)) + { + return true; + } + } + return false; +} + +bool message_store::any_message_with_hash(const crypto::hash &hash) const +{ + for (size_t i = 0; i < m_messages.size(); ++i) + { + if (m_messages[i].hash == hash) + { + return true; + } + } + return false; +} + +// Count the ids in the vector that are set i.e. not 0, while ignoring index 0 +// Mostly used to check whether we have a message for each authorized signer except me, +// with the signer index used as index into 'ids'; the element at index 0, for me, +// is ignored, to make constant subtractions of 1 for indices when filling the +// vector unnecessary +size_t message_store::get_other_signers_id_count(const std::vector<uint32_t> &ids) const +{ + size_t count = 0; + for (size_t i = 1 /* and not 0 */; i < ids.size(); ++i) + { + if (ids[i] != 0) + { + count++; + } + } + return count; +} + +// Is in every element of vector 'ids' (except at index 0) a message id i.e. not 0? +bool message_store::message_ids_complete(const std::vector<uint32_t> &ids) const +{ + return get_other_signers_id_count(ids) == (ids.size() - 1); +} + +void message_store::delete_message(uint32_t id) +{ + delete_transport_message(id); + size_t index = get_message_index_by_id(id); + m_messages.erase(m_messages.begin() + index); +} + +void message_store::delete_all_messages() +{ + for (size_t i = 0; i < m_messages.size(); ++i) + { + delete_transport_message(m_messages[i].id); + } + m_messages.clear(); +} + +// Make a message text, which is "attacker controlled data", reasonably safe to display +// This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command +void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const +{ + sanitized_text.clear(); + + // Restrict the size to fend of DOS-style attacks with heaps of data + size_t length = std::min(m.content.length(), (size_t)1000); + + for (size_t i = 0; i < length; ++i) + { + char c = m.content[i]; + if ((int)c < 32) + { + // Strip out any controls, especially ESC for getting rid of potentially dangerous + // ANSI escape sequences that a console window might interpret + c = ' '; + } + else if ((c == '<') || (c == '>')) + { + // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute + // when displayed in the GUI wallet + c = ' '; + } + sanitized_text += c; + } +} + +void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename) +{ + std::stringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + ar << *this; + std::string buf = oss.str(); + + crypto::chacha_key key; + crypto::generate_chacha_key(&state.view_secret_key, sizeof(crypto::secret_key), key, 1); + + file_data write_file_data = boost::value_initialized<file_data>(); + write_file_data.magic_string = "MMS"; + write_file_data.file_version = 0; + write_file_data.iv = crypto::rand<crypto::chacha_iv>(); + std::string encrypted_data; + encrypted_data.resize(buf.size()); + crypto::chacha20(buf.data(), buf.size(), key, write_file_data.iv, &encrypted_data[0]); + write_file_data.encrypted_data = encrypted_data; + + std::stringstream file_oss; + boost::archive::portable_binary_oarchive file_ar(file_oss); + file_ar << write_file_data; + + bool success = epee::file_io_utils::save_string_to_file(filename, file_oss.str()); + THROW_WALLET_EXCEPTION_IF(!success, tools::error::file_save_error, filename); +} + +void message_store::read_from_file(const multisig_wallet_state &state, const std::string &filename) +{ + boost::system::error_code ignored_ec; + bool file_exists = boost::filesystem::exists(filename, ignored_ec); + if (!file_exists) + { + // Simply do nothing if the file is not there; allows e.g. easy recovery + // from problems with the MMS by deleting the file + MERROR("No message store file found: " << filename); + return; + } + + std::string buf; + bool success = epee::file_io_utils::load_file_to_string(filename, buf); + THROW_WALLET_EXCEPTION_IF(!success, tools::error::file_read_error, filename); + + file_data read_file_data; + try + { + std::stringstream iss; + iss << buf; + boost::archive::portable_binary_iarchive ar(iss); + ar >> read_file_data; + } + catch (const std::exception &e) + { + MERROR("MMS file " << filename << " has bad structure <iv,encrypted_data>: " << e.what()); + THROW_WALLET_EXCEPTION_IF(true, tools::error::file_read_error, filename); + } + + crypto::chacha_key key; + crypto::generate_chacha_key(&state.view_secret_key, sizeof(crypto::secret_key), key, 1); + std::string decrypted_data; + decrypted_data.resize(read_file_data.encrypted_data.size()); + crypto::chacha20(read_file_data.encrypted_data.data(), read_file_data.encrypted_data.size(), key, read_file_data.iv, &decrypted_data[0]); + + try + { + std::stringstream iss; + iss << decrypted_data; + boost::archive::portable_binary_iarchive ar(iss); + ar >> *this; + } + catch (const std::exception &e) + { + MERROR("MMS file " << filename << " has bad structure: " << e.what()); + THROW_WALLET_EXCEPTION_IF(true, tools::error::file_read_error, filename); + } + + m_filename = filename; +} + +// Save to the same file this message store was loaded from +// Called after changes deemed "important", to make it less probable to lose messages in case of +// a crash; a better and long-term solution would of course be to use LMDB ... +void message_store::save(const multisig_wallet_state &state) +{ + if (!m_filename.empty()) + { + write_to_file(state, m_filename); + } +} + +bool message_store::get_processable_messages(const multisig_wallet_state &state, + bool force_sync, std::vector<processing_data> &data_list, std::string &wait_reason) +{ + uint32_t wallet_height = (uint32_t)state.num_transfer_details; + data_list.clear(); + wait_reason.clear(); + // In all scans over all messages looking for complete sets (1 message for each signer), + // if there are duplicates, the OLDEST of them is taken. This may not play a role with + // any of the current message types, but may with future ones, and it's probably a good + // idea to have a clear and somewhat defensive strategy. + + std::vector<uint32_t> auto_config_messages(m_num_authorized_signers, 0); + bool any_auto_config = false; + + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if ((m.type == message_type::auto_config_data) && (m.state == message_state::waiting)) + { + if (auto_config_messages[m.signer_index] == 0) + { + auto_config_messages[m.signer_index] = m.id; + any_auto_config = true; + } + // else duplicate auto config data, ignore + } + } + + if (any_auto_config) + { + bool auto_config_complete = message_ids_complete(auto_config_messages); + if (auto_config_complete) + { + processing_data data; + data.processing = message_processing::process_auto_config_data; + data.message_ids = auto_config_messages; + data.message_ids.erase(data.message_ids.begin()); + data_list.push_back(data); + return true; + } + else + { + wait_reason = tr("Auto-config cannot proceed because auto config data from other signers is not complete"); + return false; + // With ANY auto config data present but not complete refuse to check for any + // other processing. Manually delete those messages to abort such an auto config + // phase if needed. + } + } + + // Any signer config that arrived will be processed right away, regardless of other things that may wait + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if ((m.type == message_type::signer_config) && (m.state == message_state::waiting)) + { + processing_data data; + data.processing = message_processing::process_signer_config; + data.message_ids.push_back(m.id); + data_list.push_back(data); + return true; + } + } + + // ALL of the following processings depend on the signer info being complete + if (!signer_config_complete()) + { + wait_reason = tr("The signer config is not complete."); + return false; + } + + if (!state.multisig) + { + + if (!any_message_of_type(message_type::key_set, message_direction::out)) + { + // With the own key set not yet ready we must do "prepare_multisig" first; + // Key sets from other signers may be here already, but if we process them now + // the wallet will go multisig too early: we can't produce our own key set any more! + processing_data data; + data.processing = message_processing::prepare_multisig; + data_list.push_back(data); + return true; + } + + // Ids of key set messages per signer index, to check completeness + // Naturally, does not care about the order of the messages and is trivial to secure against + // key sets that were received more than once + // With full M/N multisig now possible consider only key sets of the right round, i.e. + // with not yet multisig the only possible round 0 + std::vector<uint32_t> key_set_messages(m_num_authorized_signers, 0); + + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if ((m.type == message_type::key_set) && (m.state == message_state::waiting) + && (m.round == 0)) + { + if (key_set_messages[m.signer_index] == 0) + { + key_set_messages[m.signer_index] = m.id; + } + // else duplicate key set, ignore + } + } + + bool key_sets_complete = message_ids_complete(key_set_messages); + if (key_sets_complete) + { + // Nothing else can be ready to process earlier than this, ignore everything else and give back + processing_data data; + data.processing = message_processing::make_multisig; + data.message_ids = key_set_messages; + data.message_ids.erase(data.message_ids.begin()); + data_list.push_back(data); + return true; + } + else + { + wait_reason = tr("Wallet can't go multisig because key sets from other signers are missing or not complete."); + return false; + } + } + + if (state.multisig && !state.multisig_is_ready) + { + // In the case of M/N multisig the call 'wallet2::multisig' returns already true + // after "make_multisig" but with calls to "exchange_multisig_keys" still needed, and + // sets the parameter 'ready' to false to document this particular "in-between" state. + // So what may be possible here, with all necessary messages present, is a call to + // "exchange_multisig_keys". + // Consider only messages belonging to the next round to do, which has the number + // "state.multisig_rounds_passed". + std::vector<uint32_t> additional_key_set_messages(m_num_authorized_signers, 0); + + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if ((m.type == message_type::additional_key_set) && (m.state == message_state::waiting) + && (m.round == state.multisig_rounds_passed)) + { + if (additional_key_set_messages[m.signer_index] == 0) + { + additional_key_set_messages[m.signer_index] = m.id; + } + // else duplicate key set, ignore + } + } + + bool key_sets_complete = message_ids_complete(additional_key_set_messages); + if (key_sets_complete) + { + processing_data data; + data.processing = message_processing::exchange_multisig_keys; + data.message_ids = additional_key_set_messages; + data.message_ids.erase(data.message_ids.begin()); + data_list.push_back(data); + return true; + } + else + { + wait_reason = tr("Wallet can't start another key exchange round because key sets from other signers are missing or not complete."); + return false; + } + } + + // Properly exchanging multisig sync data is easiest and most transparent + // for the user if a wallet sends its own data first and processes any received + // sync data afterwards so that's the order that the MMS enforces here. + // (Technically, it seems to work also the other way round.) + // + // To check whether a NEW round of syncing is necessary the MMS works with a + // "wallet state": new state means new syncing needed. + // + // The MMS monitors the "wallet state" by recording "wallet heights" as + // numbers of transfers present in a wallet at the time of message creation. While + // not watertight, this quite simple scheme should already suffice to trigger + // and orchestrate a sensible exchange of sync data. + if (state.has_multisig_partial_key_images || force_sync) + { + // Sync is necessary and not yet completed: Processing of transactions + // will only be possible again once properly synced + // Check first whether we generated already OUR sync info; take note of + // any processable sync info from other signers on the way in case we need it + bool own_sync_data_created = false; + std::vector<uint32_t> sync_messages(m_num_authorized_signers, 0); + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if ((m.type == message_type::multisig_sync_data) && (force_sync || (m.wallet_height == wallet_height))) + // It's data for the same "round" of syncing, on the same "wallet height", therefore relevant + // With "force_sync" take ANY waiting sync data, maybe it will work out + { + if (m.direction == message_direction::out) + { + own_sync_data_created = true; + // Ignore whether sent already or not, and assume as complete if several other signers there + } + else if ((m.direction == message_direction::in) && (m.state == message_state::waiting)) + { + if (sync_messages[m.signer_index] == 0) + { + sync_messages[m.signer_index] = m.id; + } + // else duplicate sync message, ignore + } + } + } + if (!own_sync_data_created) + { + // As explained above, creating sync data BEFORE processing such data from + // other signers reliably works, so insist on that here + processing_data data; + data.processing = message_processing::create_sync_data; + data_list.push_back(data); + return true; + } + uint32_t id_count = (uint32_t)get_other_signers_id_count(sync_messages); + // Do we have sync data from ALL other signers? + bool all_sync_data = id_count == (m_num_authorized_signers - 1); + // Do we have just ENOUGH sync data to have a minimal viable sync set? + // In cases like 2/3 multisig we don't need messages from ALL other signers, only + // from enough of them i.e. num_required_signers minus 1 messages + bool enough_sync_data = id_count >= (m_num_required_signers - 1); + bool sync = false; + wait_reason = tr("Syncing not done because multisig sync data from other signers are missing or not complete."); + if (all_sync_data) + { + sync = true; + } + else if (enough_sync_data) + { + if (force_sync) + { + sync = true; + } + else + { + // Don't sync, but give a hint how this minimal set COULD be synced if really wanted + wait_reason += (boost::format("\nUse \"mms next sync\" if you want to sync with just %s out of %s authorized signers and transact just with them") + % (m_num_required_signers - 1) % (m_num_authorized_signers - 1)).str(); + } + } + if (sync) + { + processing_data data; + data.processing = message_processing::process_sync_data; + for (size_t i = 0; i < sync_messages.size(); ++i) + { + uint32_t id = sync_messages[i]; + if (id != 0) + { + data.message_ids.push_back(id); + } + } + data_list.push_back(data); + return true; + } + else + { + // We can't proceed to any transactions until we have synced; "wait_reason" already set above + return false; + } + } + + bool waiting_found = false; + bool note_found = false; + bool sync_data_found = false; + for (size_t i = 0; i < m_messages.size(); ++i) + { + message &m = m_messages[i]; + if (m.state == message_state::waiting) + { + waiting_found = true; + switch (m.type) + { + case message_type::fully_signed_tx: + { + // We can either submit it ourselves, or send it to any other signer for submission + processing_data data; + data.processing = message_processing::submit_tx; + data.message_ids.push_back(m.id); + data_list.push_back(data); + + data.processing = message_processing::send_tx; + for (uint32_t j = 1; j < m_num_authorized_signers; ++j) + { + data.receiving_signer_index = j; + data_list.push_back(data); + } + return true; + } + + case message_type::partially_signed_tx: + { + if (m.signer_index == 0) + { + // We started this ourselves, or signed it but with still signatures missing: + // We can send it to any other signer for signing / further signing + // In principle it does not make sense to send it back to somebody who + // already signed, but the MMS does not / not yet keep track of that, + // because that would be somewhat complicated. + processing_data data; + data.processing = message_processing::send_tx; + data.message_ids.push_back(m.id); + for (uint32_t j = 1; j < m_num_authorized_signers; ++j) + { + data.receiving_signer_index = j; + data_list.push_back(data); + } + return true; + } + else + { + // Somebody else sent this to us: We can sign it + // It would be possible to just pass it on, but that's not directly supported here + processing_data data; + data.processing = message_processing::sign_tx; + data.message_ids.push_back(m.id); + data_list.push_back(data); + return true; + } + } + + case message_type::note: + note_found = true; + break; + + case message_type::multisig_sync_data: + sync_data_found = true; + break; + + default: + break; + } + } + } + if (waiting_found) + { + wait_reason = tr("There are waiting messages, but nothing is ready to process under normal circumstances"); + if (sync_data_found) + { + wait_reason += tr("\nUse \"mms next sync\" if you want to force processing of the waiting sync data"); + } + if (note_found) + { + wait_reason += tr("\nUse \"mms note\" to display the waiting notes"); + } + } + else + { + wait_reason = tr("There are no messages waiting to be processed."); + } + + return false; +} + +void message_store::set_messages_processed(const processing_data &data) +{ + for (size_t i = 0; i < data.message_ids.size(); ++i) + { + set_message_processed_or_sent(data.message_ids[i]); + } +} + +void message_store::set_message_processed_or_sent(uint32_t id) +{ + message &m = get_message_ref_by_id(id); + if (m.state == message_state::waiting) + { + // So far a fairly cautious and conservative strategy: Only delete from Bitmessage + // when fully processed (and e.g. not already after reception and writing into + // the message store file) + delete_transport_message(id); + m.state = message_state::processed; + } + else if (m.state == message_state::ready_to_send) + { + m.state = message_state::sent; + } + m.modified = (uint64_t)time(NULL); +} + +void message_store::encrypt(crypto::public_key public_key, const std::string &plaintext, + std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv) +{ + crypto::secret_key encryption_secret_key; + crypto::generate_keys(encryption_public_key, encryption_secret_key); + + crypto::key_derivation derivation; + bool success = crypto::generate_key_derivation(public_key, encryption_secret_key, derivation); + THROW_WALLET_EXCEPTION_IF(!success, tools::error::wallet_internal_error, "Failed to generate key derivation for message encryption"); + + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(&derivation, sizeof(derivation), chacha_key, 1); + iv = crypto::rand<crypto::chacha_iv>(); + ciphertext.resize(plaintext.size()); + crypto::chacha20(plaintext.data(), plaintext.size(), chacha_key, iv, &ciphertext[0]); +} + +void message_store::decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv, + const crypto::secret_key &view_secret_key, std::string &plaintext) +{ + crypto::key_derivation derivation; + bool success = crypto::generate_key_derivation(encryption_public_key, view_secret_key, derivation); + THROW_WALLET_EXCEPTION_IF(!success, tools::error::wallet_internal_error, "Failed to generate key derivation for message decryption"); + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(&derivation, sizeof(derivation), chacha_key, 1); + plaintext.resize(ciphertext.size()); + crypto::chacha20(ciphertext.data(), ciphertext.size(), chacha_key, iv, &plaintext[0]); +} + +void message_store::send_message(const multisig_wallet_state &state, uint32_t id) +{ + message &m = get_message_ref_by_id(id); + const authorized_signer &me = m_signers[0]; + const authorized_signer &receiver = m_signers[m.signer_index]; + transport_message dm; + crypto::public_key public_key; + + dm.timestamp = (uint64_t)time(NULL); + dm.subject = "MMS V0 " + tools::get_human_readable_timestamp(dm.timestamp); + dm.source_transport_address = me.transport_address; + dm.source_monero_address = me.monero_address; + if (m.type == message_type::auto_config_data) + { + // Encrypt with the public key derived from the auto-config token, and send to the + // transport address likewise derived from that token + public_key = me.auto_config_public_key; + dm.destination_transport_address = me.auto_config_transport_address; + // The destination Monero address is not yet known + memset(&dm.destination_monero_address, 0, sizeof(cryptonote::account_public_address)); + } + else + { + // Encrypt with the receiver's view public key + public_key = receiver.monero_address.m_view_public_key; + const authorized_signer &receiver = m_signers[m.signer_index]; + dm.destination_monero_address = receiver.monero_address; + dm.destination_transport_address = receiver.transport_address; + } + encrypt(public_key, m.content, dm.content, dm.encryption_public_key, dm.iv); + dm.type = (uint32_t)m.type; + dm.hash = crypto::cn_fast_hash(dm.content.data(), dm.content.size()); + dm.round = m.round; + + crypto::generate_signature(dm.hash, me.monero_address.m_view_public_key, state.view_secret_key, dm.signature); + + m_transporter.send_message(dm); + + m.state=message_state::sent; + m.sent= (uint64_t)time(NULL); +} + +bool message_store::check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages) +{ + m_run.store(true, std::memory_order_relaxed); + const authorized_signer &me = m_signers[0]; + std::vector<std::string> destinations; + destinations.push_back(me.transport_address); + for (uint32_t i = 1; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.auto_config_running) + { + destinations.push_back(m.auto_config_transport_address); + } + } + std::vector<transport_message> transport_messages; + bool r = m_transporter.receive_messages(destinations, transport_messages); + if (!m_run.load(std::memory_order_relaxed)) + { + // Stop was called, don't waste time processing the messages + // (but once started processing them, don't react to stop request anymore, avoid receiving them "partially)" + return false; + } + + bool new_messages = false; + for (size_t i = 0; i < transport_messages.size(); ++i) + { + transport_message &rm = transport_messages[i]; + if (any_message_with_hash(rm.hash)) + { + // Already seen, do not take again + } + else + { + uint32_t sender_index; + bool take = false; + message_type type = static_cast<message_type>(rm.type); + crypto::secret_key decrypt_key = state.view_secret_key; + if (type == message_type::auto_config_data) + { + // Find out which signer sent it by checking which auto config transport address + // the message was sent to + for (uint32_t i = 1; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + if (m.auto_config_transport_address == rm.destination_transport_address) + { + take = true; + sender_index = i; + decrypt_key = m.auto_config_secret_key; + break; + } + } + } + else if (type == message_type::signer_config) + { + // Typically we can't check yet whether we know the sender, so take from any + // and pretend it's from "me" because we might have nothing else yet + take = true; + sender_index = 0; + } + else + { + // Only accept from senders that are known as signer here, otherwise just ignore + take = get_signer_index_by_monero_address(rm.source_monero_address, sender_index); + } + if (take && (type != message_type::auto_config_data)) + { + // If the destination address is known, check it as well; this additional filter + // allows using the same transport address for multiple signers + take = rm.destination_monero_address == me.monero_address; + } + if (take) + { + crypto::hash actual_hash = crypto::cn_fast_hash(rm.content.data(), rm.content.size()); + THROW_WALLET_EXCEPTION_IF(actual_hash != rm.hash, tools::error::wallet_internal_error, "Message hash mismatch"); + + bool signature_valid = crypto::check_signature(actual_hash, rm.source_monero_address.m_view_public_key, rm.signature); + THROW_WALLET_EXCEPTION_IF(!signature_valid, tools::error::wallet_internal_error, "Message signature not valid"); + + std::string plaintext; + decrypt(rm.content, rm.encryption_public_key, rm.iv, decrypt_key, plaintext); + size_t index = add_message(state, sender_index, (message_type)rm.type, message_direction::in, plaintext); + message &m = m_messages[index]; + m.hash = rm.hash; + m.transport_id = rm.transport_id; + m.sent = rm.timestamp; + m.round = rm.round; + m.signature_count = rm.signature_count; + messages.push_back(m); + new_messages = true; + } + } + } + return new_messages; +} + +void message_store::delete_transport_message(uint32_t id) +{ + const message &m = get_message_by_id(id); + if (!m.transport_id.empty()) + { + m_transporter.delete_message(m.transport_id); + } +} + +std::string message_store::account_address_to_string(const cryptonote::account_public_address &account_address) const +{ + return get_account_address_as_str(m_nettype, false, account_address); +} + +const char* message_store::message_type_to_string(message_type type) +{ + switch (type) + { + case message_type::key_set: + return tr("key set"); + case message_type::additional_key_set: + return tr("additional key set"); + case message_type::multisig_sync_data: + return tr("multisig sync data"); + case message_type::partially_signed_tx: + return tr("partially signed tx"); + case message_type::fully_signed_tx: + return tr("fully signed tx"); + case message_type::note: + return tr("note"); + case message_type::signer_config: + return tr("signer config"); + case message_type::auto_config_data: + return tr("auto-config data"); + default: + return tr("unknown message type"); + } +} + +const char* message_store::message_direction_to_string(message_direction direction) +{ + switch (direction) + { + case message_direction::in: + return tr("in"); + case message_direction::out: + return tr("out"); + default: + return tr("unknown message direction"); + } +} + +const char* message_store::message_state_to_string(message_state state) +{ + switch (state) + { + case message_state::ready_to_send: + return tr("ready to send"); + case message_state::sent: + return tr("sent"); + case message_state::waiting: + return tr("waiting"); + case message_state::processed: + return tr("processed"); + case message_state::cancelled: + return tr("cancelled"); + default: + return tr("unknown message state"); + } +} + +// Convert a signer to string suitable for a column in a list, with 'max_width' +// Format: label: transport_address +std::string message_store::signer_to_string(const authorized_signer &signer, uint32_t max_width) +{ + std::string s = ""; + s.reserve(max_width); + uint32_t avail = max_width; + uint32_t label_len = signer.label.length(); + if (label_len > avail) + { + s.append(signer.label.substr(0, avail - 2)); + s.append(".."); + return s; + } + s.append(signer.label); + avail -= label_len; + uint32_t transport_addr_len = signer.transport_address.length(); + if ((transport_addr_len > 0) && (avail > 10)) + { + s.append(": "); + avail -= 2; + if (transport_addr_len <= avail) + { + s.append(signer.transport_address); + } + else + { + s.append(signer.transport_address.substr(0, avail-2)); + s.append(".."); + } + } + return s; +} + +} diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h new file mode 100644 index 000000000..7d26f7889 --- /dev/null +++ b/src/wallet/message_store.h @@ -0,0 +1,420 @@ +// 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 <cstdlib> +#include <string> +#include <vector> +#include "crypto/hash.h" +#include <boost/serialization/vector.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/optional/optional.hpp> +#include "serialization/serialization.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_basic/account_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "common/i18n.h" +#include "common/command_line.h" +#include "wipeable_string.h" +#include "message_transporter.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms" +#define AUTO_CONFIG_TOKEN_BYTES 4 +#define AUTO_CONFIG_TOKEN_PREFIX "mms" + +namespace mms +{ + enum class message_type + { + key_set, + additional_key_set, + multisig_sync_data, + partially_signed_tx, + fully_signed_tx, + note, + signer_config, + auto_config_data + }; + + enum class message_direction + { + in, + out + }; + + enum class message_state + { + ready_to_send, + sent, + + waiting, + processed, + + cancelled + }; + + enum class message_processing + { + prepare_multisig, + make_multisig, + exchange_multisig_keys, + create_sync_data, + process_sync_data, + sign_tx, + send_tx, + submit_tx, + process_signer_config, + process_auto_config_data + }; + + struct message + { + uint32_t id; + message_type type; + message_direction direction; + std::string content; + uint64_t created; + uint64_t modified; + uint64_t sent; + uint32_t signer_index; + crypto::hash hash; + message_state state; + uint32_t wallet_height; + uint32_t round; + uint32_t signature_count; + std::string transport_id; + }; + // "wallet_height" (for lack of a short name that would describe what it is about) + // is the number of transfers present in the wallet at the time of message + // construction; used to coordinate generation of sync info (which depends + // on the content of the wallet at time of generation) + + struct authorized_signer + { + std::string label; + std::string transport_address; + bool monero_address_known; + cryptonote::account_public_address monero_address; + bool me; + uint32_t index; + std::string auto_config_token; + crypto::public_key auto_config_public_key; + crypto::secret_key auto_config_secret_key; + std::string auto_config_transport_address; + bool auto_config_running; + + authorized_signer() + { + monero_address_known = false; + memset(&monero_address, 0, sizeof(cryptonote::account_public_address)); + index = 0; + auto_config_public_key = crypto::null_pkey; + auto_config_secret_key = crypto::null_skey; + auto_config_running = false; + }; + }; + + struct processing_data + { + message_processing processing; + std::vector<uint32_t> message_ids; + uint32_t receiving_signer_index = 0; + }; + + struct file_transport_message + { + cryptonote::account_public_address sender_address; + crypto::chacha_iv iv; + crypto::public_key encryption_public_key; + message internal_message; + }; + + struct auto_config_data + { + std::string label; + std::string transport_address; + cryptonote::account_public_address monero_address; + }; + + // Overal .mms file structure, with the "message_store" object serialized to and + // encrypted in "encrypted_data" + struct file_data + { + std::string magic_string; + uint32_t file_version; + crypto::chacha_iv iv; + std::string encrypted_data; + }; + + // The following struct provides info about the current state of a "wallet2" object + // at the time of a "message_store" method call that those methods need. See on the + // one hand a first parameter of this type for several of those methods, and on the + // other hand the method "wallet2::get_multisig_wallet_state" which clients like the + // CLI wallet can use to get that info. + // + // Note that in the case of a wallet that is already multisig "address" is NOT the + // multisig address, but the "original" wallet address at creation time. Likewise + // "view_secret_key" is the original view secret key then. + // + // This struct definition is here and not in "wallet2.h" to avoid circular imports. + struct multisig_wallet_state + { + cryptonote::account_public_address address; + cryptonote::network_type nettype; + crypto::secret_key view_secret_key; + bool multisig; + bool multisig_is_ready; + bool has_multisig_partial_key_images; + uint32_t multisig_rounds_passed; + size_t num_transfer_details; + std::string mms_file; + }; + + class message_store + { + public: + message_store(); + // Initialize and start to use the MMS, set the first signer, this wallet itself + // Filename, if not null and not empty, is used to create the ".mms" file + // reset it if already used, with deletion of all signers and messages + void init(const multisig_wallet_state &state, const std::string &own_label, + const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers); + void set_active(bool active) { m_active = active; }; + void set_auto_send(bool auto_send) { m_auto_send = auto_send; }; + void set_options(const boost::program_options::variables_map& vm); + void set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login); + bool get_active() const { return m_active; }; + bool get_auto_send() const { return m_auto_send; }; + uint32_t get_num_required_signers() const { return m_num_required_signers; }; + uint32_t get_num_authorized_signers() const { return m_num_authorized_signers; }; + + void set_signer(const multisig_wallet_state &state, + uint32_t index, + const boost::optional<std::string> &label, + const boost::optional<std::string> &transport_address, + const boost::optional<cryptonote::account_public_address> monero_address); + + const authorized_signer &get_signer(uint32_t index) const; + bool get_signer_index_by_monero_address(const cryptonote::account_public_address &monero_address, uint32_t &index) const; + bool get_signer_index_by_label(const std::string label, uint32_t &index) const; + const std::vector<authorized_signer> &get_all_signers() const { return m_signers; }; + bool signer_config_complete() const; + bool signer_labels_complete() const; + void get_signer_config(std::string &signer_config); + void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, + std::vector<authorized_signer> &signers); + void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config); + + void start_auto_config(const multisig_wallet_state &state); + bool check_auto_config_token(const std::string &raw_token, + std::string &adjusted_token) const; + size_t add_auto_config_data_message(const multisig_wallet_state &state, + const std::string &auto_config_token); + void process_auto_config_data_message(uint32_t id); + void stop_auto_config(); + + // Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command + // Creates the resulting messages to the right signers + void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content); + + // Go through all the messages, look at the "ready to process" ones, and check whether any single one + // or any group of them can be processed, because they are processable as single messages (like a tx + // that is fully signed and thus ready for submit to the net) or because they form a complete group + // (e.g. key sets from all authorized signers to make the wallet multisig). If there are multiple + // candidates, e.g. in 2/3 multisig sending to one OR the other signer to sign, there will be more + // than 1 element in 'data' for the user to choose. If nothing is ready "false" is returned. + // The method mostly ignores the order in which the messages were received because messages may be delayed + // (e.g. sync data from a signer arrives AFTER a transaction to submit) or because message time stamps + // may be wrong so it's not possible to order them reliably. + // Messages also may be ready by themselves but the wallet not yet ready for them (e.g. sync data already + // arriving when the wallet is not yet multisig because key sets were delayed or were lost altogether.) + // If nothing is ready 'wait_reason' may contain further info about the reason why. + bool get_processable_messages(const multisig_wallet_state &state, + bool force_sync, + std::vector<processing_data> &data_list, + std::string &wait_reason); + void set_messages_processed(const processing_data &data); + + size_t add_message(const multisig_wallet_state &state, + uint32_t signer_index, message_type type, message_direction direction, + const std::string &content); + const std::vector<message> &get_all_messages() const { return m_messages; }; + bool get_message_by_id(uint32_t id, message &m) const; + message get_message_by_id(uint32_t id) const; + void set_message_processed_or_sent(uint32_t id); + void delete_message(uint32_t id); + void delete_all_messages(); + void get_sanitized_message_text(const message &m, std::string &sanitized_text) const; + + void send_message(const multisig_wallet_state &state, uint32_t id); + bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages); + void stop() { m_run.store(false, std::memory_order_relaxed); m_transporter.stop(); } + + void write_to_file(const multisig_wallet_state &state, const std::string &filename); + void read_from_file(const multisig_wallet_state &state, const std::string &filename); + + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int ver) + { + a & m_active; + a & m_num_authorized_signers; + a & m_nettype; + a & m_num_required_signers; + a & m_signers; + a & m_messages; + a & m_next_message_id; + a & m_auto_send; + } + + static const char* message_type_to_string(message_type type); + static const char* message_direction_to_string(message_direction direction); + static const char* message_state_to_string(message_state state); + std::string signer_to_string(const authorized_signer &signer, uint32_t max_width); + + static const char *tr(const char *str) { return i18n_translate(str, "tools::mms"); } + static void init_options(boost::program_options::options_description& desc_params); + + private: + bool m_active; + uint32_t m_num_authorized_signers; + uint32_t m_num_required_signers; + bool m_auto_send; + cryptonote::network_type m_nettype; + std::vector<authorized_signer> m_signers; + std::vector<message> m_messages; + uint32_t m_next_message_id; + std::string m_filename; + message_transporter m_transporter; + std::atomic<bool> m_run; + + bool get_message_index_by_id(uint32_t id, size_t &index) const; + size_t get_message_index_by_id(uint32_t id) const; + message& get_message_ref_by_id(uint32_t id); + bool any_message_of_type(message_type type, message_direction direction) const; + bool any_message_with_hash(const crypto::hash &hash) const; + size_t get_other_signers_id_count(const std::vector<uint32_t> &ids) const; + bool message_ids_complete(const std::vector<uint32_t> &ids) const; + void encrypt(crypto::public_key public_key, const std::string &plaintext, + std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv); + void decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv, + const crypto::secret_key &view_secret_key, std::string &plaintext); + std::string create_auto_config_token(); + void setup_signer_for_auto_config(uint32_t index, const std::string token, bool receiving); + void delete_transport_message(uint32_t id); + std::string account_address_to_string(const cryptonote::account_public_address &account_address) const; + void save(const multisig_wallet_state &state); + }; +} + +BOOST_CLASS_VERSION(mms::file_data, 0) +BOOST_CLASS_VERSION(mms::message_store, 0) +BOOST_CLASS_VERSION(mms::message, 0) +BOOST_CLASS_VERSION(mms::file_transport_message, 0) +BOOST_CLASS_VERSION(mms::authorized_signer, 1) +BOOST_CLASS_VERSION(mms::auto_config_data, 0) + +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, mms::file_data &x, const boost::serialization::version_type ver) + { + a & x.magic_string; + a & x.file_version; + a & x.iv; + a & x.encrypted_data; + } + + template <class Archive> + inline void serialize(Archive &a, mms::message &x, const boost::serialization::version_type ver) + { + a & x.id; + a & x.type; + a & x.direction; + a & x.content; + a & x.created; + a & x.modified; + a & x.sent; + a & x.signer_index; + a & x.hash; + a & x.state; + a & x.wallet_height; + a & x.round; + a & x.signature_count; + a & x.transport_id; + } + + template <class Archive> + inline void serialize(Archive &a, mms::authorized_signer &x, const boost::serialization::version_type ver) + { + a & x.label; + a & x.transport_address; + a & x.monero_address_known; + a & x.monero_address; + a & x.me; + a & x.index; + if (ver < 1) + { + return; + } + a & x.auto_config_token; + a & x.auto_config_public_key; + a & x.auto_config_secret_key; + a & x.auto_config_transport_address; + a & x.auto_config_running; + } + + template <class Archive> + inline void serialize(Archive &a, mms::auto_config_data &x, const boost::serialization::version_type ver) + { + a & x.label; + a & x.transport_address; + a & x.monero_address; + } + + template <class Archive> + inline void serialize(Archive &a, mms::file_transport_message &x, const boost::serialization::version_type ver) + { + a & x.sender_address; + a & x.iv; + a & x.encryption_public_key; + a & x.internal_message; + } + + template <class Archive> + inline void serialize(Archive &a, crypto::chacha_iv &x, const boost::serialization::version_type ver) + { + a & x.data; + } + + } +} diff --git a/src/wallet/message_transporter.cpp b/src/wallet/message_transporter.cpp new file mode 100644 index 000000000..eafd13d3b --- /dev/null +++ b/src/wallet/message_transporter.cpp @@ -0,0 +1,317 @@ +// 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 "message_transporter.h" +#include "string_coding.h" +#include <boost/format.hpp> +#include "wallet_errors.h" +#include "net/http_client.h" +#include "net/net_parse_helpers.h" +#include <algorithm> + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms" +#define PYBITMESSAGE_DEFAULT_API_PORT 8442 + +namespace mms +{ + +namespace bitmessage_rpc +{ + + struct message_info + { + uint32_t encodingType; + std::string toAddress; + uint32_t read; + std::string msgid; + std::string message; + std::string fromAddress; + std::string receivedTime; + std::string subject; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(encodingType) + KV_SERIALIZE(toAddress) + KV_SERIALIZE(read) + KV_SERIALIZE(msgid) + KV_SERIALIZE(message); + KV_SERIALIZE(fromAddress) + KV_SERIALIZE(receivedTime) + KV_SERIALIZE(subject) + END_KV_SERIALIZE_MAP() + }; + + struct inbox_messages_response + { + std::vector<message_info> inboxMessages; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(inboxMessages) + END_KV_SERIALIZE_MAP() + }; + +} + +message_transporter::message_transporter() +{ + m_run = true; +} + +void message_transporter::set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login) +{ + m_bitmessage_url = bitmessage_address; + epee::net_utils::http::url_content address_parts{}; + epee::net_utils::parse_url(m_bitmessage_url, address_parts); + if (address_parts.port == 0) + { + address_parts.port = PYBITMESSAGE_DEFAULT_API_PORT; + } + m_bitmessage_login = bitmessage_login; + + m_http_client.set_server(address_parts.host, std::to_string(address_parts.port), boost::none); +} + +bool message_transporter::receive_messages(const std::vector<std::string> &destination_transport_addresses, + std::vector<transport_message> &messages) +{ + // The message body of the Bitmessage message is basically the transport message, as JSON (and nothing more). + // Weeding out other, non-MMS messages is done in a simple way: If it deserializes without error, it's an MMS message + // That JSON is Base64-encoded by the MMS because the Monero epee JSON serializer does not escape anything and happily + // includes even 0 (NUL) in strings, which might confuse Bitmessage or at least display confusingly in the client. + // There is yet another Base64-encoding of course as part of the Bitmessage API for the message body parameter + // The Bitmessage API call "getAllInboxMessages" gives back a JSON array with all the messages (despite using + // XML-RPC for the calls, and not JSON-RPC ...) + m_run.store(true, std::memory_order_relaxed); + std::string request; + start_xml_rpc_cmd(request, "getAllInboxMessages"); + end_xml_rpc_cmd(request); + std::string answer; + post_request(request, answer); + + std::string json = get_str_between_tags(answer, "<string>", "</string>"); + bitmessage_rpc::inbox_messages_response bitmessage_res; + epee::serialization::load_t_from_json(bitmessage_res, json); + size_t size = bitmessage_res.inboxMessages.size(); + messages.clear(); + + for (size_t i = 0; i < size; ++i) + { + if (!m_run.load(std::memory_order_relaxed)) + { + // Stop was called, don't waste time processing any more messages + return false; + } + const bitmessage_rpc::message_info &message_info = bitmessage_res.inboxMessages[i]; + if (std::find(destination_transport_addresses.begin(), destination_transport_addresses.end(), message_info.toAddress) != destination_transport_addresses.end()) + { + transport_message message; + bool is_mms_message = false; + try + { + // First Base64-decoding: The message body is Base64 in the Bitmessage API + std::string message_body = epee::string_encoding::base64_decode(message_info.message); + // Second Base64-decoding: The MMS uses Base64 to hide non-textual data in its JSON from Bitmessage + json = epee::string_encoding::base64_decode(message_body); + epee::serialization::load_t_from_json(message, json); + is_mms_message = true; + } + catch(const std::exception& e) + { + } + if (is_mms_message) + { + message.transport_id = message_info.msgid; + messages.push_back(message); + } + } + } + + return true; +} + +bool message_transporter::send_message(const transport_message &message) +{ + // <toAddress> <fromAddress> <subject> <message> [encodingType [TTL]] + std::string request; + start_xml_rpc_cmd(request, "sendMessage"); + add_xml_rpc_string_param(request, message.destination_transport_address); + add_xml_rpc_string_param(request, message.source_transport_address); + add_xml_rpc_base64_param(request, message.subject); + std::string json = epee::serialization::store_t_to_json(message); + std::string message_body = epee::string_encoding::base64_encode(json); // See comment in "receive_message" about reason for (double-)Base64 encoding + add_xml_rpc_base64_param(request, message_body); + add_xml_rpc_integer_param(request, 2); + end_xml_rpc_cmd(request); + std::string answer; + post_request(request, answer); + return true; +} + +bool message_transporter::delete_message(const std::string &transport_id) +{ + std::string request; + start_xml_rpc_cmd(request, "trashMessage"); + add_xml_rpc_string_param(request, transport_id); + end_xml_rpc_cmd(request); + std::string answer; + post_request(request, answer); + return true; +} + +// Deterministically derive a transport / Bitmessage address from 'seed' (the 10-hex-digits +// auto-config token will be used), but do not set it up for receiving in PyBitmessage as +// well, because it's possible the address will only ever be used to SEND auto-config data +std::string message_transporter::derive_transport_address(const std::string &seed) +{ + std::string request; + start_xml_rpc_cmd(request, "getDeterministicAddress"); + add_xml_rpc_base64_param(request, seed); + add_xml_rpc_integer_param(request, 4); // addressVersionNumber + add_xml_rpc_integer_param(request, 1); // streamNumber + end_xml_rpc_cmd(request); + std::string answer; + post_request(request, answer); + std::string address = get_str_between_tags(answer, "<string>", "</string>"); + return address; +} + +// Derive a transport address and configure it for receiving in PyBitmessage, typically +// for receiving auto-config messages by the wallet of the auto-config organizer +std::string message_transporter::derive_and_receive_transport_address(const std::string &seed) +{ + // We need to call both "get_deterministic_address" AND "createDeterministicAddresses" + // because we won't get back the address from the latter call if it exists already + std::string address = derive_transport_address(seed); + + std::string request; + start_xml_rpc_cmd(request, "createDeterministicAddresses"); + add_xml_rpc_base64_param(request, seed); + add_xml_rpc_integer_param(request, 1); // numberOfAddresses + add_xml_rpc_integer_param(request, 4); // addressVersionNumber + end_xml_rpc_cmd(request); + std::string answer; + post_request(request, answer); + + return address; +} + +bool message_transporter::delete_transport_address(const std::string &transport_address) +{ + std::string request; + start_xml_rpc_cmd(request, "deleteAddress"); + add_xml_rpc_string_param(request, transport_address); + end_xml_rpc_cmd(request); + std::string answer; + return post_request(request, answer); +} + +bool message_transporter::post_request(const std::string &request, std::string &answer) +{ + // Somehow things do not work out if one tries to connect "m_http_client" to Bitmessage + // and keep it connected over the course of several calls. But with a new connection per + // call and disconnecting after the call there is no problem (despite perhaps a small + // slowdown) + epee::net_utils::http::fields_list additional_params; + + // Basic access authentication according to RFC 7617 (which the epee HTTP classes do not seem to support?) + // "m_bitmessage_login" just contains what is needed here, "user:password" + std::string auth_string = epee::string_encoding::base64_encode((const unsigned char*)m_bitmessage_login.data(), m_bitmessage_login.size()); + auth_string.insert(0, "Basic "); + additional_params.push_back(std::make_pair("Authorization", auth_string)); + + additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8")); + const epee::net_utils::http::http_response_info* response = NULL; + std::chrono::milliseconds timeout = std::chrono::seconds(15); + bool r = m_http_client.invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params)); + if (r) + { + answer = response->m_body; + } + else + { + LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300)); + THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url); + } + m_http_client.disconnect(); // see comment above + std::string string_value = get_str_between_tags(answer, "<string>", "</string>"); + if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0)) + { + THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value); + } + + return r; +} + +// Pick some string between two delimiters +// When parsing the XML returned by PyBitmessage, don't bother to fully parse it but as a little hack rely on the +// fact that e.g. a single string returned will be, however deeply nested in "<params><param><value>...", delivered +// between the very first "<string>" and "</string>" tags to be found in the XML +std::string message_transporter::get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim) +{ + size_t first_delim_pos = s.find(start_delim); + if (first_delim_pos != std::string::npos) + { + size_t end_pos_of_first_delim = first_delim_pos + start_delim.length(); + size_t last_delim_pos = s.find(stop_delim); + if (last_delim_pos != std::string::npos) + { + return s.substr(end_pos_of_first_delim, last_delim_pos - end_pos_of_first_delim); + } + } + return std::string(); +} + +void message_transporter::start_xml_rpc_cmd(std::string &xml, const std::string &method_name) +{ + xml = (boost::format("<?xml version=\"1.0\"?><methodCall><methodName>%s</methodName><params>") % method_name).str(); +} + +void message_transporter::add_xml_rpc_string_param(std::string &xml, const std::string ¶m) +{ + xml += (boost::format("<param><value><string>%s</string></value></param>") % param).str(); +} + +void message_transporter::add_xml_rpc_base64_param(std::string &xml, const std::string ¶m) +{ + // Bitmessage expects some arguments Base64-encoded, but it wants them as parameters of type "string", not "base64" that is also part of XML-RPC + std::string encoded_param = epee::string_encoding::base64_encode(param); + xml += (boost::format("<param><value><string>%s</string></value></param>") % encoded_param).str(); +} + +void message_transporter::add_xml_rpc_integer_param(std::string &xml, const int32_t ¶m) +{ + xml += (boost::format("<param><value><int>%i</int></value></param>") % param).str(); +} + +void message_transporter::end_xml_rpc_cmd(std::string &xml) +{ + xml += "</params></methodCall>"; +} + +} diff --git a/src/wallet/message_transporter.h b/src/wallet/message_transporter.h new file mode 100644 index 000000000..8291311ce --- /dev/null +++ b/src/wallet/message_transporter.h @@ -0,0 +1,113 @@ +// 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 "serialization/keyvalue_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_basic/account_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "net/http_server_impl_base.h" +#include "net/http_client.h" +#include "common/util.h" +#include "wipeable_string.h" +#include "serialization/keyvalue_serialization.h" +#include <vector> + +namespace mms +{ + +struct transport_message +{ + cryptonote::account_public_address source_monero_address; + std::string source_transport_address; + cryptonote::account_public_address destination_monero_address; + std::string destination_transport_address; + crypto::chacha_iv iv; + crypto::public_key encryption_public_key; + uint64_t timestamp; + uint32_t type; + std::string subject; + std::string content; + crypto::hash hash; + crypto::signature signature; + uint32_t round; + uint32_t signature_count; + std::string transport_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(source_monero_address) + KV_SERIALIZE(source_transport_address) + KV_SERIALIZE(destination_monero_address) + KV_SERIALIZE(destination_transport_address) + KV_SERIALIZE_VAL_POD_AS_BLOB(iv) + KV_SERIALIZE_VAL_POD_AS_BLOB(encryption_public_key) + KV_SERIALIZE(timestamp) + KV_SERIALIZE(type) + KV_SERIALIZE(subject) + KV_SERIALIZE(content) + KV_SERIALIZE_VAL_POD_AS_BLOB(hash) + KV_SERIALIZE_VAL_POD_AS_BLOB(signature) + KV_SERIALIZE(round) + KV_SERIALIZE(signature_count) + KV_SERIALIZE(transport_id) + END_KV_SERIALIZE_MAP() +}; + +class message_transporter +{ +public: + message_transporter(); + void set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login); + bool send_message(const transport_message &message); + bool receive_messages(const std::vector<std::string> &destination_transport_addresses, + std::vector<transport_message> &messages); + bool delete_message(const std::string &transport_id); + void stop() { m_run.store(false, std::memory_order_relaxed); } + std::string derive_transport_address(const std::string &seed); + std::string derive_and_receive_transport_address(const std::string &seed); + bool delete_transport_address(const std::string &transport_address); + +private: + epee::net_utils::http::http_simple_client m_http_client; + std::string m_bitmessage_url; + epee::wipeable_string m_bitmessage_login; + std::atomic<bool> m_run; + + bool post_request(const std::string &request, std::string &answer); + static std::string get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim); + + static void start_xml_rpc_cmd(std::string &xml, const std::string &method_name); + static void add_xml_rpc_string_param(std::string &xml, const std::string ¶m); + static void add_xml_rpc_base64_param(std::string &xml, const std::string ¶m); + static void add_xml_rpc_integer_param(std::string &xml, const int32_t ¶m); + static void end_xml_rpc_cmd(std::string &xml); + +}; + +} diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 346c052b5..605531e59 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -28,7 +28,6 @@ #include "node_rpc_proxy.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "common/json_util.h" #include "storages/http_abstract_invoke.h" using namespace epee; diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index f562d6c06..b69022af4 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -30,6 +30,7 @@ #include <boost/algorithm/string.hpp> #include <boost/range/adaptor/transformed.hpp> #include <boost/filesystem.hpp> +#include "common/util.h" #include "misc_log_ex.h" #include "misc_language.h" #include "wallet_errors.h" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 129b96733..0d2faca54 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -67,6 +67,7 @@ using namespace epee; #include "common/json_util.h" #include "memwipe.h" #include "common/base58.h" +#include "common/combinator.h" #include "common/dns_utils.h" #include "common/notify.h" #include "common/perf_timer.h" @@ -177,6 +178,20 @@ namespace return public_keys; } + + bool keys_intersect(const std::unordered_set<crypto::public_key>& s1, const std::unordered_set<crypto::public_key>& s2) + { + if (s1.empty() || s2.empty()) + return false; + + for (const auto& e: s1) + { + if (s2.find(e) != s2.end()) + return true; + } + + return false; + } } namespace @@ -207,10 +222,11 @@ struct options { }; const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""}; + const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""}; const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" }; }; -void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) +void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file) { keys_file = file_path; wallet_file = file_path; @@ -222,6 +238,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, {//provided wallet file name keys_file += ".keys"; } + mms_file = file_path + ".mms"; } uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier) @@ -259,6 +276,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl auto daemon_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); auto device_name = command_line::get_arg(vm, opts.hw_device); + auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path); THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -313,7 +331,9 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); + wallet->get_message_store().set_options(vm); wallet->device_name(device_name); + wallet->device_derivation_path(device_derivation_path); try { @@ -815,7 +835,30 @@ wallet_keys_unlocker::~wallet_keys_unlocker() { if (!locked) return; - w.encrypt_keys(key); + try { w.encrypt_keys(key); } + catch (...) + { + MERROR("Failed to re-encrypt wallet keys"); + // do not propagate through dtor, we'd crash + } +} + +void wallet_device_callback::on_button_request() +{ + if (wallet) + wallet->on_button_request(); +} + +void wallet_device_callback::on_pin_request(epee::wipeable_string & pin) +{ + if (wallet) + wallet->on_pin_request(pin); +} + +void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (wallet) + wallet->on_passphrase_request(on_device, passphrase); } wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): @@ -866,12 +909,15 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0), + m_original_keys_available(false), + m_message_store(), m_key_device_type(hw::device::device_type::SOFTWARE), m_ring_history_saved(false), m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), - m_unattended(unattended) + m_unattended(unattended), + m_device_last_key_image_sync(0) { } @@ -894,6 +940,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_ return command_line::get_arg(vm, options().hw_device); } +std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm) +{ + return command_line::get_arg(vm, options().hw_device_derivation_path); +} + void wallet2::init_options(boost::program_options::options_description& desc_params) { const options opts{}; @@ -909,7 +960,9 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); + mms::message_store::init_options(desc_params); command_line::add_arg(desc_params, opts.hw_device); + command_line::add_arg(desc_params, opts.hw_device_derivation_path); command_line::add_arg(desc_params, opts.tx_notify); } @@ -929,7 +982,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( return {nullptr, password_container{}}; } auto wallet = make_basic(vm, unattended, opts, password_prompter); - if (wallet) + if (wallet && !wallet_file.empty()) { wallet->load(wallet_file, pwd->password()); } @@ -1071,15 +1124,17 @@ bool wallet2::reconnect_device() hw::device &hwdev = lookup_device(m_device_name); hwdev.set_name(m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); + hwdev.set_callback(get_device_callback()); r = hwdev.init(); if (!r){ - LOG_PRINT_L2("Could not init device"); + MERROR("Could not init device"); return false; } r = hwdev.connect(); if (!r){ - LOG_PRINT_L2("Could not connect to the device"); + MERROR("Could not connect to the device"); return false; } @@ -1356,14 +1411,12 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi //---------------------------------------------------------------------------------------------------- void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const { - const cryptonote::account_keys& keys = m_account.get_keys(); - if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields)) { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); - tx_cache_data.tx_extra_fields.clear(); - return; + if (tx_cache_data.tx_extra_fields.empty()) + return; } // Don't try to extract tx public key if tx has no ouputs @@ -2149,7 +2202,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry { THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size(); - tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true); + tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true); } ++txidx; for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) @@ -2852,10 +2905,11 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res); req.amounts.push_back(0); req.from_height = 0; - req.cumulative = true; + req.cumulative = false; req.binary = true; + req.compress = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout); + bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (!r) { @@ -2882,6 +2936,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> MWARNING("Failed to request output distribution: results are not for amount 0"); return false; } + for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i) + res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1]; start_height = res.distributions[0].data.start_height; distribution = std::move(res.distributions[0].data.distribution); return true; @@ -2977,6 +3033,7 @@ bool wallet2::clear() m_subaddresses.clear(); m_subaddress_labels.clear(); m_multisig_rounds_passed = 0; + m_device_last_key_image_sync = 0; return true; } @@ -3132,12 +3189,30 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(m_subaddress_lookahead_minor); json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator()); + value2.SetInt(m_original_keys_available ? 1 : 0); + json.AddMember("original_keys_available", value2, json.GetAllocator()); + value2.SetUint(1); json.AddMember("encrypted_secret_keys", value2, json.GetAllocator()); value.SetString(m_device_name.c_str(), m_device_name.size()); json.AddMember("device_name", value, json.GetAllocator()); + value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size()); + json.AddMember("device_derivation_path", value, json.GetAllocator()); + + std::string original_address; + std::string original_view_secret_key; + if (m_original_keys_available) + { + original_address = get_account_address_as_str(m_nettype, false, m_original_address); + value.SetString(original_address.c_str(), original_address.length()); + json.AddMember("original_address", value, json.GetAllocator()); + original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key); + value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length()); + json.AddMember("original_view_secret_key", value, json.GetAllocator()); + } + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -3145,7 +3220,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable account_data = buffer.GetString(); // Encrypt the entire JSON object. - crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand<crypto::chacha_iv>(); @@ -3257,7 +3331,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_ignore_fractional_outputs = true; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; + m_original_keys_available = false; m_device_name = ""; + m_device_derivation_path = ""; m_key_device_type = hw::device::device_type::SOFTWARE; encrypted_secret_keys = false; } @@ -3425,6 +3501,38 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default"; } } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string()); + m_device_derivation_path = field_device_derivation_path; + + if (json.HasMember("original_keys_available")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false); + m_original_keys_available = field_original_keys_available; + if (m_original_keys_available) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, true, std::string()); + address_parse_info info; + bool ok = get_account_address_from_str(info, m_nettype, field_original_address); + if (!ok) + { + LOG_ERROR("Failed to parse original_address from JSON"); + return false; + } + m_original_address = info.address; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, true, std::string()); + ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key); + if (!ok) + { + LOG_ERROR("Failed to parse original_view_secret_key from JSON"); + return false; + } + } + } + else + { + m_original_keys_available = false; + } } else { @@ -3439,6 +3547,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ hw::device &hwdev = lookup_device(m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); + hwdev.set_callback(get_device_callback()); THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name); m_account.set_device(hwdev); @@ -3749,6 +3859,10 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); + // Not possible to restore a multisig wallet that is able to activate the MMS + // (because the original keys are not (yet) part of the restore info) + m_original_keys_available = false; + create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); setup_new_blockchain(); @@ -3786,6 +3900,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_original_keys_available = false; m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); @@ -3874,6 +3989,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_original_keys_available = false; m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); @@ -3914,6 +4030,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_original_keys_available = false; m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); @@ -3945,6 +4062,8 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p auto &hwdev = lookup_device(device_name); hwdev.set_name(device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); + hwdev.set_callback(get_device_callback()); m_account.create_from_device(hwdev); m_key_device_type = m_account.get_device().get_type(); @@ -3953,6 +4072,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_original_keys_available = false; setup_keys(password); m_device_name = device_name; @@ -4065,6 +4185,15 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, m_multisig_derivations = derivations; } } + + if (!m_original_keys_available) + { + // Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages + // (making a wallet multisig overwrites those keys, see account_base::make_multisig) + m_original_address = m_account.get_keys().m_account_address; + m_original_view_secret_key = m_account.get_keys().m_view_secret_key; + m_original_keys_available = true; + } clear(); MINFO("Creating view key..."); @@ -4292,7 +4421,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, return make_multisig(password, secret_keys, public_keys, threshold); } -bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers) +bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers) { exchange_multisig_keys(password, pkeys, signers); return true; @@ -4498,8 +4627,8 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee //---------------------------------------------------------------------------------------------------- void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists) { - std::string keys_file, wallet_file; - do_prepare_file_names(file_path, keys_file, wallet_file); + std::string keys_file, wallet_file, mms_file; + do_prepare_file_names(file_path, keys_file, wallet_file, mms_file); boost::system::error_code ignore; keys_file_exists = boost::filesystem::exists(keys_file, ignore); @@ -4553,7 +4682,7 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& //---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { - do_prepare_file_names(file_path, m_keys_file, m_wallet_file); + do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file); return true; } //---------------------------------------------------------------------------------------------------- @@ -4746,6 +4875,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { MERROR("Failed to save rings, will try again next time"); } + + m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file); } //---------------------------------------------------------------------------------------------------- void wallet2::trim_hashchain() @@ -4851,6 +4982,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas const std::string old_file = m_wallet_file; const std::string old_keys_file = m_keys_file; const std::string old_address_file = m_wallet_file + ".address.txt"; + const std::string old_mms_file = m_mms_file; // save keys to the new file // if we here, main wallet file is saved and we only need to save keys and address files @@ -4880,6 +5012,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas if (!r) { LOG_ERROR("error removing file: " << old_address_file); } + // remove old message store file + if (boost::filesystem::exists(old_mms_file)) + { + r = boost::filesystem::remove(old_mms_file); + if (!r) { + LOG_ERROR("error removing file: " << old_mms_file); + } + } } else { // save to new file #ifdef WIN32 @@ -4905,6 +5045,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas std::error_code e = tools::replace_file(new_file, m_wallet_file); THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e); } + + if (m_message_store.get_active()) + { + // While the "m_message_store" object of course always exist, a file for the message + // store should only exist if the MMS is really active + m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); + } + } //---------------------------------------------------------------------------------------------------- uint64_t wallet2::balance(uint32_t index_major) const @@ -6043,7 +6191,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto for (auto &sig: ptx.multisig_sigs) { - if (sig.ignore != local_signer) + if (sig.ignore.find(local_signer) == sig.ignore.end()) { ptx.tx.rct_signatures = sig.sigs; @@ -6077,7 +6225,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto bool found = false; for (const auto &sig: ptx.multisig_sigs) { - if (sig.ignore != local_signer && exported_txs.m_signers.find(sig.ignore) == exported_txs.m_signers.end()) + if (sig.ignore.find(local_signer) == sig.ignore.end() && !keys_intersect(sig.ignore, exported_txs.m_signers)) { THROW_WALLET_EXCEPTION_IF(found, error::wallet_internal_error, "More than one transaction is final"); ptx.tx.rct_signatures = sig.sigs; @@ -7186,6 +7334,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } // get the keys for those + req.get_txid = false; m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); @@ -7517,30 +7666,56 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry // if this is a multisig wallet, create a list of multisig signers we can use std::deque<crypto::public_key> multisig_signers; size_t n_multisig_txes = 0; + std::vector<std::unordered_set<crypto::public_key>> ignore_sets; if (m_multisig && !m_transfers.empty()) { const crypto::public_key local_signer = get_multisig_signer_public_key(); size_t n_available_signers = 1; + + // At this step we need to define set of participants available for signature, + // i.e. those of them who exchanged with multisig info's for (const crypto::public_key &signer: m_multisig_signers) { if (signer == local_signer) continue; - multisig_signers.push_front(signer); for (const auto &i: m_transfers[0].m_multisig_info) { if (i.m_signer == signer) { - multisig_signers.pop_front(); multisig_signers.push_back(signer); ++n_available_signers; break; } } } - multisig_signers.push_back(local_signer); + // n_available_signers includes the transaction creator, but multisig_signers doesn't MDEBUG("We can use " << n_available_signers << "/" << m_multisig_signers.size() << " other signers"); - THROW_WALLET_EXCEPTION_IF(n_available_signers+1 < m_multisig_threshold, error::multisig_import_needed); - n_multisig_txes = n_available_signers == m_multisig_signers.size() ? m_multisig_threshold : 1; + THROW_WALLET_EXCEPTION_IF(n_available_signers < m_multisig_threshold, error::multisig_import_needed); + if (n_available_signers > m_multisig_threshold) + { + // If there more potential signers (those who exchanged with multisig info) + // than threshold needed some of them should be skipped since we don't know + // who will sign tx and who won't. Hence we don't contribute their LR pairs to the signature. + + // We create as many transactions as many combinations of excluded signers may be. + // For example, if we have 2/4 wallet and wallets are: A, B, C and D. Let A be + // transaction creator, so we need just 1 signature from set of B, C, D. + // Using "excluding" logic here we have to exclude 2-of-3 wallets. Combinations go as follows: + // BC, BD, and CD. We save these sets to use later and counting the number of required txs. + tools::Combinator<crypto::public_key> c(std::vector<crypto::public_key>(multisig_signers.begin(), multisig_signers.end())); + auto ignore_combinations = c.combine(multisig_signers.size() + 1 - m_multisig_threshold); + for (const auto& combination: ignore_combinations) + { + ignore_sets.push_back(std::unordered_set<crypto::public_key>(combination.begin(), combination.end())); + } + + n_multisig_txes = ignore_sets.size(); + } + else + { + // If we have exact count of signers just to fit in threshold we don't exclude anyone and create 1 transaction + n_multisig_txes = 1; + } MDEBUG("We will create " << n_multisig_txes << " txes"); } @@ -7608,8 +7783,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry src.mask = td.m_mask; if (m_multisig) { - crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front(); - src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore, used_L, used_L); + auto ignore_set = ignore_sets.empty() ? std::unordered_set<crypto::public_key>() : ignore_sets.front(); + src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore_set, used_L, used_L); } else src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); @@ -7671,7 +7846,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry std::vector<tools::wallet2::multisig_sig> multisig_sigs; if (m_multisig) { - crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front(); + auto ignore = ignore_sets.empty() ? std::unordered_set<crypto::public_key>() : ignore_sets.front(); multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, std::unordered_set<crypto::public_key>(), msout}); if (m_multisig_threshold < m_multisig_signers.size()) @@ -7679,7 +7854,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry const crypto::hash prefix_hash = cryptonote::get_transaction_prefix_hash(tx); // create the other versions, one for every other participant (the first one's already done above) - for (size_t signer_index = 1; signer_index < n_multisig_txes; ++signer_index) + for (size_t ignore_index = 1; ignore_index < ignore_sets.size(); ++ignore_index) { std::unordered_set<rct::key> new_used_L; size_t src_idx = 0; @@ -7687,7 +7862,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry for(size_t idx: selected_transfers) { cryptonote::tx_source_entry& src = sources_copy[src_idx]; - src.multisig_kLRki = get_multisig_composite_kLRki(idx, multisig_signers[signer_index], used_L, new_used_L); + src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore_sets[ignore_index], used_L, new_used_L); ++src_idx; } @@ -7699,7 +7874,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix"); - multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout}); + multisig_sigs.push_back({ms_tx.rct_signatures, ignore_sets[ignore_index], new_used_L, std::unordered_set<crypto::public_key>(), msout}); ms_tx.rct_signatures = tx.rct_signatures; THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_hash(ms_tx) != cryptonote::get_transaction_hash(tx), error::wallet_internal_error, "Multisig txes differ by more than the signatures"); @@ -8105,7 +8280,6 @@ void wallet2::light_wallet_get_address_txs() // for balance calculation uint64_t wallet_total_sent = 0; - uint64_t wallet_total_unlocked_sent = 0; // txs in pool std::vector<crypto::hash> pool_txs; @@ -9192,9 +9366,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_ //---------------------------------------------------------------------------------------------------- uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { auto & hwdev = get_account().get_device(); - if (!hwdev.has_ki_cold_sync()){ - throw std::invalid_argument("Device does not support cold ki sync protocol"); - } + CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol"); auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); @@ -9205,7 +9377,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { dev_cold->ki_sync(&wallet_shim, m_transfers, ski); - return import_key_images(ski, 0, spent, unspent); + // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted. + uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon()); + m_device_last_key_image_sync = time(NULL); + + return import_res; } //---------------------------------------------------------------------------------------------------- void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const @@ -10420,7 +10596,7 @@ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& w return m_account_tags; } -void wallet2::set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag) +void wallet2::set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag) { for (uint32_t account_index : account_indices) { @@ -11270,7 +11446,7 @@ rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) con return kLRki; } //---------------------------------------------------------------------------------------------------- -rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const +rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const { CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index"); @@ -11281,8 +11457,9 @@ rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto size_t n_signers_used = 1; for (const auto &p: m_transfers[n].m_multisig_info) { - if (p.m_signer == ignore) + if (ignore_set.find(p.m_signer) != ignore_set.end()) continue; + for (const auto &lr: p.m_LR) { if (used_L.find(lr.m_L) != used_L.end()) @@ -11327,7 +11504,6 @@ cryptonote::blobdata wallet2::export_multisig() for (size_t n = 0; n < m_transfers.size(); ++n) { transfer_details &td = m_transfers[n]; - const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); crypto::key_image ki; td.m_multisig_k.clear(); info[n].m_LR.clear(); @@ -11341,7 +11517,10 @@ cryptonote::blobdata wallet2::export_multisig() info[n].m_partial_key_images.push_back(ki); } - size_t nlr = m_multisig_threshold < m_multisig_signers.size() ? m_multisig_threshold - 1 : 1; + // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows: + // if we have 2/4 wallet with signers: A, B, C, D and A is a transaction creator it will need to pick up 1 signer from 3 wallets left. + // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1). + size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1); for (size_t m = 0; m < nlr; ++m) { td.m_multisig_k.push_back(rct::skGen()); @@ -11356,7 +11535,6 @@ cryptonote::blobdata wallet2::export_multisig() boost::archive::portable_binary_oarchive ar(oss); ar << info; - std::string magic(MULTISIG_EXPORT_FILE_MAGIC, strlen(MULTISIG_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string header; header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); @@ -11937,4 +12115,52 @@ uint64_t wallet2::get_segregation_fork_height() const void wallet2::generate_genesis(cryptonote::block& b) const { cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); } +//---------------------------------------------------------------------------------------------------- +mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const +{ + mms::multisig_wallet_state state; + state.nettype = m_nettype; + state.multisig = multisig(&state.multisig_is_ready); + state.has_multisig_partial_key_images = has_multisig_partial_key_images(); + state.multisig_rounds_passed = m_multisig_rounds_passed; + state.num_transfer_details = m_transfers.size(); + if (state.multisig) + { + THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Monero address not available"); + state.address = m_original_address; + state.view_secret_key = m_original_view_secret_key; + } + else + { + state.address = m_account.get_keys().m_account_address; + state.view_secret_key = m_account.get_keys().m_view_secret_key; + } + state.mms_file=m_mms_file; + return state; +} +//---------------------------------------------------------------------------------------------------- +wallet_device_callback * wallet2::get_device_callback() +{ + if (!m_device_callback){ + m_device_callback.reset(new wallet_device_callback(this)); + } + return m_device_callback.get(); +}//---------------------------------------------------------------------------------------------------- +void wallet2::on_button_request() +{ + if (0 != m_callback) + m_callback->on_button_request(); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_pin_request(epee::wipeable_string & pin) +{ + if (0 != m_callback) + m_callback->on_pin_request(pin); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (0 != m_callback) + m_callback->on_passphrase_request(on_device, passphrase); +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eb0763131..ace01a235 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -37,6 +37,7 @@ #include <boost/serialization/list.hpp> #include <boost/serialization/vector.hpp> #include <boost/serialization/deque.hpp> +#include <boost/thread/lock_guard.hpp> #include <atomic> #include "include_base_utils.h" @@ -49,6 +50,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_tx_utils.h" #include "common/unordered_containers_boost_serialization.h" +#include "common/util.h" #include "crypto/chacha.h" #include "crypto/hash.h" #include "ringct/rctTypes.h" @@ -59,6 +61,7 @@ #include "wallet_errors.h" #include "common/password.h" #include "node_rpc_proxy.h" +#include "message_store.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" @@ -98,11 +101,26 @@ namespace tools virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {} + // Device callbacks + virtual void on_button_request() {} + virtual void on_pin_request(epee::wipeable_string & pin) {} + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} // Common callbacks virtual void on_pool_tx_removed(const crypto::hash &txid) {} virtual ~i_wallet2_callback() {} }; + class wallet_device_callback : public hw::i_device_callback + { + public: + wallet_device_callback(wallet2 * wallet): wallet(wallet) {}; + void on_button_request() override; + void on_pin_request(epee::wipeable_string & pin) override; + void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override; + private: + wallet2 * wallet; + }; + struct tx_dust_policy { uint64_t dust_threshold; @@ -154,6 +172,7 @@ namespace tools { friend class ::Serialization_portability_wallet_Test; friend class wallet_keys_unlocker; + friend class wallet_device_callback; public: static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); @@ -175,6 +194,7 @@ namespace tools static bool has_testnet_option(const boost::program_options::variables_map& vm); static bool has_stagenet_option(const boost::program_options::variables_map& vm); static std::string device_name_option(const boost::program_options::variables_map& vm); + static std::string device_derivation_path_option(const boost::program_options::variables_map &vm); static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. @@ -374,7 +394,7 @@ namespace tools struct multisig_sig { rct::rctSig sigs; - crypto::public_key ignore; + std::unordered_set<crypto::public_key> ignore; std::unordered_set<rct::key> used_L; std::unordered_set<crypto::public_key> signing_keys; rct::multisig_out msout; @@ -592,7 +612,7 @@ namespace tools /*! * \brief Finalizes creation of a multisig wallet */ - bool finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers); + bool finalize_multisig(const epee::wipeable_string &password, const std::unordered_set<crypto::public_key> &pkeys, std::vector<crypto::public_key> signers); /*! * Get a packaged multisig information string */ @@ -655,7 +675,7 @@ namespace tools bool init(std::string daemon_address = "http://localhost:8080", boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); - void stop() { m_run.store(false, std::memory_order_relaxed); } + void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } i_wallet2_callback* callback() const { return m_callback; } void callback(i_wallet2_callback* callback) { m_callback = callback; } @@ -793,6 +813,7 @@ namespace tools bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const; uint64_t get_last_block_reward() const { return m_last_block_reward; } + uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; } template <class t_archive> inline void serialize(t_archive &a, const unsigned int ver) @@ -901,6 +922,9 @@ namespace tools if(ver < 26) return; a & m_tx_device; + if(ver < 27) + return; + a & m_device_last_key_image_sync; } /*! @@ -962,6 +986,8 @@ namespace tools void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } 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; } + void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys); @@ -1045,7 +1071,7 @@ namespace tools * \param account_indices Indices of accounts. * \param tag Tag's name. If empty, the accounts become untagged. */ - void set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag); + void set_account_tag(const std::set<uint32_t> &account_indices, const std::string& tag); /*! * \brief Set the label of the given tag. * \param tag Tag's name (which must be non-empty). @@ -1193,6 +1219,11 @@ namespace tools bool unblackball_output(const std::pair<uint64_t, uint64_t> &output); bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const; + // MMS ------------------------------------------------------------------------------------------------- + mms::message_store& get_message_store() { return m_message_store; }; + const mms::message_store& get_message_store() const { return m_message_store; }; + mms::multisig_wallet_state get_multisig_wallet_state() const; + bool lock_keys_file(); bool unlock_keys_file(); bool is_keys_file_locked() const; @@ -1256,7 +1287,7 @@ namespace tools void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs); void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; - rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; + rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const; void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n); @@ -1285,11 +1316,17 @@ namespace tools void setup_new_blockchain(); void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); + wallet_device_callback * get_device_callback(); + void on_button_request(); + void on_pin_request(epee::wipeable_string & pin); + void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; std::string m_daemon_address; std::string m_wallet_file; std::string m_keys_file; + std::string m_mms_file; epee::net_utils::http::http_simple_client m_http_client; hashchain m_blockchain; std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; @@ -1363,6 +1400,8 @@ namespace tools std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; std::string m_device_name; + std::string m_device_derivation_path; + uint64_t m_device_last_key_image_sync; // Aux transaction data from device std::unordered_map<crypto::hash, std::string> m_tx_device; @@ -1388,6 +1427,11 @@ namespace tools uint64_t m_last_block_reward; std::unique_ptr<tools::file_locker> m_keys_file_locker; + + mms::message_store m_message_store; + bool m_original_keys_available; + cryptonote::account_public_address m_original_address; + crypto::secret_key m_original_view_secret_key; crypto::chacha_key m_cache_key; boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh; @@ -1396,9 +1440,10 @@ namespace tools bool m_devices_registered; std::shared_ptr<tools::Notify> m_tx_notify; + std::unique_ptr<wallet_device_callback> m_device_callback; }; } -BOOST_CLASS_VERSION(tools::wallet2, 26) +BOOST_CLASS_VERSION(tools::wallet2, 27) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index b3141985d..35862bda1 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -219,6 +219,14 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct password_entry_failed : public wallet_runtime_error + { + explicit password_entry_failed(std::string&& loc, const std::string &msg = "Password entry failed") + : wallet_runtime_error(std::move(loc), msg) + { + } + }; + //---------------------------------------------------------------------------------------------------- const char* const file_error_messages[] = { "file already exists", "file not found", @@ -813,6 +821,31 @@ namespace tools std::string m_wallet_file; }; //---------------------------------------------------------------------------------------------------- + struct mms_error : public wallet_logic_error + { + protected: + explicit mms_error(std::string&& loc, const std::string& message) + : wallet_logic_error(std::move(loc), message) + { + } + }; + //---------------------------------------------------------------------------------------------------- + struct no_connection_to_bitmessage : public mms_error + { + explicit no_connection_to_bitmessage(std::string&& loc, const std::string& address) + : mms_error(std::move(loc), "no connection to PyBitmessage at address " + address) + { + } + }; + //---------------------------------------------------------------------------------------------------- + struct bitmessage_api_error : public mms_error + { + explicit bitmessage_api_error(std::string&& loc, const std::string& error_string) + : mms_error(std::move(loc), "PyBitmessage returned " + error_string) + { + } + }; + //---------------------------------------------------------------------------------------------------- #if !defined(_MSC_VER) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 0eb09b9f1..d7dc2914e 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2891,7 +2891,8 @@ namespace tools cryptonote::COMMAND_RPC_GET_HEIGHT::response hres; hres.height = 0; bool r = wal->invoke_http_json("/getheight", hreq, hres); - wal->set_refresh_from_block_height(hres.height); + if (r) + wal->set_refresh_from_block_height(hres.height); crypto::secret_key dummy_key; try { wal->generate(wallet_file, req.password, dummy_key, false, false); diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 7d36a0f68..023c220ae 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -104,5 +104,6 @@ namespace tests cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; } + bool pad_transactions() const { return false; } }; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 6b9277a30..b380aca01 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -511,7 +511,7 @@ inline bool do_replay_events(std::vector<test_event_entry>& events) // FIXME: make sure that vm has arg_testnet_on set to true or false if // this test needs for it to be so. get_test_options<t_test_class> gto; - if (!c.init(vm, NULL, >o.test_options)) + if (!c.init(vm, >o.test_options)) { MERROR("Failed to init core"); return false; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 3c6954bc6..810dec6fc 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -54,9 +54,6 @@ bool test_transaction_generation_and_ring_signature() account_base miner_acc6; miner_acc6.generate(); - std::string add_str = miner_acc3.get_public_address_str(MAINNET); - - account_base rv_acc; rv_acc.generate(); account_base rv_acc2; diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 7f3c6dfe9..7e92c21b9 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -137,7 +137,6 @@ namespace net_load_tests public: open_close_test_helper(test_tcp_server& tcp_server, size_t open_request_target, size_t max_opened_connection_count) : m_tcp_server(tcp_server) - , m_open_request_target(open_request_target) , m_max_opened_connection_count(max_opened_connection_count) , m_opened_connection_count(0) , m_next_opened_conn_idx(0) @@ -203,7 +202,6 @@ namespace net_load_tests private: test_tcp_server& m_tcp_server; - size_t m_open_request_target; size_t m_max_opened_connection_count; std::atomic<size_t> m_opened_connection_count; std::atomic<size_t> m_next_opened_conn_idx; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 7687e3c52..f7012746d 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -54,6 +54,7 @@ set(unit_tests_sources hashchain.cpp http.cpp keccak.cpp + logging.cpp main.cpp memwipe.cpp mlocker.cpp @@ -62,6 +63,7 @@ set(unit_tests_sources multiexp.cpp multisig.cpp notify.cpp + output_distribution.cpp parse_amount.cpp random.cpp serialization.cpp diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index e3dbdaef1..12625a949 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -83,6 +83,7 @@ public: cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; } + bool pad_transactions() { return false; } void stop() {} }; diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 29fa88f9d..e09ec7f7a 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -47,6 +47,9 @@ namespace "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + template<typename T> void *addressof(T &t) { return &t; } + template<> void *addressof(crypto::secret_key &k) { return addressof(unwrap(unwrap(k))); } + template<typename T> bool is_formatted() { @@ -55,7 +58,7 @@ namespace static_assert(alignof(T) == 1, "T must have 1 byte alignment"); static_assert(sizeof(T) <= sizeof(source), "T is too large for source"); static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination"); - std::memcpy(std::addressof(value), source, sizeof(T)); + std::memcpy(addressof(value), source, sizeof(T)); std::stringstream out; out << "BEGIN" << value << "END"; diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 72d8f3205..10e62c167 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -294,7 +294,7 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_initial TEST_F(positive_test_connection_to_levin_protocol_handler_calls, concurent_handler_initialization_and_destruction_is_correct) { const size_t connection_count = 10000; - auto create_and_destroy_connections = [this, connection_count]() + auto create_and_destroy_connections = [this]() { std::vector<test_connection_ptr> connections(connection_count); for (size_t i = 0; i < connection_count; ++i) diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index c2b0b7647..c384ce9a5 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -456,6 +456,35 @@ TEST(StringTools, PodToHex) ); } +TEST(StringTools, ParseHex) +{ + static const char data[] = "a10b68c2"; + for (size_t i = 0; i < sizeof(data); i += 2) + { + std::string res; + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(std::string(data, i), res)); + std::string hex = epee::string_tools::buff_to_hex_nodelimer(res); + ASSERT_EQ(hex.size(), i); + ASSERT_EQ(memcmp(data, hex.data(), i), 0); + } +} + +TEST(StringTools, ParseNotHex) +{ + std::string res; + for (size_t i = 0; i < 256; ++i) + { + std::string inputHexString = std::string(2, static_cast<char>(i)); + if ((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f')) { + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(inputHexString, res)); + } else { + ASSERT_FALSE(epee::string_tools::parse_hexstr_to_binbuff(inputHexString, res)); + } + } + + ASSERT_FALSE(epee::string_tools::parse_hexstr_to_binbuff(std::string("a"), res)); +} + TEST(StringTools, GetIpString) { EXPECT_EQ( diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index fc488bb14..ec8d1d202 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -34,101 +34,19 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" +#include "testdb.h" using namespace cryptonote; #define BLOCKS_PER_YEAR 525960 #define SECONDS_PER_YEAR 31557600 +namespace +{ -class TestDB: public BlockchainDB { +class TestDB: public BaseTestDB { public: - TestDB() {}; - virtual void open(const std::string& filename, const int db_flags = 0) { } - virtual void close() {} - virtual void sync() {} - virtual void safesyncmode(const bool onoff) {} - virtual void reset() {} - virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); } - virtual bool remove_data_file(const std::string& folder) const { return true; } - virtual std::string get_db_name() const { return std::string(); } - virtual bool lock() { return true; } - virtual void unlock() { } - virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } - virtual void batch_stop() {} - virtual void set_batch_transactions(bool) {} - virtual void block_txn_start(bool readonly=false) {} - virtual void block_txn_stop() {} - virtual void block_txn_abort() {} - virtual void drop_hard_fork_info() {} - virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } - virtual blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } - virtual blobdata get_block_blob(const crypto::hash& h) const { return blobdata(); } - virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } - virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } - virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } - virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } - virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } - virtual uint64_t get_top_block_timestamp() const { return 0; } - virtual size_t get_block_weight(const uint64_t& height) const { return 128; } - virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } - virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } - virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<block>(); } - virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } - virtual crypto::hash top_block_hash() const { return crypto::hash(); } - virtual block get_top_block() const { return block(); } virtual uint64_t height() const { return blocks.size(); } - virtual bool tx_exists(const crypto::hash& h) const { return false; } - virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } - virtual transaction get_tx(const crypto::hash& h) const { return transaction(); } - virtual bool get_tx(const crypto::hash& h, transaction &tx) const { return false; } - virtual uint64_t get_tx_count() const { return 0; } - virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const { return std::vector<transaction>(); } - 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 output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } - virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } - virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } - 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 void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) {} - 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 bool has_key_image(const crypto::key_image& img) const { return false; } - virtual void remove_block() { blocks.pop_back(); } - virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} - virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) {} - virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} - virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {} - virtual void add_spent_key(const crypto::key_image& k_image) {} - virtual void remove_spent_key(const crypto::key_image& k_image) {} - - virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const { return true; } - virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const { return true; } - virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const { return true; } - virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const { return true; } - virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const { return true; } - virtual bool is_read_only() const { return false; } - virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); } - virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const { return false; } - - virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& details) {} - virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } - virtual void remove_txpool_tx(const crypto::hash& txid) {} - virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } - virtual uint64_t get_database_size() const { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } - virtual void add_block( const block& blk , size_t block_weight , const difficulty_type& cumulative_difficulty @@ -138,6 +56,7 @@ public: ) { blocks.push_back(blk); } + virtual void remove_block() { blocks.pop_back(); } virtual block get_block_from_height(const uint64_t& height) const { return blocks.at(height); } @@ -149,13 +68,14 @@ public: virtual uint8_t get_hard_fork_version(uint64_t height) const { return versions.at(height); } - virtual void check_hard_fork_info() {} private: std::vector<block> blocks; std::deque<uint8_t> versions; }; +} + static cryptonote::block mkblock(uint8_t version, uint8_t vote) { cryptonote::block b; diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp new file mode 100644 index 000000000..476e92bef --- /dev/null +++ b/tests/unit_tests/logging.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2016-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include <boost/filesystem.hpp> +#include "gtest/gtest.h" +#include "file_io_utils.h" +#include "misc_log_ex.h" + +static std::string log_filename; + +static void init() +{ + boost::filesystem::path p = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + log_filename = p.string(); + mlog_configure(log_filename, false, 0); +} + +static void cleanup() +{ + boost::filesystem::remove(log_filename); +} + +static size_t nlines(const std::string &str) +{ + size_t n = 0; + for (const char *ptr = str.c_str(); *ptr; ++ptr) + if (*ptr == '\n') + ++n; + return n; +} + +static bool load_log_to_string(const std::string &filename, std::string &str) +{ + if (!epee::file_io_utils::load_file_to_string(filename, str)) + return false; + for (const char *ptr = str.c_str(); *ptr; ++ptr) + { + if (*ptr == '\n') + { + std::string prefix = std::string(str.c_str(), ptr - str.c_str()); + if (prefix.find("New log categories:") != std::string::npos) + { + str = std::string(ptr + 1, strlen(ptr + 1)); + break; + } + } + } + return true; +} + +static void log() +{ + MFATAL("fatal"); + MERROR("error"); + MWARNING("warning"); + MINFO("info"); + MDEBUG("debug"); + MTRACE("trace"); + + MCINFO("a.b.c.d", "a.b.c.d"); + MCINFO("a.b.c.e", "a.b.c.e"); + MCINFO("global", "global"); + MCINFO("x.y.z", "x.y.z"); + MCINFO("y.y.z", "y.y.z"); + MCINFO("x.y.x", "x.y.x"); +} + +TEST(logging, no_logs) +{ + init(); + mlog_set_categories(""); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str == ""); + cleanup(); +} + +TEST(logging, default) +{ + init(); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("fatal") != std::string::npos); + ASSERT_TRUE(str.find("error") != std::string::npos); + ASSERT_TRUE(str.find("debug") == std::string::npos); + ASSERT_TRUE(str.find("trace") == std::string::npos); + cleanup(); +} + +TEST(logging, all) +{ + init(); + mlog_set_categories("*:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("fatal") != std::string::npos); + ASSERT_TRUE(str.find("error") != std::string::npos); + ASSERT_TRUE(str.find("debug") != std::string::npos); + ASSERT_TRUE(str.find("trace") != std::string::npos); + cleanup(); +} + +TEST(logging, glob_suffix) +{ + init(); + mlog_set_categories("x.y*:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") == std::string::npos); + ASSERT_TRUE(str.find("x.y.z") != std::string::npos); + ASSERT_TRUE(str.find("x.y.x") != std::string::npos); + ASSERT_TRUE(str.find("y.y.z") == std::string::npos); + cleanup(); +} + +TEST(logging, glob_prefix) +{ + init(); + mlog_set_categories("*y.z:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") == std::string::npos); + ASSERT_TRUE(str.find("x.y.z") != std::string::npos); + ASSERT_TRUE(str.find("x.y.x") == std::string::npos); + ASSERT_TRUE(str.find("y.y.z") != std::string::npos); + cleanup(); +} + +TEST(logging, last_precedence) +{ + init(); + mlog_set_categories("gobal:FATAL,glo*:DEBUG"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(nlines(str) == 1); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("x.y.z") == std::string::npos); + ASSERT_TRUE(str.find("x.y.x") == std::string::npos); + ASSERT_TRUE(str.find("y.y.z") == std::string::npos); + cleanup(); +} + diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 0b74a6b94..4dc2d931e 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -75,7 +75,6 @@ namespace */ void test_language(const Language::Base &language) { - const std::vector<std::string> &word_list = language.get_word_list(); epee::wipeable_string w_seed = "", w_return_seed = ""; std::string seed, return_seed; // Generate a random seed without checksum diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 8408d0da1..768b95d4b 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "common/int-util.h" +#include "int-util.h" namespace { diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 7268f2690..eb3196863 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -112,7 +112,7 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M) } for (auto& wallet: wallets) { - ASSERT_FALSE(wallet.multisig() || wallet.multisig() || wallet.multisig()); + ASSERT_FALSE(wallet.multisig()); } std::vector<std::string> mxis; diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index edc4eabdf..105d21ca8 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -49,7 +49,7 @@ TEST(notify, works) tmp = "/tmp"; static const char *filename = "monero-notify-unit-test-XXXXXX"; const size_t len = strlen(tmp) + 1 + strlen(filename); - std::unique_ptr<char[]> name_template_((char*)malloc(len + 1)); + std::unique_ptr<char[]> name_template_(new char[len + 1]); char *name_template = name_template_.get(); ASSERT_TRUE(name_template != NULL); snprintf(name_template, len + 1, "%s/%s", tmp, filename); diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp new file mode 100644 index 000000000..649752ac7 --- /dev/null +++ b/tests/unit_tests/output_distribution.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" +#include "misc_log_ex.h" +#include "rpc/rpc_handler.h" +#include "blockchain_db/blockchain_db.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/blockchain.h" +#include "testdb.h" + +static const uint64_t test_distribution[32] = { + 0, 0, 0, 0, 0, 1, 5, 1, 4, 0, 0, 1, 0, 1, 2, 3, 1, 0, 2, 0, 1, 3, 8, 1, 3, 5, 7, 1, 5, 0, 2, 3 +}; +static const size_t test_distribution_size = sizeof(test_distribution) / sizeof(test_distribution[0]); + +namespace +{ + +class TestDB: public BaseTestDB +{ +public: + TestDB(size_t bc_height = test_distribution_size): blockchain_height(bc_height) { m_open = true; } + virtual uint64_t height() const override { return blockchain_height; } + + std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const override + { + std::vector<uint64_t> d; + for (uint64_t h: heights) + { + uint64_t c = 0; + for (uint64_t i = 0; i <= h; ++i) + c += test_distribution[i]; + d.push_back(c); + } + return d; + } + + uint64_t blockchain_height; +}; + +} + +bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) +{ + std::unique_ptr<cryptonote::Blockchain> bc; + cryptonote::tx_memory_pool txpool(*bc); + bc.reset(new cryptonote::Blockchain(txpool)); + struct get_test_options { + const std::pair<uint8_t, uint64_t> hard_forks[2]; + const cryptonote::test_options test_options = { + hard_forks + }; + get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} + } opts; + cryptonote::Blockchain *blockchain = bc.get(); + bool r = blockchain->init(new TestDB(test_distribution_size), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); + return r && bc->get_output_distribution(amount, from, to, start_height, distribution, base); +} + +TEST(output_distribution, extend) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({5, 0})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({55, 55})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({5, 0, 2})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({55, 55, 57})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({5, 0, 2, 3})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({55, 55, 57, 60})); +} + +TEST(output_distribution, one) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 0, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 1); + ASSERT_EQ(res->distribution.back(), 0); +} + +TEST(output_distribution, full_cumulative) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + ASSERT_EQ(res->distribution.back(), 60); +} + +TEST(output_distribution, full_noncumulative) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + for (size_t i = 0; i < 32; ++i) + ASSERT_EQ(res->distribution[i], test_distribution[i]); +} + +TEST(output_distribution, part_cumulative) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({0, 1, 6, 7, 11})); +} + +TEST(output_distribution, part_noncumulative) +{ + boost::optional<cryptonote::rpc::output_distribution_data> res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector<uint64_t>({0, 1, 5, 1, 4})); +} diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 52bdb00cf..ccef5f3e7 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -968,8 +968,6 @@ static rctSig make_sig() #define TEST_rctSig_elements(name, op) \ TEST(ringct, rctSig_##name) \ { \ - const uint64_t inputs[] = {1000, 1000}; \ - const uint64_t outputs[] = {1000, 1000}; \ rct::rctSig sig = make_sig(); \ ASSERT_TRUE(rct::verRct(sig)); \ op; \ diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index 1c67f5b58..436259bee 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -45,7 +45,7 @@ //#define VERBOSE #ifdef TEST_ORIGINAL -uint64_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t patlen) +size_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t patlen) { void* buf = start_buff; void* end=(char*)buf+buflen-patlen; @@ -63,7 +63,7 @@ uint64_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t p #define slow_memmem slow_memmem_original #else namespace cryptonote { - uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen); + size_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen); } using namespace cryptonote; #endif @@ -73,7 +73,7 @@ static const struct { const char *buf; size_t patlen; const char *pat; - uint64_t res; + size_t res; } T[]={ {0,"",0,"",0}, {1,"",0,"",0}, @@ -117,7 +117,7 @@ TEST(slowmem,Success) memcpy(buf,T[n].buf,T[n].buflen); void *pat=malloc(T[n].patlen); memcpy(pat,T[n].pat,T[n].patlen); - uint64_t res=slow_memmem(buf,T[n].buflen,pat,T[n].patlen); + size_t res=slow_memmem(buf,T[n].buflen,pat,T[n].patlen); free(pat); free(buf); ASSERT_EQ(res,T[n].res); diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp index 8ff4c5f0d..67802d736 100644 --- a/tests/unit_tests/subaddress.cpp +++ b/tests/unit_tests/subaddress.cpp @@ -49,7 +49,7 @@ class WalletSubaddress : public ::testing::Test catch (const std::exception& e) { LOG_ERROR("failed to generate wallet: " << e.what()); - throw e; + throw; } w1.add_subaddress_account(test_label); diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h new file mode 100644 index 000000000..5d9ba5833 --- /dev/null +++ b/tests/unit_tests/testdb.h @@ -0,0 +1,147 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include <string> +#include <vector> +#include <map> +#include "gtest/gtest.h" + +#include "blockchain_db/blockchain_db.h" + +class BaseTestDB: public cryptonote::BlockchainDB { +public: + BaseTestDB() {} + virtual void open(const std::string& filename, const int db_flags = 0) { } + virtual void close() {} + virtual void sync() {} + virtual void safesyncmode(const bool onoff) {} + virtual void reset() {} + virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); } + virtual bool remove_data_file(const std::string& folder) const { return true; } + virtual std::string get_db_name() const { return std::string(); } + virtual bool lock() { return true; } + virtual void unlock() { } + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } + virtual void batch_stop() {} + virtual void set_batch_transactions(bool) {} + virtual void block_txn_start(bool readonly=false) {} + virtual void block_txn_stop() {} + virtual void block_txn_abort() {} + virtual void drop_hard_fork_info() {} + virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); } + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } + virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } + virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); } + virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } + virtual uint64_t get_top_block_timestamp() const { return 0; } + virtual size_t get_block_weight(const uint64_t& height) const { return 128; } + virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } + virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } + virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); } + virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } + virtual crypto::hash top_block_hash() const { return crypto::hash(); } + virtual cryptonote::block get_top_block() const { return cryptonote::block(); } + virtual uint64_t height() const { return 1; } + virtual bool tx_exists(const crypto::hash& h) const { return false; } + virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } + virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } + virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); } + virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; } + virtual uint64_t get_tx_count() const { return 0; } + virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const { return std::vector<cryptonote::transaction>(); } + 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::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 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 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;} + virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {} + virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} + virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {} + virtual void add_spent_key(const crypto::key_image& k_image) {} + virtual void remove_spent_key(const crypto::key_image& k_image) {} + + virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const { return true; } + virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const { return true; } + virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const { return true; } + virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const { return true; } + virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const { return true; } + virtual bool is_read_only() const { return false; } + virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); } + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const { return false; } + + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) {} + virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {} + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } + virtual void remove_txpool_tx(const crypto::hash& txid) {} + virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } + virtual uint64_t get_database_size() const { return 0; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) { } + virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) {} + virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + virtual void check_hard_fork_info() {} + + virtual uint32_t get_blockchain_pruning_seed() const { return 0; } + virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } + virtual bool update_pruning() { return true; } + virtual bool check_pruning() { return true; } + virtual void prune_outputs(uint64_t amount) {} +}; + diff --git a/translations/monero.ts b/translations/monero.ts index e28a2a058..23eadff84 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -1196,7 +1196,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1356"/> - <source>usage: print_ring <key_image|txid></source> + <source>usage: print_ring <key_image> | <txid></source> <translation type="unfinished"></translation> </message> <message> @@ -1453,7 +1453,10 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2247"/> - <source>Show the incoming transfers, all or filtered by availability and address index.</source> + <source>Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image]</source> <translation type="unfinished"></translation> </message> <message> @@ -1976,7 +1979,10 @@ Pending or Failed: "failed"|"pending", "o </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2534"/> - <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)</source> + <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + +Output format: +Key Image, "absolute", list of rings</source> <translation type="unfinished"></translation> </message> <message> @@ -2826,7 +2832,7 @@ your wallet again (your wallet keys are NOT at risk in any case). <message> <location filename="../src/simplewallet/simplewallet.cpp" line="4317"/> <location filename="../src/simplewallet/simplewallet.cpp" line="4359"/> - <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</source> + <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</source> <translation type="unfinished"></translation> </message> <message> diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index 27c38ee10..238ba17df 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -1541,8 +1541,11 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2247"/> - <source>Show the incoming transfers, all or filtered by availability and address index.</source> - <translation>Afficher les transferts entrants, tous ou filtrés par disponibilité et index d'adresse.</translation> + <source>Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image]</source> + <translation type="unfinished">Afficher les transferts entrants, tous ou filtrés par disponibilité et index d'adresse.</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2250"/> @@ -2271,8 +2274,8 @@ votre portefeuille à nouveau (mais les clés de votre portefeuille ne risquent <message> <location filename="../src/simplewallet/simplewallet.cpp" line="4317"/> <location filename="../src/simplewallet/simplewallet.cpp" line="4359"/> - <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</source> - <translation>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</translation> + <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</source> + <translation>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="4378"/> @@ -2984,8 +2987,11 @@ subaddress-lookahead <major>:<minor> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2534"/> - <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)</source> - <translation>Afficher le(s) cercle(s) utilisé(s) pour dépenser une image de clé ou une transaction (si la taille de cercle est > 1)</translation> + <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + +Output format: +Key Image, "absolute", list of rings</source> + <translation type="unfinished">Afficher le(s) cercle(s) utilisé(s) pour dépenser une image de clé ou une transaction (si la taille de cercle est > 1)</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2537"/> diff --git a/translations/monero_it.ts b/translations/monero_it.ts index 5ab96d7dc..09872fea8 100644 --- a/translations/monero_it.ts +++ b/translations/monero_it.ts @@ -1364,8 +1364,11 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1586"/> - <source>Show the incoming transfers, all or filtered by availability and address index.</source> - <translation>Mostra i trasferimenti in entrata, tutti o filtrati per disponibilità ed indice di indirizzo.</translation> + <source>Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image]</source> + <translation type="unfinished">Mostra i trasferimenti in entrata, tutti o filtrati per disponibilità ed indice di indirizzo.</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1589"/> @@ -2191,7 +2194,7 @@ your wallet again (your wallet keys are NOT at risk in any case). <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3339"/> <location filename="../src/simplewallet/simplewallet.cpp" line="3381"/> - <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</source> + <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</source> <translation type="unfinished"></translation> </message> <message> diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts index 17815d982..7305b42f8 100644 --- a/translations/monero_ja.ts +++ b/translations/monero_ja.ts @@ -1461,7 +1461,10 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1586"/> - <source>Show the incoming transfers, all or filtered by availability and address index.</source> + <source>Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image]</source> <translation type="unfinished"></translation> </message> <message> @@ -2279,7 +2282,7 @@ your wallet again (your wallet keys are NOT at risk in any case). <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3339"/> <location filename="../src/simplewallet/simplewallet.cpp" line="3381"/> - <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</source> + <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</source> <translation type="unfinished"></translation> </message> <message> diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts index b2387cdb1..26ad43f7b 100644 --- a/translations/monero_sv.ts +++ b/translations/monero_sv.ts @@ -1469,8 +1469,11 @@ Denna transaktion låses upp vid block %llu, om ungefär %s dagar (förutsatt en </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1586"/> - <source>Show the incoming transfers, all or filtered by availability and address index.</source> - <translation>Visa inkommande överföringar: alla eller filtrerade efter tillgänglighet och adressindex.</translation> + <source>Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image]</source> + <translation type="unfinished">Visa inkommande överföringar: alla eller filtrerade efter tillgänglighet och adressindex.</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1589"/> @@ -2351,8 +2354,8 @@ din plånbok igen (din plånboks nycklar är dock INTE hotade i vilket fall som <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3339"/> <location filename="../src/simplewallet/simplewallet.cpp" line="3381"/> - <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]</source> - <translation>användning: incoming_transfers [available|unavailable] [verbose] [index=<N>]</translation> + <source>usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</source> + <translation>användning: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3400"/> |