diff options
59 files changed, 1617 insertions, 1420 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 801f85b7a..7aae84977 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -154,7 +154,7 @@ jobs: - name: install monero dependencies run: ${{env.APT_INSTALL_LINUX}} - name: install Python dependencies - run: pip install requests psutil monotonic zmq + run: pip install requests psutil monotonic zmq deepdiff - name: tests env: CTEST_OUTPUT_ON_FAILURE: ON diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml new file mode 100644 index 000000000..16bd43ea7 --- /dev/null +++ b/.github/workflows/copyright.yml @@ -0,0 +1,55 @@ +name: ci/gh-actions/copyright +on: + schedule: + - cron: '0 0 1 1 *' +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Make changes to pull request + continue-on-error: true + shell: bash + run: | + year=$(date +%Y) + echo "YEAR=$(echo $year)" >> $GITHUB_ENV + find . -print0 | while IFS= read -r -d '' file + do + if [[ -d $file ]] || [[ $file == *".git"* ]]; then + continue + fi + line=$(grep .*Copyright.*Monero.* $file || true) + if [[ -z $line ]]; then + continue + fi + fromTo=$(grep -o "[0-9]\{4\}-[0-9]\{4\}" <<< $line || true) + if [[ ! -z $fromTo ]]; then + # string contains "FROM-TO" + # we need to replace FROM with current year + to=$(awk '{split($0, array, "-"); print array[2]}' <<< ${fromTo}) + repl=${line/"$to"/"$year"} + else + # we only have a FROM year + # find occurance of 4 digits + from=$(grep -o "[0-9]\{4\}" <<< $line || true) + fromTo="${from}-${year}" + # replace FROM with FROM-TO + repl=${line/"$from"/"$fromTo"} + fi + sed -i "s|${line}|${repl}|g" $file + done + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "Copyright: Update to ${{ env.YEAR }}" + committer: GitHub <noreply@github.com> + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: bump-copyright-year + delete-branch: true + title: "Copyright: Update to ${{ env.YEAR }}" + body: | + Happy new year! + draft: false diff --git a/CMakeLists.txt b/CMakeLists.txt index d036f7456..16d4a7e3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1113,7 +1113,8 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") set(EXTRA_LIBRARIES socket nsl resolv) elseif(NOT MSVC AND NOT DEPENDS) find_library(RT rt) - set(EXTRA_LIBRARIES ${RT}) + find_library(Z z) + set(EXTRA_LIBRARIES ${RT} ${Z}) endif() list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) @@ -639,6 +639,12 @@ More info and versions in the [Debian package tracker](https://tracker.debian.or emerge net-p2p/monero ``` +* Alpine Linux: + + ```bash + apk add monero + ``` + * macOS [(homebrew)](https://brew.sh/) ```bash brew install monero diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk index 9516f86ab..c9d3e0064 100644 --- a/contrib/depends/packages/expat.mk +++ b/contrib/depends/packages/expat.mk @@ -1,8 +1,8 @@ package=expat -$(package)_version=2.4.1 -$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_4_1 +$(package)_version=2.5.0 +$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_5_0 $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2f9b6a580b94577b150a7d5617ad4643a4301a6616ff459307df3e225bcfbf40 +$(package)_sha256_hash=6f0e6e01f7b30025fa05c85fdad1e5d0ec7fd35d9f61b22f34998de11969ff67 define $(package)_set_vars $(package)_config_opts=--enable-static diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index 990b85093..bdfb031ed 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -1,20 +1,19 @@ package=openssl -$(package)_version=1.1.1t +$(package)_version=3.0.9 $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b +$(package)_sha256_hash=eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" -$(package)_config_env_android=ANDROID_NDK_HOME="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib -$(package)_build_env_android=ANDROID_NDK_HOME="$(host_prefix)/native" -$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl +$(package)_config_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib +$(package)_build_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" +$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl --libdir=$(host_prefix)/lib $(package)_config_opts+=no-capieng $(package)_config_opts+=no-dso $(package)_config_opts+=no-dtls1 $(package)_config_opts+=no-ec_nistp_64_gcc_128 $(package)_config_opts+=no-gost -$(package)_config_opts+=no-heartbeats $(package)_config_opts+=no-md2 $(package)_config_opts+=no-rc5 $(package)_config_opts+=no-rdrand @@ -22,8 +21,8 @@ $(package)_config_opts+=no-rfc3779 $(package)_config_opts+=no-sctp $(package)_config_opts+=no-shared $(package)_config_opts+=no-ssl-trace -$(package)_config_opts+=no-ssl2 $(package)_config_opts+=no-ssl3 +$(package)_config_opts+=no-tests $(package)_config_opts+=no-unit-test $(package)_config_opts+=no-weak-ssl-ciphers $(package)_config_opts+=no-zlib @@ -49,7 +48,7 @@ $(package)_config_opts_x86_64_freebsd=BSD-x86_64 endef define $(package)_preprocess_cmds - sed -i.old 's|"engines", "apps", "test", "util", "tools", "fuzz"|"engines", "tools"|' Configure + sed -i.old 's|crypto ssl apps util tools fuzz providers doc|crypto ssl util tools providers|' build.info endef define $(package)_config_cmds diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk index 421c51f7f..166cc3f79 100644 --- a/contrib/depends/packages/unbound.mk +++ b/contrib/depends/packages/unbound.mk @@ -11,6 +11,7 @@ define $(package)_set_vars $(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) --with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only $(package)_config_opts_linux=--with-pic $(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix) + $(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes $(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread" endef diff --git a/contrib/epee/include/byte_stream.h b/contrib/epee/include/byte_stream.h index 4f565326a..0904f9aa1 100644 --- a/contrib/epee/include/byte_stream.h +++ b/contrib/epee/include/byte_stream.h @@ -74,6 +74,7 @@ namespace epee public: using char_type = std::uint8_t; using Ch = char_type; + using value_type = char_type; //! Increase internal buffer by at least `byte_stream_increase` bytes. byte_stream() noexcept @@ -86,6 +87,7 @@ namespace epee ~byte_stream() noexcept = default; byte_stream& operator=(byte_stream&& rhs) noexcept; + std::uint8_t* data() noexcept { return buffer_.get(); } const std::uint8_t* data() const noexcept { return buffer_.get(); } std::uint8_t* tellp() const noexcept { return next_write_; } std::size_t available() const noexcept { return end_ - next_write_; } diff --git a/contrib/epee/include/net/abstract_http_client.h b/contrib/epee/include/net/abstract_http_client.h index 46b3747cd..29a7ce19b 100644 --- a/contrib/epee/include/net/abstract_http_client.h +++ b/contrib/epee/include/net/abstract_http_client.h @@ -54,7 +54,6 @@ namespace net_utils std::string convert(char val); std::string conver_to_url_format(const std::string& uri); std::string convert_from_url_format(const std::string& uri); - std::string convert_to_url_format_force_all(const std::string& uri); namespace http { diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 81aa725d1..d88f18194 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -583,11 +583,8 @@ namespace net_utils break; } } - else if (ec.value()) - terminate(); else { - cancel_timer(); - on_interrupted(); + terminate(); } }; m_strand.post( diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index bd6ffe930..5122f1677 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -102,7 +102,6 @@ public: uint64_t m_max_packet_size; uint64_t m_invoke_timeout; - int invoke(int command, message_writer in_msg, std::string& buff_out, boost::uuids::uuid connection_id); template<class callback_t> int invoke_async(int command, message_writer in_msg, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); @@ -165,14 +164,6 @@ public: }; std::atomic<bool> m_protocol_released; - std::atomic<bool> m_invoke_buf_ready; - - volatile int m_invoke_result_code; - - critical_section m_local_inv_buff_lock; - std::string m_local_inv_buff; - - critical_section m_call_lock; std::atomic<uint32_t> m_wait_count; std::atomic<uint32_t> m_close_called; @@ -318,8 +309,6 @@ public: m_wait_count = 0; m_oponent_protocol_ver = 0; m_connection_initialized = false; - m_invoke_buf_ready = false; - m_invoke_result_code = LEVIN_ERROR_CONNECTION; } virtual ~async_protocol_handler() { @@ -521,21 +510,8 @@ public: } else { - invoke_response_handlers_guard.unlock(); - //use sync call scenario - if(!m_wait_count && !m_close_called) - { - MERROR(m_connection_context << "no active invoke when response came, wtf?"); - return false; - }else - { - CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); - m_local_inv_buff = std::string((const char*)buff_to_invoke.data(), buff_to_invoke.size()); - buff_to_invoke = epee::span<const uint8_t>((const uint8_t*)NULL, 0); - m_invoke_result_code = m_current_head.m_return_code; - CRITICAL_REGION_END(); - m_invoke_buf_ready = true; - } + MERROR("Received levin response but have no invoke handlers"); + return false; } }else { @@ -639,9 +615,6 @@ public: int err_code = LEVIN_OK; do { - CRITICAL_REGION_LOCAL(m_call_lock); - - m_invoke_buf_ready = false; CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); if (command == m_connection_context.handshake_command()) @@ -673,55 +646,6 @@ public: return true; } - int invoke(int command, message_writer in_msg, std::string& buff_out) - { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( - boost::bind(&async_protocol_handler::finish_outer_call, this)); - - CRITICAL_REGION_LOCAL(m_call_lock); - - m_invoke_buf_ready = false; - - if (command == m_connection_context.handshake_command()) - m_max_packet_size = m_config.m_max_packet_size; - - if (!send_message(in_msg.finalize_invoke(command))) - { - LOG_ERROR_CC(m_connection_context, "Failed to send request"); - return LEVIN_ERROR_CONNECTION; - } - - uint64_t ticks_start = misc_utils::get_tick_count(); - size_t prev_size = 0; - - while(!m_invoke_buf_ready && !m_protocol_released) - { - if(m_cache_in_buffer.size() - prev_size >= MIN_BYTES_WANTED) - { - prev_size = m_cache_in_buffer.size(); - ticks_start = misc_utils::get_tick_count(); - } - if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout) - { - MWARNING(m_connection_context << "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection "); - close(); - return LEVIN_ERROR_CONNECTION_TIMEDOUT; - } - if(!m_pservice_endpoint->call_run_once_service_io()) - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - - if(m_protocol_released) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); - buff_out.swap(m_local_inv_buff); - m_local_inv_buff.clear(); - CRITICAL_REGION_END(); - - return m_invoke_result_code; - } - /*! Sends `message` without adding a levin header. The message must have been created with `make_noise_notify`, `make_fragmented_notify`, or `message_writer::finalize_notify`. See additional instructions for @@ -827,14 +751,6 @@ int async_protocol_handler_config<t_connection_context>::find_and_lock_connectio return LEVIN_OK; } //------------------------------------------------------------------------------------------ -template<class t_connection_context> -int async_protocol_handler_config<t_connection_context>::invoke(int command, message_writer in_msg, std::string& buff_out, boost::uuids::uuid connection_id) -{ - async_protocol_handler<t_connection_context>* aph; - int r = find_and_lock_connection(connection_id, aph); - return LEVIN_OK == r ? aph->invoke(command, std::move(in_msg), buff_out) : r; -} -//------------------------------------------------------------------------------------------ template<class t_connection_context> template<class callback_t> int async_protocol_handler_config<t_connection_context>::invoke_async(int command, message_writer in_msg, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout) { diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index c615b20e6..aaeae292f 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -26,13 +26,10 @@ // #pragma once -#include <boost/utility/string_ref.hpp> -#include <chrono> -#include <string> -#include "byte_slice.h" -#include "portable_storage_template_helper.h" + #include "net/http_base.h" -#include "net/http_server_handlers_map2.h" +#include "net/jsonrpc_structs.h" +#include "portable_storage_template_helper.h" namespace epee { @@ -136,12 +133,5 @@ namespace epee epee::json_rpc::error error_struct; return invoke_http_json_rpc(uri, method_name, out_struct, result_struct, error_struct, transport, timeout, http_method, req_id); } - - template<class t_command, class t_transport> - bool invoke_http_json_rpc(const boost::string_ref uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0") - { - return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); - } - - } -} + } // namespace net_utils +} // namespace epee diff --git a/contrib/epee/include/string_coding.h b/contrib/epee/include/string_coding.h index 0d9c6c244..1e9da6fb2 100644 --- a/contrib/epee/include/string_coding.h +++ b/contrib/epee/include/string_coding.h @@ -35,7 +35,7 @@ namespace epee namespace string_encoding { inline - std::string& base64_chars() + const std::string& base64_chars() { static std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" diff --git a/contrib/epee/src/abstract_http_client.cpp b/contrib/epee/src/abstract_http_client.cpp index 3ae09c90e..8897ad9ae 100644 --- a/contrib/epee/src/abstract_http_client.cpp +++ b/contrib/epee/src/abstract_http_client.cpp @@ -116,16 +116,6 @@ namespace net_utils return result; } //---------------------------------------------------------------------------------------------------- - std::string convert_to_url_format_force_all(const std::string& uri) - { - std::string result; - - for(size_t i = 0; i!= uri.size(); i++) - { - result += convert(uri[i]); - } - return result; - } namespace http { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7f0b0c18f..cf38466fe 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -81,6 +81,9 @@ target_link_libraries(common PRIVATE ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) +target_include_directories(common + PRIVATE + ${Boost_INCLUDE_DIRS}) #monero_install_headers(common # ${common_headers}) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h new file mode 100644 index 000000000..5824c9c37 --- /dev/null +++ b/src/common/container_helpers.h @@ -0,0 +1,170 @@ +// Copyright (c) 2022, 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. + +// Miscellaneous container helpers. + +#pragma once + +//local headers + +//third party headers + +//standard headers +#include <algorithm> +#include <unordered_map> +#include <utility> + +//forward declarations + + +namespace tools +{ + +/// convert an arbitrary function to functor +template <typename F> +inline auto as_functor(F f) +{ + return [f = std::move(f)](auto&&... args) { return f(std::forward<decltype(args)>(args)...); }; +} +/// convert a binary comparison function to a functor +/// note: for most use-cases 'const T&' will work because only non-trivial types need a user-defined comparison operation +template <typename T, typename ComparisonOpT = bool(const T&, const T&)> +inline auto compare_func(ComparisonOpT comparison_op_func) +{ + static_assert( + std::is_same< + bool, + decltype( + comparison_op_func( + std::declval<std::remove_cv_t<T>>(), + std::declval<std::remove_cv_t<T>>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(T, T)" + ); + + return as_functor(std::move(comparison_op_func)); +} +/// test if a container is sorted and unique according to a comparison criteria (defaults to operator<) +/// NOTE: ComparisonOpT must establish 'strict weak ordering' https://en.cppreference.com/w/cpp/named_req/Compare +template <typename T, typename ComparisonOpT = std::less<typename T::value_type>> +bool is_sorted_and_unique(const T &container, ComparisonOpT comparison_op = ComparisonOpT{}) +{ + using ValueT = typename T::value_type; + static_assert( + std::is_same< + bool, + decltype( + comparison_op( + std::declval<std::remove_cv_t<ValueT>>(), + std::declval<std::remove_cv_t<ValueT>>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(ValueT, ValueT)" + ); + + if (!std::is_sorted(container.begin(), container.end(), comparison_op)) + return false; + + if (std::adjacent_find(container.begin(), + container.end(), + [comparison_op = std::move(comparison_op)](const ValueT &a, const ValueT &b) -> bool + { + return !comparison_op(a, b) && !comparison_op(b, a); + }) + != container.end()) + return false; + + return true; +} +/// specialization for raw function pointers +template <typename T> +bool is_sorted_and_unique(const T &container, + bool (*const comparison_op_func)(const typename T::value_type&, const typename T::value_type&)) +{ + return is_sorted_and_unique(container, compare_func<typename T::value_type>(comparison_op_func)); +} +/// convenience wrapper for checking if the key to a mapped object is correct for that object +/// example: std::unorderd_map<rct::key, std::pair<rct::key, rct::xmr_amount>> where the map key is supposed to +/// reproduce the pair's rct::key; use the predicate to check that relationship +template <typename KeyT, typename ValueT, typename PredT> +bool keys_match_internal_values(const std::unordered_map<KeyT, ValueT> &map, PredT check_key_func) +{ + static_assert( + std::is_same< + bool, + decltype( + check_key_func( + std::declval<std::remove_cv_t<KeyT>>(), + std::declval<std::remove_cv_t<ValueT>>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(KeyT, ValueT)" + ); + + for (const auto &map_element : map) + { + if (!check_key_func(map_element.first, map_element.second)) + return false; + } + + return true; +} +/// convenience wrapper for getting the last element after emplacing back +template <typename ContainerT> +typename ContainerT::value_type& add_element(ContainerT &container) +{ + container.emplace_back(); + return container.back(); +} +/// convenience erasor for unordered maps: std::erase_if(std::unordered_map) is C++20 +template <typename KeyT, typename ValueT, typename PredT> +void for_all_in_map_erase_if(std::unordered_map<KeyT, ValueT> &map_inout, PredT predicate) +{ + using MapValueT = typename std::unordered_map<KeyT, ValueT>::value_type; + static_assert( + std::is_same< + bool, + decltype(predicate(std::declval<std::remove_cv_t<MapValueT>>())) + >::value, + "invalid callable - expected callable in form bool(Value)" + ); + + for (auto map_it = map_inout.begin(); map_it != map_inout.end();) + { + if (predicate(*map_it)) + map_it = map_inout.erase(map_it); + else + ++map_it; + } +} + +} //namespace tools diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 324566afd..ce4555ae3 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -340,6 +340,7 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec dnssec_valid = result->secure && !result->bogus; if (dnssec_available && !dnssec_valid) MWARNING("Invalid DNSSEC " << get_record_name(record_type) << " record signature for " << url << ": " << result->why_bogus); + MWARNING("Possibly your DNS service is problematic. You can have monerod use an alternate via env variable DNS_PUBLIC. Example: DNS_PUBLIC=tcp://9.9.9.9"); if (result->havedata) { for (size_t i=0; result->data[i] != NULL; i++) diff --git a/src/common/util.cpp b/src/common/util.cpp index a0074f44c..c89a85267 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -78,7 +78,6 @@ using namespace epee; #include <strsafe.h> #else #include <sys/file.h> - #include <sys/utsname.h> #include <sys/stat.h> #endif #include <boost/filesystem.hpp> @@ -302,277 +301,6 @@ namespace tools #endif } -#ifdef WIN32 - std::string get_windows_version_display_string() - { - typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); - typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); -#define BUFSIZE 10000 - - char pszOS[BUFSIZE] = {0}; - OSVERSIONINFOEX osvi; - SYSTEM_INFO si; - PGNSI pGNSI; - PGPI pGPI; - BOOL bOsVersionInfoEx; - DWORD dwType; - - ZeroMemory(&si, sizeof(SYSTEM_INFO)); - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi); - - if(!bOsVersionInfoEx) return pszOS; - - // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. - - pGNSI = (PGNSI) GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), - "GetNativeSystemInfo"); - if(NULL != pGNSI) - pGNSI(&si); - else GetSystemInfo(&si); - - if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && - osvi.dwMajorVersion > 4 ) - { - StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft ")); - - // Test for the specific product. - if ( osvi.dwMajorVersion == 10 ) - { - if ( osvi.dwMinorVersion == 0 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 ")); - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " )); - } - } - - if ( osvi.dwMajorVersion == 6 ) - { - if ( osvi.dwMinorVersion == 0 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista ")); - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " )); - } - - if ( osvi.dwMinorVersion == 1 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 ")); - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " )); - } - - if ( osvi.dwMinorVersion == 2 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 ")); - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " )); - } - - if ( osvi.dwMinorVersion == 3 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 ")); - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " )); - } - - pGPI = (PGPI) GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), - "GetProductInfo"); - - pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - - switch( dwType ) - { - case PRODUCT_ULTIMATE: - StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" )); - break; - case PRODUCT_PROFESSIONAL: - StringCchCat(pszOS, BUFSIZE, TEXT("Professional" )); - break; - case PRODUCT_HOME_PREMIUM: - StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" )); - break; - case PRODUCT_HOME_BASIC: - StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" )); - break; - case PRODUCT_ENTERPRISE: - StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); - break; - case PRODUCT_BUSINESS: - StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" )); - break; - case PRODUCT_STARTER: - StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" )); - break; - case PRODUCT_CLUSTER_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" )); - break; - case PRODUCT_DATACENTER_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" )); - break; - case PRODUCT_DATACENTER_SERVER_CORE: - StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" )); - break; - case PRODUCT_ENTERPRISE_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" )); - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" )); - break; - case PRODUCT_SMALLBUSINESS_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" )); - break; - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" )); - break; - case PRODUCT_STANDARD_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" )); - break; - case PRODUCT_STANDARD_SERVER_CORE: - StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" )); - break; - case PRODUCT_WEB_SERVER: - StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" )); - break; - } - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) - { - if( GetSystemMetrics(SM_SERVERR2) ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, ")); - else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003")); - else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server")); - else if( osvi.wProductType == VER_NT_WORKSTATION && - si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) - { - StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition")); - } - else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, ")); - - // Test for the server type. - if ( osvi.wProductType != VER_NT_WORKSTATION ) - { - if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" )); - } - - else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" )); - else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" )); - } - - else - { - if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" )); - else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" )); - else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" )); - } - } - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) - { - StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP ")); - if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" )); - else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) - { - StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 ")); - - if ( osvi.wProductType == VER_NT_WORKSTATION ) - { - StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); - } - else - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" )); - else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" )); - } - } - - // Include service pack (if any) and build number. - - if( strlen(osvi.szCSDVersion) > 0 ) - { - StringCchCat(pszOS, BUFSIZE, TEXT(" ") ); - StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion); - } - - TCHAR buf[80]; - - StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); - StringCchCat(pszOS, BUFSIZE, buf); - - if ( osvi.dwMajorVersion >= 6 ) - { - if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) - StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" )); - else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) - StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit")); - } - - return pszOS; - } - else - { - printf( "This sample does not support this version of Windows.\n"); - return pszOS; - } - } -#else -std::string get_nix_version_display_string() -{ - struct utsname un; - - if(uname(&un) < 0) - return std::string("*nix: failed to get os version"); - return std::string() + un.sysname + " " + un.version + " " + un.release; -} -#endif - - - - std::string get_os_version_string() - { -#ifdef WIN32 - return get_windows_version_display_string(); -#else - return get_nix_version_display_string(); -#endif - } - - #ifdef WIN32 std::string get_special_folder_path(int nfolder, bool iscreate) diff --git a/src/common/variant.h b/src/common/variant.h new file mode 100644 index 000000000..ffb34e40a --- /dev/null +++ b/src/common/variant.h @@ -0,0 +1,167 @@ +// Copyright (c) 2022, 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. + +// Variant wrapper class. + +#pragma once + +//local headers + +//third party headers +#include <boost/blank.hpp> +#include <boost/mpl/begin_end.hpp> +#include <boost/mpl/distance.hpp> +#include <boost/mpl/find.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/none_t.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/get.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant.hpp> + +//standard headers +#include <stdexcept> +#include <type_traits> +#include <utility> + +//forward declarations + + +namespace tools +{ + +[[noreturn]] inline void variant_static_visitor_blank_err() +{ throw std::runtime_error("variant: tried to visit an empty variant."); } +[[noreturn]] inline void variant_unwrap_err() +{ throw std::runtime_error("variant: tried to access value of incorrect type."); } + +//// +// variant: convenience wrapper around boost::variant with a cleaner interface +// - the variant is 'optional' - an empty variant will evaluate to 'false' and an initialized variant will be 'true' +/// +template <typename ResultT> +struct variant_static_visitor : public boost::static_visitor<ResultT> +{ + /// provide visitation for empty variants + /// - add this to your visitor with: using variant_static_visitor::operator(); + [[noreturn]] ResultT operator()(const boost::blank) { variant_static_visitor_blank_err(); } + [[noreturn]] ResultT operator()(const boost::blank) const { variant_static_visitor_blank_err(); } +}; + +template <typename... Types> +class variant final +{ + using VType = boost::variant<boost::blank, Types...>; + +public: +//constructors + /// default constructor + variant() = default; + variant(boost::none_t) : variant{} {} //act like boost::optional + + /// construct from variant type (use enable_if to avoid issues with copy/move constructor) + template <typename T, + typename std::enable_if< + !std::is_same< + std::remove_cv_t<std::remove_reference_t<T>>, + variant<Types...> + >::value, + bool + >::type = true> + variant(T &&value) : m_value{std::forward<T>(value)} {} + +//overloaded operators + /// boolean operator: true if the variant isn't empty/uninitialized + explicit operator bool() const noexcept { return !this->is_empty(); } + +//member functions + /// check if empty/uninitialized + bool is_empty() const noexcept { return m_value.which() == 0; } + + /// check the variant type + template <typename T> + bool is_type() const noexcept { return this->index() == this->type_index_of<T>(); } + + /// try to get a handle to the embedded value (return nullptr on failure) + template <typename T> + T* try_unwrap() noexcept { return boost::strict_get< T>(&m_value); } + template <typename T> + const T* try_unwrap() const noexcept { return boost::strict_get<const T>(&m_value); } + + /// get a handle to the embedded value + template <typename T> + T& unwrap() + { + T *value_ptr{this->try_unwrap<T>()}; + if (!value_ptr) variant_unwrap_err(); + return *value_ptr; + } + template <typename T> + const T& unwrap() const + { + const T *value_ptr{this->try_unwrap<T>()}; + if (!value_ptr) variant_unwrap_err(); + return *value_ptr; + } + + /// get the type index of the currently stored type + int index() const noexcept { return m_value.which(); } + + /// get the type index of a requested type (compile error for invalid types) (boost::mp11 is boost 1.66.0) + template <typename T> + static constexpr int type_index_of() noexcept + { + using types = boost::mpl::vector<boost::blank, Types...>; + using elem = typename boost::mpl::find<types, T>::type; + using begin = typename boost::mpl::begin<types>::type; + return boost::mpl::distance<begin, elem>::value; + } + + /// check if two variants have the same type + static bool same_type(const variant<Types...> &v1, const variant<Types...> &v2) noexcept + { return v1.index() == v2.index(); } + + /// apply a visitor to the variant + template <typename VisitorT> + typename VisitorT::result_type visit(VisitorT &&visitor) + { + return boost::apply_visitor(std::forward<VisitorT>(visitor), m_value); + } + template <typename VisitorT> + typename VisitorT::result_type visit(VisitorT &&visitor) const + { + return boost::apply_visitor(std::forward<VisitorT>(visitor), m_value); + } + +private: +//member variables + /// variant of all value types + VType m_value; +}; + +} //namespace tools diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index cdbcf48c2..9aeee3d55 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1229,7 +1229,7 @@ namespace cryptonote char *end = NULL; errno = 0; const unsigned long long ull = strtoull(buf, &end, 10); - CHECK_AND_ASSERT_THROW_MES(ull != ULONG_MAX || errno == 0, "Failed to parse rounded amount: " << buf); + CHECK_AND_ASSERT_THROW_MES(ull != ULLONG_MAX || errno == 0, "Failed to parse rounded amount: " << buf); CHECK_AND_ASSERT_THROW_MES(ull != 0 || amount == 0, "Overflow in rounding"); return ull; } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 249896ee8..91ee86d60 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -523,7 +523,7 @@ namespace cryptonote bool miner::worker_thread() { const uint32_t th_local_index = m_thread_index++; // atomically increment, getting value before increment - crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency()); + bool rx_set = false; MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); MGINFO("Miner thread was started ["<< th_local_index << "]"); @@ -575,6 +575,13 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; + + if ((b.major_version >= RX_BLOCK_VERSION) && !rx_set) + { + crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency()); + rx_set = true; + } + m_gbh(b, height, NULL, tools::get_max_concurrency(), h); if(check_hash(h, local_diff)) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 72ddd0dc9..7b0c9e495 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -173,11 +173,6 @@ namespace cryptonote , "Check for new versions of monero: [disabled|notify|download|update]" , "notify" }; - static const command_line::arg_descriptor<bool> arg_fluffy_blocks = { - "fluffy-blocks" - , "Relay blocks as fluffy blocks (obsolete, now default)" - , true - }; static const command_line::arg_descriptor<bool> arg_no_fluffy_blocks = { "no-fluffy-blocks" , "Relay blocks as normal blocks" @@ -340,7 +335,6 @@ namespace cryptonote command_line::add_arg(desc, arg_show_time_stats); command_line::add_arg(desc, arg_block_sync_size); command_line::add_arg(desc, arg_check_updates); - command_line::add_arg(desc, arg_fluffy_blocks); command_line::add_arg(desc, arg_no_fluffy_blocks); command_line::add_arg(desc, arg_test_dbg_lock_sleep); command_line::add_arg(desc, arg_offline); @@ -393,9 +387,6 @@ namespace cryptonote m_offline = get_arg(vm, arg_offline); m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints); - if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks)) - MWARNING(arg_fluffy_blocks.name << " is obsolete, it is now default"); - if (command_line::get_arg(vm, arg_test_drop_download) == true) test_drop_download(); @@ -576,7 +567,11 @@ namespace cryptonote else if(options[0] == "fastest") { db_flags = DBF_FASTEST; +#ifdef _WIN32 sync_threshold = 1000; // default to fastest:async:1000 +#else + sync_threshold = 100000; // default to fastest:async:100000 +#endif sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async; } else diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 325737789..af667dc0c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -281,6 +281,11 @@ namespace cryptonote cnx.ip = cnx.host; cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv4_network_address>().port()); } + else if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) + { + cnx.ip = cnx.host; + cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv6_network_address>().port()); + } cnx.rpc_port = cntxt.m_rpc_port; cnx.rpc_credits_per_hash = cntxt.m_rpc_credits_per_hash; @@ -979,8 +984,18 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); + std::unordered_set<blobdata> seen; for (const auto &blob: arg.txs) + { MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash);, ret, "Including transaction " << hash); + if (seen.find(blob) != seen.end()) + { + LOG_PRINT_CCONTEXT_L1("Duplicate transaction in notification, dropping connection"); + drop_connection(context, false, false); + return 1; + } + seen.insert(blob); + } if(context.m_state != cryptonote_connection_context::state_normal) return 1; diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 506d75490..44505eb8f 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -86,10 +86,6 @@ namespace daemon_args "daemon_command" , "Hidden" }; - const command_line::arg_descriptor<bool> arg_os_version = { - "os-version" - , "OS for which this executable was compiled" - }; const command_line::arg_descriptor<unsigned> arg_max_concurrency = { "max-concurrency" , "Max number of threads to use for a parallel job" diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 23c313c9d..e2ff27daa 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -143,7 +143,6 @@ int main(int argc, char const * argv[]) command_line::add_arg(visible_options, command_line::arg_help); command_line::add_arg(visible_options, command_line::arg_version); - command_line::add_arg(visible_options, daemon_args::arg_os_version); command_line::add_arg(visible_options, daemon_args::arg_config_file); // Settings @@ -159,6 +158,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); command_line::add_arg(core_settings, daemon_args::arg_zmq_pub); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_disabled); + command_line::add_arg(core_settings, daemonizer::arg_non_interactive); daemonizer::init_options(hidden_options, visible_options); daemonize::t_executor::init_options(core_settings); @@ -203,13 +203,6 @@ int main(int argc, char const * argv[]) return 0; } - // OS - if (command_line::get_arg(vm, daemon_args::arg_os_version)) - { - std::cout << "OS: " << tools::get_os_version_string() << ENDL; - return 0; - } - std::string config = command_line::get_arg(vm, daemon_args::arg_config_file); boost::filesystem::path config_path(config); boost::system::error_code ec; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 0dcfc9d53..fbf26db85 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -644,7 +644,14 @@ bool t_rpc_command_executor::print_connections() { } } - tools::msg_writer() << std::setw(30) << std::left << "Remote Host" + auto longest_host = *std::max_element(res.connections.begin(), res.connections.end(), + [](const auto &info1, const auto &info2) + { + return info1.address.length() < info2.address.length(); + }); + int host_field_width = std::max(15, 8 + (int) longest_host.address.length()); + + tools::msg_writer() << std::setw(host_field_width) << std::left << "Remote Host" << std::setw(8) << "Type" << std::setw(6) << "SSL" << std::setw(20) << "Peer id" @@ -661,11 +668,11 @@ bool t_rpc_command_executor::print_connections() { for (auto & info : res.connections) { std::string address = info.incoming ? "INC " : "OUT "; - address += info.ip + ":" + info.port; + address += info.address; //std::string in_out = info.incoming ? "INC " : "OUT "; tools::msg_writer() //<< std::setw(30) << std::left << in_out - << std::setw(30) << std::left << address + << std::setw(host_field_width) << std::left << address << std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type)) << std::setw(6) << (info.ssl ? "yes" : "no") << std::setw(20) << info.peer_id diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index d313ff3f1..8c76c0004 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -57,6 +57,11 @@ namespace daemonizer , T_executor && executor // universal ref , boost::program_options::variables_map const & vm ); + + const command_line::arg_descriptor<bool> arg_non_interactive = { + "non-interactive" + , "Run non-interactive" + }; } #ifdef WIN32 diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index 5defd1e69..82b87cc97 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -47,10 +47,6 @@ namespace daemonizer "pidfile" , "File path to write the daemon's PID to (optional, requires --detach)" }; - const command_line::arg_descriptor<bool> arg_non_interactive = { - "non-interactive" - , "Run non-interactive" - }; } inline void init_options( @@ -60,7 +56,6 @@ namespace daemonizer { command_line::add_arg(normal_options, arg_detach); command_line::add_arg(normal_options, arg_pidfile); - command_line::add_arg(normal_options, arg_non_interactive); } inline boost::filesystem::path get_default_data_dir() diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index d3e5d66dd..dbe8e4a02 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -61,11 +61,6 @@ namespace daemonizer "run-as-service" , "Hidden -- true if running as windows service" }; - const command_line::arg_descriptor<bool> arg_non_interactive = { - "non-interactive" - , "Run non-interactive" - }; - std::string get_argument_string(int argc, char const * argv[]) { std::string result = ""; @@ -87,7 +82,6 @@ namespace daemonizer command_line::add_arg(normal_options, arg_start_service); command_line::add_arg(normal_options, arg_stop_service); command_line::add_arg(hidden_options, arg_is_service); - command_line::add_arg(hidden_options, arg_non_interactive); } inline boost::filesystem::path get_default_data_dir() diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 9031f8aa1..e618bfa6f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3011,7 +3011,6 @@ bool simple_wallet::scan_tx(const std::vector<std::string> &args) } txids.insert(txid); } - std::vector<crypto::hash> txids_v(txids.begin(), txids.end()); if (!m_wallet->is_trusted_daemon()) { message_writer(console_color_red, true) << tr("WARNING: this operation may reveal the txids to the remote node and affect your privacy"); @@ -3024,7 +3023,9 @@ bool simple_wallet::scan_tx(const std::vector<std::string> &args) LOCK_IDLE_SCOPE(); m_in_manual_refresh.store(true); try { - m_wallet->scan_tx(txids_v); + m_wallet->scan_tx(txids); + } catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) { + fail_msg_writer() << e.what() << ". Either connect to a trusted daemon by passing --trusted-daemon when starting the wallet, or use rescan_bc to rescan the chain."; } catch (const std::exception &e) { fail_msg_writer() << e.what(); } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 3f192b11b..0c3aaf853 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1230,11 +1230,15 @@ bool WalletImpl::scanTransactions(const std::vector<std::string> &txids) } txids_u.insert(txid); } - std::vector<crypto::hash> txids_v(txids_u.begin(), txids_u.end()); try { - m_wallet->scan_tx(txids_v); + m_wallet->scan_tx(txids_u); + } + catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) + { + setStatusError(e.what()); + return false; } catch (const std::exception &e) { diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index dd0f6eb0c..1142e46ce 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -328,4 +328,23 @@ boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<cr return boost::optional<std::string>(); } +boost::optional<std::string> NodeRPCProxy::get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header) +{ + if (m_offline) + return boost::optional<std::string>("offline"); + + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req_t = AUTO_VAL_INIT(req_t); + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response resp_t = AUTO_VAL_INIT(resp_t); + req_t.height = height; + + { + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req_t, resp_t, m_http_client, rpc_timeout); + RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "getblockheaderbyheight"); + } + + block_header = std::move(resp_t.block_header); + return boost::optional<std::string>(); +} + } diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 3ec96baaa..0dcfd0f83 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -57,6 +57,7 @@ public: boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees); boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask); boost::optional<std::string> get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f); + boost::optional<std::string> get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header); private: boost::optional<std::string> get_info(); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 378fccfab..b9cae0cac 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1168,6 +1168,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_first_refresh_done(false), m_refresh_from_block_height(0), m_explicit_refresh_from_block_height(true), + m_skip_to_height(0), m_confirm_non_default_ring_size(true), m_ask_password(AskPasswordToDecrypt), m_max_reorg_depth(ORPHANED_BLOCKS_MAX_COUNT), @@ -1609,14 +1610,13 @@ std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& in return m_subaddress_labels[index.major][index.minor]; } //---------------------------------------------------------------------------------------------------- -void wallet2::scan_tx(const std::vector<crypto::hash> &txids) +wallet2::tx_entry_data wallet2::get_tx_entries(const std::unordered_set<crypto::hash> &txids) { - // Get the transactions from daemon in batches and add them to a priority queue ordered in chronological order - auto cmp_tx_entry = [](const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry& l, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry& r) - { return l.block_height > r.block_height; }; + tx_entry_data tx_entries; + tx_entries.tx_entries.reserve(txids.size()); - std::priority_queue<cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry, std::vector<COMMAND_RPC_GET_TRANSACTIONS::entry>, decltype(cmp_tx_entry)> txq(cmp_tx_entry); const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp, hardcoded in daemon code + std::unordered_set<crypto::hash>::const_iterator it = txids.begin(); for(size_t slice = 0; slice < txids.size(); slice += SLICE_SIZE) { cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); @@ -1625,7 +1625,10 @@ void wallet2::scan_tx(const std::vector<crypto::hash> &txids) size_t ntxes = slice + SLICE_SIZE > txids.size() ? txids.size() - slice : SLICE_SIZE; for (size_t i = slice; i < slice + ntxes; ++i) - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids[i])); + { + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(*it)); + ++it; + } { const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; @@ -1635,17 +1638,254 @@ void wallet2::scan_tx(const std::vector<crypto::hash> &txids) } for (auto& tx_info : res.txs) - txq.push(tx_info); + { + if (!tx_info.in_pool) + { + tx_entries.lowest_height = std::min(tx_info.block_height, tx_entries.lowest_height); + tx_entries.highest_height = std::max(tx_info.block_height, tx_entries.highest_height); + } + + cryptonote::transaction tx; + crypto::hash tx_hash; + THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error, "Failed to get transaction from daemon"); + tx_entries.tx_entries.emplace_back(process_tx_entry_t{ std::move(tx_info), std::move(tx), std::move(tx_hash) }); + } } - // Process the transactions in chronologically ascending order - while(!txq.empty()) { - auto& tx_info = txq.top(); - cryptonote::transaction tx; - crypto::hash tx_hash; - THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error, "Failed to get transaction from daemon (2)"); - process_new_transaction(tx_hash, tx, tx_info.output_indices, tx_info.block_height, 0, tx_info.block_timestamp, false, tx_info.in_pool, tx_info.double_spend_seen, {}, {}); - txq.pop(); + return tx_entries; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::sort_scan_tx_entries(std::vector<process_tx_entry_t> &unsorted_tx_entries) +{ + // If any txs we're scanning have the same height, then we need to request the + // blocks those txs are in to see what order they appear in the chain. We + // need to scan txs in the same order they appear in the chain so that the + // `m_transfers` container holds entries in a consistently sorted order. + // This ensures that hot wallets <> cold wallets both maintain the same order + // of m_transfers, which they rely on when importing/exporting. Same goes + // for multisig wallets when they synchronize. + std::set<uint64_t> entry_heights; + std::set<uint64_t> entry_heights_requested; + COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request req; + COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response res; + for (const auto & tx_info : unsorted_tx_entries) + { + if (!tx_info.tx_entry.in_pool && !cryptonote::is_coinbase(tx_info.tx)) + { + const uint64_t height = tx_info.tx_entry.block_height; + if (entry_heights.find(height) == entry_heights.end()) + { + entry_heights.insert(height); + } + else if (entry_heights_requested.find(height) == entry_heights_requested.end()) + { + req.heights.push_back(height); + entry_heights_requested.insert(height); + } + } + } + + { + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, *m_http_client, rpc_timeout); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to get blocks by height from daemon"); + THROW_WALLET_EXCEPTION_IF(res.blocks.size() != req.heights.size(), error::wallet_internal_error, "Failed to get blocks by height from daemon"); + } + + std::unordered_map<uint64_t, cryptonote::block> parsed_blocks; + for (size_t i = 0; i < res.blocks.size(); ++i) + { + const auto &blk = res.blocks[i]; + cryptonote::block parsed_block; + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_block_from_blob(blk.block, parsed_block), + error::wallet_internal_error, "Failed to parse block"); + parsed_blocks[req.heights[i]] = std::move(parsed_block); + } + + // sort tx_entries in chronologically ascending order; pool txs to the back + auto cmp_tx_entry = [&](const process_tx_entry_t& l, const process_tx_entry_t& r) + { + if (l.tx_entry.in_pool) + return false; + else if (r.tx_entry.in_pool) + return true; + else if (l.tx_entry.block_height > r.tx_entry.block_height) + return false; + else if (l.tx_entry.block_height < r.tx_entry.block_height) + return true; + else // l.tx_entry.block_height == r.tx_entry.block_height + { + // coinbase tx is the first tx in a block + if (cryptonote::is_coinbase(l.tx)) + return true; + if (cryptonote::is_coinbase(r.tx)) + return false; + + // see which tx hash comes first in the block + THROW_WALLET_EXCEPTION_IF(parsed_blocks.find(l.tx_entry.block_height) == parsed_blocks.end(), + error::wallet_internal_error, "Expected block not returned by daemon"); + const auto &blk = parsed_blocks[l.tx_entry.block_height]; + for (const auto &tx_hash : blk.tx_hashes) + { + if (tx_hash == l.tx_hash) + return true; + if (tx_hash == r.tx_hash) + return false; + } + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Tx hashes not found in block"); + return false; + } + }; + std::sort(unsorted_tx_entries.begin(), unsorted_tx_entries.end(), cmp_tx_entry); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set<crypto::hash> &tx_hashes_to_reprocess, detached_blockchain_data &dbd) +{ + LOG_PRINT_L0("Processing " << txs_to_scan.tx_entries.size() << " txs, re-processing " + << txs_to_reprocess.tx_entries.size() << " txs"); + + // Sort the txs in chronologically ascending order they appear in the chain + std::vector<process_tx_entry_t> process_txs; + process_txs.reserve(txs_to_scan.tx_entries.size() + txs_to_reprocess.tx_entries.size()); + process_txs.insert(process_txs.end(), txs_to_scan.tx_entries.begin(), txs_to_scan.tx_entries.end()); + process_txs.insert(process_txs.end(), txs_to_reprocess.tx_entries.begin(), txs_to_reprocess.tx_entries.end()); + sort_scan_tx_entries(process_txs); + + for (const auto &tx_info : process_txs) + { + const auto &tx_entry = tx_info.tx_entry; + + // Ignore callbacks when re-processing a tx to avoid confusing feedback to user + bool ignore_callbacks = tx_hashes_to_reprocess.find(tx_info.tx_hash) != tx_hashes_to_reprocess.end(); + process_new_transaction( + tx_info.tx_hash, + tx_info.tx, + tx_entry.output_indices, + tx_entry.block_height, + 0, + tx_entry.block_timestamp, + cryptonote::is_coinbase(tx_info.tx), + tx_entry.in_pool, + tx_entry.double_spend_seen, + {}, {}, // unused caches + ignore_callbacks); + + // Re-set destination addresses if they were previously set + if (m_confirmed_txs.find(tx_info.tx_hash) != m_confirmed_txs.end() && + dbd.detached_confirmed_txs_dests.find(tx_info.tx_hash) != dbd.detached_confirmed_txs_dests.end()) + { + m_confirmed_txs[tx_info.tx_hash].m_dests = std::move(dbd.detached_confirmed_txs_dests[tx_info.tx_hash]); + } + } + + LOG_PRINT_L0("Done processing " << txs_to_scan.tx_entries.size() << " txs and re-processing " + << txs_to_reprocess.tx_entries.size() << " txs"); +} +//---------------------------------------------------------------------------------------------------- +void reattach_blockchain(hashchain &blockchain, wallet2::detached_blockchain_data &dbd) +{ + if (!dbd.detached_blockchain.empty()) + { + LOG_PRINT_L0("Re-attaching " << dbd.detached_blockchain.size() << " blocks"); + for (size_t i = 0; i < dbd.detached_blockchain.size(); ++i) + blockchain.push_back(dbd.detached_blockchain[i]); + } + + THROW_WALLET_EXCEPTION_IF(blockchain.size() != dbd.original_chain_size, + error::wallet_internal_error, "Unexpected blockchain size after re-attaching"); +} +//---------------------------------------------------------------------------------------------------- +bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std::unordered_set<crypto::hash> &requested_txids, const wallet2::transfer_container &transfers, + const wallet2::payment_container &payments, const serializable_unordered_map<crypto::hash, wallet2::confirmed_transfer_details> &confirmed_txs) +{ + for (const auto &td : transfers) + if (td.m_block_height >= height && requested_txids.find(td.m_txid) == requested_txids.end()) + return true; + + for (const auto &pmt : payments) + if (pmt.second.m_block_height >= height && requested_txids.find(pmt.second.m_tx_hash) == requested_txids.end()) + return true; + + for (const auto &ct : confirmed_txs) + if (ct.second.m_block_height >= height && requested_txids.find(ct.first) == requested_txids.end()) + return true; + + return false; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::scan_tx(const std::unordered_set<crypto::hash> &txids) +{ + // Get the transactions from daemon in batches sorted lowest height to highest + tx_entry_data txs_to_scan = get_tx_entries(txids); + if (txs_to_scan.tx_entries.empty()) + return; + + // Re-process wallet's txs >= lowest scan_tx height. Re-processing ensures + // process_new_transaction is called with txs in chronological order. Say that + // tx2 spends an output from tx1, and the user calls scan_tx(tx1) *after* tx2 + // has already been scanned. In this case, we will "re-process" tx2 *after* + // processing tx1 to ensure the wallet picks up that tx2 spends the output + // from tx1, and to ensure transfers are placed in the sorted transfers + // container in chronological order. Note: in the above example, if tx2 is + // a sweep to a different wallet's address, the wallet will not be able to + // detect tx2. The wallet would need to scan tx1 first in that case. + // TODO: handle this sweep case + detached_blockchain_data dbd; + dbd.original_chain_size = m_blockchain.size(); + if (m_blockchain.size() > txs_to_scan.lowest_height) + { + // When connected to an untrusted daemon, if we will need to re-process 1+ + // tx that the user did not request to scan, then we fail out because + // re-requesting those unexpected txs from the daemon poses a more severe + // and unintuitive privacy risk to the user + THROW_WALLET_EXCEPTION_IF(!is_trusted_daemon() && + has_nonrequested_tx_at_height_or_above_requested(txs_to_scan.lowest_height, txids, m_transfers, m_payments, m_confirmed_txs), + error::wont_reprocess_recent_txs_via_untrusted_daemon + ); + + LOG_PRINT_L0("Re-processing wallet's existing txs (if any) starting from height " << txs_to_scan.lowest_height); + dbd = detach_blockchain(txs_to_scan.lowest_height); + } + std::unordered_set<crypto::hash> tx_hashes_to_reprocess; + tx_hashes_to_reprocess.reserve(dbd.detached_tx_hashes.size()); + for (const auto &tx_hash : dbd.detached_tx_hashes) + { + if (txids.find(tx_hash) == txids.end()) + tx_hashes_to_reprocess.insert(tx_hash); + } + // re-request txs from daemon to re-process with all tx data needed + tx_entry_data txs_to_reprocess = get_tx_entries(tx_hashes_to_reprocess); + + process_scan_txs(txs_to_scan, txs_to_reprocess, tx_hashes_to_reprocess, dbd); + reattach_blockchain(m_blockchain, dbd); + + // If the highest scan_tx height exceeds the wallet's known scan height, then + // the wallet should skip ahead to the scan_tx's height in order to service + // the request in a timely manner. Skipping unrequested transactions avoids + // generating sequences of calls to process_new_transaction which process + // transactions out-of-order, relative to their order in the blockchain, as + // the process_new_transaction implementation requires transactions to be + // processed in blockchain order. If a user misses a tx, they should either + // use rescan_bc, or manually scan missed txs with scan_tx. + uint64_t skip_to_height = txs_to_scan.highest_height + 1; + if (skip_to_height > m_blockchain.size()) + { + m_skip_to_height = skip_to_height; + LOG_PRINT_L0("Skipping refresh to height " << skip_to_height); + + // update last block reward here because the refresh loop won't necessarily set it + try + { + cryptonote::block_header_response block_header; + if (m_node_rpc_proxy.get_block_header_by_height(txs_to_scan.highest_height, block_header)) + throw std::runtime_error("Failed to request block header by height"); + m_last_block_reward = block_header.reward; + } + catch (...) { MERROR("Failed getting block header at height " << txs_to_scan.highest_height); } + + // TODO: use fast_refresh instead of refresh to update m_blockchain. It needs refactoring to work correctly here. + // Or don't refresh at all, and let it update on the next refresh loop. + refresh(is_trusted_daemon()); } } //---------------------------------------------------------------------------------------------------- @@ -1946,7 +2186,7 @@ bool wallet2::spends_one_of_ours(const cryptonote::transaction &tx) const 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) +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, bool ignore_callbacks) { PERF_TIMER(process_new_transaction); // In this function, tx (probably) only contains the base information @@ -1988,7 +2228,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (pk_index > 1) break; LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid); - if(0 != m_callback) + if(!ignore_callbacks && 0 != m_callback) m_callback->on_skip_transaction(height, txid, tx); break; } @@ -2201,7 +2441,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); - if (0 != m_callback) + if (!ignore_callbacks && 0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += amount; @@ -2279,7 +2519,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); - if (0 != m_callback) + if (!ignore_callbacks && 0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, burnt, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += extra_amount; @@ -2333,7 +2573,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid); set_spent(it->second, height); - if (0 != m_callback) + if (!ignore_callbacks && 0 != m_callback) m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index); } } @@ -2568,7 +2808,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const { // seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup - return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height); + return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height && height >= m_skip_to_height); } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) @@ -2976,7 +3216,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") + std::to_string(reorg_depth)); - detach_blockchain(current_index, output_tracker_cache); + handle_reorg(current_index, output_tracker_cache); process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); } else @@ -3621,9 +3861,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // pull the first set of blocks get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); m_run.store(true, std::memory_order_relaxed); - if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) { + if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size() || m_skip_to_height > m_blockchain.size()) { if (!start_height) - start_height = m_refresh_from_block_height; + start_height = std::max(m_refresh_from_block_height, m_skip_to_height);; // we can shortcut by only pulling hashes up to the start_height fast_refresh(start_height, blocks_start_height, short_chain_history); // regenerate the history now that we've got a full set of hashes @@ -3863,15 +4103,10 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) +wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { LOG_PRINT_L0("Detaching blockchain on height " << height); - - // size 1 2 3 4 5 6 7 8 9 - // block 0 1 2 3 4 5 6 7 8 - // C - THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(), - error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); + detached_blockchain_data dbd; size_t transfers_detached = 0; @@ -3913,16 +4148,32 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui THROW_WALLET_EXCEPTION_IF(it_pk == m_pub_keys.end(), error::wallet_internal_error, "public key not found"); m_pub_keys.erase(it_pk); } + transfers_detached = std::distance(it, m_transfers.end()); + dbd.detached_tx_hashes.reserve(transfers_detached); + for (size_t i = i_start; i!=m_transfers.size();i++) + dbd.detached_tx_hashes.insert(std::move(m_transfers[i].m_txid)); + MDEBUG(transfers_detached << " transfers detached / expected " << dbd.detached_tx_hashes.size()); m_transfers.erase(it, m_transfers.end()); - const uint64_t blocks_detached = m_blockchain.size() - height; - m_blockchain.crop(height); + uint64_t blocks_detached = 0; + dbd.original_chain_size = m_blockchain.size(); + if (height >= m_blockchain.offset()) + { + for (uint64_t i = height; i < m_blockchain.size(); ++i) + dbd.detached_blockchain.push_back(m_blockchain[i]); + blocks_detached = m_blockchain.size() - height; + m_blockchain.crop(height); + MDEBUG(blocks_detached << " blocks detached / expected " << dbd.detached_blockchain.size()); + } for (auto it = m_payments.begin(); it != m_payments.end(); ) { if(height <= it->second.m_block_height) + { + dbd.detached_tx_hashes.insert(it->second.m_tx_hash); it = m_payments.erase(it); + } else ++it; } @@ -3930,7 +4181,11 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui for (auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); ) { if(height <= it->second.m_block_height) + { + dbd.detached_tx_hashes.insert(it->first); + dbd.detached_confirmed_txs_dests[it->first] = std::move(it->second.m_dests); it = m_confirmed_txs.erase(it); + } else ++it; } @@ -3939,6 +4194,17 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui m_callback->on_reorg(height, blocks_detached, transfers_detached); LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); + return dbd; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) +{ + // size 1 2 3 4 5 6 7 8 9 + // block 0 1 2 3 4 5 6 7 8 + // C + THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(), + error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); + detach_blockchain(height, output_tracker_cache); } //---------------------------------------------------------------------------------------------------- bool wallet2::deinit() @@ -3971,6 +4237,7 @@ bool wallet2::clear() m_multisig_rounds_passed = 0; m_device_last_key_image_sync = 0; m_pool_info_query_time = 0; + m_skip_to_height = 0; return true; } //---------------------------------------------------------------------------------------------------- @@ -3988,6 +4255,7 @@ void wallet2::clear_soft(bool keep_key_images) m_scanned_pool_txs[0].clear(); m_scanned_pool_txs[1].clear(); m_pool_info_query_time = 0; + m_skip_to_height = 0; cryptonote::block b; generate_genesis(b); @@ -4117,6 +4385,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee: value2.SetUint64(m_refresh_from_block_height); json.AddMember("refresh_height", value2, json.GetAllocator()); + value2.SetUint64(m_skip_to_height); + json.AddMember("skip_to_height", value2, json.GetAllocator()); + value2.SetInt(m_confirm_non_default_ring_size ? 1 :0); json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator()); @@ -4349,6 +4620,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_auto_refresh = true; m_refresh_type = RefreshType::RefreshDefault; m_refresh_from_block_height = 0; + m_skip_to_height = 0; m_confirm_non_default_ring_size = true; m_ask_password = AskPasswordToDecrypt; cryptonote::set_default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); @@ -4499,6 +4771,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0); m_refresh_from_block_height = field_refresh_height; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, skip_to_height, uint64_t, Uint64, false, 0); + m_skip_to_height = field_skip_to_height; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true); m_confirm_non_default_ring_size = field_confirm_non_default_ring_size; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt); @@ -5874,23 +6148,16 @@ void wallet2::trim_hashchain() if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset()) { MINFO("Fixing empty hashchain"); - cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res = AUTO_VAL_INIT(res); - - bool r; - { - const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; - req.height = m_blockchain.size() - 1; - r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, *m_http_client, rpc_timeout); - } - - if (r && res.status == CORE_RPC_STATUS_OK) + try { + cryptonote::block_header_response block_header; + if (m_node_rpc_proxy.get_block_header_by_height(m_blockchain.size() - 1, block_header)) + throw std::runtime_error("Failed to request block header by height"); crypto::hash hash; - epee::string_tools::hex_to_pod(res.block_header.hash, hash); + epee::string_tools::hex_to_pod(block_header.hash, hash); m_blockchain.refill(hash); } - else + catch(...) { MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon"); } @@ -13337,7 +13604,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) if (!td.m_key_image_partial) continue; MINFO("Multisig info importing from block height " << td.m_block_height); - detach_blockchain(td.m_block_height); + handle_reorg(td.m_block_height); break; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 5e922494b..554a766bf 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -810,6 +810,30 @@ private: bool empty() const { return tx_extra_fields.empty() && primary.empty() && additional.empty(); } }; + struct detached_blockchain_data + { + hashchain detached_blockchain; + size_t original_chain_size; + std::unordered_set<crypto::hash> detached_tx_hashes; + std::unordered_map<crypto::hash, std::vector<cryptonote::tx_destination_entry>> detached_confirmed_txs_dests; + }; + + struct process_tx_entry_t + { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry tx_entry; + cryptonote::transaction tx; + crypto::hash tx_hash; + }; + + struct tx_entry_data + { + std::vector<process_tx_entry_t> tx_entries; + uint64_t lowest_height; + uint64_t highest_height; + + tx_entry_data(): lowest_height((uint64_t)-1), highest_height(0) {} + }; + /*! * \brief Generates a wallet or restores one. Assumes the multisig setup * has already completed for the provided multisig info. @@ -1360,7 +1384,7 @@ private: std::string get_spend_proof(const crypto::hash &txid, const std::string &message); bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str); - void scan_tx(const std::vector<crypto::hash> &txids); + void scan_tx(const std::unordered_set<crypto::hash> &txids); /*! * \brief Generates a proof that proves the reserve of unspent funds @@ -1644,10 +1668,11 @@ private: */ bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional<crypto::chacha_key>& keys_to_encrypt); - void 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 = NULL); + void 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 = NULL, bool ignore_callbacks = false); bool should_skip_block(const cryptonote::block &b, uint64_t height) const; void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); - void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); + detached_blockchain_data detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); + void handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; bool clear(); void clear_soft(bool keep_key_images=false); @@ -1703,6 +1728,9 @@ private: crypto::chacha_key get_ringdb_key(); void setup_keys(const epee::wipeable_string &password); size_t get_transfer_details(const crypto::key_image &ki) const; + tx_entry_data get_tx_entries(const std::unordered_set<crypto::hash> &txids); + void sort_scan_tx_entries(std::vector<process_tx_entry_t> &unsorted_tx_entries); + void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set<crypto::hash> &tx_hashes_to_reprocess, detached_blockchain_data &dbd); void register_devices(); hw::device& lookup_device(const std::string & device_descriptor); @@ -1793,6 +1821,9 @@ private: // m_refresh_from_block_height was defaulted to zero.*/ bool m_explicit_refresh_from_block_height; uint64_t m_pool_info_query_time; + uint64_t m_skip_to_height; + // m_skip_to_height is useful when we don't want to modify the wallet's restore height. + // m_refresh_from_block_height is also a wallet's restore height which should remain constant unless explicitly modified by the user. bool m_confirm_non_default_ring_size; AskPasswordType m_ask_password; uint64_t m_max_reorg_depth; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index a2b248434..6706e77ff 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -93,6 +93,8 @@ namespace tools // get_output_distribution // deprecated_rpc_access // wallet_files_doesnt_correspond + // scan_tx_error * + // wont_reprocess_recent_txs_via_untrusted_daemon // // * - class with protected ctor @@ -916,6 +918,23 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct scan_tx_error : public wallet_logic_error + { + protected: + explicit scan_tx_error(std::string&& loc, const std::string& message) + : wallet_logic_error(std::move(loc), message) + { + } + }; + //---------------------------------------------------------------------------------------------------- + struct wont_reprocess_recent_txs_via_untrusted_daemon : public scan_tx_error + { + explicit wont_reprocess_recent_txs_via_untrusted_daemon(std::string&& loc) + : scan_tx_error(std::move(loc), "The wallet has already seen 1 or more recent transactions than the scanned tx") + { + } + }; + //---------------------------------------------------------------------------------------------------- #if !defined(_MSC_VER) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 81e26c120..7c46d9887 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3173,7 +3173,7 @@ namespace tools return false; } - std::vector<crypto::hash> txids; + std::unordered_set<crypto::hash> txids; std::list<std::string>::const_iterator i = req.txids.begin(); while (i != req.txids.end()) { @@ -3186,11 +3186,15 @@ namespace tools } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data()); - txids.push_back(txid); + txids.insert(txid); } try { m_wallet->scan_tx(txids); + } catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what() + std::string(". Either connect to a trusted daemon or rescan the chain."); + return false; } catch (const std::exception &e) { handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; @@ -4694,6 +4698,7 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); command_line::add_arg(desc_params, arg_no_initial_sync); + command_line::add_arg(hidden_options, daemonizer::arg_non_interactive); daemonizer::init_options(hidden_options, desc_params); desc_params.add(hidden_options); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f14f7ff5a..39e7ed8a9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,8 +105,6 @@ if (TREZOR_DEBUG) add_subdirectory(trezor) endif() -# add_subdirectory(daemon_tests) - set(hash_targets_sources hash-target.cpp) diff --git a/tests/README.md b/tests/README.md index c63294e9b..0d2180d67 100644 --- a/tests/README.md +++ b/tests/README.md @@ -43,10 +43,6 @@ ctest To run the same tests on a release build, replace `debug` with `release`. -# Daemon tests - -[TODO] - # Functional tests [TODO] @@ -54,7 +50,7 @@ Functional tests are located under the `tests/functional_tests` directory. Building all the tests requires installing the following dependencies: ```bash -pip install requests psutil monotonic zmq +pip install requests psutil monotonic zmq deepdiff ``` First, run a regtest daemon in the offline mode and with a fixed difficulty: diff --git a/tests/cryptolib.pl b/tests/cryptolib.pl deleted file mode 100644 index dce58482d..000000000 --- a/tests/cryptolib.pl +++ /dev/null @@ -1,261 +0,0 @@ -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -use Math::BigInt only => 'GMP'; -use Digest::Keccak qw(keccak_256); - -my $p = Math::BigInt->new(2)->bpow(255)->bsub(19); #F_p -my $l = Math::BigInt->new(2)->bpow(252)->badd('27742317777372353535851937790883648493'); -#my $d = Math::BigInt->new(486662); #motgomery: y^2 = x^3 + 486662x^2 + x -my $d = Math::BigInt->new(-121665)->bmul(minv(121666))->bmod($p); #twisted edwards: -x^2 +y^2 = 1 + d*x^2*y^2 -my $x0 = Math::BigInt->new('15112221349535400772501151409588531511454012693041857206046113283949847762202'); -my $y0 = Math::BigInt->new('46316835694926478169428394003475163141307993866256225615783033603165251855960'); #y0 = 4/5 -my $m = Math::BigInt->new('7237005577332262213973186563042994240829374041602535252466099000494570602493'); #p = 8m+5 -my $ps = $p->copy()->bdec->bdiv(4); -my $pl = $p->copy()->bdec->bdiv(2); -my $ii = Math::BigInt->new(2)->bmodpow($ps,$p); #sqrt(-1) - -sub ec_rec { - my $y = Math::BigInt->new($_[0]); - my $xx = $y->copy()->bpow(2)->bdec()->bmul(minv($y->copy()->bpow(2)->bmul($d)->binc))->bmod($p); - return 0 if !($xx->copy()->bmodpow($pl,$p)->binc->bmod($p)); - my $p2 = $p->copy()->badd(3)->bdiv(8); - my $x = $xx->copy()->bmodpow($p2, $p); - if ($x->copy()->bpow(2)->bsub($xx)->bmod($p)) {$x->bmul($ii)->bmod($p)} - if ($x->is_odd) {$x = $p->copy()->bsub($x)}; - return $x; - } - -sub h2i { - return Math::BigInt->new('0x'.(unpack 'H*', (reverse pack 'H*', shift)));; - } - -sub i2h { - my $t = substr(Math::BigInt->new(shift)->as_hex(),2,64); - if (length($t)%2 == 1) {$t = '0'.$t} - return unpack 'H*', (reverse pack 'H*', $t); - } - - -sub random { - return keccak_256(rand(2**20)); - #return keccak_256(3); #I swear that's random! - } - -sub ec_pack { - my $x = Math::BigInt->new($_[0]); - my $y = Math::BigInt->new($_[1]); - my $or = Math::BigInt->new(2)->bpow(255); - $y |= $or if ($x->is_odd()); - return unpack 'H*', (reverse pack 'H*', substr($y->as_hex(),2,64)); - } - -sub ec_unpack { - my $y = Math::BigInt->new(h2i(shift)); - my $b = $y >> 255; - my $and = Math::BigInt->new(2)->bpow(255)->bdec(); - $y &= $and; - my $x = ec_rec($y); - return (0,0) if $x==0; - ($b==0) || ($x = $p->copy()->bsub($x)); - return ($x,$y); - } - - -sub minv { - my $x = Math::BigInt->new(shift); - $x->bmodpow($p-2,$p); - return $x; - } - - -sub ec_doub { - my $x = Math::BigInt->new($_[0]); - my $y = Math::BigInt->new($_[1]); - - #$t = $x->copy()->bpow(2)->bmul(3)->badd($x->copy()->bmul($d)->bmul(2))->binc()->bmul(minv($y->copy()->bmul(2))); #montgomery - #$x2 = $t->copy()->bpow(2)->bsub($d)->bsub($x)->bsub($x)->bmod($p); #montgomery - #$y2 = $x->copy()->bmul(2)->badd($x)->badd($d)->bmul($t)->bsub($t->copy()->bpow(3))->bsub($y)->bmod($p); #montgomery - $t = $x->copy()->bmul($x)->bmul($y)->bmul($y)->bmul($d)->bmod($p); - $x3 = $x->copy()->bmul($y)->bmul(2)->bmul(minv($t+1))->bmod($p); - $y3 = $y->copy()->bpow(2)->badd($x->copy()->bpow(2))->bmul(minv(1-$t))->bmod($p); - return ($x3,$y3); - } -sub ec_add { - my $x1 = Math::BigInt->new($_[0]); - my $y1 = Math::BigInt->new($_[1]); - my $x2 = Math::BigInt->new($_[2]); - my $y2 = Math::BigInt->new($_[3]); - - #$t = $y2->copy()->bsub($y1)->bmul(minv($x2->copy()->bsub($x1))); - #$x3 = $t->copy()->bpow(2)->bsub($d)->bsub($x1)->bsub($x2)->bmod($p); - #$y3 = $x1->copy()->bmul(2)->badd($x2)->badd($d)->bmul($t)->bsub($t->copy()->bpow(3))->bsub($y1)->bmod($p); - $t = $x1->copy->bmul($x2)->bmul($y1)->bmul($y2)->bmul($d)->bmod($p); - $x3 = $x1->copy()->bmul($y2)->badd($y1->copy()->bmul($x2))->bmul(minv($t+1))->bmod($p); - $y3 = $y1->copy()->bmul($y2)->badd($x1->copy()->bmul($x2))->bmul(minv(1-$t))->bmod($p); - - - return ($x3,$y3); - } - -sub ec_mul { - my $n = Math::BigInt->new($_[0]); - my $x = Math::BigInt->new($_[1]); - my $y = Math::BigInt->new($_[2]); - - if ($n->is_one()) { - return ($x,$y); - last; - } - elsif ($n->is_even()) { - $n->bdiv(2); - return ec_mul($n,&ec_doub($x,$y)); - } - else { - $n->bdec()->bdiv(2); - return ec_add($x,$y,ec_mul($n,&ec_doub($x,$y))); - } - } - -sub pkeygen { - my $key = Math::BigInt->new(h2i(shift))->bmod($l); - return ec_pack(ec_mul($key,$x0,$y0)); - } - -sub ec_hash { - my $h = pack 'H*', shift; - my $h = Math::BigInt->new('0x'.(unpack 'H*', reverse keccak_256($h))); - my ($x,$y) = (0,0); - while ($x == 0) { - ($x,$y) = ec_unpack(i2h($h)); - $h->binc(); - } - return ec_mul(8,$x,$y); - } - -sub im_gen { - my ($x,$y) = ec_hash(shift); - my $k = Math::BigInt->new(h2i(shift))->bmod($l); - return ec_pack(ec_mul($k,$x,$y)); - } - - -sub sign { - my ($m,$sec_key) = @_; - my $sec_key = Math::BigInt->new(h2i($sec_key)); - my ($x,$y) = ec_mul($sec_key,$x0,$y0); - my $k = Math::BigInt->new('0x'.(unpack 'H*', random()))->bmod($l); - #my $k = Math::BigInt->new('5267557024171956683337957876581522196748200715787296882078421399301151717969'); - my $e = unpack 'H*', keccak_256($m.(pack 'H*', ec_pack(ec_mul($k,$x0,$y0)))); - my $s = i2h(Math::BigInt->new(h2i($e))->bmul($sec_key)->bneg()->badd($k)->bmod($l)); - $e = i2h(Math::BigInt->new(h2i($e))->bmod($l)); - return ($s,$e); - } - -sub check_s { - my ($m,$pt,$s1,$e1) = @_; - my ($x,$y) = ec_unpack($pt); - my $s = Math::BigInt->new(h2i($s1))->bmod($l); - my $e = Math::BigInt->new(h2i($e1))->bmod($l); - my ($x1,$y1) = ec_add(ec_mul($s,$x0,$y0),ec_mul($e,$x,$y)); - $m = $m.(pack 'H*', ec_pack($x1,$y1)); - my $ev = Math::BigInt->new(h2i(unpack 'H*', keccak_256($m)))->bmod($l); - - return !$ev->bcmp($e); - } - -sub r_sign { - my ($m,$image,$sec_key,$index,@pkeys) = @_; - my ($ix,$iy) = ec_unpack($image); - my $n = @pkeys; - my $data = $m; - my $w = $a = $b = $hx = $hy = $px = $py = 0; - my @zc = (); - my $sum = Math::BigInt->new(); - #print "begin signing ($n keys)\n"; - for $i (0..$n-1) { - ($hx, $hy) = ec_hash(@pkeys[$i]); - ($px,$py) = ec_unpack(@pkeys[$i]); - if ($i == $index) { - $w = Math::BigInt->new('0x'.(unpack 'H*', random()))->bmod($l); - $a = pack 'H*', ec_pack(ec_mul($w,$x0,$y0)); - $b = pack 'H*', ec_pack(ec_mul($w,$hx,$hy)); - push @zc,0,0; - } - else { - $z = Math::BigInt->new('0x'.(unpack 'H*', random()))->bmod($l); - $c = Math::BigInt->new('0x'.(unpack 'H*', random()))->bmod($l); - $sum->badd($c); - $a = pack 'H*', ec_pack(ec_add(ec_mul($z,$x0,$y0),ec_mul($c,$px,$py))); - $b = pack 'H*', ec_pack(ec_add(ec_mul($z,$hx,$hy),ec_mul($c,$ix,$iy))); - push @zc,i2h($z),i2h($c); - } - $data = $data.$a.$b; - #print "key number $i done\n"; - } - #print "generating ringsig..\n"; - my $h = unpack 'H*', keccak_256($data); - my $cy = Math::BigInt->new(h2i($h))->bsub($sum)->bmod($l); - my $zy = $cy->copy()->bmul(h2i($sec_key))->bneg()->badd($w)->bmod($l); - @zc[2*$index] = i2h($zy); - @zc[2*$index+1] = i2h($cy); - return @zc; - } - -sub r_check_s { - my ($m,$image,@zc) = @_; - my $n = @zc/3; - for $j (0..$n-1) { - @pkeys[$j] = shift @zc; - } - my $data = $m; - my ($ix,$iy) = ec_unpack($image); - my $a = $b = $hx = $hy = $px = $py = $z = $c = 0; - my $sum = Math::BigInt->new(); - #print "\nBegin checking ($n keys)\n"; - for $i (0..$n-1) { - $z = Math::BigInt->new(h2i(shift @zc))->bmod($l); - $c = Math::BigInt->new(h2i(shift @zc))->bmod($l); - $sum->badd($c)->bmod($l); - ($px,$py) = ec_unpack(@pkeys[$i]); - $a = pack 'H*', ec_pack(ec_add(ec_mul($z,$x0,$y0),ec_mul($c,$px,$py))); - ($hx, $hy) = ec_hash(@pkeys[$i]); - $b = pack 'H*', ec_pack(ec_add(ec_mul($z,$hx,$hy),ec_mul($c,$ix,$iy))); - $data = $data.$a.$b; - #print "key number $i done\n"; - } - my $h = Math::BigInt->new(h2i(unpack 'H*', keccak_256($data)))->bmod($l); - - return !$h->bcmp($sum); - } - - - - diff --git a/tests/cryptotest.pl b/tests/cryptotest.pl deleted file mode 100644 index 67ccc1d79..000000000 --- a/tests/cryptotest.pl +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -require 'cryptolib.pl'; - -$key = 'fc7557a2595788aea7205ffd801b8a157dc9c698adb2c598ba543eaa67cb700e'; -$pt = '664422cf6f4100dc6b3298e41ca53b173a98918fc9cb50fc2d590b7d1285f4ab'; -$m = keccak_256(pack 'H*', 'c8fedd380dbae40ffb52'); - - -$s = '26a9589121e569ee0ac2e8ac7a7ea331d348f9a0fa8d28926d27c7506759e406'; -$e = '780be966ad89ba526cc7adf4b771adbdaa0568038e6a30e776839a81e57dee0c'; - -print " self SIG -- OK\n" if check_s($m,$pt,sign($m,$key)); -print " test SIG -- OK\n" if check_s($m,$pt,$s,$e); - -@aa = r_sign($m,im_gen($pt,$key),$key,1,ec_pack(ec_mul(111,$x0,$y0)),$pt,ec_pack(ec_mul(47,$x0,$y0))); -print " self RSIG -- OK\n" if r_check_s($m,im_gen($pt,$key),ec_pack(ec_mul(111,$x0,$y0)),$pt,ec_pack(ec_mul(47,$x0,$y0)),@aa); - -$k1 = '6a7a81a52ba91b9785b484d761bfb3ad9a473c147e17b7fbbc3992e8c97108d7'; -$sk1 = '3ce3eb784016a53fa915053d24f55dc8fbc7af3fabc915701adb67e61a25f50f'; -$k2 = '0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88'; -$sk2 = '4967a2bfa0c8a0afc0df238d068b6c7182577afd0781c9d3720bb7a6cf71630c'; #main key -$m = keccak_256(pack 'H*', '5020c4d530b6ec6cb4d9'); -@sig = ('b7903a4a3aca7253bb98be335014bebb33683aedca0bc46e288e229ecfccbe0e', - '2c15e4de88ff38d655e2deef0e06a7ca4541a7754c37e7b20875cce791754508', - '6acae497177b2eeaf658b813eaf50e1e06f3d1107694beff9b520c65ee624f05', - '026c8d9801f7330aa82426adf5bacf4546d83df0cc12321ede90df8c0d9aa800'); - - -print " test RSIG -- OK" if r_check_s($m,im_gen($k2,$sk2),$k1, $k2, @sig); diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt deleted file mode 100644 index 182231132..000000000 --- a/tests/daemon_tests/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -set(transfers_sources - transfers.cpp) - -set(transfers_headers) - -monero_add_minimal_executable(transfers - ${transfers_sources} - ${transfers_headers}) -target_link_libraries(transfers - PRIVATE - useragent - rpc - cryptonote_core - cncrypto - common - epee - ${GTEST_LIBRARIES}) - -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_transfers") -add_custom_target(test_transfers - COMMAND transfers - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_transfers") diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp deleted file mode 100644 index 2980c93ed..000000000 --- a/tests/daemon_tests/transfers.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2014-2023, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#include "gtest/gtest.h" -#include <sstream> -#include "wallet/wallet.h" -#include "rpc/core_rpc_server.h" -#include "cryptonote_basic/account.h" -#include "net/http_client_abstract_invoke.h" -using namespace std; -using namespace epee::misc_utils; -using namespace cryptonote; - -string daemon_address = "http://localhost:23400"; - -#define ACCS 5 - -TEST(Transfers, Transfers) -{ - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - - cout << "TESTING: transfers" << endl; - - net_utils::http::http_simple_client http_client; - wallet miner, accs[100], receiver; - miner.generate(); - ASSERT_TRUE(miner.init()); - ASSERT_TRUE(miner.store("miner.b2wallet")); - cout << "miner: " << miner.get_account().get_public_address_str(false) << endl; - - for (int i = 0; i < ACCS; i++) { - ostringstream s; - s << "acc" << setw(2) << setfill('0') << i << ".b2wallet"; - accs[i].generate(); - assert(accs[i].init()); - assert(accs[i].store(s.str())); - } - receiver.generate(); - assert(receiver.init()); - receiver.store("receiver.b2wallet"); - - { - COMMAND_RPC_START_MINE::request req; - req.miner_address = miner.get_account().get_public_address_str(false); - req.threads_count = 1; - COMMAND_RPC_START_MINE::response res; - bool r = net_utils::http::invoke_http_json_remote_command(daemon_address + "/start_mine", req, res, http_client); - ASSERT_TRUE(r); - } - - string s; - //getline(cin, s); - sleep_no_w(1000); - ASSERT_TRUE(miner.refresh()); - cout << "miner balance: " << miner.balance() << endl; - - vector<pair<account_public_address, uint64_t>> d_accs; - for (int i = 0; i < ACCS; i++) - d_accs.push_back(make_pair(accs[i].get_account().get_keys().m_account_address, 1)); - ASSERT_TRUE(miner.transfer(d_accs)); - - //getline(cin, s); - sleep_no_w(1000); - for (int i = 0; i < ACCS; i++) { - ASSERT_TRUE(accs[i].refresh()); - ASSERT_TRUE(accs[i].transfer(receiver.get_account().get_keys().m_account_address, 1)); - } - - //getline(cin, s); - cout << "wait for block" << endl; - sleep_no_w(10000); - receiver.refresh(); - ASSERT_TRUE(receiver.balance() == ACCS); - cout << "OK" << endl; -} diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 0daf71b59..306eba073 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -67,7 +67,7 @@ target_link_libraries(make_test_signature monero_add_minimal_executable(cpu_power_test cpu_power_test.cpp) find_program(PYTHON3_FOUND python3 REQUIRED) -execute_process(COMMAND ${PYTHON3_FOUND} "-c" "import requests; import psutil; import monotonic; import zmq; print('OK')" OUTPUT_VARIABLE REQUESTS_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${PYTHON3_FOUND} "-c" "import requests; import psutil; import monotonic; import zmq; import deepdiff; print('OK')" OUTPUT_VARIABLE REQUESTS_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) if (REQUESTS_OUTPUT STREQUAL "OK") add_test( NAME functional_tests_rpc @@ -76,6 +76,6 @@ if (REQUESTS_OUTPUT STREQUAL "OK") NAME check_missing_rpc_methods COMMAND ${PYTHON3_FOUND} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}") else() - message(WARNING "functional_tests_rpc and check_missing_rpc_methods skipped, needs the 'requests', 'psutil', 'monotonic', and 'zmq' python modules") + message(WARNING "functional_tests_rpc and check_missing_rpc_methods skipped, needs the 'requests', 'psutil', 'monotonic', 'zmq', and 'deepdiff' python modules") set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc check_missing_rpc_methods) endif() diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index bd80f8f3c..ddc930eb0 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -30,6 +30,9 @@ from __future__ import print_function import json +import pprint +from deepdiff import DeepDiff +pp = pprint.PrettyPrinter(indent=2) """Test simple transfers """ @@ -37,6 +40,12 @@ import json from framework.daemon import Daemon from framework.wallet import Wallet +seeds = [ + 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted', + 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', + 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', +] + class TransferTest(): def run_test(self): self.reset() @@ -53,6 +62,7 @@ class TransferTest(): self.check_rescan() self.check_is_key_image_spent() self.check_multiple_submissions() + self.check_scan_tx() def reset(self): print('Resetting blockchain') @@ -63,11 +73,6 @@ class TransferTest(): def create(self): print('Creating wallets') - seeds = [ - 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted', - 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', - 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', - ] self.wallet = [None] * len(seeds) for i in range(len(seeds)): self.wallet[i] = Wallet(idx = i) @@ -864,5 +869,217 @@ class TransferTest(): res = self.wallet[0].get_balance() assert res.balance == balance + def check_scan_tx(self): + daemon = Daemon() + + print('Testing scan_tx') + + def diff_transfers(actual_transfers, expected_transfers): + diff = DeepDiff(actual_transfers, expected_transfers) + if diff != {}: + pp.pprint(diff) + assert diff == {} + + # set up sender_wallet + sender_wallet = self.wallet[0] + try: sender_wallet.close_wallet() + except: pass + sender_wallet.restore_deterministic_wallet(seed = seeds[0]) + sender_wallet.auto_refresh(enable = False) + sender_wallet.refresh() + res = sender_wallet.get_transfers() + out_len = 0 if 'out' not in res else len(res.out) + sender_starting_balance = sender_wallet.get_balance().balance + amount = 1000000000000 + assert sender_starting_balance > amount + + # set up receiver_wallet + receiver_wallet = self.wallet[1] + try: receiver_wallet.close_wallet() + except: pass + receiver_wallet.restore_deterministic_wallet(seed = seeds[1]) + receiver_wallet.auto_refresh(enable = False) + receiver_wallet.refresh() + res = receiver_wallet.get_transfers() + in_len = 0 if 'in' not in res else len(res['in']) + receiver_starting_balance = receiver_wallet.get_balance().balance + + # transfer from sender_wallet to receiver_wallet + dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount} + res = sender_wallet.transfer([dst]) + assert len(res.tx_hash) == 32*2 + txid = res.tx_hash + assert res.amount == amount + assert res.fee > 0 + fee = res.fee + + expected_sender_balance = sender_starting_balance - (amount + fee) + expected_receiver_balance = receiver_starting_balance + amount + + test = 'Checking scan_tx on outgoing pool tx' + for attempt in range(2): # test re-scanning + print(test + ' (' + ('first attempt' if attempt == 0 else 're-scanning tx') + ')') + sender_wallet.scan_tx([txid]) + res = sender_wallet.get_transfers() + assert 'pool' not in res or len(res.pool) == 0 + if out_len == 0: + assert 'out' not in res + else: + assert len(res.out) == out_len + assert len(res.pending) == 1 + tx = [x for x in res.pending if x.txid == txid] + assert len(tx) == 1 + tx = tx[0] + assert tx.amount == amount + assert tx.fee == fee + assert len(tx.destinations) == 1 + assert tx.destinations[0].amount == amount + assert tx.destinations[0].address == dst['address'] + assert sender_wallet.get_balance().balance == expected_sender_balance + + test = 'Checking scan_tx on incoming pool tx' + for attempt in range(2): # test re-scanning + print(test + ' (' + ('first attempt' if attempt == 0 else 're-scanning tx') + ')') + receiver_wallet.scan_tx([txid]) + res = receiver_wallet.get_transfers() + assert 'pending' not in res or len(res.pending) == 0 + if in_len == 0: + assert 'in' not in res + else: + assert len(res['in']) == in_len + assert 'pool' in res and len(res.pool) == 1 + tx = [x for x in res.pool if x.txid == txid] + assert len(tx) == 1 + tx = tx[0] + assert tx.amount == amount + assert tx.fee == fee + assert receiver_wallet.get_balance().balance == expected_receiver_balance + + # mine the tx + height = daemon.generateblocks(dst['address'], 1).height + block_header = daemon.getblockheaderbyheight(height = height).block_header + miner_txid = block_header.miner_tx_hash + expected_receiver_balance += block_header.reward + + print('Checking scan_tx on outgoing tx before refresh') + sender_wallet.scan_tx([txid]) + res = sender_wallet.get_transfers() + assert 'pending' not in res or len(res.pending) == 0 + assert 'pool' not in res or len (res.pool) == 0 + assert len(res.out) == out_len + 1 + tx = [x for x in res.out if x.txid == txid] + assert len(tx) == 1 + tx = tx[0] + assert tx.amount == amount + assert tx.fee == fee + assert len(tx.destinations) == 1 + assert tx.destinations[0].amount == amount + assert tx.destinations[0].address == dst['address'] + assert sender_wallet.get_balance().balance == expected_sender_balance + + print('Checking scan_tx on outgoing tx after refresh') + sender_wallet.refresh() + sender_wallet.scan_tx([txid]) + diff_transfers(sender_wallet.get_transfers(), res) + assert sender_wallet.get_balance().balance == expected_sender_balance + + print("Checking scan_tx on outgoing wallet's earliest tx") + earliest_height = height + earliest_txid = txid + for x in res['in']: + if x.height < earliest_height: + earliest_height = x.height + earliest_txid = x.txid + sender_wallet.scan_tx([earliest_txid]) + diff_transfers(sender_wallet.get_transfers(), res) + assert sender_wallet.get_balance().balance == expected_sender_balance + + test = 'Checking scan_tx on outgoing wallet restored at current height' + for i, out_tx in enumerate(res.out): + if 'destinations' in out_tx: + del res.out[i]['destinations'] # destinations are not expected after wallet restore + out_txids = [x.txid for x in res.out] + in_txids = [x.txid for x in res['in']] + all_txs = out_txids + in_txids + for test_type in ["all txs", "incoming first", "duplicates within", "duplicates across"]: + print(test + ' (' + test_type + ')') + sender_wallet.close_wallet() + sender_wallet.restore_deterministic_wallet(seed = seeds[0], restore_height = height) + assert sender_wallet.get_transfers() == {} + if test_type == "all txs": + sender_wallet.scan_tx(all_txs) + elif test_type == "incoming first": + sender_wallet.scan_tx(in_txids) + sender_wallet.scan_tx(out_txids) + # TODO: test_type == "outgoing first" + elif test_type == "duplicates within": + sender_wallet.scan_tx(all_txs + all_txs) + elif test_type == "duplicates across": + sender_wallet.scan_tx(all_txs) + sender_wallet.scan_tx(all_txs) + else: + assert True == False + diff_transfers(sender_wallet.get_transfers(), res) + assert sender_wallet.get_balance().balance == expected_sender_balance + + print('Sanity check against outgoing wallet restored at height 0') + sender_wallet.close_wallet() + sender_wallet.restore_deterministic_wallet(seed = seeds[0], restore_height = 0) + sender_wallet.refresh() + diff_transfers(sender_wallet.get_transfers(), res) + assert sender_wallet.get_balance().balance == expected_sender_balance + + print('Checking scan_tx on incoming txs before refresh') + receiver_wallet.scan_tx([txid, miner_txid]) + res = receiver_wallet.get_transfers() + assert 'pending' not in res or len(res.pending) == 0 + assert 'pool' not in res or len (res.pool) == 0 + assert len(res['in']) == in_len + 2 + tx = [x for x in res['in'] if x.txid == txid] + assert len(tx) == 1 + tx = tx[0] + assert tx.amount == amount + assert tx.fee == fee + assert receiver_wallet.get_balance().balance == expected_receiver_balance + + print('Checking scan_tx on incoming txs after refresh') + receiver_wallet.refresh() + receiver_wallet.scan_tx([txid, miner_txid]) + diff_transfers(receiver_wallet.get_transfers(), res) + assert receiver_wallet.get_balance().balance == expected_receiver_balance + + print("Checking scan_tx on incoming wallet's earliest tx") + earliest_height = height + earliest_txid = txid + for x in res['in']: + if x.height < earliest_height: + earliest_height = x.height + earliest_txid = x.txid + receiver_wallet.scan_tx([earliest_txid]) + diff_transfers(receiver_wallet.get_transfers(), res) + assert receiver_wallet.get_balance().balance == expected_receiver_balance + + print('Checking scan_tx on incoming wallet restored at current height') + txids = [x.txid for x in res['in']] + if 'out' in res: + txids = txids + [x.txid for x in res.out] + receiver_wallet.close_wallet() + receiver_wallet.restore_deterministic_wallet(seed = seeds[1], restore_height = height) + assert receiver_wallet.get_transfers() == {} + receiver_wallet.scan_tx(txids) + if 'out' in res: + for i, out_tx in enumerate(res.out): + if 'destinations' in out_tx: + del res.out[i]['destinations'] # destinations are not expected after wallet restore + diff_transfers(receiver_wallet.get_transfers(), res) + assert receiver_wallet.get_balance().balance == expected_receiver_balance + + print('Sanity check against incoming wallet restored at height 0') + receiver_wallet.close_wallet() + receiver_wallet.restore_deterministic_wallet(seed = seeds[1], restore_height = 0) + receiver_wallet.refresh() + diff_transfers(receiver_wallet.get_transfers(), res) + assert receiver_wallet.get_balance().balance == expected_receiver_balance + if __name__ == '__main__': TransferTest().run_test() diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 92bcedcba..9cae5df1f 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -101,12 +101,14 @@ int main(int argc, char** argv) const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); const std::string timings_database = command_line::get_arg(vm, arg_timings_database); - Params p; + Params core_params; if (!timings_database.empty()) - p.td = TimingsDatabase(timings_database); - p.verbose = command_line::get_arg(vm, arg_verbose); - p.stats = command_line::get_arg(vm, arg_stats); - p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); + core_params.td = TimingsDatabase(timings_database); + core_params.verbose = command_line::get_arg(vm, arg_verbose); + core_params.stats = command_line::get_arg(vm, arg_stats); + core_params.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); + + ParamsShuttle p{core_params}; performance_timer timer; timer.start(); diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 531e9d7fb..0f16ff8fc 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -31,6 +31,8 @@ #pragma once #include <iostream> +#include <memory> +#include <type_traits> #include <stdint.h> #include <boost/chrono.hpp> @@ -41,7 +43,7 @@ #include "common/perf_timer.h" #include "common/timings.h" -class performance_timer +class performance_timer final { public: typedef boost::chrono::high_resolution_clock clock; @@ -67,7 +69,7 @@ private: clock::time_point m_start; }; -struct Params +struct Params final { TimingsDatabase td; bool verbose; @@ -75,45 +77,79 @@ struct Params unsigned loop_multiplier; }; -template <typename T> -class test_runner +struct ParamsShuttle +{ + Params core_params; + + ParamsShuttle() = default; + + ParamsShuttle(Params ¶ms) : core_params{params} + {} + + virtual ~ParamsShuttle() = default; // virtual for non-final type +}; + +template <typename T, typename ParamsT, + typename std::enable_if<!std::is_same<ParamsT, ParamsShuttle>::value, bool>::type = true> +bool init_test(T &test, ParamsT ¶ms_shuttle) +{ + // assume if the params shuttle isn't the base shuttle type, then the test must take the shuttle as an input on init + if (!test.init(params_shuttle)) + return false; + + return true; +} + +template <typename T, typename ParamsT, + typename std::enable_if<std::is_same<ParamsT, ParamsShuttle>::value, bool>::type = true> +bool init_test(T &test, ParamsT ¶ms_shuttle) +{ + if (!test.init()) + return false; + + return true; +} + +template <typename T, typename ParamsT> +class test_runner final { public: - test_runner(const Params ¶ms) + test_runner(const ParamsT ¶ms_shuttle) : m_elapsed(0) - , m_params(params) - , m_per_call_timers(T::loop_count * params.loop_multiplier, {true}) + , m_params_shuttle(params_shuttle) + , m_core_params(params_shuttle.core_params) + , m_per_call_timers(T::loop_count * params_shuttle.core_params.loop_multiplier, {true}) { } - bool run() + int run() { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); T test; - if (!test.init()) - return false; + if (!init_test(test, m_params_shuttle)) + return -1; performance_timer timer; timer.start(); warm_up(); - if (m_params.verbose) + if (m_core_params.verbose) std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; timer.start(); - for (size_t i = 0; i < T::loop_count * m_params.loop_multiplier; ++i) + for (size_t i = 0; i < T::loop_count * m_core_params.loop_multiplier; ++i) { - if (m_params.stats) + if (m_core_params.stats) m_per_call_timers[i].resume(); if (!test.test()) - return false; - if (m_params.stats) + return i + 1; + if (m_core_params.stats) m_per_call_timers[i].pause(); } m_elapsed = timer.elapsed_ms(); m_stats.reset(new Stats<tools::PerformanceTimer, uint64_t>(m_per_call_timers)); - return true; + return 0; } int elapsed_time() const { return m_elapsed; } @@ -122,7 +158,7 @@ public: int time_per_call(int scale = 1) const { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); - return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier); + return m_elapsed * scale / (T::loop_count * m_core_params.loop_multiplier); } uint64_t get_min() const { return m_stats->get_min(); } @@ -156,20 +192,25 @@ private: private: volatile uint64_t m_warm_up; ///<! This field is intended for preclude compiler optimizations int m_elapsed; - Params m_params; + Params m_core_params; + ParamsT m_params_shuttle; std::vector<tools::PerformanceTimer> m_per_call_timers; std::unique_ptr<Stats<tools::PerformanceTimer, uint64_t>> m_stats; }; -template <typename T> -void run_test(const std::string &filter, Params ¶ms, const char* test_name) +template <typename T, typename ParamsT> +bool run_test(const std::string &filter, ParamsT ¶ms_shuttle, const char* test_name) { + static_assert(std::is_base_of<ParamsShuttle, ParamsT>::value, "Must use a ParamsShuttle."); + Params ¶ms = params_shuttle.core_params; + boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) - return; + return true; - test_runner<T> runner(params); - if (runner.run()) + test_runner<T, ParamsT> runner(params_shuttle); + int run_result{runner.run()}; + if (run_result == 0) { if (params.verbose) { @@ -227,16 +268,24 @@ void run_test(const std::string &filter, Params ¶ms, const char* test_name) double pc = fabs(100. * (prev_instance.mean - runner.get_mean()) / prev_instance.mean); cmp = ", " + std::to_string(pc) + "% " + (mean > prev_instance.mean ? "slower" : "faster"); } -cmp += " -- " + std::to_string(prev_instance.mean); + cmp += " -- " + std::to_string(prev_instance.mean); } std::cout << " (min " << mins << " " << unit << ", 90th " << p95s << " " << unit << ", median " << meds << " " << unit << ", std dev " << stddevs << " " << unit << ")" << cmp; } std::cout << std::endl; } + else if (run_result == -1) + { + std::cout << test_name << " - FAILED ON INIT" << std::endl; + return false; + } else { - std::cout << test_name << " - FAILED" << std::endl; + std::cout << test_name << " - FAILED ON TEST LOOP " << run_result << std::endl; + return false; } + + return true; } #define QUOTEME(x) #x diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index d57c9bd7b..cbed5e6de 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -90,6 +90,7 @@ set(unit_tests_sources hardfork.cpp unbound.cpp uri.cpp + variant.cpp varint.cpp ver_rct_non_semantics_simple_cached.cpp ringct.cpp diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp new file mode 100644 index 000000000..d7ded8e4b --- /dev/null +++ b/tests/unit_tests/variant.cpp @@ -0,0 +1,436 @@ +// Copyright (c) 2023, 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 "common/variant.h" + +#include <boost/mpl/deref.hpp> +#include <boost/variant/recursive_wrapper.hpp> +#include <boost/variant/recursive_variant.hpp> + +#include "gtest/gtest.h" + +#include <sstream> +#include <type_traits> +#include <vector> + +using tools::variant; +using tools::variant_static_visitor; + +namespace +{ +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template <typename T> +using strip_all_t = std::remove_reference_t<std::remove_cv_t<T>>; +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template <typename T, typename U> +using strip_same = std::is_same<strip_all_t<T>, strip_all_t<U>>; +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + typename PositiveType, + typename TestType, + typename... VariantTypes, + class VecTypes = boost::mpl::vector<VariantTypes...>, + class VecBegin = typename boost::mpl::begin<VecTypes>::type, + class VecIndexT = typename boost::mpl::find<VecTypes, TestType>::type, + size_t TYPE_INDEX = boost::mpl::distance<VecBegin, VecIndexT>::value, + bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1 +> +static std::enable_if_t<LAST_VARIANT_TYPE> +test_is_type_match(const variant<VariantTypes...>& v) +{ + constexpr bool expected = strip_same<PositiveType, TestType>(); + const bool actual = v.template is_type<TestType>(); + EXPECT_EQ(expected, actual); + + EXPECT_FALSE(v.template is_type<boost::blank>()); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + typename PositiveType, + typename TestType, + typename... VariantTypes, + class VecTypes = boost::mpl::vector<VariantTypes...>, + class VecBegin = typename boost::mpl::begin<VecTypes>::type, + class VecIndexT = typename boost::mpl::find<VecTypes, TestType>::type, + size_t TYPE_INDEX = boost::mpl::distance<VecBegin, VecIndexT>::value, + bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1 +> +static std::enable_if_t<!LAST_VARIANT_TYPE> +test_is_type_match(const variant<VariantTypes...>& v) +{ + constexpr bool expected = strip_same<PositiveType, TestType>(); + const bool actual = v.template is_type<TestType>(); + EXPECT_EQ(expected, actual); + + using NextTypeIt = typename boost::mpl::advance<VecIndexT, boost::mpl::int_<1>>::type; + using NextTestType = typename boost::mpl::deref<NextTypeIt>::type; + test_is_type_match<PositiveType, NextTestType>(v); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + typename VariantType0, + typename... VariantTypesRest, + typename AssignType +> +static void test_is_type_ref +( + variant<VariantType0, VariantTypesRest...>& v, + AssignType&& val +) +{ + v = val; + test_is_type_match<AssignType, VariantType0>(v); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + typename VariantType0, + typename... VariantTypesRest, + typename AssignType0, + typename... AssignTypesRest +> +static void test_is_type_ref +( + variant<VariantType0, VariantTypesRest...>& v, + AssignType0&& val_0, + AssignTypesRest&&... val_rest +) +{ + v = val_0; + test_is_type_match<AssignType0, VariantType0>(v); + test_is_type_ref(v, val_rest...); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template <typename... VariantTypes> +static void test_is_type_full(VariantTypes&&... test_vals) +{ + variant<VariantTypes...> v; + test_is_type_ref(v, test_vals...); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + size_t IJ = 0, + typename... VariantTypes, + bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes) +> +static std::enable_if_t<END> +test_same_type_ref +( + variant<VariantTypes...>& v1, + variant<VariantTypes...>& v2, + const std::tuple<VariantTypes...>& tup_i, + const std::tuple<VariantTypes...>& tup_j +) +{ /* trivial end case */ } +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template +< + size_t IJ = 0, + typename... VariantTypes, + bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes) +> +static std::enable_if_t<!END> +test_same_type_ref +( + variant<VariantTypes...>& v1, + variant<VariantTypes...>& v2, + const std::tuple<VariantTypes...>& tup_i, + const std::tuple<VariantTypes...>& tup_j +) +{ + constexpr size_t I = IJ / sizeof...(VariantTypes); + constexpr size_t J = IJ % sizeof...(VariantTypes); + constexpr bool expected = I == J; + + v1 = std::get<I>(tup_i); + v2 = std::get<J>(tup_j); + const bool actual = variant<VariantTypes...>::same_type(v1, v2); + + EXPECT_EQ(expected, actual); + + test_same_type_ref<IJ + 1>(v1, v2, tup_i, tup_j); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +template <typename... VariantTypes> +static void test_same_type_full +( + const std::tuple<VariantTypes...>& vals_i, + const std::tuple<VariantTypes...>& vals_j +) +{ + using Variant = variant<VariantTypes...>; + Variant v_i; + Variant v_j; + test_same_type_ref(v_i, v_j, vals_i, vals_j); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +struct test_stringify_visitor: public variant_static_visitor<std::string> +{ + template <typename T> + static std::string stringify(const T& t) + { + std::stringstream ss; + ss << typeid(T).name(); + ss << "::"; + ss << t; + return ss.str(); + } + + template <class Variant, typename T> + static void test_visitation(const Variant& v, const T& t) + { + EXPECT_EQ(test_stringify_visitor::stringify(t), v.visit(test_stringify_visitor())); + } + + // Make sure boost::blank errors + using variant_static_visitor::operator(); + + // Visitation implementation + template <typename T> + std::string operator()(const T& t) const + { + return test_stringify_visitor::stringify(t); + } +}; +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +} // anonymous namespace + +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, operatorbool) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_FALSE(v); + v = (int16_t) 2023; + EXPECT_TRUE(v); + v = (int16_t) 0; + EXPECT_TRUE(v); + v = boost::blank{}; + EXPECT_FALSE(v); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, is_empty) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_TRUE(v.is_empty()); + v = (int16_t) 2023; + EXPECT_FALSE(v.is_empty()); + v = (int16_t) 0; + EXPECT_FALSE(v.is_empty()); + v = boost::blank{}; + EXPECT_TRUE(v.is_empty()); + + variant<> v2; + EXPECT_TRUE(v2.is_empty()); + v2 = boost::blank{}; + EXPECT_TRUE(v2.is_empty()); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, is_type) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_TRUE(v.is_type<boost::blank>()); + v = (int16_t) 2023; + EXPECT_TRUE(v.is_type<int16_t>()); + + test_is_type_full((uint32_t) 2023, (char) '\n', std::string("HOWDY")); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, try_unwrap) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_FALSE(v.try_unwrap<int8_t>()); + v = (int16_t) 5252; + ASSERT_TRUE(v.try_unwrap<int16_t>()); + EXPECT_EQ(5252, *v.try_unwrap<int16_t>()); + EXPECT_FALSE(v.try_unwrap<uint16_t>()); + EXPECT_FALSE(v.try_unwrap<std::string>()); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, unwrap) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_THROW(v.unwrap<int8_t>(), std::runtime_error); + v = (int16_t) 5252; + EXPECT_EQ(5252, v.unwrap<int16_t>()); + EXPECT_THROW(v.unwrap<uint16_t>(), std::runtime_error); + EXPECT_THROW(v.unwrap<std::string>(), std::runtime_error); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, mutation) +{ + variant<uint8_t> v; + v = (uint8_t) 5; + EXPECT_EQ(5, v.unwrap<uint8_t>()); + uint8_t &intref{v.unwrap<uint8_t>()}; + intref = 10; + EXPECT_EQ(10, v.unwrap<uint8_t>()); + EXPECT_TRUE(v.try_unwrap<uint8_t>()); + uint8_t *intptr{v.try_unwrap<uint8_t>()}; + *intptr = 15; + EXPECT_EQ(15, v.unwrap<uint8_t>()); + + const variant<uint8_t> &v_ref{v}; + EXPECT_EQ(15, v_ref.unwrap<uint8_t>()); + EXPECT_TRUE(v_ref.try_unwrap<uint8_t>()); + EXPECT_EQ(15, *(v_ref.try_unwrap<uint8_t>())); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, index) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_EQ(0, v.index()); + v = (int8_t) 7; + EXPECT_EQ(1, v.index()); + v = (uint8_t) 7; + EXPECT_EQ(2, v.index()); + v = (int16_t) 7; + EXPECT_EQ(3, v.index()); + v = (uint16_t) 7; + EXPECT_EQ(4, v.index()); + v = "verifiable variant vying for vengence versus visa"; + EXPECT_EQ(5, v.index()); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, type_index_of) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_EQ(0, decltype(v)::type_index_of<boost::blank>()); + EXPECT_EQ(1, decltype(v)::type_index_of<int8_t>()); + EXPECT_EQ(2, decltype(v)::type_index_of<uint8_t>()); + EXPECT_EQ(3, decltype(v)::type_index_of<int16_t>()); + EXPECT_EQ(4, decltype(v)::type_index_of<uint16_t>()); + EXPECT_EQ(5, decltype(v)::type_index_of<std::string>()); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, constexpr_type_index_of) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + constexpr int TINDEX0 = decltype(v)::type_index_of<boost::blank>(); + EXPECT_EQ(0, TINDEX0); + constexpr int TINDEX5 = decltype(v)::type_index_of<std::string>(); + EXPECT_EQ(5, TINDEX5); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, same_type) +{ + const std::tuple<int, std::string, char> vals_i(77840, "Hullubaloo", '\0'); + const std::tuple<int, std::string, char> vals_j(1876, "Canneck", '\t'); + test_same_type_full(vals_i, vals_j); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, visit) +{ + variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v; + EXPECT_THROW(v.visit(test_stringify_visitor()), std::runtime_error); + + v = "Rev"; + test_stringify_visitor::test_visitation(v, std::string("Rev")); + + v = (int16_t) 2001; + test_stringify_visitor::test_visitation(v, (int16_t) 2001); + EXPECT_NE(test_stringify_visitor::stringify((uint16_t) 2001), v.visit(test_stringify_visitor())); +} +//------------------------------------------------------------------------------------------------------------------- +TEST(variant, ad_hoc_recursion) +{ + struct left_t; + struct right_t; + + using twisty = variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>; + + struct left_t + { + twisty l; + }; + + struct right_t + { + twisty r; + }; + + auto right = [](twisty&& t = {}) -> twisty + { + right_t r; + r.r = t; + return r; + }; + + auto left = [](twisty&& t = {}) -> twisty + { + left_t l; + l.l = t; + return l; + }; + + struct twisty_counter: variant_static_visitor<std::pair<int, int>> + { + std::pair<int, int> operator()(boost::blank) const + { + return {0, 0}; + } + + std::pair<int, int> operator()(const left_t& l) const + { + auto count = l.l.visit(twisty_counter()); + count.first += 1; + return count; + } + + std::pair<int, int> operator()(const right_t& r) const + { + auto count = r.r.visit(twisty_counter()); + count.second += 1; + return count; + } + }; + + const twisty tw = left(left(right(right(left(right(left(right(left())))))))); + + int left_count, right_count; + std::tie(left_count, right_count) = tw.visit(twisty_counter()); + + EXPECT_EQ(5, left_count); + EXPECT_EQ(4, right_count); +} +//------------------------------------------------------------------------------------------------------------------- diff --git a/utils/fish/monerod.fish b/utils/fish/monerod.fish index d2836a6b2..29be7c3ec 100644 --- a/utils/fish/monerod.fish +++ b/utils/fish/monerod.fish @@ -2,7 +2,6 @@ complete -c monerod -f complete -c monerod -l help -d "Produce help message" complete -c monerod -l version -d "Output version information" -complete -c monerod -l os-version -d "OS for which this executable was compiled" complete -c monerod -l config-file -r -d "Specify configuration file" complete -c monerod -l detach -d "Run as daemon" complete -c monerod -l pidfile -r -F -d "File path to write the daemon's PID to (optional, requires --detach)" @@ -33,7 +32,6 @@ complete -c monerod -l fast-block-sync -r -d "Sync up most of the way by using e complete -c monerod -l show-time-stats -r -d "(=0) Show time-stats when processing blocks/txs and disk synchronization. Default: 0" complete -c monerod -l block-sync-size -r -d "(=0) How many blocks to sync at once during chain synchronization (0 = adaptive). Default: 0" complete -c monerod -l check-updates -x -a "disabled notify download update" -d "Check for new versions of monero. Default: notify" -complete -c monerod -l fluffy-blocks -d "Relay blocks as fluffy blocks (obsolete, now default)" complete -c monerod -l no-fluffy-blocks -d "Relay blocks as normal blocks" complete -c monerod -l test-dbg-lock-sleep -r -d "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests." complete -c monerod -l offline -d "Do not listen for peers, nor connect to any" diff --git a/utils/munin_plugins/alt_blocks_count b/utils/munin_plugins/alt_blocks_count deleted file mode 100644 index 510adb080..000000000 --- a/utils/munin_plugins/alt_blocks_count +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title alt_blocks_count -graph_vlabel alt_blocks_count -graph_category monero -alt_blocks_count.label alt_blocks_count -EOM - exit 0;; -esac - -printf "alt_blocks_count.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep alt_blocks_count | cut -d ' ' -f2 diff --git a/utils/munin_plugins/difficulty b/utils/munin_plugins/difficulty deleted file mode 100644 index b97f5731f..000000000 --- a/utils/munin_plugins/difficulty +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title difficulty -graph_vlabel difficulty -graph_category monero -difficulty.label difficulty -EOM - exit 0;; -esac - -printf "difficulty.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep difficulty | cut -d ' ' -f2 diff --git a/utils/munin_plugins/grey_peerlist_size b/utils/munin_plugins/grey_peerlist_size deleted file mode 100644 index a18b4323a..000000000 --- a/utils/munin_plugins/grey_peerlist_size +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title grey_peerlist_size -graph_vlabel grey_peerlist_size -graph_category monero -grey_peerlist_size.label grey_peerlist_size -EOM - exit 0;; -esac - -printf "grey_peerlist_size.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep grey_peerlist_size | cut -d ' ' -f2 diff --git a/utils/munin_plugins/height b/utils/munin_plugins/height deleted file mode 100644 index bf7571a85..000000000 --- a/utils/munin_plugins/height +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title Height -graph_vlabel height -graph_category monero -height.label height -height.type COUNTER -EOM - exit 0;; -esac - -printf "height.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep height | cut -d ' ' -f2 diff --git a/utils/munin_plugins/incoming_connections_count b/utils/munin_plugins/incoming_connections_count deleted file mode 100644 index f6b5ac7d7..000000000 --- a/utils/munin_plugins/incoming_connections_count +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title incoming_connections_count -graph_vlabel incoming_connections_count -graph_category monero -incoming_connections_count.label incoming_connections_count -EOM - exit 0;; -esac - -printf "incoming_connections_count.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep incoming_connections_count | cut -d ' ' -f2 diff --git a/utils/munin_plugins/outgoing_connections_count b/utils/munin_plugins/outgoing_connections_count deleted file mode 100644 index 33135a9dc..000000000 --- a/utils/munin_plugins/outgoing_connections_count +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title outgoing_connections_count -graph_vlabel outgoing_connections_count -graph_category monero -outgoing_connections_count.label outgoing_connections_count -EOM - exit 0;; -esac - -printf "outgoing_connections_count.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep outgoing_connections_count | cut -d ' ' -f2 diff --git a/utils/munin_plugins/tx_count b/utils/munin_plugins/tx_count deleted file mode 100644 index 11508a3cd..000000000 --- a/utils/munin_plugins/tx_count +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title tx_count -graph_vlabel tx_count -graph_category monero -tx_count.label tx_count -tx_count.type COUNTER -EOM - exit 0;; -esac - -printf "tx_count.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_count| cut -d ' ' -f2 diff --git a/utils/munin_plugins/tx_pool_size b/utils/munin_plugins/tx_pool_size deleted file mode 100644 index 8985ea99f..000000000 --- a/utils/munin_plugins/tx_pool_size +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title tx_pool_size -graph_vlabel tx_pool_size -graph_category monero -tx_pool_size.label tx_pool_size -EOM - exit 0;; -esac - -printf "tx_pool_size.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_pool_size| cut -d ' ' -f2 diff --git a/utils/munin_plugins/white_peerlist_size b/utils/munin_plugins/white_peerlist_size deleted file mode 100644 index 8db255f07..000000000 --- a/utils/munin_plugins/white_peerlist_size +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2014-2023, The Monero Project -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -case $1 in - config) - cat <<'EOM' -graph_title white_peerlist_size -graph_vlabel white_peerlist_size -graph_category monero -white_peerlist_size.label white_peerlist_size -EOM - exit 0;; -esac - -printf "white_peerlist_size.value " -# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep white_peerlist_size | cut -d ' ' -f2 |