aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt13
-rw-r--r--Dockerfile14
-rw-r--r--README.md4
-rw-r--r--cmake/CheckTrezor.cmake79
-rw-r--r--cmake/FindLibUSB.cmake140
-rw-r--r--cmake/test-libusb-version.c52
-rw-r--r--contrib/depends/Makefile18
-rw-r--r--contrib/depends/README.md14
-rw-r--r--contrib/depends/config.site.in19
-rw-r--r--contrib/depends/packages/bdb.mk31
-rw-r--r--contrib/depends/packages/ldns.mk2
-rw-r--r--contrib/depends/packages/libICE.mk2
-rw-r--r--contrib/depends/packages/libSM.mk2
-rw-r--r--contrib/depends/packages/libusb.mk8
-rw-r--r--contrib/depends/packages/miniupnpc.mk28
-rw-r--r--contrib/depends/packages/native_cdrkit.mk2
-rw-r--r--contrib/depends/packages/packages.mk2
-rw-r--r--contrib/depends/packages/qt.mk2
-rw-r--r--contrib/depends/packages/unbound.mk2
-rw-r--r--contrib/depends/packages/unwind.mk2
-rw-r--r--contrib/depends/packages/xproto.mk2
-rw-r--r--contrib/depends/packages/zlib.mk2
-rw-r--r--contrib/epee/include/int-util.h (renamed from src/common/int-util.h)0
-rw-r--r--contrib/epee/include/misc_os_dependent.h9
-rw-r--r--contrib/epee/include/mlocker.h2
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h4
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl18
-rw-r--r--contrib/epee/include/net/connection_basic.hpp13
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h3
-rw-r--r--contrib/epee/include/net/levin_client.inl41
-rw-r--r--contrib/epee/include/net/levin_helper.h41
-rw-r--r--contrib/epee/include/net/levin_protocol_handler.h19
-rw-r--r--contrib/epee/include/net/levin_protocol_handler_async.h60
-rw-r--r--contrib/epee/include/span.h2
-rw-r--r--contrib/epee/include/storages/portable_storage.h9
-rw-r--r--contrib/epee/include/string_tools.h50
-rw-r--r--contrib/epee/src/connection_basic.cpp33
-rw-r--r--contrib/epee/src/mlocker.cpp31
-rw-r--r--contrib/epee/src/mlog.cpp8
-rw-r--r--contrib/epee/src/net_utils_base.cpp2
-rw-r--r--contrib/epee/src/network_throttle-detail.cpp16
-rw-r--r--contrib/epee/src/readline_buffer.cpp4
-rw-r--r--contrib/epee/tests/src/net/test_net.h4
-rw-r--r--contrib/valgrind/monero.supp9
-rw-r--r--external/easylogging++/easylogging++.cc15
-rw-r--r--external/easylogging++/easylogging++.h9
-rw-r--r--src/blockchain_db/blockchain_db.h12
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp111
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h4
-rw-r--r--src/blockchain_utilities/CMakeLists.txt33
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp96
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp2
-rw-r--r--src/blockchain_utilities/blockchain_prune_known_spent_data.cpp305
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp2
-rw-r--r--src/checkpoints/checkpoints.cpp8
-rw-r--r--src/checkpoints/checkpoints.h1
-rw-r--r--src/common/CMakeLists.txt7
-rw-r--r--src/common/base58.cpp3
-rw-r--r--src/common/base58.h2
-rw-r--r--src/common/combinator.cpp50
-rw-r--r--src/common/combinator.h96
-rw-r--r--src/common/command_line.cpp3
-rw-r--r--src/common/dns_utils.cpp4
-rw-r--r--src/common/download.cpp3
-rw-r--r--src/common/http_connection.h3
-rw-r--r--src/common/i18n.cpp2
-rw-r--r--src/common/password.cpp3
-rw-r--r--src/common/scoped_message_writer.h2
-rw-r--r--src/common/threadpool.cpp4
-rw-r--r--src/common/util.cpp12
-rw-r--r--src/common/util.h2
-rw-r--r--src/crypto/aesb.c2
-rw-r--r--src/crypto/chacha.c2
-rw-r--r--src/crypto/crypto.cpp1
-rw-r--r--src/crypto/crypto.h3
-rw-r--r--src/crypto/groestl_tables.h2
-rw-r--r--src/crypto/hash-ops.h2
-rw-r--r--src/crypto/keccak.c2
-rw-r--r--src/crypto/skein_port.h2
-rw-r--r--src/crypto/slow-hash.c43
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h17
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp4
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.h2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp9
-rw-r--r--src/cryptonote_basic/difficulty.cpp2
-rw-r--r--src/cryptonote_basic/miner.cpp33
-rw-r--r--src/cryptonote_basic/miner.h5
-rw-r--r--src/cryptonote_core/blockchain.cpp205
-rw-r--r--src/cryptonote_core/blockchain.h11
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp31
-rw-r--r--src/cryptonote_core/cryptonote_core.h12
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h6
-rw-r--r--src/cryptonote_core/tx_pool.cpp2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp22
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl39
-rw-r--r--src/daemon/command_parser_executor.cpp25
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp6
-rw-r--r--src/daemon/core.h17
-rw-r--r--src/daemon/daemon.cpp9
-rw-r--r--src/daemon/main.cpp20
-rw-r--r--src/daemon/rpc.h3
-rw-r--r--src/daemon/rpc_command_executor.cpp27
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/daemonizer/posix_fork.cpp1
-rw-r--r--src/daemonizer/windows_service.cpp3
-rw-r--r--src/debug_utilities/cn_deserialize.cpp5
-rw-r--r--src/device/device.hpp10
-rw-r--r--src/device/device_default.cpp2
-rw-r--r--src/device/device_ledger.cpp2
-rw-r--r--src/device_trezor/CMakeLists.txt42
-rw-r--r--src/device_trezor/device_trezor.cpp80
-rw-r--r--src/device_trezor/device_trezor.hpp7
-rw-r--r--src/device_trezor/device_trezor_base.cpp222
-rw-r--r--src/device_trezor/device_trezor_base.hpp156
-rw-r--r--src/device_trezor/trezor.hpp2
-rw-r--r--src/device_trezor/trezor/tools/README.md41
-rw-r--r--src/device_trezor/trezor/tools/pb2cpp.py67
-rw-r--r--src/device_trezor/trezor/tools/py2backports/__init__.py0
-rw-r--r--src/device_trezor/trezor/tools/py2backports/tempfile.py72
-rw-r--r--src/device_trezor/trezor/tools/py2backports/weakref.py148
-rw-r--r--src/device_trezor/trezor/transport.cpp403
-rw-r--r--src/device_trezor/trezor/transport.hpp67
-rw-r--r--src/device_trezor/trezor/trezor_defs.hpp39
-rw-r--r--src/mnemonics/electrum-words.cpp10
-rw-r--r--src/mnemonics/electrum-words.h1
-rw-r--r--src/p2p/net_node.h4
-rw-r--r--src/ringct/bulletproofs.cc25
-rw-r--r--src/ringct/rctOps.cpp346
-rw-r--r--src/ringct/rctSigs.cpp35
-rw-r--r--src/ringct/rctSigs.h4
-rw-r--r--src/ringct/rctTypes.h3
-rw-r--r--src/rpc/CMakeLists.txt3
-rw-r--r--src/rpc/core_rpc_server.cpp148
-rw-r--r--src/rpc/core_rpc_server.h7
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h91
-rw-r--r--src/rpc/daemon_handler.cpp4
-rw-r--r--src/rpc/daemon_messages.cpp72
-rw-r--r--src/rpc/message_data_structs.h1
-rw-r--r--src/rpc/rpc_handler.cpp23
-rw-r--r--src/rpc/rpc_handler.h2
-rw-r--r--src/rpc/zmq_server.cpp1
-rw-r--r--src/serialization/json_archive.h2
-rw-r--r--src/simplewallet/simplewallet.cpp1627
-rw-r--r--src/simplewallet/simplewallet.h50
-rw-r--r--src/wallet/CMakeLists.txt9
-rw-r--r--src/wallet/message_store.cpp1445
-rw-r--r--src/wallet/message_store.h420
-rw-r--r--src/wallet/message_transporter.cpp317
-rw-r--r--src/wallet/message_transporter.h113
-rw-r--r--src/wallet/node_rpc_proxy.cpp1
-rw-r--r--src/wallet/ringdb.cpp1
-rw-r--r--src/wallet/wallet2.cpp310
-rw-r--r--src/wallet/wallet2.h57
-rw-r--r--src/wallet/wallet_errors.h33
-rw-r--r--src/wallet/wallet_rpc_server.cpp3
-rw-r--r--tests/core_proxy/core_proxy.h1
-rw-r--r--tests/core_tests/chaingen.h2
-rw-r--r--tests/core_tests/transaction_tests.cpp3
-rw-r--r--tests/net_load_tests/net_load_tests.h2
-rw-r--r--tests/unit_tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/ban.cpp1
-rw-r--r--tests/unit_tests/crypto.cpp5
-rw-r--r--tests/unit_tests/epee_levin_protocol_handler_async.cpp2
-rw-r--r--tests/unit_tests/epee_utils.cpp29
-rw-r--r--tests/unit_tests/hardfork.cpp94
-rw-r--r--tests/unit_tests/logging.cpp177
-rw-r--r--tests/unit_tests/mnemonics.cpp1
-rw-r--r--tests/unit_tests/mul_div.cpp2
-rw-r--r--tests/unit_tests/multisig.cpp2
-rw-r--r--tests/unit_tests/notify.cpp2
-rw-r--r--tests/unit_tests/output_distribution.cpp171
-rw-r--r--tests/unit_tests/ringct.cpp2
-rw-r--r--tests/unit_tests/slow_memmem.cpp8
-rw-r--r--tests/unit_tests/subaddress.cpp2
-rw-r--r--tests/unit_tests/testdb.h147
-rw-r--r--translations/monero.ts14
-rw-r--r--translations/monero_fr.ts18
-rw-r--r--translations/monero_it.ts9
-rw-r--r--translations/monero_ja.ts7
-rw-r--r--translations/monero_sv.ts11
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 \
diff --git a/README.md b/README.md
index 472ca580c..15953e8a9 100644
--- a/README.md
+++ b/README.md
@@ -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>> &registry) {
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>> &registry);
-#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 &param)
+{
+ 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 &param)
+{
+ // 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 &param)
+{
+ 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 &param);
+ static void add_xml_rpc_base64_param(std::string &xml, const std::string &param);
+ static void add_xml_rpc_integer_param(std::string &xml, const int32_t &param);
+ 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, &gto.test_options))
+ if (!c.init(vm, &gto.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 &lt;key_image|txid&gt;</source>
+ <source>usage: print_ring &lt;key_image&gt; | &lt;txid&gt;</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(&quot;T&quot;|&quot;F&quot;), &quot;locked&quot;|&quot;unlocked&quot;, 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: &quot;failed&quot;|&quot;pending&quot;, &quot;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 &gt; 1)</source>
+ <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is &gt; 1)
+
+Output format:
+Key Image, &quot;absolute&quot;, 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=&lt;N&gt;]</source>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</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&apos;adresse.</translation>
+ <source>Show the incoming transfers, all or filtered by availability and address index.
+
+Output format:
+Amount, Spent(&quot;T&quot;|&quot;F&quot;), &quot;locked&quot;|&quot;unlocked&quot;, 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&apos;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=&lt;N&gt;]</source>
- <translation>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N&gt;]</translation>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</source>
+ <translation>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</translation>
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="4378"/>
@@ -2984,8 +2987,11 @@ subaddress-lookahead &lt;major&gt;:&lt;minor&gt;
</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 &gt; 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 &gt; 1)</translation>
+ <source>Print the ring(s) used to spend a given key image or transaction (if the ring size is &gt; 1)
+
+Output format:
+Key Image, &quot;absolute&quot;, 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 &gt; 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(&quot;T&quot;|&quot;F&quot;), &quot;locked&quot;|&quot;unlocked&quot;, 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=&lt;N&gt;]</source>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</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(&quot;T&quot;|&quot;F&quot;), &quot;locked&quot;|&quot;unlocked&quot;, 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=&lt;N&gt;]</source>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</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(&quot;T&quot;|&quot;F&quot;), &quot;locked&quot;|&quot;unlocked&quot;, 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=&lt;N&gt;]</source>
- <translation>användning: incoming_transfers [available|unavailable] [verbose] [index=&lt;N&gt;]</translation>
+ <source>usage: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</source>
+ <translation>användning: incoming_transfers [available|unavailable] [verbose] [index=&lt;N1&gt;[,&lt;N2&gt;[,...]]]</translation>
</message>
<message>
<location filename="../src/simplewallet/simplewallet.cpp" line="3400"/>