diff options
92 files changed, 2573 insertions, 927 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d506d817..aaad0c3a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,9 +20,9 @@ jobs: - uses: actions/checkout@v1 with: submodules: recursive - - uses: numworks/setup-msys2@v1 - - name: update pacman - run: msys2do pacman -Syu --noconfirm + - uses: eine/setup-msys2@v0 + with: + update: true - name: install monero dependencies run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git - name: build diff --git a/CMakeLists.txt b/CMakeLists.txt index 2089b7254..7a739cd58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,12 @@ message(STATUS "CMake version ${CMAKE_VERSION}") project(monero) -include(FindCcache) # Has to be included after the project() macro, to be able to read the CXX variable. +option (USE_CCACHE "Use ccache if a usable instance is found" ON) +if (USE_CCACHE) + include(FindCcache) # Has to be included after the project() macro, to be able to read the CXX variable. +else() + message(STATUS "ccache deselected") +endif() enable_language(C ASM) @@ -262,6 +267,12 @@ else() endif() option(BUILD_DEBUG_UTILITIES "Build debug utilities." DEFAULT_BUILD_DEBUG_UTILITIES) +if(OSSFUZZ) + message(STATUS "Using OSS-Fuzz fuzzing system") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DOSSFUZZ") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSSFUZZ") +endif() + # Check whether we're on a 32-bit or 64-bit system if(CMAKE_SIZEOF_VOID_P EQUAL "8") set(DEFAULT_BUILD_64 ON) @@ -646,7 +657,8 @@ else() endif() # linker - if (NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))) + if (NOT SANITIZE AND NOT OSSFUZZ AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))) + # PIE executables randomly crash at startup with ASAN # Windows binaries die on startup with PIE when compiled with GCC <9.x add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS) endif() @@ -768,7 +780,8 @@ else() endif(ARM) - if(ANDROID AND NOT BUILD_GUI_DEPS STREQUAL "ON" OR IOS) + # random crash on startup when asan is on if pie is enabled + if(NOT SANITIZE AND ANDROID AND NOT BUILD_GUI_DEPS STREQUAL "ON" OR IOS) #From Android 5: "only position independent executables (PIE) are supported" message(STATUS "Enabling PIE executable") set(PIC_FLAG "") @@ -90,7 +90,7 @@ As with many development projects, the repository on Github is considered to be Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard. Alternatively you can send XMR to the Monero donation address via the `donate` command (type `help` in the command-line wallet for details). -The Monero donation address is: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`) +The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`) The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H` @@ -132,7 +132,7 @@ Dates are provided in the format YYYY-MM-DD. | 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format | 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format -| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs +| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs | XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. @@ -219,7 +219,7 @@ invokes cmake commands as needed. ```bash cd monero - git checkout release-v0.15 + git checkout release-v0.16 make ``` @@ -292,7 +292,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.15.0.0 + git checkout tags/v0.16.0.0 ``` * Build: @@ -409,10 +409,10 @@ application. cd monero ``` -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.15.0.0'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.16.0.0'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.15.0.0 + git checkout v0.16.0.0 ``` * If you are on a 64-bit system, run: diff --git a/cmake/FindCcache.cmake b/cmake/FindCcache.cmake index fa357610b..a0734a312 100644 --- a/cmake/FindCcache.cmake +++ b/cmake/FindCcache.cmake @@ -41,17 +41,20 @@ find_program(CCACHE_FOUND ccache) if (CCACHE_FOUND) - set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp") + # Try to compile a test program with ccache, in order to verify if it really works. (needed on exotic setups) + # Create a temporary file with a simple program. + set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp") file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }") + # And run the found ccache on it. execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET) if (${RET} EQUAL 0) - message("found usable ccache: ${CCACHE_FOUND}") + # Success + message(STATUS "Found usable ccache: ${CCACHE_FOUND}") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}") set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}") else() - message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}") - endif() + message(STATUS "Found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}") + endif() else() - message("ccache NOT found!") + message(STATUS "ccache NOT found! Please install it for faster rebuilds.") endif() - diff --git a/contrib/codefresh/codefresh.yml b/contrib/codefresh/codefresh.yml deleted file mode 100644 index a22debfa1..000000000 --- a/contrib/codefresh/codefresh.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '1.0' -steps: - init_submodules: - title: Init Submodules - commands: - - git submodule update --init --recursive - image: codefreshio/git-image:latest - working_directory: ${{main_clone}} - - BuildingDockerImage: - title: Building Docker Image - type: build - image_name: monero - working_directory: ./ - tag: '${{CF_BRANCH_TAG_NORMALIZED}}' - dockerfile: Dockerfile - build_arguments: - - NPROC=1 diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index a7788aeb8..08d9b8802 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -465,7 +465,7 @@ eof: bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, std::function<std::string(void)> prompt, const std::string& usage = "") { async_console_handler console_handler; - return console_handler.run(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), prompt, usage); + return console_handler.run(ptsrv, std::bind<bool>(no_srv_param_adapter<t_server, t_handler>, std::placeholders::_1, std::placeholders::_2, handlr), prompt, usage); } template<class t_server, class t_handler> @@ -634,7 +634,7 @@ eof: bool run_handling(std::function<std::string(void)> prompt, const std::string& usage_string, std::function<void(void)> exit_handler = NULL) { - return m_console_handler.run(boost::bind(&console_handlers_binder::process_command_str, this, _1), prompt, usage_string, exit_handler); + return m_console_handler.run(std::bind(&console_handlers_binder::process_command_str, this, std::placeholders::_1), prompt, usage_string, exit_handler); } void print_prompt() diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl index 8e339e006..cb2bd54f9 100644 --- a/contrib/epee/include/md5_l.inl +++ b/contrib/epee/include/md5_l.inl @@ -277,7 +277,7 @@ namespace md5 /* Zeroize sensitive information. */ - MD5_memset ((POINTER)context, 0, sizeof (*context)); + memwipe ((POINTER)context, sizeof (*context)); } /* MD5 basic transformation. Transforms state based on block. @@ -369,7 +369,7 @@ namespace md5 /* Zeroize sensitive information. */ - MD5_memset ((POINTER)x, 0, sizeof (x)); + memwipe ((POINTER)x, sizeof (x)); } /* Note: Replace "for loop" with standard memcpy if possible. @@ -431,9 +431,9 @@ namespace md5 MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ /* scrub the pads and key context (if used) */ - MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad)); - MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad)); - MD5_memset( (POINTER)&tk, 0, sizeof(tk)); + memwipe( (POINTER)&k_ipad, sizeof(k_ipad)); + memwipe( (POINTER)&k_opad, sizeof(k_opad)); + memwipe( (POINTER)&tk, sizeof(tk)); /* and we're done. */ } @@ -459,7 +459,7 @@ namespace md5 state->istate[lupe] = htonl(hmac.ictx.state[lupe]); state->ostate[lupe] = htonl(hmac.octx.state[lupe]); } - MD5_memset( (POINTER)&hmac, 0, sizeof(hmac)); + memwipe( (POINTER)&hmac, sizeof(hmac)); } diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 43ede3cc1..cbacd118c 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -32,7 +32,6 @@ -#include <boost/bind.hpp> #include <boost/foreach.hpp> #include <boost/uuid/random_generator.hpp> #include <boost/chrono.hpp> @@ -210,15 +209,15 @@ PRAGMA_WARNING_DISABLE_VS(4355) socket().async_receive(boost::asio::buffer(buffer_), boost::asio::socket_base::message_peek, strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_receive, self, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); + std::bind(&connection<t_protocol_handler>::handle_receive, self, + std::placeholders::_1, + std::placeholders::_2))); else async_read_some(boost::asio::buffer(buffer_), strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_read, self, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); + std::bind(&connection<t_protocol_handler>::handle_read, self, + std::placeholders::_1, + std::placeholders::_2))); #if !defined(_WIN32) || !defined(__i686) // not supported before Windows7, too lazy for runtime check // Just exclude for 32bit windows builds @@ -688,7 +687,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) reset_timer(get_default_timeout(), false); async_write(boost::asio::buffer(m_send_que.front().data(), size_now ) , strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2) + std::bind(&connection<t_protocol_handler>::handle_write, self, std::placeholders::_1, std::placeholders::_2) ) ); //_dbg3("(chunk): " << size_now); @@ -892,7 +891,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) CHECK_AND_ASSERT_MES( size_now == m_send_que.front().size(), void(), "Unexpected queue size"); async_write(boost::asio::buffer(m_send_que.front().data(), size_now) , strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2) + std::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), std::placeholders::_1, std::placeholders::_2) ) ); //_dbg3("(normal)" << size_now); @@ -1402,7 +1401,7 @@ POP_WARNINGS shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->cond.notify_one(); shared_context->connect_mut.unlock(); }; - sock_.async_connect(remote_endpoint, boost::bind<void>(connect_callback, _1, local_shared_context)); + sock_.async_connect(remote_endpoint, std::bind<void>(connect_callback, std::placeholders::_1, local_shared_context)); while(local_shared_context->ec == boost::asio::error::would_block) { bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout)); diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 6cd19f17b..d88b53c94 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -31,7 +31,7 @@ #include <boost/thread.hpp> -#include <boost/bind.hpp> +#include <boost/bind/bind.hpp> #include "net/abstract_tcp_server2.h" #include "http_protocol_handler.h" diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h index b18e04a27..4633fa546 100644 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -28,6 +28,7 @@ #include "portable_storage_template_helper.h" #include <boost/utility/value_init.hpp> +#include <functional> #include "span.h" #include "net/levin_base.h" @@ -294,20 +295,20 @@ namespace epee #define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \ if(!is_notify && command_id == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in, typename_out>(this, command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in, typename_out>(this, command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);} #define HANDLE_INVOKE_T2(COMMAND, func) \ if(!is_notify && COMMAND::ID == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename COMMAND::request, typename COMMAND::response>(command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename COMMAND::request, typename COMMAND::response>(command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);} #define HANDLE_NOTIFY2(command_id, func, type_name_in) \ if(is_notify && command_id == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in>(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);} #define HANDLE_NOTIFY_T2(NOTIFY, func) \ if(is_notify && NOTIFY::ID == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename NOTIFY::request>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename NOTIFY::request>(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);} #define CHAIN_INVOKE_MAP2(func) \ diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 2256f6b83..5a6cc0b51 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -196,7 +196,7 @@ namespace misc_utils uint32_t dst = 0; for (int i = 0; i < 4; ++i) { - const unsigned char tmp = isx[(int)*++it]; + const unsigned char tmp = isx[(unsigned char)*++it]; CHECK_AND_ASSERT_THROW_MES(tmp != 0xff, "Bad Unicode encoding"); dst = dst << 4 | tmp; } diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp index 99c37fae3..faf7689be 100644 --- a/contrib/epee/src/byte_slice.cpp +++ b/contrib/epee/src/byte_slice.cpp @@ -133,10 +133,13 @@ namespace epee template<typename T> byte_slice::byte_slice(const adapt_buffer, T&& buffer) - : storage_(nullptr), portion_(to_byte_span(to_span(buffer))) + : storage_(nullptr), portion_(nullptr) { if (!buffer.empty()) + { storage_ = allocate_slice<adapted_byte_slice<T>>(0, std::move(buffer)); + portion_ = to_byte_span(to_span(static_cast<adapted_byte_slice<T> *>(storage_.get())->buffer)); + } } byte_slice::byte_slice(std::initializer_list<span<const std::uint8_t>> sources) @@ -173,9 +176,14 @@ namespace epee byte_slice::byte_slice(byte_stream&& stream) noexcept : storage_(nullptr), portion_(stream.data(), stream.size()) { - std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice); - new (data) raw_byte_slice{}; - storage_.reset(reinterpret_cast<raw_byte_slice*>(data)); + if (stream.size()) + { + std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice); + new (data) raw_byte_slice{}; + storage_.reset(reinterpret_cast<raw_byte_slice*>(data)); + } + else + portion_ = nullptr; } byte_slice::byte_slice(byte_slice&& source) noexcept @@ -205,14 +213,17 @@ namespace epee byte_slice byte_slice::take_slice(const std::size_t max_bytes) noexcept { byte_slice out{}; - std::uint8_t const* const ptr = data(); - out.portion_ = {ptr, portion_.remove_prefix(max_bytes)}; - if (portion_.empty()) - out.storage_ = std::move(storage_); // no atomic inc/dec - else - out = {storage_.get(), out.portion_}; + if (max_bytes) + { + std::uint8_t const* const ptr = data(); + out.portion_ = {ptr, portion_.remove_prefix(max_bytes)}; + if (portion_.empty()) + out.storage_ = std::move(storage_); // no atomic inc/dec + else + out = {storage_.get(), out.portion_}; + } return out; } diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index e96bf627f..bcde215be 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -100,7 +100,7 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net:FATAL,net.http:FATAL,net.ssl:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,serialization:FATAL,daemon.rpc.payment:ERROR,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:WARNING,net:FATAL,net.http:FATAL,net.ssl:FATAL,net.p2p:FATAL,net.cn:FATAL,daemon.rpc:FATAL,global:INFO,verify:FATAL,serialization:FATAL,daemon.rpc.payment:ERROR,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO,perf.*:DEBUG"; diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index c09786c36..c0f230887 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -126,7 +126,7 @@ Setup for LXC: ```bash GH_USER=fluffypony -VERSION=v0.15.0.0 +VERSION=v0.16.0.0 ./gitian-build.py --setup $GH_USER $VERSION ``` @@ -182,7 +182,7 @@ If you chose to do detached signing using `--detach-sign` above (recommended), y ```bash GH_USER=fluffypony -VERSION=v0.15.0.0 +VERSION=v0.16.0.0 gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml index 02614b1a5..de98efafe 100644 --- a/contrib/gitian/gitian-android.yml +++ b/contrib/gitian/gitian-android.yml @@ -1,5 +1,5 @@ --- -name: "monero-android-0.15" +name: "monero-android-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml index 0220b82a5..e97c3802b 100644 --- a/contrib/gitian/gitian-freebsd.yml +++ b/contrib/gitian/gitian-freebsd.yml @@ -1,5 +1,5 @@ --- -name: "monero-freebsd-0.15" +name: "monero-freebsd-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 9c2ebac9b..bd42637aa 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "monero-linux-0.15" +name: "monero-linux-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index ecc7d4b59..4d44c4845 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "monero-osx-0.15" +name: "monero-osx-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index bd85695c1..196b6ebbe 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "monero-win-0.15" +name: "monero-win-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/snap/monerod-wrapper b/contrib/snap/monerod-wrapper deleted file mode 100755 index f7266e11c..000000000 --- a/contrib/snap/monerod-wrapper +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ ! -d "$SNAP_USER_DATA/etc" ]; then - mkdir $SNAP_USER_DATA/etc/ - cp -R $SNAP/etc/monerod.conf $SNAP_USER_DATA/etc/monerod.conf -fi - -exec "$SNAP/bin/monerod" "$@" diff --git a/contrib/snap/monerod.conf b/contrib/snap/monerod.conf deleted file mode 100644 index 1078e73b2..000000000 --- a/contrib/snap/monerod.conf +++ /dev/null @@ -1,10 +0,0 @@ -# Configuration for monerod -# Syntax: any command line option may be specified as 'clioptionname=value'. -# Boolean options such as 'no-igd' are specified as 'no-igd=1'. -# See 'monerod --help' for all available options. - -# Overridden by snap: -# data-dir=/var/lib/monero -# log-file=/var/log/monero/monero.log - -log-level=0 diff --git a/contrib/snap/setup/gui/icon.png b/contrib/snap/setup/gui/icon.png Binary files differdeleted file mode 100644 index b7e821270..000000000 --- a/contrib/snap/setup/gui/icon.png +++ /dev/null diff --git a/contrib/snap/snapcraft.yaml b/contrib/snap/snapcraft.yaml deleted file mode 100644 index b3b75d278..000000000 --- a/contrib/snap/snapcraft.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: monero -version: 0.11.1.0-1 -summary: "Monero: the secure, private, untraceable cryptocurrency https://getmonero.org" -description: | - Monero is a private, secure, untraceable, decentralised digital currency. - You are your bank, you control your funds, and nobody can trace your transfers - unless you allow them to do so. -grade: devel -confinement: strict - -apps: - monerod: - daemon: forking - command: | - monerod-wrapper --detach --data-dir ${SNAP_COMMON} --config-file ${SNAP_USER_DATA}/etc/monerod.conf - plugs: - - network - - network-bind - monero-wallet-rpc: - command: | - monero-wallet-rpc --log-file ${SNAP_USER_DATA} - plugs: - - home - - network - - network-bind - monero-wallet-cli: - command: | - monero-wallet-cli --log-file ${SNAP_USER_DATA} - plugs: - - home - - network - -parts: - cmake-build: - plugin: cmake - configflags: - - -DBDB_STATIC=1 - - -DBoost_USE_STATIC_LIBS=1 - - -DBoost_USE_STATIC_RUNTIME=1 - - -DARCH=default - source: . - build-packages: - - gcc - - pkg-config - - libunbound-dev - - libevent-dev - - libboost-all-dev - - libzmqpp-dev - - libzmq3-dev - - libsodium-dev - - libdb-dev - - libunwind-dev - - libminiupnpc-dev - - libldns-dev - - libexpat1-dev - - libreadline6-dev - - bison - - doxygen - - graphviz - stage-packages: - - libminiupnpc10 - - libunbound2 - - libunwind8 - prime: - - bin - - usr/lib/ - - -usr/lib/gcc - - -usr/share - - dist-files: - plugin: dump - source: . - organize: - contrib/snap/monerod.conf: etc/monerod.conf - contrib/snap/monerod-wrapper: bin/monerod-wrapper - prime: - - etc - - bin diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 71b165f4f..a8916a7d0 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -80,4 +80,5 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) +add_subdirectory(qrcodegen) add_subdirectory(randomx EXCLUDE_FROM_ALL) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 8439bec0b..0d748c225 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2475,6 +2475,100 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { } } + +template<typename Transform> +static inline std::string utf8canonical(const std::string &s, Transform t = [](wint_t c)->wint_t { return c; }) +{ + std::string sc = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + wint_t cp = 0; + int bytes = 1; + char wbuf[8], *wptr; + while (avail--) + { + if ((*ptr & 0x80) == 0) + { + cp = *ptr++; + bytes = 1; + } + else if ((*ptr & 0xe0) == 0xc0) + { + if (avail < 1) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x1f) << 6; + cp |= *ptr++ & 0x3f; + --avail; + bytes = 2; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if (avail < 2) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0xf) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 2; + bytes = 3; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if (avail < 3) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x7) << 18; + cp |= (*ptr++ & 0x3f) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 3; + bytes = 4; + } + else + throw std::runtime_error("Invalid UTF-8"); + + cp = t(cp); + if (cp <= 0x7f) + bytes = 1; + else if (cp <= 0x7ff) + bytes = 2; + else if (cp <= 0xffff) + bytes = 3; + else if (cp <= 0x10ffff) + bytes = 4; + else + throw std::runtime_error("Invalid code point UTF-8 transformation"); + + wptr = wbuf; + switch (bytes) + { + case 1: *wptr++ = cp; break; + case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break; + case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + default: throw std::runtime_error("Invalid UTF-8"); + } + *wptr = 0; + sc.append(wbuf, bytes); + cp = 0; + bytes = 1; + } + return sc; +} + +void sanitize(std::string &s) +{ + s = utf8canonical(s, [](wint_t c)->wint_t { + if (c == 9 || c == 10 || c == 13) + return c; + if (c < 0x20) + return '?'; + if (c == 0x7f) + return '?'; + if (c >= 0x80 && c <= 0x9f) + return '?'; + return c; + }); +} + void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine) { if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) { if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { @@ -2506,6 +2600,8 @@ void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, m_data->logMessage()->logger()->logBuilder()->setColor(el::base::utils::colorFromLevel(level), false); ELPP_COUT << rawLinePrefix; m_data->logMessage()->logger()->logBuilder()->setColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default); + try { sanitize(rawLinePayload); } + catch (const std::exception &e) { rawLinePayload = "<Invalid UTF-8 in log>"; } ELPP_COUT << rawLinePayload; m_data->logMessage()->logger()->logBuilder()->setColor(el::Color::Default, false); ELPP_COUT << std::flush; diff --git a/external/qrcodegen/CMakeLists.txt b/external/qrcodegen/CMakeLists.txt new file mode 100644 index 000000000..a9060e3e8 --- /dev/null +++ b/external/qrcodegen/CMakeLists.txt @@ -0,0 +1,7 @@ +project(libqrcodegen) + +add_library(qrcodegen STATIC QrCode.cpp) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +target_include_directories(qrcodegen PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/external/qrcodegen/QrCode.cpp b/external/qrcodegen/QrCode.cpp new file mode 100644 index 000000000..b9de86215 --- /dev/null +++ b/external/qrcodegen/QrCode.cpp @@ -0,0 +1,862 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <algorithm> +#include <climits> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <sstream> +#include <stdexcept> +#include <utility> +#include "QrCode.hpp" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector<uint8_t> &data) { + if (data.size() > static_cast<unsigned int>(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast<uint32_t>(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast<uint32_t>(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast<uint32_t>(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector<QrSegment> QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector<QrSegment> result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector<uint8_t> bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast<uint8_t>(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast<uint32_t>(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast<uint32_t>(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast<uint32_t>(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector<bool> &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast<int>(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector<bool> &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw std::logic_error("Assertion error"); + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector<QrSegment> segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) { + vector<QrSegment> segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + if (dataUsedBits == -1) + throw std::logic_error("Assertion error"); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + if (bb.size() != static_cast<unsigned int>(dataUsedBits)) + throw std::logic_error("Assertion error"); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8; + if (bb.size() > dataCapacityBits) + throw std::logic_error("Assertion error"); + bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8); + if (bb.size() % 8 != 0) + throw std::logic_error("Assertion error"); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector<uint8_t> dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk) : + // Initialize fields and check arguments + version(ver), + errorCorrectionLevel(ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast<size_t>(size); + modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all white + isFunction = vector<vector<bool> >(sz, vector<bool>(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (msk < 0 || msk > 7) + throw std::logic_error("Assertion error"); + this->mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +std::string QrCode::toSvgString(int border) const { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"; + sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "; + sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n"; + sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"; + sb << "\t<path d=\""; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + if (getModule(x, y)) { + if (x != 0 || y != 0) + sb << " "; + sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z"; + } + } + } + sb << "\" fill=\"#000000\"/>\n"; + sb << "</svg>\n"; + return sb.str(); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector<int> alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int msk) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >> 15 != 0) + throw std::logic_error("Assertion error"); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always black +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast<long>(version) << 12 | rem; // uint18 + if (bits >> 18 != 0) + throw std::logic_error("Assertion error"); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isBlack) { + size_t ux = static_cast<size_t>(x); + size_t uy = static_cast<size_t>(y); + modules .at(uy).at(ux) = isBlack; + isFunction.at(uy).at(ux) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x)); +} + + +vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const { + if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector<vector<uint8_t> > blocks; + const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast<int>(dat.size()); + const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector<uint8_t> result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + if (result.size() != static_cast<unsigned int>(rawCodewords)) + throw std::logic_error("Assertion error"); + return result; +} + + +void QrCode::drawCodewords(const vector<uint8_t> &data) { + if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast<size_t>(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method + } + } + } + if (i != data.size() * 8) + throw std::logic_error("Assertion error"); +} + + +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast<size_t>(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw std::logic_error("Assertion error"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array<int,7> runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array<int,7> runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of black and white modules + int black = 0; + for (const vector<bool> &row : modules) { + for (bool color : row) { + if (color) + black++; + } + } + int total = size * size; // Note that size is odd, so black/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = static_cast<int>((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; + return result; +} + + +vector<int> QrCode::getAlignmentPatternPositions() const { + if (version == 1) + return vector<int>(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + vector<int> result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + if (!(208 <= result && result <= 29648)) + throw std::logic_error("Assertion error"); + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK [static_cast<int>(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver]; +} + + +vector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector<uint8_t> result(static_cast<size_t>(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) { + vector<uint8_t> result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw std::logic_error("Assertion error"); + return static_cast<uint8_t>(z); +} + + +int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const { + int n = runHistory.at(1); + if (n > size * 3) + throw std::logic_error("Assertion error"); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const { + if (currentRunColor) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add white border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + + +bool QrCode::getBit(long x, int i) { + return ((x >> i) & 1) != 0; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + + + +BitBuffer::BitBuffer() + : std::vector<bool>() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +} diff --git a/external/qrcodegen/QrCode.hpp b/external/qrcodegen/QrCode.hpp new file mode 100644 index 000000000..7341e4102 --- /dev/null +++ b/external/qrcodegen/QrCode.hpp @@ -0,0 +1,556 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <array> +#include <cstdint> +#include <stdexcept> +#include <string> +#include <vector> + + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector<std::uint8_t> &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector<QrSegment> makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: Mode mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector<bool> data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(Mode md, int numCh, const std::vector<bool> &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(Mode md, int numCh, std::vector<bool> &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: Mode getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector<bool> &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector<QrSegment> &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ + public: enum class Ecc { + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl); + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ + public: static QrCode encodeSegments(const std::vector<QrSegment> &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ + private: int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ + private: int size; + + /* The error correction level used in this QR Code. */ + private: Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through getModule(). + private: std::vector<std::vector<bool> > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector<std::vector<bool> > isFunction; + + + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int msk); + + + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ + public: int getVersion() const; + + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ + public: int getSize() const; + + + /* + * Returns this QR Code's error correction level. + */ + public: Ecc getErrorCorrectionLevel() const; + + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ + public: bool getModule(int x, int y) const; + + + /* + * Returns a string of SVG code for an image depicting this QR Code, with the given number + * of border modules. The string always uses Unix newlines (\n), regardless of the platform. + */ + public: std::string toSvgString(int border) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int msk); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + private: void setFunctionModule(int x, int y, bool isBlack); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector<std::uint8_t> &data); + + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + private: void applyMask(int msk); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. + private: std::vector<int> getAlignmentPatternPositions() const; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + private: static std::vector<std::uint8_t> reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private: int finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const; + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: void finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const; + + + // Returns true iff the i'th bit of x is set to 1. + private: static bool getBit(long x, int i); + + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. + public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. + public: static constexpr int MAX_VERSION = 40; + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + +}; + + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector<bool> { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +} @@ -1 +0,0 @@ -contrib/snap
\ No newline at end of file diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index ee166e3af..567505ac1 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -43,7 +43,6 @@ #include <algorithm> #include <cstdio> #include <fstream> -#include <boost/iostreams/copy.hpp> #include <atomic> #include "common/command_line.h" diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 7575f631c..23dbb64d7 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -41,7 +41,6 @@ #include <algorithm> #include <cstdio> #include <fstream> -#include <boost/iostreams/copy.hpp> #include <atomic> #include "common/command_line.h" diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex b14f9e8d2..fa58387ab 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 3be8dd0b2..df85b244c 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -34,6 +34,7 @@ #include "string_tools.h" #include "storages/portable_storage_template_helper.h" // epee json include #include "serialization/keyvalue_serialization.h" +#include <functional> #include <vector> using namespace epee; @@ -133,11 +134,9 @@ namespace cryptonote //--------------------------------------------------------------------------- uint64_t checkpoints::get_max_height() const { - std::map< uint64_t, crypto::hash >::const_iterator highest = - std::max_element( m_points.begin(), m_points.end(), - ( boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _1) < - boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) ); - return highest->first; + if (m_points.empty()) + return 0; + return m_points.rbegin()->first; } //--------------------------------------------------------------------------- const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const @@ -211,6 +210,8 @@ namespace cryptonote ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e"); ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29"); ADD_CHECKPOINT(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2"); + ADD_CHECKPOINT(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314"); + ADD_CHECKPOINT(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5"); return true; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d89cabd07..8e427b6b8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -86,7 +86,8 @@ set(common_private_headers updates.h aligned.h timings.h - combinator.h) + combinator.h + utf8.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/utf8.h b/src/common/utf8.h new file mode 100644 index 000000000..60247f1b2 --- /dev/null +++ b/src/common/utf8.h @@ -0,0 +1,114 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <cctype> +#include <cwchar> +#include <stdexcept> + +namespace tools +{ + template<typename T, typename Transform> + inline T utf8canonical(const T &s, Transform t = [](wint_t c)->wint_t { return c; }) + { + T sc = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + wint_t cp = 0; + int bytes = 1; + char wbuf[8], *wptr; + while (avail--) + { + if ((*ptr & 0x80) == 0) + { + cp = *ptr++; + bytes = 1; + } + else if ((*ptr & 0xe0) == 0xc0) + { + if (avail < 1) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x1f) << 6; + cp |= *ptr++ & 0x3f; + --avail; + bytes = 2; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if (avail < 2) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0xf) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 2; + bytes = 3; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if (avail < 3) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x7) << 18; + cp |= (*ptr++ & 0x3f) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 3; + bytes = 4; + } + else + throw std::runtime_error("Invalid UTF-8"); + + cp = t(cp); + if (cp <= 0x7f) + bytes = 1; + else if (cp <= 0x7ff) + bytes = 2; + else if (cp <= 0xffff) + bytes = 3; + else if (cp <= 0x10ffff) + bytes = 4; + else + throw std::runtime_error("Invalid code point UTF-8 transformation"); + + wptr = wbuf; + switch (bytes) + { + case 1: *wptr++ = cp; break; + case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break; + case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + default: throw std::runtime_error("Invalid UTF-8"); + } + *wptr = 0; + sc.append(wbuf, bytes); + cp = 0; + bytes = 1; + } + return sc; + } +} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 5f58ca853..608d250fe 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -86,7 +86,7 @@ DISABLE_VS_WARNINGS(4267) //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : - m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), + m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_reset_timestamps_and_difficulties_height(true), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_long_term_effective_median_block_weight(0), @@ -427,6 +427,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if (num_popped_blocks > 0) { m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; m_hardfork->reorganize_from_chain_height(get_current_blockchain_height()); uint64_t top_block_height; crypto::hash top_block_hash = get_tail_id(top_block_height); @@ -567,6 +568,7 @@ block Blockchain::pop_block_from_blockchain() CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; block popped_block; std::vector<transaction> popped_txs; @@ -644,6 +646,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; invalidate_block_template_cache(); m_db->reset(); m_db->drop_alt_blocks(); @@ -812,12 +815,20 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // less blocks than desired if there aren't enough. difficulty_type Blockchain::get_difficulty_for_next_block() { + LOG_PRINT_L3("Blockchain::" << __func__); + + std::stringstream ss; + bool print = false; + + int done = 0; + ss << "get_difficulty_for_next_block: height " << m_db->height() << std::endl; if (m_fixed_difficulty) { return m_db->height() ? m_fixed_difficulty : 1; } - LOG_PRINT_L3("Blockchain::" << __func__); +start: + difficulty_type D = 0; crypto::hash top_hash = get_tail_id(); { @@ -826,21 +837,32 @@ difficulty_type Blockchain::get_difficulty_for_next_block() // something a bit out of date, but that's fine since anything which // requires the blockchain lock will have acquired it in the first place, // and it will be unlocked only when called from the getinfo RPC + ss << "Locked, tail id " << top_hash << ", cached is " << m_difficulty_for_next_block_top_hash << std::endl; if (top_hash == m_difficulty_for_next_block_top_hash) - return m_difficulty_for_next_block; + { + ss << "Same, using cached diff " << m_difficulty_for_next_block << std::endl; + D = m_difficulty_for_next_block; + } } CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector<uint64_t> timestamps; std::vector<difficulty_type> difficulties; uint64_t height; - top_hash = get_tail_id(height); // get it again now that we have the lock - ++height; // top block height to blockchain height + auto new_top_hash = get_tail_id(height); // get it again now that we have the lock + ++height; + if (!(new_top_hash == top_hash)) D=0; + ss << "Re-locked, height " << height << ", tail id " << new_top_hash << (new_top_hash == top_hash ? "" : " (different)") << std::endl; + top_hash = new_top_hash; + // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). + bool check = false; + if (m_reset_timestamps_and_difficulties_height) + m_timestamps_and_difficulties_height = 0; if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT) { uint64_t index = height - 1; @@ -855,8 +877,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_timestamps_and_difficulties_height = height; timestamps = m_timestamps; difficulties = m_difficulties; + check = true; } - else + //else + std::vector<uint64_t> timestamps_from_cache = timestamps; + std::vector<difficulty_type> difficulties_from_cache = difficulties; + { uint64_t offset = height - std::min <uint64_t> (height, static_cast<uint64_t>(DIFFICULTY_BLOCKS_COUNT)); if (offset == 0) @@ -869,22 +895,68 @@ difficulty_type Blockchain::get_difficulty_for_next_block() timestamps.reserve(height - offset); difficulties.reserve(height - offset); } + ss << "Looking up " << (height - offset) << " from " << offset << std::endl; for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); difficulties.push_back(m_db->get_block_cumulative_difficulty(offset)); } + if (check) if (timestamps != timestamps_from_cache || difficulties !=difficulties_from_cache) + { + ss << "Inconsistency XXX:" << std::endl; + ss << "top hash: "<<top_hash << std::endl; + ss << "timestamps: " << timestamps_from_cache.size() << " from cache, but " << timestamps.size() << " without" << std::endl; + ss << "difficulties: " << difficulties_from_cache.size() << " from cache, but " << difficulties.size() << " without" << std::endl; + ss << "timestamps_from_cache:" << std::endl; for (const auto &v :timestamps_from_cache) ss << " " << v << std::endl; + ss << "timestamps:" << std::endl; for (const auto &v :timestamps) ss << " " << v << std::endl; + ss << "difficulties_from_cache:" << std::endl; for (const auto &v :difficulties_from_cache) ss << " " << v << std::endl; + ss << "difficulties:" << std::endl; for (const auto &v :difficulties) ss << " " << v << std::endl; + + uint64_t dbh = m_db->height(); + uint64_t sh = dbh < 10000 ? 0 : dbh - 10000; + ss << "History from -10k at :" << dbh << ", from " << sh << std::endl; + for (uint64_t h = sh; h < dbh; ++h) + { + uint64_t ts = m_db->get_block_timestamp(h); + difficulty_type d = m_db->get_block_cumulative_difficulty(h); + ss << " " << h << " " << ts << " " << d << std::endl; + } + print = true; + } m_timestamps_and_difficulties_height = height; m_timestamps = timestamps; m_difficulties = difficulties; } + size_t target = get_difficulty_target(); difficulty_type diff = next_difficulty(timestamps, difficulties, target); CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; m_difficulty_for_next_block = diff; + if (D && D != diff) + { + ss << "XXX Mismatch at " << height << "/" << top_hash << "/" << get_tail_id() << ": cached " << D << ", real " << diff << std::endl; + print = true; + } + + ++done; + if (done == 1 && D && D != diff) + { + print = true; + ss << "Might be a race. Let's see what happens if we try again..." << std::endl; + epee::misc_utils::sleep_no_w(100); + goto start; + } + ss << "Diff for " << top_hash << ": " << diff << std::endl; + if (print) + { + MGINFO("START DUMP"); + MGINFO(ss.str()); + MGINFO("END DUMP"); + MGINFO("Please send moneromooo on Freenode the contents of this log, from a couple dozen lines before START DUMP to END DUMP"); + } return diff; } //------------------------------------------------------------------ @@ -914,6 +986,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, } m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // remove blocks from blockchain until we get back to where we should be. while (m_db->height() != rollback_height) @@ -950,6 +1023,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // if empty alt chain passed (not sure how that could happen), return false CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -1639,6 +1713,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; uint64_t block_height = get_block_height(b); if(0 == block_height) { @@ -2493,6 +2568,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } db_rtxn_guard rtxn_guard(m_db); + total_height = get_current_blockchain_height(); blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), false, "Error getting blocks"); @@ -4317,7 +4393,14 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) try { if (m_batch_success) + { m_db->batch_stop(); + if (m_reset_timestamps_and_difficulties_height) + { + m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = false; + } + } else m_db->batch_abort(); success = true; @@ -5028,7 +5111,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "fce1dc7c17f7679f5f447df206b8f5fe2ef6b1a2845e59f650850a0ef00d265f"; +static const char expected_block_hashes_hash[] = "8b48d259d4b1126801b1f329683a26e1d16237420197cd3ccc76af2c55a36e83"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 25ea015b6..966e98f7e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1067,6 +1067,7 @@ namespace cryptonote std::vector<uint64_t> m_timestamps; std::vector<difficulty_type> m_difficulties; uint64_t m_timestamps_and_difficulties_height; + bool m_reset_timestamps_and_difficulties_height; uint64_t m_long_term_block_weights_window; uint64_t m_long_term_effective_median_block_weight; mutable crypto::hash m_long_term_block_weights_cache_tip_hash; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2f38c89f3..c19d8c524 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -650,7 +650,7 @@ namespace cryptonote r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - r = m_mempool.init(max_txpool_weight); + r = m_mempool.init(max_txpool_weight, m_nettype == FAKECHAIN); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); // now that we have a valid m_blockchain_storage, we can clean out any @@ -1656,7 +1656,6 @@ namespace cryptonote m_starter_message_showed = true; } - m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); @@ -1667,31 +1666,6 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::check_fork_time() - { - if (m_nettype == FAKECHAIN) - return true; - - HardFork::State state = m_blockchain_storage.get_hard_fork_state(); - el::Level level; - switch (state) { - case HardFork::LikelyForked: - level = el::Level::Warning; - MCLOG_RED(level, "global", "**********************************************************************"); - MCLOG_RED(level, "global", "Last scheduled hard fork is too far in the past."); - MCLOG_RED(level, "global", "We are most likely forked from the network. Daemon update needed now."); - MCLOG_RED(level, "global", "**********************************************************************"); - break; - case HardFork::UpdateNeeded: - level = el::Level::Info; - MCLOG(level, "global", el::Color::Default, "Last scheduled hard fork time suggests a daemon update will be released within the next couple months."); - break; - default: - break; - } - return true; - } - //----------------------------------------------------------------------------------------------- uint8_t core::get_ideal_hard_fork_version() const { return get_blockchain_storage().get_ideal_hard_fork_version(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 5f80071d2..472204119 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1001,18 +1001,6 @@ namespace cryptonote bool check_tx_inputs_keyimages_domain(const transaction& tx) const; /** - * @brief checks HardFork status and prints messages about it - * - * Checks the status of HardFork and logs/prints if an update to - * the daemon is necessary. - * - * @note see Blockchain::get_hard_fork_state and HardFork::State - * - * @return true - */ - bool check_fork_time(); - - /** * @brief attempts to relay any transactions in the mempool which need it * * @return true diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index d2fb50cbd..74aab88c4 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -116,7 +116,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_cookie(0), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_mine_stem_txes(false) { } @@ -1351,13 +1351,18 @@ namespace cryptonote for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; - if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy)) + if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) { MERROR(" failed to find tx meta"); continue; } - LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase)); + LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method()); + if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem)) + { + LOG_PRINT_L2(" tx relay method is " << (unsigned)meta.get_relay_method()); + continue; + } if (meta.pruned) { LOG_PRINT_L2(" tx is pruned"); @@ -1522,7 +1527,7 @@ namespace cryptonote return n_removed; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init(size_t max_txpool_weight) + bool tx_memory_pool::init(size_t max_txpool_weight, bool mine_stem_txes) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1578,6 +1583,7 @@ namespace cryptonote lock.commit(); } + m_mine_stem_txes = mine_stem_txes; m_cookie = 0; // Ignore deserialization error diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index ded1f012f..8955d7551 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -205,10 +205,11 @@ namespace cryptonote * @brief loads pool state (if any) from disk, and initializes pool * * @param max_txpool_weight the max weight in bytes + * @param mine_stem_txes whether to mine txes in stem relay mode * * @return true */ - bool init(size_t max_txpool_weight = 0); + bool init(size_t max_txpool_weight = 0, bool mine_stem_txes = false); /** * @brief attempts to save the transaction pool state to disk @@ -603,6 +604,7 @@ private: size_t m_txpool_max_weight; size_t m_txpool_weight; + bool m_mine_stem_txes; mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 4a7cc0386..89860fe41 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -51,7 +51,8 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) #define LOCALHOST_INT 2130706433 -#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 500 +#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 100 +static_assert(CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT >= BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4, "Invalid CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT"); namespace cryptonote { diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 87e75b271..02c416af5 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -308,9 +308,9 @@ namespace cryptonote if (version >= 6 && version != hshd.top_version) { if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version()) - MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" << + MDEBUG(context << " peer claims higher version than we think (" << (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version << - ") - we may be forked from the network and a software upgrade may be needed"); + ") - we may be forked from the network and a software upgrade may be needed, or that peer is broken or malicious"); return false; } } @@ -793,6 +793,12 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested fluffy tx before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -884,6 +890,8 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -987,6 +995,12 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested objects before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)"); if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT) { @@ -1717,6 +1731,12 @@ skip: int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested chain before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } NOTIFY_RESPONSE_CHAIN_ENTRY::request r; if(!m_core.find_blockchain_supplement(arg.block_ids, !arg.prune, r)) { diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 3aa207253..77dfe332f 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -511,7 +511,7 @@ bool t_rpc_command_executor::show_status() { } std::stringstream str; - str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections") + str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %u(out)+%u(in) connections") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) @@ -521,7 +521,6 @@ bool t_rpc_command_executor::show_status() { % get_mining_speed(cryptonote::difficulty_type(ires.wide_difficulty) / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, net_height, ires.target) - % (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked") % (unsigned)ires.outgoing_connections_count % (unsigned)ires.incoming_connections_count ; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 9e7f5c8ab..a6c2d96ed 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1468,8 +1468,8 @@ namespace hw { offset = set_command_header(INS_PREFIX_HASH,2,cnt); len = pref_length - pref_offset; //options - if (len > (BUFFER_SEND_SIZE-7)) { - len = BUFFER_SEND_SIZE-7; + if (len > (BUFFER_SEND_SIZE-offset-3)) { + len = BUFFER_SEND_SIZE-offset-3; this->buffer_send[offset] = 0x80; } else { this->buffer_send[offset] = 0x00; diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 752ff6e19..7c3e8785d 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -678,8 +678,10 @@ namespace trezor { throw exc::TrezorException("Trezor firmware 2.0.10 and lower are not supported. Please update."); } - // default client version, higher versions check will be added unsigned client_version = 1; + if (trezor_version >= pack_version(2, 3, 1)){ + client_version = 3; + } #ifdef WITH_TREZOR_DEBUGGING // Override client version for tests diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 05ebdff63..a56090de0 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -32,6 +32,7 @@ #endif #include <algorithm> +#include <functional> #include <boost/endian/conversion.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/ip/udp.hpp> @@ -711,7 +712,7 @@ namespace trezor{ // Start the asynchronous operation itself. The handle_receive function // used as a callback will update the ec and length variables. m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint, - boost::bind(&UdpTransport::handle_receive, _1, _2, &ec, &length)); + std::bind(&UdpTransport::handle_receive, std::placeholders::_1, std::placeholders::_2, &ec, &length)); // Block until the asynchronous operation has completed. do { diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index 7895b900e..bf8793aa2 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -41,6 +41,7 @@ #include <boost/algorithm/string.hpp>
#include "misc_log_ex.h"
#include "fnv1.h"
+#include "common/utf8.h"
/*!
* \namespace Language
@@ -73,78 +74,11 @@ namespace Language return prefix;
}
- template<typename T>
- inline T utf8canonical(const T &s)
- {
- T sc = "";
- size_t avail = s.size();
- const char *ptr = s.data();
- wint_t cp = 0;
- int bytes = 1;
- char wbuf[8], *wptr;
- while (avail--)
- {
- if ((*ptr & 0x80) == 0)
- {
- cp = *ptr++;
- bytes = 1;
- }
- else if ((*ptr & 0xe0) == 0xc0)
- {
- if (avail < 1)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x1f) << 6;
- cp |= *ptr++ & 0x3f;
- --avail;
- bytes = 2;
- }
- else if ((*ptr & 0xf0) == 0xe0)
- {
- if (avail < 2)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0xf) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 2;
- bytes = 3;
- }
- else if ((*ptr & 0xf8) == 0xf0)
- {
- if (avail < 3)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x7) << 18;
- cp |= (*ptr++ & 0x3f) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 3;
- bytes = 4;
- }
- else
- throw std::runtime_error("Invalid UTF-8");
-
- cp = std::towlower(cp);
- wptr = wbuf;
- switch (bytes)
- {
- case 1: *wptr++ = cp; break;
- case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- default: throw std::runtime_error("Invalid UTF-8");
- }
- *wptr = 0;
- sc += T(wbuf, bytes);
- cp = 0;
- bytes = 1;
- }
- return sc;
- }
-
struct WordHash
{
std::size_t operator()(const epee::wipeable_string &s) const
{
- const epee::wipeable_string sc = utf8canonical(s);
+ const epee::wipeable_string sc = tools::utf8canonical(s, [](wint_t c) -> wint_t { return std::towlower(c); });
return epee::fnv::FNV1a(sc.data(), sc.size());
}
};
@@ -153,8 +87,8 @@ namespace Language {
bool operator()(const epee::wipeable_string &s0, const epee::wipeable_string &s1) const
{
- const epee::wipeable_string s0c = utf8canonical(s0);
- const epee::wipeable_string s1c = utf8canonical(s1);
+ const epee::wipeable_string s0c = tools::utf8canonical(s0, [](wint_t c) -> wint_t { return std::towlower(c); });
+ const epee::wipeable_string s1c = tools::utf8canonical(s1, [](wint_t c) -> wint_t { return std::towlower(c); });
return s0c == s1c;
}
};
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index c587deaf5..fb3a38b07 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -31,7 +31,7 @@ // IP blocking adapted from Boolberry #include <algorithm> -#include <boost/bind.hpp> +#include <boost/bind/bind.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem/operations.hpp> #include <boost/optional/optional.hpp> diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b968801be..bc561abc4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -68,6 +68,11 @@ using namespace epee; #define DEFAULT_PAYMENT_DIFFICULTY 1000 #define DEFAULT_PAYMENT_CREDITS_PER_HASH 100 +#define RESTRICTED_BLOCK_HEADER_RANGE 1000 +#define RESTRICTED_TRANSACTIONS_COUNT 100 +#define RESTRICTED_SPENT_KEY_IMAGES_COUNT 5000 +#define RESTRICTED_BLOCK_COUNT 1000 + #define RPC_TRACKER(rpc) \ PERF_TIMER(rpc); \ RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc)) @@ -265,25 +270,25 @@ namespace cryptonote { if (!m_restricted && nettype() != FAKECHAIN) { - MERROR("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); + MFATAL("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); return false; } cryptonote::address_parse_info info; if (!get_account_address_from_str(info, nettype(), address)) { - MERROR("Invalid payment address: " << address); + MFATAL("Invalid payment address: " << address); return false; } if (info.is_subaddress) { - MERROR("Payment address may not be a subaddress: " << address); + MFATAL("Payment address may not be a subaddress: " << address); return false; } uint64_t diff = command_line::get_arg(vm, arg_rpc_payment_difficulty); uint64_t credits = command_line::get_arg(vm, arg_rpc_payment_credits); if (diff == 0 || credits == 0) { - MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given"); + MFATAL("Payments difficulty and/or payments credits are 0, but a payment address was given"); return false; } m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); @@ -303,7 +308,7 @@ namespace cryptonote if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), command_line::get_arg(vm, arg_bootstrap_daemon_login))) { - MERROR("Failed to parse bootstrap daemon address"); + MFATAL("Failed to parse bootstrap daemon address"); return false; } @@ -639,6 +644,13 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r)) return r; + const bool restricted = m_restricted && ctx; + if (restricted && req.heights.size() > RESTRICTED_BLOCK_COUNT) + { + res.status = "Too many blocks requested in restricted mode"; + return true; + } + res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -793,11 +805,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.txs_hashes.size() > RESTRICTED_TRANSACTIONS_COUNT) + { + res.status = "Too many transactions requested in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -1027,11 +1045,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.key_images.size() > RESTRICTED_SPENT_KEY_IMAGES_COUNT) + { + res.status = "Too many key images queried in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); + std::vector<crypto::key_image> key_images; for(const auto& ki_hex_str: req.key_images) { @@ -2034,6 +2058,14 @@ namespace cryptonote CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false); + const bool restricted = m_restricted && ctx; + if (restricted && req.hashes.size() > RESTRICTED_BLOCK_COUNT) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested in restricted mode"; + return false; + } + auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool { crypto::hash block_hash; bool hash_parsed = parse_hash256(hash, block_hash); @@ -2069,7 +2101,6 @@ namespace cryptonote return true; }; - const bool restricted = m_restricted && ctx; if (!req.hash.empty()) { if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp)) @@ -2101,6 +2132,14 @@ namespace cryptonote error_resp.message = "Invalid start/end heights."; return false; } + const bool restricted = m_restricted && ctx; + if (restricted && req.end_height - req.start_height > RESTRICTED_BLOCK_HEADER_RANGE) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested."; + return false; + } + CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false); for (uint64_t h = req.start_height; h <= req.end_height; ++h) { @@ -2127,7 +2166,6 @@ namespace cryptonote return false; } res.headers.push_back(block_header_response()); - const bool restricted = m_restricted && ctx; bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back(), req.fill_pow_hash && !restricted); if (!response_filled) { @@ -2778,7 +2816,7 @@ namespace cryptonote crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); cryptonote::blobdata txblob; - if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) + if (m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(std::move(txblob)); diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 8ea8bd649..1458049ab 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -48,6 +48,7 @@ #define CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW -16 #define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17 #define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18 +#define CORE_RPC_ERROR_CODE_RESTRICTED -19 static inline const char *get_rpc_server_error_message(int64_t code) { @@ -70,6 +71,7 @@ static inline const char *get_rpc_server_error_message(int64_t code) case CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW: return "Payment too low"; case CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT: return "Duplicate payment"; case CORE_RPC_ERROR_CODE_STALE_PAYMENT: return "Stale payment"; + case CORE_RPC_ERROR_CODE_RESTRICTED: return "Parameters beyond restricted allowance"; default: MERROR("Unknown error: " << code); return "Unknown error"; } } diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 9153e76ea..8601bd0b4 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -30,7 +30,7 @@ #include <boost/algorithm/string.hpp> #include <boost/asio/ip/address.hpp> -#include <boost/bind.hpp> +#include <functional> #include "common/command_line.h" #include "common/i18n.h" #include "hex.h" @@ -221,7 +221,7 @@ namespace cryptonote std::vector<std::string> access_control_origins; boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(",")); - std::for_each(access_control_origins.begin(), access_control_origins.end(), boost::bind(&boost::trim<std::string>, _1, std::locale::classic())); + std::for_each(access_control_origins.begin(), access_control_origins.end(), std::bind(&boost::trim<std::string>, std::placeholders::_1, std::locale::classic())); config.access_control_origins = std::move(access_control_origins); } diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 49bf6ba58..edc8f0dda 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -92,6 +92,7 @@ namespace cryptonote uint64_t rpc_payment::balance(const crypto::public_key &client, int64_t delta) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found uint64_t credits = info.credits; if (delta > 0 && credits > std::numeric_limits<uint64_t>::max() - delta) @@ -107,6 +108,7 @@ namespace cryptonote bool rpc_payment::pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (ts < info.last_request_timestamp || (ts == info.last_request_timestamp && !same_ts)) { @@ -130,6 +132,7 @@ namespace cryptonote bool rpc_payment::get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found const uint64_t now = time(NULL); bool need_template = top != info.top || now >= info.block_template_update_time + STALE_THRESHOLD; @@ -180,6 +183,7 @@ namespace cryptonote bool rpc_payment::submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (cookie != info.cookie && cookie != info.cookie - 1) { @@ -272,6 +276,7 @@ namespace cryptonote bool rpc_payment::foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const { + boost::lock_guard<boost::mutex> lock(mutex); for (std::unordered_map<crypto::public_key, client_info>::const_iterator i = m_client_info.begin(); i != m_client_info.end(); ++i) { if (!f(i->first, i->second)) @@ -283,8 +288,9 @@ namespace cryptonote bool rpc_payment::load(std::string directory) { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); m_directory = std::move(directory); - std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME; + std::string state_file_path = m_directory + "/" + RPC_PAYMENTS_DATA_FILENAME; MINFO("loading rpc payments data from " << state_file_path); std::ifstream data; data.open(state_file_path, std::ios_base::binary | std::ios_base::in); @@ -313,6 +319,7 @@ namespace cryptonote bool rpc_payment::store(const std::string &directory_) const { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); const std::string &directory = directory_.empty() ? m_directory : directory_; MDEBUG("storing rpc payments data to " << directory); if (!tools::create_directories_if_necessary(directory)) @@ -345,6 +352,7 @@ namespace cryptonote unsigned int rpc_payment::flush_by_age(time_t seconds) { + boost::lock_guard<boost::mutex> lock(mutex); unsigned int count = 0; const time_t now = time(NULL); time_t seconds0 = seconds; @@ -358,7 +366,7 @@ namespace cryptonote for (std::unordered_map<crypto::public_key, client_info>::iterator i = m_client_info.begin(); i != m_client_info.end(); ) { std::unordered_map<crypto::public_key, client_info>::iterator j = i++; - const time_t t = std::max(j->second.last_request_timestamp, j->second.update_time); + const time_t t = std::max(j->second.last_request_timestamp / 1000000, j->second.update_time); const bool erase = t < ((j->second.credits == 0) ? threshold0 : threshold); if (erase) { @@ -372,6 +380,7 @@ namespace cryptonote uint64_t rpc_payment::get_hashes(unsigned int seconds) const { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); uint64_t hashes = 0; for (std::map<uint64_t, uint64_t>::const_reverse_iterator i = m_hashrate.crbegin(); i != m_hashrate.crend(); ++i) @@ -385,6 +394,7 @@ namespace cryptonote void rpc_payment::prune_hashrate(unsigned int seconds) { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); std::map<uint64_t, uint64_t>::iterator i; for (i = m_hashrate.begin(); i != m_hashrate.end(); ++i) diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h index 8594cffcf..dcd43f8d5 100644 --- a/src/rpc/rpc_payment.h +++ b/src/rpc/rpc_payment.h @@ -31,6 +31,7 @@ #include <string> #include <unordered_set> #include <unordered_map> +#include <boost/thread/mutex.hpp> #include <boost/serialization/version.hpp> #include "cryptonote_basic/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -139,6 +140,7 @@ namespace cryptonote uint64_t m_nonces_stale; uint64_t m_nonces_bad; uint64_t m_nonces_dupe; + mutable boost::mutex mutex; }; } diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 6228b4bec..5c042aa7b 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -120,7 +120,7 @@ void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest) throw WRONG_TYPE("string"); } - if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.Size()})) + if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.GetStringLength()})) { throw BAD_INPUT(); } diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 147979b67..a0820c8eb 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -49,6 +49,7 @@ target_link_libraries(simplewallet common mnemonics ${EPEE_READLINE} + qrcodegen version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e677bc24d..cc88735fa 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -33,6 +33,11 @@ * * \brief Source file that defines simple_wallet class. */ + +// use boost bind placeholders for now +#define BOOST_BIND_GLOBAL_PLACEHOLDERS 1 +#include <boost/bind.hpp> + #include <locale.h> #include <thread> #include <iostream> @@ -69,10 +74,12 @@ #include "version.h" #include <stdexcept> #include "wallet/message_store.h" +#include "QrCode.hpp" #ifdef WIN32 #include <boost/locale.hpp> #include <boost/filesystem.hpp> +#include <fcntl.h> #endif #ifdef HAVE_READLINE @@ -250,6 +257,7 @@ namespace const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]"); const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config"); const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]"); + const char* USAGE_MMS_CONFIG_CHECKSUM("mms config_checksum"); const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config"); const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); @@ -269,6 +277,7 @@ namespace const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info"); const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc"); const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc"); + const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]"); const char* USAGE_VERSION("version"); const char* USAGE_HELP_ADVANCED("help_advanced [<command>]"); const char* USAGE_HELP("help"); @@ -2388,6 +2397,62 @@ bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args) return true; } +bool simple_wallet::show_qr_code(const std::vector<std::string> &args) +{ + uint32_t subaddress_index = 0; + if (args.size() >= 1) + { + if (!string_tools::get_xtype_from_string(subaddress_index, args[0])) + { + fail_msg_writer() << tr("invalid index: must be an unsigned integer"); + return true; + } + if (subaddress_index >= m_wallet->get_num_subaddresses(m_current_subaddress_account)) + { + fail_msg_writer() << tr("<subaddress_index> is out of bounds"); + return true; + } + } + +#ifdef _WIN32 +#define PRINT_UTF8(pre, x) std::wcout << pre ## x +#define WTEXTON() _setmode(_fileno(stdout), _O_WTEXT) +#define WTEXTOFF() _setmode(_fileno(stdout), _O_TEXT) +#else +#define PRINT_UTF8(pre, x) std::cout << x +#define WTEXTON() +#define WTEXTOFF() +#endif + + WTEXTON(); + try + { + const std::string address = "monero:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index}); + const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(address.c_str(), qrcodegen::QrCode::Ecc::LOW); + for (int y = -2; y < qr.getSize() + 2; y+=2) + { + for (int x = -2; x < qr.getSize() + 2; x++) + { + if (qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2588"); + else if (qr.getModule(x, y) && !qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2580"); + else if (!qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2584"); + else + PRINT_UTF8(L, " "); + } + PRINT_UTF8(, std::endl); + } + } + catch (const std::length_error&) + { + fail_msg_writer() << tr("Failed to generate QR code, input too large"); + } + WTEXTOFF(); + return true; +} + bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -3460,7 +3525,7 @@ simple_wallet::simple_wallet() tr("Interface with the MMS (Multisig Messaging System)\n" "<subcommand> is one of:\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" - " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" + " send_signer_config, start_auto_config, stop_auto_config, auto_config, config_checksum\n" "Get help about a subcommand with: help_advanced mms <subcommand>")); m_cmd_binder.set_handler("mms init", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), @@ -3529,6 +3594,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_START_AUTO_CONFIG), tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); + m_cmd_binder.set_handler("mms config_checksum", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), + tr(USAGE_MMS_CONFIG_CHECKSUM), + tr("Get a checksum that allows signers to easily check for identical MMS configuration")); m_cmd_binder.set_handler("mms stop_auto_config", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_STOP_AUTO_CONFIG), @@ -3611,6 +3680,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1), tr(USAGE_STOP_MINING_FOR_RPC), tr("Stop mining to pay for RPC access")); + m_cmd_binder.set_handler("show_qr_code", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_qr_code, _1), + tr(USAGE_SHOW_QR_CODE), + tr("Show address as QR code")); m_cmd_binder.set_handler("help_advanced", boost::bind(&simple_wallet::on_command, this, &simple_wallet::help_advanced, _1), tr(USAGE_HELP_ADVANCED), @@ -5466,7 +5539,7 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) +void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { if (m_locked) return; @@ -5477,7 +5550,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tr("idx ") << subaddr_index; const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000; - if (height >= warn_height) + if (height >= warn_height && !is_change) { std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); // failure ok @@ -10361,6 +10434,14 @@ bool simple_wallet::user_confirms(const std::string &question) return !std::cin.eof() && command_line::is_yes(answer); } +bool simple_wallet::user_confirms_auto_config() +{ + message_writer(console_color_red, true) << tr("WARNING: Using MMS auto-config mechanisms is not trustless"); + message_writer() << tr("A malicious auto-config manager could send you info about own wallets instead of other signers' info"); + message_writer() << tr("If in doubt do not use auto-config or at least compare configs using the \"mms config_checksum\" command"); + return user_confirms("Accept the risks and continue?"); +} + bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound) { bool valid = false; @@ -10513,7 +10594,7 @@ void simple_wallet::show_message(const mms::message &m) case mms::message_type::additional_key_set: case mms::message_type::note: display_content = true; - ms.get_sanitized_message_text(m, sanitized_text); + sanitized_text = mms::message_store::get_sanitized_text(m.content, 1000); break; default: display_content = false; @@ -10862,6 +10943,11 @@ void simple_wallet::mms_next(const std::vector<std::string> &args) { break; } + if (!user_confirms_auto_config()) + { + message_writer() << tr("You can use the \"mms delete\" command to delete any unwanted message"); + break; + } } ms.process_signer_config(state, m.content); ms.stop_auto_config(); @@ -11188,6 +11274,18 @@ void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args) list_signers(ms.get_all_signers()); } +void simple_wallet::mms_config_checksum(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms config_checksum"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + LOCK_IDLE_SCOPE(); + message_writer() << ms.get_config_checksum(); +} + void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args) { if (args.size() != 0) @@ -11218,6 +11316,10 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args) fail_msg_writer() << tr("Invalid auto-config token"); return; } + if (!user_confirms_auto_config()) + { + return; + } mms::authorized_signer me = ms.get_signer(0); if (me.auto_config_running) { @@ -11330,6 +11432,10 @@ bool simple_wallet::mms(const std::vector<std::string> &args) { mms_start_auto_config(mms_args); } + else if (sub_command == "config_checksum") + { + mms_config_checksum(mms_args); + } else if (sub_command == "stop_auto_config") { mms_stop_auto_config(mms_args); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index bce6a16cd..5154ff1ef 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -53,7 +53,7 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet" // Hardcode Monero's donation address (see #1447) -constexpr const char MONERO_DONATION_ADDR[] = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; +constexpr const char MONERO_DONATION_ADDR[] = "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H"; /*! * \namespace cryptonote @@ -258,6 +258,7 @@ namespace cryptonote bool rpc_payment_info(const std::vector<std::string> &args); bool start_mining_for_rpc(const std::vector<std::string> &args); bool stop_mining_for_rpc(const std::vector<std::string> &args); + bool show_qr_code(const std::vector<std::string> &args); bool net_stats(const std::vector<std::string>& args); bool public_nodes(const std::vector<std::string>& args); bool welcome(const std::vector<std::string>& args); @@ -342,7 +343,7 @@ namespace cryptonote //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time); + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time); virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index); virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); @@ -478,6 +479,7 @@ namespace cryptonote void ask_send_all_ready_messages(); void check_for_messages(); bool user_confirms(const std::string &question); + bool user_confirms_auto_config(); bool get_message_from_arg(const std::string &arg, mms::message &m); bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound); @@ -498,6 +500,7 @@ namespace cryptonote void mms_help(const std::vector<std::string> &args); void mms_send_signer_config(const std::vector<std::string> &args); void mms_start_auto_config(const std::vector<std::string> &args); + void mms_config_checksum(const std::vector<std::string> &args); void mms_stop_auto_config(const std::vector<std::string> &args); void mms_auto_config(const std::vector<std::string> &args); }; diff --git a/src/version.cpp.in b/src/version.cpp.in index ccb88f1fe..2071acb8c 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.15.0.0" +#define DEF_MONERO_VERSION "0.16.0.0" #define DEF_MONERO_RELEASE_NAME "Carbon Chamaeleon" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index ba0678d0f..66e248427 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -157,7 +157,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { std::string tx_hash = epee::string_tools::pod_to_hex(txid); diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp index 25a8bd4ef..fb07b42f0 100644 --- a/src/wallet/message_store.cpp +++ b/src/wallet/message_store.cpp @@ -39,6 +39,7 @@ #include "serialization/binary_utils.h" #include "common/base58.h" #include "common/util.h" +#include "common/utf8.h" #include "string_tools.h" @@ -129,18 +130,18 @@ void message_store::set_signer(const multisig_wallet_state &state, authorized_signer &m = m_signers[index]; if (label) { - m.label = label.get(); + m.label = get_sanitized_text(label.get(), 50); } if (transport_address) { - m.transport_address = transport_address.get(); + m.transport_address = get_sanitized_text(transport_address.get(), 200); } if (monero_address) { m.monero_address_known = true; m.monero_address = monero_address.get(); } - // Save to minimize the chance to loose that info (at least while in beta) + // Save to minimize the chance to loose that info save(state); } @@ -202,6 +203,13 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con } uint32_t num_signers = (uint32_t)signers.size(); THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers)); + for (uint32_t i = 0; i < num_signers; ++i) + { + authorized_signer &m = signers[i]; + m.label = get_sanitized_text(m.label, 50); + m.transport_address = get_sanitized_text(m.transport_address, 200); + m.auto_config_token = get_sanitized_text(m.auto_config_token, 20); + } } void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) @@ -242,10 +250,10 @@ void message_store::process_signer_config(const multisig_wallet_state &state, co } } authorized_signer &modify = m_signers[take_index]; - modify.label = m.label; // ALWAYS set label, see comments above + modify.label = get_sanitized_text(m.label, 50); // ALWAYS set label, see comments above if (!modify.me) { - modify.transport_address = m.transport_address; + modify.transport_address = get_sanitized_text(m.transport_address, 200); modify.monero_address_known = m.monero_address_known; if (m.monero_address_known) { @@ -392,6 +400,45 @@ void message_store::process_auto_config_data_message(uint32_t id) signer.auto_config_running = false; } +void add_hash(crypto::hash &sum, const crypto::hash &summand) +{ + for (uint32_t i = 0; i < crypto::HASH_SIZE; ++i) + { + uint32_t x = (uint32_t)sum.data[i]; + uint32_t y = (uint32_t)summand.data[i]; + sum.data[i] = (char)((x + y) % 256); + } +} + +// Calculate a checksum that allows signers to make sure they work with an identical signer config +// by exchanging and comparing checksums out-of-band i.e. not using the MMS; +// Because different signers have a different order of signers in the config work with "adding" +// individual hashes because that operation is commutative +std::string message_store::get_config_checksum() const +{ + crypto::hash sum = crypto::null_hash; + uint32_t num = SWAP32LE(m_num_authorized_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + num = SWAP32LE(m_num_required_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + add_hash(sum, crypto::cn_fast_hash(m.transport_address.data(), m.transport_address.size())); + if (m.monero_address_known) + { + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_spend_public_key, sizeof(m.monero_address.m_spend_public_key))); + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_view_public_key, sizeof(m.monero_address.m_view_public_key))); + } + } + std::string checksum_bytes; + checksum_bytes += sum.data[0]; + checksum_bytes += sum.data[1]; + checksum_bytes += sum.data[2]; + checksum_bytes += sum.data[3]; + return epee::string_tools::buff_to_hex_nodelimer(checksum_bytes); +} + void message_store::stop_auto_config() { for (uint32_t i = 0; i < m_num_authorized_signers; ++i) @@ -661,32 +708,38 @@ void message_store::delete_all_messages() m_messages.clear(); } -// Make a message text, which is "attacker controlled data", reasonably safe to display +// Make a text, which is "attacker controlled data", reasonably safe to display // This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command -void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const +std::string message_store::get_sanitized_text(const std::string &text, size_t max_length) { - sanitized_text.clear(); - // Restrict the size to fend of DOS-style attacks with heaps of data - size_t length = std::min(m.content.length(), (size_t)1000); + size_t length = std::min(text.length(), max_length); + std::string sanitized_text = text.substr(0, length); - for (size_t i = 0; i < length; ++i) + try { - char c = m.content[i]; - if ((int)c < 32) + sanitized_text = tools::utf8canonical(sanitized_text, [](wint_t c) { - // Strip out any controls, especially ESC for getting rid of potentially dangerous - // ANSI escape sequences that a console window might interpret - c = ' '; - } - else if ((c == '<') || (c == '>')) - { - // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute - // when displayed in the GUI wallet - c = ' '; - } - sanitized_text += c; + if ((c < 0x20) || (c == 0x7f) || (c >= 0x80 && c <= 0x9f)) + { + // Strip out any controls, especially ESC for getting rid of potentially dangerous + // ANSI escape sequences that a console window might interpret + c = '?'; + } + else if ((c == '<') || (c == '>')) + { + // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute + // when displayed in the GUI wallet + c = '?'; + } + return c; + }); + } + catch (const std::exception &e) + { + sanitized_text = "(Illegal UTF-8 string)"; } + return sanitized_text; } void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename) diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h index d40daf186..9055fd776 100644 --- a/src/wallet/message_store.h +++ b/src/wallet/message_store.h @@ -242,6 +242,7 @@ namespace mms size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token); void process_auto_config_data_message(uint32_t id); + std::string get_config_checksum() const; void stop_auto_config(); // Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command @@ -275,7 +276,7 @@ namespace mms void set_message_processed_or_sent(uint32_t id); void delete_message(uint32_t id); void delete_all_messages(); - void get_sanitized_message_text(const message &m, std::string &sanitized_text) const; + static std::string get_sanitized_text(const std::string &text, size_t max_length); void send_message(const multisig_wallet_state &state, uint32_t id); bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 286eaee55..d7ed3e999 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -947,7 +947,7 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) { - shim->get_tx_pub_key_from_received_outs = boost::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, _1); + shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1); } bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry, cryptonote::transaction &tx, crypto::hash &tx_hash) @@ -1867,6 +1867,20 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has } } //---------------------------------------------------------------------------------------------------- +bool wallet2::spends_one_of_ours(const cryptonote::transaction &tx) const +{ + for (const auto &in: tx.vin) + { + if (in.type() != typeid(cryptonote::txin_to_key)) + continue; + const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in); + auto it = m_key_images.find(in_to_key.k_image); + if (it != m_key_images.end()) + return true; + } + return false; +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { PERF_TIMER(process_new_transaction); @@ -2153,7 +2167,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += amount; notify = true; @@ -2230,7 +2244,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += extra_amount; notify = true; @@ -3990,13 +4004,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ // Load keys from buffer boost::optional<crypto::chacha_key> keys_to_encrypt; - try { - r = wallet2::load_keys_buf(keys_file_buf, password, keys_to_encrypt); - } catch (const std::exception& e) { - std::size_t found = string(e.what()).find("failed to deserialize keys buffer"); - THROW_WALLET_EXCEPTION_IF(found != std::string::npos, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); - throw e; - } + r = wallet2::load_keys_buf(keys_file_buf, password, keys_to_encrypt); // Rewrite with encrypted keys if unencrypted, ignore errors if (r && keys_to_encrypt != boost::none) @@ -9024,7 +9032,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); continue; } - if (!is_spent(td2, false) && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) + if (!is_spent(td2, false) && !td2.m_frozen && !td2.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -13086,7 +13094,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); std::vector<std::vector<rct::key>> k; - auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(k.data(), k.size() * sizeof(k[0]));}); + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){for (auto &v: k) memwipe(v.data(), v.size() * sizeof(v[0]));}); k.reserve(m_transfers.size()); for (const auto &td: m_transfers) k.push_back(td.m_multisig_k); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 273b17d21..712f91613 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -131,7 +131,7 @@ private: public: // Full wallet callbacks virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) {} + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {} virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} @@ -1517,6 +1517,7 @@ private: void check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_credits, double expected_cost); bool should_expand(const cryptonote::subaddress_index &index) const; + bool spends_one_of_ours(const cryptonote::transaction &tx) const; cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index da91ba051..edaa9b20a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -30,6 +30,7 @@ #pragma once +#include <functional> #include <vector> #include <iostream> #include <stdint.h> @@ -856,10 +857,10 @@ inline bool do_replay_file(const std::string& filename) } #define REGISTER_CALLBACK(CB_NAME, CLBACK) \ - register_callback(CB_NAME, boost::bind(&CLBACK, this, _1, _2, _3)); + register_callback(CB_NAME, std::bind(&CLBACK, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #define REGISTER_CALLBACK_METHOD(CLASS, METHOD) \ - register_callback(#METHOD, boost::bind(&CLASS::METHOD, this, _1, _2, _3)); + register_callback(#METHOD, std::bind(&CLASS::METHOD, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \ test_generator generator; \ diff --git a/tests/data/fuzz/tx-extra/TXEXTRA1 b/tests/data/fuzz/tx-extra/TXEXTRA1 Binary files differnew file mode 100644 index 000000000..08852abe3 --- /dev/null +++ b/tests/data/fuzz/tx-extra/TXEXTRA1 diff --git a/tests/data/fuzz/tx-extra/TXEXTRA2 b/tests/data/fuzz/tx-extra/TXEXTRA2 Binary files differnew file mode 100644 index 000000000..170301145 --- /dev/null +++ b/tests/data/fuzz/tx-extra/TXEXTRA2 diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py index 20e7b5f6f..37a5915c9 100755 --- a/tests/functional_tests/address_book.py +++ b/tests/functional_tests/address_book.py @@ -145,7 +145,7 @@ class AddressBookTest(): res = wallet.get_address_book() assert len(res.entries) == 1 e = res.entries[0] - assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert e.description == 'dev fund' # UTF-8 @@ -173,7 +173,7 @@ class AddressBookTest(): # get them back res = wallet.get_address_book([0]) assert len(res.entries) == 1 - assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert res.entries[0].description == 'dev fund' res = wallet.get_address_book([1]) assert len(res.entries) == 1 @@ -213,12 +213,12 @@ class AddressBookTest(): assert e.index == 1 assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' assert e.description == u'えんしゅう' - res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A') + res = wallet.edit_address_book(1, address = '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H') res = wallet.get_address_book([1]) assert len(res.entries) == 1 e = res.entries[0] assert e.index == 1 - assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert e.description == u'えんしゅう' ok = False try: res = wallet.edit_address_book(1, address = '') @@ -237,7 +237,7 @@ class AddressBookTest(): wallet.delete_address_book(0) res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up assert len(res.entries) == 1 - assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert res.entries[0].description == u'えんしゅう' wallet.delete_address_book(2) wallet.delete_address_book(0) diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py index 71be785b8..c0c1d23c2 100755 --- a/tests/functional_tests/speed.py +++ b/tests/functional_tests/speed.py @@ -67,7 +67,7 @@ class SpeedTest(): destinations = [] for i in range(3): - destinations.append({"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}) + destinations.append({"amount":1,"address":'888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}) self._test_speed_generateblocks(daemon=daemon, blocks=70) for i in range(1, 10): @@ -89,7 +89,7 @@ class SpeedTest(): print('Test speed of transfer') start = time.time() - destinations = [{"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}] + destinations = [{"amount":1,"address":'888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}] res = wallet.transfer_split(destinations) print('generating tx took: ', time.time() - start, 'seconds') diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index 72222282b..e886200da 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -241,6 +241,17 @@ class TransferTest(): assert x.fee == txes[txid].fee assert x.tx_blob == txes[txid].tx_blob + print('Checking relaying txes') + res = daemon.get_transaction_pool_hashes() + assert len(res.tx_hashes) > 0 + txid = res.tx_hashes[0] + daemon.relay_tx([txid]) + res = daemon.get_transactions([txid]) + assert len(res.txs) == 1 + assert res.txs[0].tx_hash == txid + assert res.txs[0].in_pool + assert res.txs[0].relayed + daemon.flush_txpool() self.check_empty_pool() diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py index 07d5b9cf1..d201cdf68 100755 --- a/tests/functional_tests/validate_address.py +++ b/tests/functional_tests/validate_address.py @@ -94,7 +94,7 @@ class AddressValidationTest(): def check_openalias_addresses(self): print('Validating openalias addresses') addresses = [ - ['donate@getmonero.org', '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'] + ['donate@getmonero.org', '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'] ] for address in addresses: res = self.wallet.validate_address(address[0]) @@ -102,7 +102,7 @@ class AddressValidationTest(): res = self.wallet.validate_address(address[0], allow_openalias = True) assert res.valid assert not res.integrated - assert not res.subaddress + assert res.subaddress assert res.nettype == 'mainnet' assert res.openalias_address == address[1] diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index 5d543649c..48a49edab 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -34,7 +34,8 @@ target_link_libraries(block_fuzz_tests epee device ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET block_fuzz_tests PROPERTY FOLDER "tests") @@ -47,7 +48,8 @@ target_link_libraries(transaction_fuzz_tests epee device ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET transaction_fuzz_tests PROPERTY FOLDER "tests") @@ -61,7 +63,8 @@ target_link_libraries(signature_fuzz_tests epee device ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET signature_fuzz_tests PROPERTY FOLDER "tests") @@ -75,7 +78,8 @@ target_link_libraries(cold-outputs_fuzz_tests epee device ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET cold-outputs_fuzz_tests PROPERTY FOLDER "tests") @@ -89,7 +93,8 @@ target_link_libraries(cold-transaction_fuzz_tests epee device ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET cold-transaction_fuzz_tests PROPERTY FOLDER "tests") @@ -101,7 +106,8 @@ target_link_libraries(load-from-binary_fuzz_tests epee ${Boost_PROGRAM_OPTIONS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET load-from-binary_fuzz_tests PROPERTY FOLDER "tests") @@ -113,7 +119,8 @@ target_link_libraries(load-from-json_fuzz_tests epee ${Boost_PROGRAM_OPTIONS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET load-from-json_fuzz_tests PROPERTY FOLDER "tests") @@ -125,7 +132,8 @@ target_link_libraries(base58_fuzz_tests epee ${Boost_PROGRAM_OPTIONS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET base58_fuzz_tests PROPERTY FOLDER "tests") @@ -138,7 +146,8 @@ target_link_libraries(parse-url_fuzz_tests ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET parse-url_fuzz_tests PROPERTY FOLDER "tests") @@ -153,7 +162,8 @@ target_link_libraries(http-client_fuzz_tests ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET http-client_fuzz_tests PROPERTY FOLDER "tests") @@ -168,7 +178,8 @@ target_link_libraries(levin_fuzz_tests ${Boost_REGEX_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET levin_fuzz_tests PROPERTY FOLDER "tests") @@ -183,8 +194,27 @@ target_link_libraries(bulletproof_fuzz_tests ${Boost_REGEX_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) set_property(TARGET bulletproof_fuzz_tests PROPERTY FOLDER "tests") +add_executable(tx-extra_fuzz_tests tx-extra.cpp fuzzer.cpp) +target_link_libraries(tx-extra_fuzz_tests + PRIVATE + cryptonote_basic + common + epee + ${Boost_THREAD_LIBRARY} + ${Boost_CHRONO_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) +set_property(TARGET tx-extra_fuzz_tests + PROPERTY + FOLDER "tests") + diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp index 07eb1504c..3a06353ec 100644 --- a/tests/fuzz/base58.cpp +++ b/tests/fuzz/base58.cpp @@ -27,50 +27,13 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "include_base_utils.h" -#include "file_io_utils.h" #include "common/base58.h" #include "fuzzer.h" -class Base58Fuzzer: public Fuzzer -{ -public: - Base58Fuzzer() {} - virtual int init(); - virtual int run(const std::string &filename); -}; - -int Base58Fuzzer::init() -{ - return 0; -} - -int Base58Fuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { - std::string data; - tools::base58::decode(s, data); - } - catch (const std::exception &e) - { - std::cerr << "Failed to load from binary: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - Base58Fuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() +BEGIN_SIMPLE_FUZZER() + std::string data; + tools::base58::decode(std::string((const char*)buf, len), data); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index 6a21eb8c9..f8be29842 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -33,36 +33,10 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "fuzzer.h" -class BlockFuzzer: public Fuzzer -{ -public: - virtual int run(const std::string &filename); +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() -private: -}; - -int BlockFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } +BEGIN_SIMPLE_FUZZER() cryptonote::block b = AUTO_VAL_INIT(b); - if(!parse_and_validate_block_from_blob(s, b)) - { - std::cout << "Error: failed to parse block from file " << filename << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - BlockFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} + parse_and_validate_block_from_blob(std::string((const char*)buf, len), b); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp index 4d2b01fcd..e0f183bc5 100644 --- a/tests/fuzz/bulletproof.cpp +++ b/tests/fuzz/bulletproof.cpp @@ -33,40 +33,13 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "fuzzer.h" -class BulletproofFuzzer: public Fuzzer -{ -public: - virtual int run(const std::string &filename); +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() -private: -}; - -int BulletproofFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } +BEGIN_SIMPLE_FUZZER() std::stringstream ss; - ss << s; + ss << std::string((const char*)buf, len); binary_archive<false> ba(ss); rct::Bulletproof proof = AUTO_VAL_INIT(proof); - bool r = ::serialization::serialize(ba, proof); - if(!r) - { - std::cout << "Error: failed to parse bulletproof from file " << filename << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - BulletproofFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} + ::serialization::serialize(ba, proof); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index bad4c3e5c..fea235079 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -34,70 +34,28 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -class ColdOutputsFuzzer: public Fuzzer -{ -public: - ColdOutputsFuzzer(): wallet(cryptonote::TESTNET) {} - virtual int init(); - virtual int run(const std::string &filename); +static tools::wallet2 *wallet = NULL; -private: - tools::wallet2 wallet; -}; +BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet; + wallet = &local_wallet; -int ColdOutputsFuzzer::init() -{ static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - try - { - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); - } - catch (const std::exception &e) - { - std::cerr << "Error on ColdOutputsFuzzer::init: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int ColdOutputsFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - s = std::string("\x01\x16serialization::archive") + s; - try - { - std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs; - std::stringstream iss; - iss << s; - boost::archive::portable_binary_iarchive ar(iss); - ar >> outputs; - size_t n_outputs = wallet.import_outputs(outputs); - std::cout << boost::lexical_cast<std::string>(n_outputs) << " outputs imported" << std::endl; - } - catch (const std::exception &e) - { - std::cerr << "Failed to import outputs: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - ColdOutputsFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} - + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); +END_INIT_SIMPLE_FUZZER() + +BEGIN_SIMPLE_FUZZER() + std::string s = std::string("\x01\x16serialization::archive") + std::string((const char*)buf, len); + std::pair<size_t, std::vector<tools::wallet2::transfer_details>> outputs; + std::stringstream iss; + iss << s; + boost::archive::portable_binary_iarchive ar(iss); + ar >> outputs; + size_t n_outputs = wallet->import_outputs(outputs); + std::cout << boost::lexical_cast<std::string>(n_outputs) << " outputs imported" << std::endl; +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index 725495680..32c84ac74 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -34,71 +34,29 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -class ColdTransactionFuzzer: public Fuzzer -{ -public: - ColdTransactionFuzzer(): wallet(cryptonote::TESTNET) {} - virtual int init(); - virtual int run(const std::string &filename); +static tools::wallet2 *wallet = NULL; -private: - tools::wallet2 wallet; -}; +BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet; + wallet = &local_wallet; - -int ColdTransactionFuzzer::init() -{ static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - try - { - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); - } - catch (const std::exception &e) - { - std::cerr << "Error on ColdTransactionFuzzer::init: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int ColdTransactionFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - s = std::string("\x01\x16serialization::archive") + s; - try - { - tools::wallet2::unsigned_tx_set exported_txs; - std::stringstream iss; - iss << s; - boost::archive::portable_binary_iarchive ar(iss); - ar >> exported_txs; - std::vector<tools::wallet2::pending_tx> ptx; - bool success = wallet.sign_tx(exported_txs, "/tmp/cold-transaction-test-signed", ptx); - std::cout << (success ? "signed" : "error") << std::endl; - } - catch (const std::exception &e) - { - std::cerr << "Failed to sign transaction: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - ColdTransactionFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); +END_INIT_SIMPLE_FUZZER() + +BEGIN_SIMPLE_FUZZER() + std::string s = std::string("\x01\x16serialization::archive") + std::string((const char*)buf, len); + tools::wallet2::unsigned_tx_set exported_txs; + std::stringstream iss; + iss << s; + boost::archive::portable_binary_iarchive ar(iss); + ar >> exported_txs; + std::vector<tools::wallet2::pending_tx> ptx; + bool success = wallet->sign_tx(exported_txs, "/tmp/cold-transaction-test-signed", ptx); + std::cout << (success ? "signed" : "error") << std::endl; +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index 54cdf9238..eaaef5aa5 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -33,6 +33,8 @@ #include "common/util.h" #include "fuzzer.h" +#ifndef OSSFUZZ + #if (!defined(__clang__) || (__clang__ < 5)) static int __AFL_LOOP(int) { @@ -74,3 +76,5 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer) CATCH_ENTRY_L0("run_fuzzer", 1); } + +#endif diff --git a/tests/fuzz/fuzzer.h b/tests/fuzz/fuzzer.h index 64abbb348..f45e69f73 100644 --- a/tests/fuzz/fuzzer.h +++ b/tests/fuzz/fuzzer.h @@ -27,6 +27,56 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string> +#include "file_io_utils.h" + +#ifdef OSSFUZZ + +#define BEGIN_INIT_SIMPLE_FUZZER() \ + static int init() \ + { \ + try \ + { + +#define END_INIT_SIMPLE_FUZZER() \ + } \ + catch (const std::exception &e) \ + { \ + fprintf(stderr, "Exception: %s\n", e.what()); \ + return 1; \ + } \ + return 0; \ + } + +#define BEGIN_SIMPLE_FUZZER() \ +extern "C" { \ + int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) \ + { \ + try \ + { \ + static bool first = true; \ + if (first) \ + { \ + if (init()) \ + return 1; \ + first = false; \ + } \ + +#define END_SIMPLE_FUZZER() \ + } \ + catch (const std::exception &e) \ + { \ + fprintf(stderr, "Exception: %s\n", e.what()); \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ + } \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ + } \ +} + +#else class Fuzzer { @@ -36,3 +86,61 @@ public: }; int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer); + +#define BEGIN_INIT_SIMPLE_FUZZER() \ + class SimpleFuzzer: public Fuzzer \ + { \ + virtual int init() \ + { \ + try \ + { + +#define END_INIT_SIMPLE_FUZZER() \ + } \ + catch (const std::exception &e) \ + { \ + fprintf(stderr, "Exception: %s\n", e.what()); \ + return 1; \ + } \ + return 0; \ + } + +#define BEGIN_SIMPLE_FUZZER() \ + virtual int run(const std::string &filename) \ + { \ + try \ + { \ + std::string s; \ + if (!epee::file_io_utils::load_file_to_string(filename, s)) \ + { \ + std::cout << "Error: failed to load file " << filename << std::endl; \ + return 1; \ + } \ + const uint8_t *buf = (const uint8_t*)s.data(); \ + const size_t len = s.size(); \ + { + +#define END_SIMPLE_FUZZER() \ + } \ + } \ + catch (const std::exception &e) \ + { \ + fprintf(stderr, "Exception: %s\n", e.what()); \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ + } \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ + } \ + }; \ + int main(int argc, const char **argv) \ + { \ + TRY_ENTRY(); \ + SimpleFuzzer fuzzer; \ + return run_fuzzer(argc, argv, fuzzer); \ + CATCH_ENTRY_L0("main", 1); \ + } + +#endif diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index 6a28c9e86..4b12b36bb 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -58,48 +58,11 @@ private: std::string data; }; -class HTTPClientFuzzer: public Fuzzer -{ -public: - HTTPClientFuzzer() {} - virtual int init(); - virtual int run(const std::string &filename); - -private: - epee::net_utils::http::http_simple_client_template<dummy_client> client; -}; - -int HTTPClientFuzzer::init() -{ - return 0; -} - -int HTTPClientFuzzer::run(const std::string &filename) -{ - std::string s; +static epee::net_utils::http::http_simple_client_template<dummy_client> client; - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { - client.test(s, std::chrono::milliseconds(1000)); - } - catch (const std::exception &e) - { - std::cerr << "Failed to test http client: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - HTTPClientFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() +BEGIN_SIMPLE_FUZZER() + client.test(std::string((const char*)buf, len), std::chrono::milliseconds(1000)); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index 92d09e0c6..012d05f36 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -279,26 +279,10 @@ namespace #endif } -class LevinFuzzer: public Fuzzer -{ -public: - LevinFuzzer() {} //: handler(endpoint, config, context) {} - virtual int init(); - virtual int run(const std::string &filename); - -private: - //epee::net_utils::connection_context_base context; - //epee::levin::async_protocol_handler<> handler; -}; +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() -int LevinFuzzer::init() -{ - return 0; -} - -int LevinFuzzer::run(const std::string &filename) -{ - std::string s; +BEGIN_SIMPLE_FUZZER() #if 0 epee::levin::bucket_head2 req_head; @@ -313,13 +297,6 @@ int LevinFuzzer::run(const std::string &filename) fwrite(&req_head,sizeof(req_head),1, f); fclose(f); #endif - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { //std::unique_ptr<test_connection> conn = new test(); boost::asio::io_service io_service; test_levin_protocol_handler_config m_handler_config; @@ -329,21 +306,5 @@ int LevinFuzzer::run(const std::string &filename) conn->start(); //m_commands_handler.invoke_out_buf(expected_out_data); //m_commands_handler.return_code(expected_return_code); - conn->m_protocol_handler.handle_recv(s.data(), s.size()); - } - catch (const std::exception &e) - { - std::cerr << "Failed to test http client: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - LevinFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} - + conn->m_protocol_handler.handle_recv(buf, len); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp index b9a3682a0..6d6826e2e 100644 --- a/tests/fuzz/load_from_binary.cpp +++ b/tests/fuzz/load_from_binary.cpp @@ -33,46 +33,10 @@ #include "storages/portable_storage_base.h" #include "fuzzer.h" -class PortableStorageFuzzer: public Fuzzer -{ -public: - PortableStorageFuzzer() {} - virtual int init(); - virtual int run(const std::string &filename); -}; - -int PortableStorageFuzzer::init() -{ - return 0; -} - -int PortableStorageFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { - epee::serialization::portable_storage ps; - ps.load_from_binary(s); - } - catch (const std::exception &e) - { - std::cerr << "Failed to load from binary: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - PortableStorageFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() +BEGIN_SIMPLE_FUZZER() + epee::serialization::portable_storage ps; + ps.load_from_binary(std::string((const char*)buf, len)); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp index 86d287782..8788a92b6 100644 --- a/tests/fuzz/load_from_json.cpp +++ b/tests/fuzz/load_from_json.cpp @@ -33,46 +33,10 @@ #include "storages/portable_storage_base.h" #include "fuzzer.h" -class PortableStorageFuzzer: public Fuzzer -{ -public: - PortableStorageFuzzer() {} - virtual int init(); - virtual int run(const std::string &filename); -}; - -int PortableStorageFuzzer::init() -{ - return 0; -} - -int PortableStorageFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { - epee::serialization::portable_storage ps; - ps.load_from_json(s); - } - catch (const std::exception &e) - { - std::cerr << "Failed to load from binary: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - PortableStorageFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() +BEGIN_SIMPLE_FUZZER() + epee::serialization::portable_storage ps; + ps.load_from_json(std::string((const char*)buf, len)); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp index 1c693a044..cc1323eb5 100644 --- a/tests/fuzz/parse_url.cpp +++ b/tests/fuzz/parse_url.cpp @@ -31,46 +31,10 @@ #include "net/net_parse_helpers.h" #include "fuzzer.h" -class ParseURLFuzzer: public Fuzzer -{ -public: - ParseURLFuzzer() {} - virtual int init(); - virtual int run(const std::string &filename); -}; - -int ParseURLFuzzer::init() -{ - return 0; -} - -int ParseURLFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - try - { - epee::net_utils::http::url_content url; - epee::net_utils::parse_url(s, url); - } - catch (const std::exception &e) - { - std::cerr << "Failed to load from binary: " << e.what() << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - ParseURLFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() +BEGIN_SIMPLE_FUZZER() + epee::net_utils::http::url_content url; + epee::net_utils::parse_url(std::string((const char*)buf, len), url); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index 4ee92e6dc..8f528b20e 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -34,66 +34,31 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -class SignatureFuzzer: public Fuzzer -{ -public: - SignatureFuzzer(): Fuzzer(), wallet(cryptonote::TESTNET) {} - virtual int init(); - virtual int run(const std::string &filename); +static tools::wallet2 *wallet = NULL; +static cryptonote::account_public_address address; -private: - tools::wallet2 wallet; - cryptonote::account_public_address address; -}; +BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet(cryptonote::TESTNET); + wallet = &local_wallet; -int SignatureFuzzer::init() -{ static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - try - { - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); - cryptonote::address_parse_info info; - if (!cryptonote::get_account_address_from_str_or_url(info, cryptonote::TESTNET, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN")) - { - std::cerr << "failed to parse address" << std::endl; - return 1; - } - address = info.address; - } - catch (const std::exception &e) + cryptonote::address_parse_info info; + if (!cryptonote::get_account_address_from_str_or_url(info, cryptonote::TESTNET, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN")) { - std::cerr << "Error on SignatureFuzzer::init: " << e.what() << std::endl; + std::cerr << "failed to parse address" << std::endl; return 1; } - return 0; -} - -int SignatureFuzzer::run(const std::string &filename) -{ - std::string s; + address = info.address; +END_INIT_SIMPLE_FUZZER() - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } - - bool valid = wallet.verify("test", address, s); +BEGIN_SIMPLE_FUZZER() + bool valid = wallet->verify("test", address, std::string((const char*)buf, len)); std::cout << "Signature " << (valid ? "valid" : "invalid") << std::endl; - - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - SignatureFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index 9469f82fe..94f0a849f 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -33,36 +33,10 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "fuzzer.h" -class TransactionFuzzer: public Fuzzer -{ -public: - virtual int run(const std::string &filename); +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() -private: -}; - -int TransactionFuzzer::run(const std::string &filename) -{ - std::string s; - - if (!epee::file_io_utils::load_file_to_string(filename, s)) - { - std::cout << "Error: failed to load file " << filename << std::endl; - return 1; - } +BEGIN_SIMPLE_FUZZER() cryptonote::transaction tx = AUTO_VAL_INIT(tx); - if(!parse_and_validate_tx_from_blob(s, tx)) - { - std::cout << "Error: failed to parse transaction from file " << filename << std::endl; - return 1; - } - return 0; -} - -int main(int argc, const char **argv) -{ - TRY_ENTRY(); - TransactionFuzzer fuzzer; - return run_fuzzer(argc, argv, fuzzer); - CATCH_ENTRY_L0("main", 1); -} + parse_and_validate_tx_from_blob(std::string((const char*)buf, len), tx); +END_SIMPLE_FUZZER() diff --git a/tests/fuzz/tx-extra.cpp b/tests/fuzz/tx-extra.cpp new file mode 100644 index 000000000..35b14b802 --- /dev/null +++ b/tests/fuzz/tx-extra.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "include_base_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "fuzzer.h" + +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() + +BEGIN_SIMPLE_FUZZER() + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + cryptonote::parse_tx_extra(std::vector<uint8_t>(buf, buf + len), tx_extra_fields); +END_SIMPLE_FUZZER() + diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h index 1ac471ba6..1748ce096 100644 --- a/tests/performance_tests/equality.h +++ b/tests/performance_tests/equality.h @@ -35,7 +35,7 @@ struct memcmp32 { - static const size_t loop_count = 1000000000; + static const size_t loop_count = 10000000; static int call(const unsigned char *k0, const unsigned char *k1){ return memcmp(k0, k1, 32); } }; diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 9f95eb896..ae7aabe08 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -195,7 +195,7 @@ void run_test(const std::string &filter, Params ¶ms, const char* test_name) scale = 1000; time_per_call = runner.time_per_call(1000); #ifdef _WIN32 - unit = "\xb5s"; + unit = "us"; #else unit = "µs"; #endif diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 2e93f9e67..0f91671a7 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -387,6 +387,29 @@ TEST(ByteSlice, Construction) EXPECT_FALSE(std::is_copy_assignable<epee::byte_slice>()); } +TEST(ByteSlice, DataReturnedMatches) +{ + for (int i = 64; i > 0; i--) + { + std::string sso_string(i, 'a'); + std::string original = sso_string; + epee::byte_slice slice{std::move(sso_string)}; + + EXPECT_EQ(slice.size(), original.size()); + EXPECT_EQ(memcmp(slice.data(), original.data(), original.size()), 0); + } + + for (int i = 64; i > 0; i--) + { + std::vector<uint8_t> sso_vector(i, 'a'); + std::vector<uint8_t> original = sso_vector; + epee::byte_slice slice{std::move(sso_vector)}; + + EXPECT_EQ(slice.size(), original.size()); + EXPECT_EQ(memcmp(slice.data(), original.data(), original.size()), 0); + } +} + TEST(ByteSlice, NoExcept) { EXPECT_TRUE(std::is_nothrow_default_constructible<epee::byte_slice>()); @@ -667,6 +690,23 @@ TEST(ByteSlice, TakeSlice) EXPECT_TRUE(boost::range::equal(base_string, slice)); const epee::span<const std::uint8_t> original = epee::to_span(slice); + const epee::byte_slice empty_slice = slice.take_slice(0); + EXPECT_EQ(original.begin(), slice.begin()); + EXPECT_EQ(slice.begin(), slice.cbegin()); + EXPECT_EQ(original.end(), slice.end()); + EXPECT_EQ(slice.end(), slice.cend()); + + EXPECT_EQ(nullptr, empty_slice.begin()); + EXPECT_EQ(nullptr, empty_slice.cbegin()); + EXPECT_EQ(nullptr, empty_slice.end()); + EXPECT_EQ(nullptr, empty_slice.cend()); + EXPECT_EQ(nullptr, empty_slice.data()); + EXPECT_TRUE(empty_slice.empty()); + EXPECT_EQ(0u, empty_slice.size()); + + EXPECT_FALSE(slice.empty()); + EXPECT_EQ(slice.cbegin(), slice.data()); + const epee::byte_slice slice2 = slice.take_slice(remove_size); EXPECT_EQ(original.begin() + remove_size, slice.begin()); @@ -1061,6 +1101,20 @@ TEST(ByteStream, ToByteSlice) EXPECT_EQ(nullptr, stream.data()); EXPECT_EQ(nullptr, stream.tellp()); EXPECT_TRUE(equal(source, slice)); + + stream = epee::byte_stream{}; + stream.reserve(1); + EXPECT_NE(nullptr, stream.data()); + EXPECT_NE(nullptr, stream.tellp()); + + const epee::byte_slice empty_slice{std::move(stream)}; + EXPECT_TRUE(empty_slice.empty()); + EXPECT_EQ(0u, empty_slice.size()); + EXPECT_EQ(nullptr, empty_slice.begin()); + EXPECT_EQ(nullptr, empty_slice.cbegin()); + EXPECT_EQ(nullptr, empty_slice.end()); + EXPECT_EQ(nullptr, empty_slice.cend()); + EXPECT_EQ(nullptr, empty_slice.data()); } TEST(ToHex, String) diff --git a/utils/gpg_keys/rbrunner7.asc b/utils/gpg_keys/rbrunner7.asc new file mode 100644 index 000000000..5a3713e42 --- /dev/null +++ b/utils/gpg_keys/rbrunner7.asc @@ -0,0 +1,31 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF6po6kBCADSKkQYShjqwMWKLv5EtBr92+3cbDBsqInmBUKbpznRmPaFVrE6 +h7rK1GrQJslddHM8amB75UPQv4kImU4EsHreoOwA0teKtKhYs53LbgZLg4D6A6Sh +AxT4/e39lp+/Bjoi1slZTNwG7LVe0NQs0y9v2FgbZwTvxKX59rpDkQzQbCoP8Jxa +E0eh1Yp3c7YHXnx6NUygS+tle9veehPQHDi6dPRfFp+Usk9Ojpws5PoBNdBsoNXH +dRGRtKTgyD/FajwLatrJnGnL8NVzM0rMT05n1qsdZIWD/wKwgqmZCPbIJDZaC/QO +AepIKWkPrWtJl6WVseDDTU7vMM0cHq+i0EmTABEBAAG0JlJlbsOpIEJydW5uZXIg +PHJicnVubmVyQGRyZWFtc2hhcmUuY2g+iQFUBBMBCAA+FiEEn4wKfpzYWyxSK58w +aiUdVx9/e8MFAl6po6kCGyMFCQPCE3cFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA +CgkQaiUdVx9/e8NVBAgAgPqN5VCpNy7Mnlu74LYbWX+uFihp5O2x5QaGP79KKXHj ++iiyKZLjk7fA1/XGg3zUlNOq5IGuQwFMWJICaEr6Ph7CoH+Zn9J5GG8QBACP7Rgy +04+NqzgUlL1tGkLOsqUF0Vfhe1ux8g71OyDFDeo3YD1Jru/piwXoaHDxOZAbhwYj +WXUuzghlS2GslF1I9BRoMQcQDFcLSX+l2sVCVtlkeIuqNHSFWhruOvBBEj9RwM8j +Dm8h5shH62EEjr+KGuOiZJX/M9FRNk+qFMhfWWmXDmKc9CFV/ElAyXCSenEAzcV6 +ayXBjB1spHXRee7bOmLd/ku6qGbwpGp5aTBbNOPMV7kBDQReqaOpAQgA0PaRFp1s +usThDLPc5/Xpy6H4m21LOyoF9VqLNQdVSIqqx8b5FqPjyTw3N957XXBvzAHdO0ig +831tZb/JYU13jnUMnbtNHMwel+SNRWuqn+YU9/shN1B8Js///IwIFV2KCQKuQTRn +ea2OPIlffdJYXPf0/BFWxr80Hb1EWkC6bpc1gsqrcGPzenVEV0PPdiAzsGWOzJkJ +e69qgGLW2ulP7PUb8m4z7jXChzYQ1ki5t2BM7Ta30qWJ6qyvccOJxSAYDVHeT44q +C83Vyeum/1n6isTlH1J6G760mX5/S3vIRfyE3eXjfOpluUf1kWScFuokUSFm+n7k +BsHmzjjk9JuNlQARAQABiQE8BBgBCAAmFiEEn4wKfpzYWyxSK58waiUdVx9/e8MF +Al6po6kCGwwFCQPCE3cACgkQaiUdVx9/e8NjCAgAjkMYw55h3fJj/wmyiF+G53uR +kh6Z/07HKdFyhe+1698F49V68xWBMym56O5vv6iUBH4xBeyPTqC/nZ6v/L5m3z2F +7f69hxaH7mp4BiLugcr2mkx8N6qCJBwSsVnql1/E5TvHWVjp00ikB5oYTRj7zcmo +tLV9q2nBFjk5iWuoH5GtJ3PvIYZilIlqEqIF9trCD89sXZnATEI9Z8N+jFWAgYxQ +f63xVHeW7vTA3ACyA7Qmf8XPjgDMlW4ANrgyGzmcZxGIpjNAlezBp/Bojw22Srrh +uCRqoKg4lANxBJw9S6SO06QuKGJ7utIQ3YkqQFfzz47Q3sEvhLKvbSiBowazvw== +=sdcD +-----END PGP PUBLIC KEY BLOCK----- + diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service index 5f37e54b2..dbb9e8aaa 100644 --- a/utils/systemd/monerod.service +++ b/utils/systemd/monerod.service @@ -7,6 +7,8 @@ User=monero Group=monero WorkingDirectory=~ RuntimeDirectory=monero +StateDirectory=monero +LogsDirectory=monero # Clearnet config # |