diff --git a/.gitignore b/.gitignore index c3b4edd6d58..a3bc8e32248 100755 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,6 @@ __pycache__/ # brew build lock file contrib/brew/Brewfile.lock.json + +*.pyc +*.log diff --git a/.gitmodules b/.gitmodules index 06f0bcc6104..088e5dba285 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,12 @@ [submodule "external/oxen-encoding"] path = external/oxen-encoding url = https://github.com/oxen-io/oxen-encoding.git +[submodule "external/nlohmann-json"] + path = external/nlohmann-json + url = https://github.com/nlohmann/json.git +[submodule "external/date"] + path = external/date + url = https://github.com/HowardHinnant/date.git +[submodule "external/Catch2"] + path = external/Catch2 + url = https://github.com/catchorg/Catch2 diff --git a/.vscode/settings.json b/.vscode/settings.json index e706357dec0..cd968a26133 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,20 @@ "thread": "cpp", "typeindex": "cpp", "typeinfo": "cpp", - "valarray": "cpp" + "valarray": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "execution": "cpp", + "ios": "cpp", + "locale": "cpp", + "print": "cpp", + "queue": "cpp", + "stack": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ca507de6fb..34063c84ec5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,12 +48,12 @@ cmake_minimum_required(VERSION 3.10) message(STATUS "CMake version ${CMAKE_VERSION}") # Has to be set before `project()`, and ignored on non-macos: -set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)") +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "macOS deployment target (Apple clang only)") project(beldex - VERSION 6.0.0 + VERSION 7.0.0 LANGUAGES CXX C) -set(BELDEX_RELEASE_CODENAME "Hermes") +set(BELDEX_RELEASE_CODENAME "Obscura") # String value to append to the full version string; this is intended to easily identify whether a # binary was build from the release or development branches. This should be permanently set to an @@ -290,6 +290,7 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/loki-mq cppzmq) if(BUILD_TESTS) check_submodule(external/googletest) + check_submodule(external/Catch2) endif() check_submodule(external/uWebSockets uSockets) check_submodule(external/ghc-filesystem) @@ -534,7 +535,7 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_FLAG}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAG}") - set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=uninitialized") + set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=uninitialized -Wno-error=attributes -Wno-error=unused-but-set-variable") option(WARNINGS_AS_ERRORS "Enable warning as errors" OFF) if(NOT MINGW AND WARNINGS_AS_ERRORS) @@ -570,7 +571,6 @@ else() # if those don't work for your compiler, single it out where appropriate if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT OPENBSD) - set(C_SECURITY_FLAGS "${C_SECURITY_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1") set(C_SECURITY_FLAGS "${C_SECURITY_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1") set(CXX_SECURITY_FLAGS "${CXX_SECURITY_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1") endif() @@ -605,6 +605,12 @@ else() add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS) endif() + # GCC 12's stringop-overflow warnings are really broken, with tons and tons of false positives all + # over the place (not just in our code, but also in its own stdlibc++ code). + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12) + set(CXX_WARNINGS "${CXX_WARNINGS} -Wno-stringop-overflow") + endif() + # Removed in GCC 9.1 (or before ?), but still accepted, so spams the output if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1) add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS) @@ -856,7 +862,7 @@ if(BUILD_STATIC_DEPS) # sqlite3 target already set up else() add_library(sqlite3 INTERFACE) - pkg_check_modules(SQLITE3 REQUIRED sqlite3 IMPORTED_TARGET) + pkg_check_modules(SQLITE3 REQUIRED sqlite3>=3.24.0 IMPORTED_TARGET) message(STATUS "Found sqlite3 ${SQLITE3_VERSION}") target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3) endif() diff --git a/Makefile b/Makefile index 90ac981b73a..bd9ea03be5f 100755 --- a/Makefile +++ b/Makefile @@ -97,11 +97,11 @@ release: cmake-release release-test: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) && $(MAKE) test + cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D USE_LTO=OFF -D RANDOMX_ENABLE_JIT=OFF -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) && $(MAKE) test release-all: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -DUSE_LTO=OFF -DRANDOMX_ENABLE_JIT=OFF -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) release-static: mkdir -p $(builddir)/release @@ -109,7 +109,7 @@ release-static: release-full-static: mkdir -p $(builddir)/release-static - cd $(builddir)/release-static && cmake -DBUILD_STATIC_DEPS=ON -DCMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) + cd $(builddir)/release-static && cmake -DBUILD_STATIC_DEPS=ON -DUSE_LTO=OFF -DRANDOMX_ENABLE_JIT=OFF -DCMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) release-full-static-archive: release-full-static cd $(builddir)/release-static && $(MAKE) strip_binaries && $(MAKE) create_archive diff --git a/README.md b/README.md index 4c05fe2d3ac..1fb366e0f5a 100755 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ library archives (`.a`). | pkg-config | any | NO | `pkg-config` | `base-devel` | `pkgconf` | NO | | | Boost | 1.65 | NO | `libboost-all-dev`[2] | `boost` | `boost-devel` | NO | C++ libraries | | libzmq | 4.3.0 | YES | `libzmq3-dev` | `zeromq` | `zeromq-devel` | NO | ZeroMQ library | -| sqlite3 | ? | YES | `libsqlite3-dev` | `sqlite` | `sqlite-devel` | NO | Beldex Name System | +| sqlite3 | 3.24.0 | YES | `libsqlite3-dev` | `sqlite` | `sqlite-devel` | NO | Beldex Name System | | libsodium | 1.0.9 | YES | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography | | libcurl | 4.0 | NO | `curl` | `curl-devel` | NO | HTTP | RPC | | libuv (Win) | any | NO | (Windows only) | -- | -- | NO | RPC event loop | diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index 395ea4194db..bea813828aa 100755 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -6,7 +6,7 @@ set(LOCAL_MIRROR "" CACHE STRING "local mirror path/URL for lib downloads") set(BOOST_VERSION 1.83.0 CACHE STRING "boost version") -set(BOOST_MIRROR ${LOCAL_MIRROR} https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source +set(BOOST_MIRROR ${LOCAL_MIRROR} https://archives.boost.io/release/${BOOST_VERSION}/source CACHE STRING "boost download mirror(s)") string(REPLACE "." "_" BOOST_VERSION_ ${BOOST_VERSION}) set(BOOST_SOURCE boost_${BOOST_VERSION_}.tar.bz2) @@ -79,11 +79,11 @@ set(ZMQ_SOURCE zeromq-${ZMQ_VERSION}.tar.gz) set(ZMQ_HASH SHA512=e198ef9f82d392754caadd547537666d4fba0afd7d027749b3adae450516bcf284d241d4616cad3cb4ad9af8c10373d456de92dc6d115b037941659f141e7c0e CACHE STRING "libzmq source hash") -set(ZLIB_VERSION 1.2.11 CACHE STRING "zlib version") -set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net/fossils +set(ZLIB_VERSION 1.3.1 CACHE STRING "zlib version") +set(ZLIB_MIRROR ${LOCAL_MIRROR} https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION} CACHE STRING "zlib mirror(s)") -set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.gz) -set(ZLIB_HASH SHA512=73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae +set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.xz) +set(ZLIB_HASH SHA256=38ef96b8dfe510d42707d9c781877914792541133e1870841463bfa73f883e32 CACHE STRING "zlib source hash") set(CURL_VERSION 7.76.1 CACHE STRING "curl version") diff --git a/contrib/epee/include/epee/console_handler.h b/contrib/epee/include/epee/console_handler.h index 0cab99621f6..8767b66e1d1 100755 --- a/contrib/epee/include/epee/console_handler.h +++ b/contrib/epee/include/epee/console_handler.h @@ -194,7 +194,7 @@ namespace epee if (m_read_status == state_cancelled) return false; - int retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100); + auto retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100); switch (retval) { case WAIT_FAILED: @@ -529,8 +529,7 @@ namespace epee catch (const std::exception &e) { rdln::suspend_readline pause_readline; - std::cout << "Command errored: " << cmd.front() << ", " << e.what(); - } + std::cout << "Command errored: " << cmd.front() << ", " << e.what() << "\n"; } return false; } diff --git a/contrib/epee/include/epee/misc_os_dependent.h b/contrib/epee/include/epee/misc_os_dependent.h deleted file mode 100755 index 7ad63ad2a25..00000000000 --- a/contrib/epee/include/epee/misc_os_dependent.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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. -// -#ifdef _WIN32 -#include -#endif - -#ifdef WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - - //#ifdef _WIN32_WINNT - // #undef _WIN32_WINNT - // #define _WIN32_WINNT 0x0600 - //#endif - - -#include -#endif - -#ifdef __MACH__ -#include -#include -#endif - -#include -#include - -#pragma once -namespace epee -{ -namespace misc_utils -{ - - inline uint64_t get_ns_count() - { -#if defined(_MSC_VER) - return ::GetTickCount64() * 1000000; -#elif defined(WIN32) - static LARGE_INTEGER pcfreq = {0}; - LARGE_INTEGER ticks; - if (!pcfreq.QuadPart) - QueryPerformanceFrequency(&pcfreq); - QueryPerformanceCounter(&ticks); - ticks.QuadPart *= 1000000000; /* we want nsec */ - return ticks.QuadPart / pcfreq.QuadPart; -#elif defined(__MACH__) - clock_serv_t cclock; - mach_timespec_t mts; - - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - - return ((uint64_t)mts.tv_sec * 1000000000) + (mts.tv_nsec); -#else - struct timespec ts; - if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - return ((uint64_t)ts.tv_sec * 1000000000) + (ts.tv_nsec); -#endif - } - - inline uint64_t get_tick_count() - { - return get_ns_count() / 1000000; - } - - - inline std::string get_thread_string_id() - { -#if defined(_WIN32) - return boost::lexical_cast(GetCurrentThreadId()); -#elif defined(__GNUC__) - return boost::lexical_cast(pthread_self()); -#endif - } - - inline bool get_gmt_time(time_t t, struct tm &tm) - { -#ifdef _WIN32 - return gmtime_s(&tm, &t); -#else - return gmtime_r(&t, &tm); -#endif - } -} -} diff --git a/contrib/epee/include/epee/net/abstract_tcp_server2.h b/contrib/epee/include/epee/net/abstract_tcp_server2.h index 9d6a66b8b78..44b7a74e393 100755 --- a/contrib/epee/include/epee/net/abstract_tcp_server2.h +++ b/contrib/epee/include/epee/net/abstract_tcp_server2.h @@ -302,7 +302,7 @@ namespace net_utils { auto ptr = std::make_shared>(io_service_, std::move(callback), timeout); //needed call handler here ?... - ptr->m_timer.expires_from_now(ptr->m_period); + ptr->m_timer.expires_after(ptr->m_period); ptr->m_timer.async_wait([this, ptr] (const boost::system::error_code&) { global_timer_handler(ptr); }); return true; } @@ -313,14 +313,14 @@ namespace net_utils //if handler return false - he don't want to be called anymore if(!ptr->call_handler()) return; - ptr->m_timer.expires_from_now(ptr->m_period); + ptr->m_timer.expires_after(ptr->m_period); ptr->m_timer.async_wait([this, ptr] (const boost::system::error_code&) { global_timer_handler(ptr); }); } template bool async_call(t_handler t_callback) { - io_service_.post(std::move(t_callback)); + boost::asio::post(io_service_, std::move(t_callback)); return true; } @@ -340,11 +340,11 @@ namespace net_utils struct worker { worker() - : io_service(), work(io_service) + : io_service(), work(io_service.get_executor()) {} boost::asio::io_service io_service; - boost::asio::io_service::work work; + boost::asio::executor_work_guard work; }; std::unique_ptr m_io_service_local_instance; boost::asio::io_service& io_service_; diff --git a/contrib/epee/include/epee/net/abstract_tcp_server2.inl b/contrib/epee/include/epee/net/abstract_tcp_server2.inl index 8b3fc80c118..b4b23b0427e 100755 --- a/contrib/epee/include/epee/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/epee/net/abstract_tcp_server2.inl @@ -33,7 +33,6 @@ #include -#include #include #include "../warnings.h" #include "../string_tools.h" @@ -146,7 +145,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if (remote_ep.address().is_v4()) { - const unsigned long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); + const unsigned long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_uint()); return start(is_income, is_multithreaded, ipv4_network_address{uint32_t(ip_), remote_ep.port()}); } else @@ -170,11 +169,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_is_multithreaded = is_multithreaded; m_local = real_remote.is_loopback() || real_remote.is_local(); - // create a random uuid, we don't need crypto strength here - const boost::uuids::uuid random_uuid = boost::uuids::random_generator()(); - + // create a random id, we don't need crypto strength here context = t_connection_context{}; - context.set_details(random_uuid, std::move(real_remote), is_income); + context.set_details(connection_id_t::random(), std::move(real_remote), is_income); boost::system::error_code ec; auto local_ep = socket().local_endpoint(ec); @@ -232,7 +229,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(!self) return false; - strand_.post(boost::bind(&connection::call_back_starter, self)); + strand_.post(boost::bind(&connection::call_back_starter, self), std::allocator{}); CATCH_ENTRY_L0("connection::request_callback()", false); return true; } @@ -610,11 +607,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return std::chrono::milliseconds{DEFAULT_TIMEOUT_MS_REMOTE >> shift}; } //--------------------------------------------------------------------------------- - template + template std::chrono::milliseconds connection::get_timeout_from_bytes_read(size_t bytes) { std::chrono::milliseconds ms{(unsigned)(bytes * TIMEOUT_EXTRA_MS_PER_BYTE)}; - if (auto remaining = std::chrono::duration_cast(m_timer.expires_at() - std::chrono::steady_clock::now()); + if (auto remaining = std::chrono::duration_cast(m_timer.expiry() - std::chrono::steady_clock::now()); remaining > 0ms) ms += remaining; if (ms > get_default_timeout()) @@ -661,11 +658,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) } if (add) { - if (const auto cur = std::chrono::duration_cast(m_timer.expires_at() - std::chrono::steady_clock::now()); + if (const auto cur = std::chrono::duration_cast(m_timer.expiry() - std::chrono::steady_clock::now()); cur > 0s) ms += cur; } - m_timer.expires_from_now(ms); + m_timer.expires_after(ms); m_timer.async_wait([=](const boost::system::error_code& ec) { if(ec == boost::asio::error::operation_aborted) @@ -883,8 +880,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) try { boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast(port), boost::asio::ip::tcp::resolver::query::canonical_name); - boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); + boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, boost::lexical_cast(port), boost::asio::ip::tcp::resolver::canonical_name).begin(); acceptor_.open(endpoint.protocol()); #if !defined(_WIN32) acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); @@ -919,8 +915,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4 boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name); - boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); + boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(address_ipv6, boost::lexical_cast(port_ipv6), boost::asio::ip::tcp::resolver::canonical_name).begin(); acceptor_ipv6.open(endpoint.protocol()); #if !defined(_WIN32) acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); @@ -1235,7 +1230,7 @@ POP_WARNINGS sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address_v4(bind_ip.c_str()), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) @@ -1321,13 +1316,12 @@ POP_WARNINGS bool try_ipv6 = false; boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); boost::system::error_code resolve_error; - boost::asio::ip::tcp::resolver::iterator iterator; + boost::asio::ip::tcp::resolver::results_type results; try { //resolving ipv4 address as ipv6 throws, catch here and move on - iterator = resolver.resolve(query, resolve_error); + results = resolver.resolve(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error); } catch (const boost::system::system_error& e) { @@ -1345,8 +1339,8 @@ POP_WARNINGS std::string bind_ip_to_use; - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) + + if(!results.size()) { if (!m_use_ipv6) { @@ -1366,11 +1360,9 @@ POP_WARNINGS if (try_ipv6) { - boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); - - iterator = resolver.resolve(query6, resolve_error); + results = resolver.resolve(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error); - if(iterator == end) + if(!results.size()) { MERROR("Failed to resolve " << adr); return false; @@ -1393,7 +1385,7 @@ POP_WARNINGS MTRACE("Trying to connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use); //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + boost::asio::ip::tcp::endpoint remote_endpoint(*(results.begin())); auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout); if (try_connect_result == CONNECT_FAILURE) @@ -1437,13 +1429,13 @@ POP_WARNINGS bool try_ipv6 = false; boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + boost::system::error_code resolve_error; - boost::asio::ip::tcp::resolver::iterator iterator; + boost::asio::ip::tcp::resolver::results_type results; try { //resolving ipv4 address as ipv6 throws, catch here and move on - iterator = resolver.resolve(query, resolve_error); + results = resolver.resolve(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error); } catch (const boost::system::system_error& e) { @@ -1459,9 +1451,7 @@ POP_WARNINGS throw; } - boost::asio::ip::tcp::resolver::iterator end; - - if(iterator == end) + if(!results.size()) { if (!try_ipv6) { @@ -1476,11 +1466,9 @@ POP_WARNINGS if (try_ipv6) { - boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); - - iterator = resolver.resolve(query6, resolve_error); + results = resolver.resolve(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error); - if(iterator == end) + if(!results.size()) { MERROR("Failed to resolve " << adr); return false; @@ -1488,12 +1476,12 @@ POP_WARNINGS } - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + boost::asio::ip::tcp::endpoint remote_endpoint(*(results.begin())); sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip.c_str()), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) @@ -1507,7 +1495,7 @@ POP_WARNINGS std::shared_ptr sh_deadline(new boost::asio::steady_timer(io_service_)); //start deadline - sh_deadline->expires_from_now(std::chrono::milliseconds(conn_timeout)); + sh_deadline->expires_after(std::chrono::milliseconds(conn_timeout)); sh_deadline->async_wait([=](const boost::system::error_code& error) { if(error != boost::asio::error::operation_aborted) diff --git a/contrib/epee/include/epee/net/connection_basic.hpp b/contrib/epee/include/epee/net/connection_basic.hpp index 1f11ed20c16..a517afe1dc0 100755 --- a/contrib/epee/include/epee/net/connection_basic.hpp +++ b/contrib/epee/include/epee/net/connection_basic.hpp @@ -49,6 +49,11 @@ #include #include +#if BOOST_VERSION >= 108700 +namespace boost::asio { + using io_service = io_context; +} +#endif #include "../shared_sv.h" @@ -135,7 +140,6 @@ class connection_basic { // not-templated base class for rapid developmet of som // handlers and sleep void sleep_before_packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?) - static void save_limit_to_file(int limit); ///< for dr-monero static double get_sleep_time(size_t cb); }; diff --git a/contrib/epee/include/epee/net/enums.h b/contrib/epee/include/epee/net/enums.h index 51029e0319d..165815f9ea5 100755 --- a/contrib/epee/include/epee/net/enums.h +++ b/contrib/epee/include/epee/net/enums.h @@ -30,13 +30,11 @@ #include #include +#include -namespace epee -{ -using namespace std::literals; - -namespace net_utils +namespace epee::net_utils { + using namespace std::literals; enum class address_type : std::uint8_t { // Do not change values, this will break serialization @@ -55,10 +53,19 @@ namespace net_utils tor = 3 }; - // implementations in src/net_utils_base.cpp + //! \return String name of address type + constexpr std::string_view to_string(address_type a) noexcept { + switch (a) { + case address_type::ipv4: return "IPv4"sv; + case address_type::ipv6: return "IPv6"sv; + case address_type::i2p: return "I2P"sv; + case address_type::tor: return "Tor"sv; + default: return "invalid"sv; + } + } //! \return String name of zone or "invalid" on error. - constexpr std::string_view zone_to_string(zone value) noexcept { + constexpr std::string_view to_string(zone value) noexcept { switch(value) { case zone::public_: return "public"sv; case zone::i2p: return "i2p"sv; @@ -74,6 +81,10 @@ namespace net_utils if (value == "tor"sv) return zone::tor; return zone::invalid; } -} // net_utils -} // epee + // implementations in src/net_utils_base.cpp + + std::ostream& operator<<(std::ostream& o, address_type a); + std::ostream& operator<<(std::ostream& o, zone z); + +} // epee::net_utils diff --git a/contrib/epee/include/epee/net/levin_base.h b/contrib/epee/include/epee/net/levin_base.h index 98ba2f96003..6de37462258 100755 --- a/contrib/epee/include/epee/net/levin_base.h +++ b/contrib/epee/include/epee/net/levin_base.h @@ -72,7 +72,6 @@ namespace levin #pragma pack(pop) -#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 #define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default #define LEVIN_PACKET_REQUEST 0x00000001 diff --git a/contrib/epee/include/epee/net/levin_protocol_handler.h b/contrib/epee/include/epee/net/levin_protocol_handler.h index 38fbae9f01a..5ffaa46ccd9 100755 --- a/contrib/epee/include/epee/net/levin_protocol_handler.h +++ b/contrib/epee/include/epee/net/levin_protocol_handler.h @@ -29,7 +29,6 @@ #ifndef _LEVIN_PROTOCOL_HANDLER_H_ #define _LEVIN_PROTOCOL_HANDLER_H_ -#include #include "levin_base.h" #include "../int-util.h" diff --git a/contrib/epee/include/epee/net/levin_protocol_handler_async.h b/contrib/epee/include/epee/net/levin_protocol_handler_async.h index a069ac6a2f8..c69371927d9 100755 --- a/contrib/epee/include/epee/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/epee/net/levin_protocol_handler_async.h @@ -26,17 +26,16 @@ #pragma once #include -#include #include #include #include #include +#include #include "levin_base.h" #include "buffer.h" #include "../scope_leaver.h" -#include "../misc_os_dependent.h" #include "../int-util.h" #include @@ -54,13 +53,6 @@ namespace epee namespace levin { -struct uuid_hasher { - size_t operator()(const boost::uuids::uuid& uid) const - { - return boost::uuids::hash_value(uid); - } -}; - /************************************************************************/ /* */ /************************************************************************/ @@ -70,15 +62,15 @@ class async_protocol_handler; template class async_protocol_handler_config { - typedef std::unordered_map*, uuid_hasher > connections_map; + using connections_map = std::unordered_map*>; std::recursive_mutex m_connects_lock; connections_map m_connects; void add_connection(async_protocol_handler* pc); void del_connection(async_protocol_handler* pc); - async_protocol_handler* find_connection(boost::uuids::uuid connection_id) const; - int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph); + async_protocol_handler *find_connection(connection_id_t connection_id) const; + int find_and_lock_connection(connection_id_t connection_id, async_protocol_handler *&aph); friend class async_protocol_handler; @@ -90,27 +82,27 @@ class async_protocol_handler_config public: typedef t_connection_context connection_context; uint64_t m_max_packet_size; - uint64_t m_invoke_timeout; + std::chrono::nanoseconds m_invoke_timeout; - int invoke(int command, const epee::span in_buff, std::string& buff_out, boost::uuids::uuid connection_id); + int invoke(int command, const epee::span in_buff, std::string& buff_out, connection_id_t connection_id); template - int invoke_async(int command, const epee::span in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); + int invoke_async(int command, const epee::span in_buff, connection_id_t connection_id, const callback_t &cb, std::chrono::nanoseconds timeout = 0s); - int notify(int command, const epee::span in_buff, boost::uuids::uuid connection_id); - int send(epee::shared_sv message, const boost::uuids::uuid& connection_id); - bool close(boost::uuids::uuid connection_id); + int notify(int command, const epee::span in_buff, connection_id_t connection_id); + int send(epee::shared_sv message, const connection_id_t &connection_id); + bool close(connection_id_t connection_id); bool update_connection_context(const t_connection_context& contxt); - bool request_callback(boost::uuids::uuid connection_id); + bool request_callback(connection_id_t connection_id); template bool foreach_connection(const callback_t &cb); template - bool for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb); + bool for_connection(const connection_id_t &connection_id, const callback_t &cb); size_t get_connections_count(); size_t get_out_connections_count(); size_t get_in_connections_count(); void set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(0ns) {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); @@ -136,11 +128,6 @@ class async_protocol_handler if(!m_pservice_endpoint->do_send(shared_sv{std::move(data)})) return false; - MDEBUG(m_connection_context << "LEVIN_PACKET_SENT. [len=" << head.m_cb - << ", flags" << head.m_flags - << ", r?=" << head.m_have_to_return_data - <<", cmd = " << head.m_command - << ", ver=" << head.m_protocol_version); return true; } @@ -189,19 +176,17 @@ class async_protocol_handler template struct anvoke_handler: invoke_response_handler_base { - anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command) + anvoke_handler(const callback_t& cb, std::chrono::milliseconds timeout, async_protocol_handler& con, int command) :m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false), m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command) { if(m_con.start_outer_call()) { - MDEBUG(con.get_context_ref() << "anvoke_handler, timeout: " << timeout); - m_timer.expires_from_now(std::chrono::milliseconds(timeout)); + m_timer.expires_after(std::chrono::milliseconds(timeout)); m_timer.async_wait([&con, command, cb, timeout](const boost::system::error_code& ec) { if(ec == boost::asio::error::operation_aborted) return; - MINFO(con.get_context_ref() << "Timeout on invoke operation happened, command: " << command << " timeout: " << timeout); epee::span fake; cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); con.close(); @@ -218,7 +203,7 @@ class async_protocol_handler bool m_timer_started; bool m_cancel_timer_called; bool m_timer_cancelled; - uint64_t m_timeout; + std::chrono::milliseconds m_timeout; int m_command; virtual bool handle(int res, const epee::span buff, typename async_protocol_handler::connection_context& context) { @@ -246,26 +231,22 @@ class async_protocol_handler if(!m_cancel_timer_called) { m_cancel_timer_called = true; - boost::system::error_code ignored_ec; - m_timer_cancelled = 1 == m_timer.cancel(ignored_ec); + m_timer_cancelled = 1 == m_timer.cancel(); } return m_timer_cancelled; } virtual void reset_timer() { - boost::system::error_code ignored_ec; - if (!m_cancel_timer_called && m_timer.cancel(ignored_ec) > 0) + if (!m_cancel_timer_called && m_timer.cancel() > 0) { callback_t& cb = m_cb; - uint64_t timeout = m_timeout; async_protocol_handler& con = m_con; int command = m_command; - m_timer.expires_from_now(std::chrono::milliseconds(m_timeout)); - m_timer.async_wait([&con, cb, command, timeout](const boost::system::error_code& ec) + m_timer.expires_after(m_timeout); + m_timer.async_wait([&con, cb, command, timeout=m_timeout](const boost::system::error_code& ec) { if(ec == boost::asio::error::operation_aborted) return; - MINFO(con.get_context_ref() << "Timeout on invoke operation happened, command: " << command << " timeout: " << timeout); epee::span fake; cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); con.close(); @@ -278,12 +259,12 @@ class async_protocol_handler std::list > m_invoke_response_handlers; template - bool add_invoke_response_handler(const callback_t &cb, uint64_t timeout, async_protocol_handler& con, int command) + bool add_invoke_response_handler(const callback_t &cb, std::chrono::nanoseconds timeout_ns, async_protocol_handler& con, int command) { + auto timeout = std::chrono::duration_cast(timeout_ns); std::lock_guard lock{m_invoke_response_handlers_lock}; if (m_protocol_released) { - MERROR("Adding response handler to a released object"); return false; } std::shared_ptr handler(std::make_shared>(cb, timeout, con, command)); @@ -326,20 +307,14 @@ class async_protocol_handler { std::this_thread::sleep_for(100ms); } - CHECK_AND_ASSERT_MES_NO_RET(0 == m_wait_count, "Failed to wait for operation completion. m_wait_count = " << m_wait_count); - - MTRACE(m_connection_context << "~async_protocol_handler()"); - } catch (...) { /* ignore */ } } bool start_outer_call() { - MTRACE(m_connection_context << "[levin_protocol] -->> start_outer_call"); if(!m_pservice_endpoint->add_ref()) { - MERROR(m_connection_context << "[levin_protocol] -->> start_outer_call failed"); return false; } m_wait_count++; @@ -347,7 +322,6 @@ class async_protocol_handler } bool finish_outer_call() { - MTRACE(m_connection_context << "[levin_protocol] <<-- finish_outer_call"); m_wait_count--; m_pservice_endpoint->release(); return true; @@ -404,7 +378,6 @@ class async_protocol_handler if(!m_config.m_pcommands_handler) { - MERROR(m_connection_context << "Commands handler not set!"); return false; } @@ -415,9 +388,6 @@ class async_protocol_handler // flipped to subtraction; prevent overflow since m_max_packet_size is variable and public if(cb > m_config.m_max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size()) { - MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size - << ", packet received " << m_cache_in_buffer.size() + cb - << ", connection will be closed."); return false; } @@ -440,7 +410,6 @@ class async_protocol_handler //async call scenario std::shared_ptr response_handler = m_invoke_response_handlers.front(); response_handler->reset_timer(); - MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); } } break; @@ -468,7 +437,6 @@ class async_protocol_handler if (m_fragment_buffer.size() < sizeof(bucket_head2)) { - MERROR(m_connection_context << "Fragmented data too small for levin header"); return false; } @@ -480,12 +448,6 @@ class async_protocol_handler bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); - MDEBUG(m_connection_context << "LEVIN_PACKET_RECEIVED. [len=" << m_current_head.m_cb - << ", flags" << m_current_head.m_flags - << ", r?=" << m_current_head.m_have_to_return_data - <<", cmd = " << m_current_head.m_command - << ", v=" << m_current_head.m_protocol_version); - if(is_response) {//response to some invoke @@ -508,7 +470,6 @@ class async_protocol_handler //use sync call scenario if(m_wait_count == 0 && m_close_called == 0) { - MERROR(m_connection_context << "no active invoke when response came, wtf?"); return false; }else { @@ -534,12 +495,6 @@ class async_protocol_handler if(!m_pservice_endpoint->do_send(shared_sv{std::move(return_buff)})) return false; - - MDEBUG(m_connection_context << "LEVIN_PACKET_SENT. [len=" << head.m_cb - << ", flags" << head.m_flags - << ", r?=" << head.m_have_to_return_data - <<", cmd = " << head.m_command - << ", ver=" << head.m_protocol_version); } else m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context); @@ -558,7 +513,6 @@ class async_protocol_handler { if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.span(8).data()) != SWAP64LE(LEVIN_SIGNATURE)) { - MWARNING(m_connection_context << "Signature mismatch, connection will be closed"); return false; } is_continue = false; @@ -578,7 +532,6 @@ class async_protocol_handler #endif if(LEVIN_SIGNATURE != phead.m_signature) { - LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); return false; } m_current_head = phead; @@ -588,15 +541,11 @@ class async_protocol_handler m_oponent_protocol_ver = m_current_head.m_protocol_version; if(m_current_head.m_cb > m_config.m_max_packet_size) { - LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size - << ", packet header received " << m_current_head.m_cb - << ", connection will be closed."); return false; } } break; default: - LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); return false; } } @@ -615,12 +564,12 @@ class async_protocol_handler } template - bool async_invoke(int command, const epee::span in_buff, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + bool async_invoke(int command, const epee::span in_buff, const callback_t &cb, std::chrono::nanoseconds timeout = 0ns) { auto scope_exit_handler = misc_utils::create_scope_leave_handler( [this] { return finish_outer_call(); }); - if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + if(timeout == 0ns) timeout = m_config.m_invoke_timeout; int err_code = LEVIN_OK; @@ -646,7 +595,6 @@ class async_protocol_handler if(!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); err_code = LEVIN_ERROR_CONNECTION; break; } @@ -687,11 +635,10 @@ class async_protocol_handler if (!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { - LOG_ERROR_CC(m_connection_context, "Failed to send request"); return LEVIN_ERROR_CONNECTION; } - uint64_t ticks_start = misc_utils::get_tick_count(); + auto start = std::chrono::steady_clock::now(); size_t prev_size = 0; while(!m_invoke_buf_ready && !m_deletion_initiated && !m_protocol_released) @@ -699,11 +646,10 @@ class async_protocol_handler 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(); + start = std::chrono::steady_clock::now(); } - if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout) + if (std::chrono::steady_clock::now() > 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; } @@ -736,7 +682,6 @@ class async_protocol_handler if (!send_message(command, in_buff, LEVIN_PACKET_REQUEST, false)) { - LOG_ERROR_CC(m_connection_context, "Failed to send notify message"); return -1; } @@ -760,15 +705,13 @@ class async_protocol_handler const std::size_t length = message.view.size(); if (!m_pservice_endpoint->do_send(std::move(message))) { - LOG_ERROR_CC(m_connection_context, "Failed to send message, dropping it"); return -1; } - MDEBUG(m_connection_context << "LEVIN_PACKET_SENT. [len=" << (length - sizeof(bucket_head2)) << ", r?=0]"); return 1; } //------------------------------------------------------------------------------------------ - boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;} + connection_id_t get_connection_id() {return m_connection_context.m_connection_id;} //------------------------------------------------------------------------------------------ t_connection_context& get_context_ref() {return m_connection_context;} }; @@ -786,7 +729,7 @@ void async_protocol_handler_config::del_connection(async_p template void async_protocol_handler_config::delete_connections(size_t count, bool incoming) { - std::vector connections; + std::vector connections; std::lock_guard lock{m_connects_lock}; for (auto& c: m_connects) { @@ -810,7 +753,6 @@ void async_protocol_handler_config::delete_connections(siz } catch (const std::out_of_range &e) { - MWARNING("Connection not found in m_connects, continuing"); } --count; } @@ -839,14 +781,14 @@ void async_protocol_handler_config::add_connection(async_p } //------------------------------------------------------------------------------------------ template -async_protocol_handler* async_protocol_handler_config::find_connection(boost::uuids::uuid connection_id) const +async_protocol_handler* async_protocol_handler_config::find_connection(connection_id_t connection_id) const { auto it = m_connects.find(connection_id); return it == m_connects.end() ? 0 : it->second; } //------------------------------------------------------------------------------------------ template -int async_protocol_handler_config::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph) +int async_protocol_handler_config::find_and_lock_connection(connection_id_t connection_id, async_protocol_handler*& aph) { std::lock_guard lock{m_connects_lock}; aph = find_connection(connection_id); @@ -858,7 +800,7 @@ int async_protocol_handler_config::find_and_lock_connectio } //------------------------------------------------------------------------------------------ template -int async_protocol_handler_config::invoke(int command, const epee::span in_buff, std::string& buff_out, boost::uuids::uuid connection_id) +int async_protocol_handler_config::invoke(int command, const epee::span in_buff, std::string& buff_out, connection_id_t connection_id) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); @@ -866,7 +808,7 @@ int async_protocol_handler_config::invoke(int command, con } //------------------------------------------------------------------------------------------ template template -int async_protocol_handler_config::invoke_async(int command, const epee::span in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout) +int async_protocol_handler_config::invoke_async(int command, const epee::span in_buff, connection_id_t connection_id, const callback_t &cb, std::chrono::nanoseconds timeout) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); @@ -887,7 +829,7 @@ bool async_protocol_handler_config::foreach_connection(con } //------------------------------------------------------------------------------------------ template template -bool async_protocol_handler_config::for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb) +bool async_protocol_handler_config::for_connection(const connection_id_t &connection_id, const callback_t &cb) { std::lock_guard lock{m_connects_lock}; async_protocol_handler* aph = find_connection(connection_id); @@ -937,7 +879,7 @@ void async_protocol_handler_config::set_handler(levin_comm } //------------------------------------------------------------------------------------------ template -int async_protocol_handler_config::notify(int command, const epee::span in_buff, boost::uuids::uuid connection_id) +int async_protocol_handler_config::notify(int command, const epee::span in_buff, connection_id_t connection_id) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); @@ -945,7 +887,7 @@ int async_protocol_handler_config::notify(int command, con } //------------------------------------------------------------------------------------------ template -int async_protocol_handler_config::send(shared_sv message, const boost::uuids::uuid& connection_id) +int async_protocol_handler_config::send(shared_sv message, const connection_id_t& connection_id) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); @@ -953,7 +895,7 @@ int async_protocol_handler_config::send(shared_sv message, } //------------------------------------------------------------------------------------------ template -bool async_protocol_handler_config::close(boost::uuids::uuid connection_id) +bool async_protocol_handler_config::close(connection_id_t connection_id) { std::lock_guard lock{m_connects_lock}; async_protocol_handler* aph = find_connection(connection_id); @@ -976,7 +918,7 @@ bool async_protocol_handler_config::update_connection_cont } //------------------------------------------------------------------------------------------ template -bool async_protocol_handler_config::request_callback(boost::uuids::uuid connection_id) +bool async_protocol_handler_config::request_callback(connection_id_t connection_id) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); diff --git a/contrib/epee/include/epee/net/local_ip.h b/contrib/epee/include/epee/net/local_ip.h index e85c075a9f9..48ac5915900 100755 --- a/contrib/epee/include/epee/net/local_ip.h +++ b/contrib/epee/include/epee/net/local_ip.h @@ -57,11 +57,14 @@ namespace epee return is_link_local || is_unique_local_unicast; } - inline - bool is_ipv6_loopback(const std::string& ip) + inline bool is_ipv6_loopback(const std::string &ip) { - // ipv6 loopback is ::1 +// ipv6 loopback is ::1 +#if BOOST_VERSION >= 106600 + return boost::asio::ip::make_address_v6(ip).is_loopback(); +#else return boost::asio::ip::address_v6::from_string(ip).is_loopback(); +#endif } inline diff --git a/contrib/epee/include/epee/net/net_utils_base.h b/contrib/epee/include/epee/net/net_utils_base.h index 67145a9e004..a1df7cfa042 100755 --- a/contrib/epee/include/epee/net/net_utils_base.h +++ b/contrib/epee/include/epee/net/net_utils_base.h @@ -29,8 +29,7 @@ #ifndef _NET_UTILS_BASE_H_ #define _NET_UTILS_BASE_H_ -#include -#include +#include #include #include #include @@ -47,6 +46,10 @@ #define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24)) #endif +namespace boost::asio { + using io_service = io_context; +} + #if BOOST_VERSION >= 107000 #define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) #else @@ -55,6 +58,18 @@ namespace epee { + +struct connection_id_t : std::array { + // Makes a random connection id. *NOT* cryptographically secure random. + static connection_id_t random(); + + bool is_nil() const { //TODO have to add constexpr in c++20 + return std::all_of(begin(), end(), [](auto x) { return x == 0; }); + } +}; + +std::ostream& operator<<(std::ostream& out, const connection_id_t& c); + namespace net_utils { class ipv4_network_address @@ -225,6 +240,7 @@ namespace net_utils virtual std::string str() const = 0; virtual std::string host_str() const = 0; + virtual uint16_t port() const = 0; virtual bool is_loopback() const = 0; virtual bool is_local() const = 0; virtual address_type get_type_id() const = 0; @@ -255,6 +271,7 @@ namespace net_utils virtual std::string str() const override { return value.str(); } virtual std::string host_str() const override { return value.host_str(); } + virtual uint16_t port() const override { return value.port(); } virtual bool is_loopback() const override { return value.is_loopback(); } virtual bool is_local() const override { return value.is_local(); } virtual address_type get_type_id() const override { return value.get_type_id(); } @@ -303,6 +320,7 @@ namespace net_utils bool is_same_host(const network_address &other) const; std::string str() const { return self ? self->str() : ""; } std::string host_str() const { return self ? self->host_str() : ""; } + uint16_t port() const { return self ? self->port() : 0; } bool is_loopback() const { return self ? self->is_loopback() : false; } bool is_local() const { return self ? self->is_local() : false; } address_type get_type_id() const { return self ? self->get_type_id() : address_type::invalid; } @@ -346,7 +364,7 @@ namespace net_utils /************************************************************************/ struct connection_context_base { - const boost::uuids::uuid m_connection_id; + const connection_id_t m_connection_id; const network_address m_remote_address; const bool m_is_income; std::chrono::steady_clock::time_point m_started; @@ -359,7 +377,7 @@ namespace net_utils double m_max_speed_down; double m_max_speed_up; - connection_context_base(boost::uuids::uuid connection_id, + connection_context_base(connection_id_t connection_id, const network_address &remote_address, bool is_income, std::chrono::steady_clock::time_point last_recv = std::chrono::steady_clock::time_point::min(), std::chrono::steady_clock::time_point last_send = std::chrono::steady_clock::time_point::min(), @@ -406,7 +424,7 @@ namespace net_utils private: template friend class connection; - void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income) + void set_details(connection_id_t connection_id, const network_address &remote_address, bool is_income) { this->~connection_context_base(); new(this) connection_context_base(connection_id, remote_address, is_income); @@ -434,16 +452,14 @@ namespace net_utils //some helpers - - std::string print_connection_context(const connection_context_base& ctx); std::string print_connection_context_short(const connection_context_base& ctx); inline MAKE_LOGGABLE(connection_context_base, ct, os) -{ - os << "[" << epee::net_utils::print_connection_context_short(ct) << "] "; - return os; -} + { + os << "[" << epee::net_utils::print_connection_context_short(ct) << "] "; + return os; + } #define LOG_ERROR_CC(ct, message) MERROR(ct << message) #define LOG_WARNING_CC(ct, message) MWARNING(ct << message) @@ -469,4 +485,19 @@ inline MAKE_LOGGABLE(connection_context_base, ct, os) } } +namespace std { +template <> +struct hash { + size_t operator()(const epee::connection_id_t& id) const { + constexpr size_t inverse_golden_ratio = sizeof(size_t) >= 8 ? 0x9e37'79b9'7f4a'7c15 : 0x9e37'79b9; + + uint64_t a, b; + std::memcpy(&a, id.data(), 8); + std::memcpy(&b, id.data() + 8, 8); + auto h = hash{}(a); + return hash{}(b) + inverse_golden_ratio + (h << 6) + (h >> 2); + } +}; +} // namespace std + #endif //_NET_UTILS_BASE_H_ diff --git a/contrib/epee/include/epee/net/network_throttle-detail.hpp b/contrib/epee/include/epee/net/network_throttle-detail.hpp index fde1e3f46c9..b790e1a4d53 100755 --- a/contrib/epee/include/epee/net/network_throttle-detail.hpp +++ b/contrib/epee/include/epee/net/network_throttle-detail.hpp @@ -77,33 +77,33 @@ class network_throttle : public i_network_throttle { public: network_throttle(const std::string &nameshort, const std::string &name, int window_size=-1); virtual ~network_throttle(); - virtual void set_name(const std::string &name); - virtual void set_target_speed( network_speed_kbps target ); - virtual network_speed_kbps get_target_speed(); + void set_name(const std::string &name) override; + void set_target_speed( network_speed_kbps target ) override; + network_speed_kbps get_target_speed() override; // add information about events: - virtual void handle_trafic_exact(size_t packet_size); ///< count the new traffic/packet; the size is exact considering all network costs - virtual void handle_trafic_tcp(size_t packet_size); ///< count the new traffic/packet; the size is as TCP, we will consider MTU etc + void handle_trafic_exact(size_t packet_size) override; ///< count the new traffic/packet; the size is exact considering all network costs + void handle_trafic_tcp(size_t packet_size) override; ///< count the new traffic/packet; the size is as TCP, we will consider MTU etc - virtual void tick(); ///< poke and update timers/history (recalculates, moves the history if needed, checks the real clock etc) + void tick() override; ///< poke and update timers/history (recalculates, moves the history if needed, checks the real clock etc) - virtual double get_time_seconds() const ; ///< timer that we use, time in seconds, monotionic + double get_time_seconds() const override; ///< timer that we use, time in seconds, monotionic // time calculations: - virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const; ///< MAIN LOGIC (see base class for info) + void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const override; ///< MAIN LOGIC (see base class for info) - virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size); ///< increase the timer if needed, and get the package size - virtual network_time_seconds get_sleep_time(size_t packet_size) const; ///< gets the Delay (recommended Delay time) from calc. (not safe: only if time didnt change?) TODO + network_time_seconds get_sleep_time_after_tick(size_t packet_size) override; ///< increase the timer if needed, and get the package size + network_time_seconds get_sleep_time(size_t packet_size) const override; ///< gets the Delay (recommended Delay time) from calc. (not safe: only if time didnt change?) TODO - virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported - virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame - virtual double get_current_speed() const; - virtual void get_stats(uint64_t &total_packets, uint64_t &total_bytes) const; + size_t get_recommended_size_of_planned_transport() const override; ///< what should be the size (bytes) of next data block to be transported + size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame + double get_current_speed() const; + std::pair get_stats() const override; private: - virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13 - virtual void _handle_trafic_exact(size_t packet_size, size_t orginal_size); - virtual void logger_handle_net(const std::string &filename, double time, size_t size); + network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13 + void _handle_trafic_exact(size_t packet_size, size_t orginal_size); + void logger_handle_net(const std::string &filename, double time, size_t size) override; }; /*** diff --git a/contrib/epee/include/epee/net/network_throttle.hpp b/contrib/epee/include/epee/net/network_throttle.hpp index 11982cf8a2f..187f186e32e 100755 --- a/contrib/epee/include/epee/net/network_throttle.hpp +++ b/contrib/epee/include/epee/net/network_throttle.hpp @@ -119,7 +119,8 @@ class i_network_throttle { virtual double get_time_seconds() const =0; // a timer virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0; - virtual void get_stats(uint64_t &total_packets, uint64_t &total_bytes) const =0; + // Returns {total packets, total bytes} + virtual std::pair get_stats() const =0; }; diff --git a/contrib/epee/include/epee/profile_tools.h b/contrib/epee/include/epee/profile_tools.h deleted file mode 100755 index b7cfe05c96f..00000000000 --- a/contrib/epee/include/epee/profile_tools.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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. -// - - -#ifndef _PROFILE_TOOLS_H_ -#define _PROFILE_TOOLS_H_ - -#include -#include "misc_os_dependent.h" - -namespace epee -{ - -#ifdef ENABLE_PROFILING -#define PROFILE_FUNC(immortal_ptr_str) static profile_tools::local_call_account lcl_acc(immortal_ptr_str); \ - profile_tools::call_frame cf(lcl_acc); - -#define PROFILE_FUNC_SECOND(immortal_ptr_str) static profile_tools::local_call_account lcl_acc2(immortal_ptr_str); \ - profile_tools::call_frame cf2(lcl_acc2); - -#define PROFILE_FUNC_THIRD(immortal_ptr_str) static profile_tools::local_call_account lcl_acc3(immortal_ptr_str); \ - profile_tools::call_frame cf3(lcl_acc3); - -#define PROFILE_FUNC_ACC(acc) \ - profile_tools::call_frame cf(acc); - - -#else -#define PROFILE_FUNC(immortal_ptr_str) -#define PROFILE_FUNC_SECOND(immortal_ptr_str) -#define PROFILE_FUNC_THIRD(immortal_ptr_str) -#endif - -#define START_WAY_POINTS() uint64_t _____way_point_time = epee::misc_utils::get_tick_count(); -#define WAY_POINT(name) {uint64_t delta = epee::misc_utils::get_tick_count()-_____way_point_time; MDEBUG("Way point " << name << ": " << delta);_____way_point_time = misc_utils::get_tick_count();} -#define WAY_POINT2(name, avrg_obj) {uint64_t delta = epee::misc_utils::get_tick_count()-_____way_point_time; avrg_obj.push(delta); MDEBUG("Way point " << name << ": " << delta);_____way_point_time = misc_utils::get_tick_count();} - - -#define TIME_MEASURE_START(var_name) uint64_t var_name = epee::misc_utils::get_tick_count(); -#define TIME_MEASURE_PAUSE(var_name) var_name = epee::misc_utils::get_tick_count() - var_name; -#define TIME_MEASURE_RESTART(var_name) var_name = epee::misc_utils::get_tick_count() - var_name; -#define TIME_MEASURE_FINISH(var_name) var_name = epee::misc_utils::get_tick_count() - var_name; - -#define TIME_MEASURE_NS_START(var_name) uint64_t var_name = epee::misc_utils::get_ns_count(); -#define TIME_MEASURE_NS_PAUSE(var_name) var_name = epee::misc_utils::get_ns_count() - var_name; -#define TIME_MEASURE_NS_RESTART(var_name) var_name = epee::misc_utils::get_ns_count() - var_name; -#define TIME_MEASURE_NS_FINISH(var_name) var_name = epee::misc_utils::get_ns_count() - var_name; - -namespace profile_tools -{ - struct local_call_account - { - local_call_account(const char* pstr):m_count_of_call(0), m_summary_time_used(0),m_pname(pstr) - {} - ~local_call_account() - { - MINFO("PROFILE "<( - std::chrono::duration_cast(elapsed).count()); - - //::QueryPerformanceCounter((LARGE_INTEGER *)&ret_time); - //m_call_time = (ret_time-m_call_time)/1000; - m_cc.m_summary_time_used += useconds_used; - } - - private: - local_call_account& m_cc; - std::chrono::steady_clock::time_point m_call_time; - }; - - -} -} - - -#endif //_PROFILE_TOOLS_H_ diff --git a/contrib/epee/include/epee/serialization/keyvalue_serialization.h b/contrib/epee/include/epee/serialization/keyvalue_serialization.h index 73b5a04298d..e487761ca5c 100755 --- a/contrib/epee/include/epee/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/epee/serialization/keyvalue_serialization.h @@ -158,8 +158,8 @@ public: \ using int_t = std::underlying_type_t; \ int_t int_value = is_store ? static_cast(this_ref.enum_) : 0; \ epee::serialization::perform_serialize(int_value, stg, parent_section, #enum_); \ - if (!is_store) \ - const_cast(this_ref.enum_) = static_cast(int_value); \ + if constexpr (!is_store) \ + this_ref.enum_ = static_cast(int_value); \ } while(0); // Stashes `this` in the storage object's context for a dependent type that needs to access it. diff --git a/contrib/epee/include/epee/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/epee/serialization/keyvalue_serialization_overloads.h index 4660339a209..c4df26cf76c 100755 --- a/contrib/epee/include/epee/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/epee/serialization/keyvalue_serialization_overloads.h @@ -163,11 +163,13 @@ namespace epee { static_assert(Size > 0, "cannot deserialize empty std::array"); size_t next_i = 0; - for (auto [it, end] = stg.template converting_array_range(pname, parent_section); it != end; ++it) { - CHECK_AND_ASSERT_MES(next_i < array.size(), false, "too many values to deserialize into fixed size std::array"); - array[next_i++] = *it; - } - CHECK_AND_ASSERT_MES(next_i == array.size(), false, "not enough values to deserialize into fixed size std::array"); + try { + for (auto [it, end] = stg.template converting_array_range(pname, parent_section); it != end; ++it) { + CHECK_AND_ASSERT_MES(next_i < array.size(), false, "too many values to deserialize into fixed size std::array"); + array[next_i++] = *it; + } + CHECK_AND_ASSERT_MES(next_i == array.size(), false, "not enough values to deserialize into fixed size std::array"); + } catch (const std::out_of_range&) { return false; } return true; } //-------------------------------------------------------------------------------------------------------------------- diff --git a/contrib/epee/include/epee/storages/levin_abstract_invoke2.h b/contrib/epee/include/epee/storages/levin_abstract_invoke2.h index cd3ac4cbf94..efdcbbea57a 100755 --- a/contrib/epee/include/epee/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/epee/storages/levin_abstract_invoke2.h @@ -84,7 +84,7 @@ namespace epee } template - bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + bool invoke_remote_command2(connection_id_t conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) { typename serialization::portable_storage stg; @@ -108,7 +108,7 @@ namespace epee } template - bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + bool async_invoke_remote_command2(connection_id_t conn_id, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, std::chrono::nanoseconds inv_timeout = 0ns) { typename serialization::portable_storage stg; const_cast(out_struct).store(stg);//TODO: add true const support to searilzation @@ -148,7 +148,7 @@ namespace epee } template - bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport) + bool notify_remote_command2(connection_id_t conn_id, int command, const t_arg& out_struct, t_transport& transport) { serialization::portable_storage stg; diff --git a/contrib/epee/include/epee/storages/parserse_base_utils.h b/contrib/epee/include/epee/storages/parserse_base_utils.h index c25982b5603..f91c33310d9 100755 --- a/contrib/epee/include/epee/storages/parserse_base_utils.h +++ b/contrib/epee/include/epee/storages/parserse_base_utils.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include diff --git a/contrib/epee/include/epee/storages/portable_storage_base.h b/contrib/epee/include/epee/storages/portable_storage_base.h index deb0aae5097..df511f09551 100755 --- a/contrib/epee/include/epee/storages/portable_storage_base.h +++ b/contrib/epee/include/epee/storages/portable_storage_base.h @@ -33,6 +33,7 @@ #include #include #include +#include #include "../misc_log_ex.h" #include "../int-util.h" diff --git a/contrib/epee/include/epee/storages/portable_storage_from_bin.h b/contrib/epee/include/epee/storages/portable_storage_from_bin.h index 5d795a95ebd..72c43cd398a 100755 --- a/contrib/epee/include/epee/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/epee/storages/portable_storage_from_bin.h @@ -29,7 +29,8 @@ #pragma once #include "portable_storage_base.h" -#include +#include +#include namespace epee { @@ -110,7 +111,7 @@ namespace epee static_assert(std::is_integral_v); read(&v, sizeof(T)); if constexpr (sizeof(T) > 1) - boost::endian::little_to_native(v); + oxenc::little_to_host(v); } template @@ -157,7 +158,7 @@ namespace epee case SERIALIZE_TYPE_TAG: return read_ae(); case SERIALIZE_TYPE_TAG
: return read_ae
(); //case SERIALIZE_TYPE_ARRAY: return read_ae(); // nested arrays not supported - default: CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << (int)type); + default: { CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << (int)type); return {}; } } } @@ -210,7 +211,7 @@ namespace epee case SERIALIZE_TYPE_TAG: return read_se(); case SERIALIZE_TYPE_TAG
: return read_se
(); //case SERIALIZE_TYPE_ARRAY: return read_se(); // nested arrays not supported - default: CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << (int)ent_type); + default: { CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << (int)ent_type); return {}; } } } inline diff --git a/contrib/epee/include/epee/storages/portable_storage_to_bin.h b/contrib/epee/include/epee/storages/portable_storage_to_bin.h index c37317f8aa0..2fbd396058e 100755 --- a/contrib/epee/include/epee/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/epee/storages/portable_storage_to_bin.h @@ -30,7 +30,7 @@ #include "../pragma_comp_defs.h" #include "portable_storage_base.h" -#include +#include #include namespace epee @@ -71,16 +71,15 @@ namespace epee void pack_entry_to_buff(std::ostream& strm, T v) { if constexpr (sizeof(T) > 1) - boost::endian::native_to_little_inplace(v); + oxenc::host_to_little_inplace(v); strm.write(reinterpret_cast(&v), sizeof(v)); } inline void pack_entry_to_buff(std::ostream& strm, double v) { - static_assert(std::numeric_limits::is_iec559 && sizeof(double) == 8 && - (boost::endian::order::native == boost::endian::order::big || boost::endian::order::native == boost::endian::order::little)); + static_assert(std::numeric_limits::is_iec559 && sizeof(double) == 8 && (oxenc::little_endian || oxenc::big_endian)); char* buff = reinterpret_cast(&v); - if constexpr (boost::endian::order::native == boost::endian::order::big) { + if constexpr (oxenc::big_endian) { size_t i = 8; while (i) strm.put(buff[--i]); } else { diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 8c957dd32c5..3ea86391e50 100755 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -65,7 +65,9 @@ target_link_libraries(epee PUBLIC easylogging oxenmq::oxenmq + oxenc::oxenc PRIVATE filesystem Boost::thread + date::date extra) diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 060abcd5b44..1b9da48887a 100755 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -176,7 +176,6 @@ void connection_basic::set_rate_up_limit(uint64_t limit) { std::lock_guard lock{network_throttle_manager::m_lock_get_global_throttle_out}; network_throttle_manager::get_global_throttle_out().set_target_speed(limit); } - save_limit_to_file(limit); } void connection_basic::set_rate_down_limit(uint64_t limit) { @@ -189,7 +188,6 @@ void connection_basic::set_rate_down_limit(uint64_t limit) { std::lock_guard lock{network_throttle_manager::m_lock_get_global_throttle_inreq}; network_throttle_manager::get_global_throttle_inreq().set_target_speed(limit); } - save_limit_to_file(limit); } uint64_t connection_basic::get_rate_up_limit() { @@ -209,9 +207,6 @@ uint64_t connection_basic::get_rate_down_limit() { } return limit; } - -void connection_basic::save_limit_to_file(int limit) { -} void connection_basic::set_tos_flag(int tos) { connection_basic_pimpl::m_default_tos = tos; diff --git a/contrib/epee/src/levin_base.cpp b/contrib/epee/src/levin_base.cpp index e8e876abd16..cc4a2e41695 100755 --- a/contrib/epee/src/levin_base.cpp +++ b/contrib/epee/src/levin_base.cpp @@ -31,6 +31,7 @@ #include "epee/int-util.h" +using namespace std::literals; namespace epee { namespace levin diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index b2ac4cc83b7..6ca05d03521 100755 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -28,6 +28,8 @@ #ifndef _MLOG_H_ #define _MLOG_H_ +#include +#include #ifdef _WIN32 #include #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING @@ -39,7 +41,6 @@ #include #include #include "epee/string_tools.h" -#include "epee/misc_os_dependent.h" #include "epee/misc_log_ex.h" #ifndef USE_GHC_FILESYSTEM @@ -67,23 +68,6 @@ namespace fs = ghc::filesystem; using namespace epee; -static std::string generate_log_filename(const char *base) -{ - std::string filename(base); - static unsigned int fallback_counter = 0; - char tmp[200]; - struct tm tm; - time_t now = time(NULL); - if (!epee::misc_utils::get_gmt_time(now, tm)) - snprintf(tmp, sizeof(tmp), "part-%u", ++fallback_counter); - else - strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm); - tmp[sizeof(tmp) - 1] = 0; - filename += "-"; - filename += tmp; - return filename; -} - std::string mlog_get_default_log_path(const char *default_filename) { std::string process_name = epee::string_tools::get_current_module_name(); @@ -179,7 +163,7 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck); el::Helpers::installPreRollOutCallback([filename_base, max_log_files](const char *name, size_t){ - std::string rname = generate_log_filename(filename_base.c_str()); + std::string rname = filename_base + "-" + date::format("%Y-%m-%d-%H-%M-%S", std::chrono::system_clock::now()); int ret = rename(name, rname.c_str()); if (ret < 0) { diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp index 746c2d5e911..9fb169b0d46 100755 --- a/contrib/epee/src/net_utils_base.cpp +++ b/contrib/epee/src/net_utils_base.cpp @@ -1,12 +1,14 @@ #include "epee/net/net_utils_base.h" -#include +#include +#include #include "epee/string_tools.h" #include "epee/net/local_ip.h" +#include "epee/net/enums.h" -namespace epee { namespace net_utils +namespace epee::net_utils { bool ipv4_network_address::equal(const ipv4_network_address& other) const noexcept { return is_same_host(other) && port() == other.port(); } @@ -104,5 +106,47 @@ namespace epee { namespace net_utils ss << ctx.m_remote_address.str() << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } -}} + std::ostream& operator<<(std::ostream& o, address_type a) + { + return o << to_string(a); + } + std::ostream& operator<<(std::ostream& o, zone z) + { + return o << to_string(z); + } +} // namespace epee::net_utils + + +namespace epee { + + static std::mt19937_64 seed_rng() { + std::random_device dev; + // each dev() gives us 32 bits of random data; 256 bits ought to be plenty for what we need: + std::seed_seq seed{{dev(), dev(), dev(), dev(), dev(), dev(), dev(), dev()}}; + std::mt19937_64 rng{seed}; + return rng; + } + + connection_id_t connection_id_t::random() { + static thread_local auto rng = seed_rng(); + uint64_t x[2]; + x[0] = rng(); + x[1] = rng(); + connection_id_t conn_id; + static_assert(sizeof(conn_id) == sizeof(x)); + std::memcpy(conn_id.data(), &x, sizeof(x)); + return conn_id; + } + + std::ostream& operator<<(std::ostream& out, const connection_id_t& c) { + // Output in uuid form: + // 00112233-4455-6677-8899-101112131415 + return out << oxenc::to_hex(c.begin(), c.begin() + 4) << '-' + << oxenc::to_hex(c.begin() + 4, c.begin() + 6) << '-' + << oxenc::to_hex(c.begin() + 6, c.begin() + 8) << '-' + << oxenc::to_hex(c.begin() + 8, c.begin() + 10) << '-' + << oxenc::to_hex(c.begin() + 10, c.end()); + } + + } // namespace epee \ No newline at end of file diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 4683285c238..c4e54ae9e6a 100755 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -133,7 +133,7 @@ void network_throttle::set_name(const std::string &name) void network_throttle::set_target_speed( network_speed_kbps target ) { - m_target_speed = target * 1024; + m_target_speed = target * 1024; MINFO("Setting LIMIT: " << target << " kbps"); } @@ -342,9 +342,8 @@ double network_throttle::get_current_speed() const { return bytes_transferred / ((m_history.size() - 1) * m_slot_size); } -void network_throttle::get_stats(uint64_t &total_packets, uint64_t &total_bytes) const { - total_packets = m_total_packets; - total_bytes = m_total_bytes; +std::pair network_throttle::get_stats() const { + return {m_total_packets, m_total_bytes}; } diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 7034db8a7f2..e69ba079b69 100755 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h index b4edcff90f2..e8f673560bc 100755 --- a/contrib/epee/tests/src/net/test_net.h +++ b/contrib/epee/tests/src/net/test_net.h @@ -119,7 +119,7 @@ namespace tests }; typedef epee::misc_utils::struct_init response; }; - typedef boost::uuids::uuid uuid; + class test_levin_server: public levin::levin_commands_handler<> { @@ -146,13 +146,13 @@ namespace tests } template - bool invoke(uuid con_id, int command, t_request& req, t_response& resp) + bool invoke(connection_id_t con_id, int command, t_request& req, t_response& resp) { return invoke_remote_command(con_id, command, req, resp, m_net_server.get_config_object()); } template< class t_response, class t_request, class callback_t> - bool invoke_async(uuid con_id, int command, t_request& req, callback_t cb) + bool invoke_async(connection_id_t con_id, int command, t_request& req, callback_t cb) { return async_invoke_remote_command(con_id, command, req, m_net_server.get_config_object(), cb); } diff --git a/contrib/format.sh b/contrib/format.sh new file mode 100644 index 00000000000..7d5513a8853 --- /dev/null +++ b/contrib/format.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +CLANG_FORMAT_DESIRED_VERSION=11 + +TARGET_DIRS=(src/wallet3 src/sqlitedb) + +binary=$(which clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null) +if [ $? -ne 0 ]; then + binary=$(which clang-format-mp-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null) +fi +if [ $? -ne 0 ]; then + binary=$(which clang-format 2>/dev/null) + if [ $? -ne 0 ]; then + echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script." + exit 1 + fi + version=$(clang-format --version) + if [[ ! $version == *"clang-format version $CLANG_FORMAT_DESIRED_VERSION"* ]]; then + echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script." + exit 1 + fi +fi + +cd "$(dirname $0)/../" +if [ "$1" = "verify" ] ; then + for d in ${TARGET_DIRS[@]}; do + if [ $($binary --output-replacements-xml $(find $d | grep -E '\.([hc](pp)?|mm?)$' | grep -v '\#') | grep '' | wc -l) -ne 0 ] ; then + exit 1 + fi + done +else + for d in ${TARGET_DIRS[@]}; do + echo "Formatting $d" + $binary -i $(find $d | grep -E '\.([hc](pp)?|mm)$' | grep -v '\#') &> /dev/null + done +fi + +swift_format=$(which swiftformat 2>/dev/null) +if [ $? -eq 0 ]; then + if [ "$1" = "verify" ] ; then + for f in $(find daemon | grep -E '\.swift$' | grep -v '\#') ; do + if [ $($swift_format --quiet --dryrun < "$f" | diff "$f" - | wc -l) -ne 0 ] ; then + exit 1 + fi + done + else + $swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '\#') + fi + +fi \ No newline at end of file diff --git a/docs/daemon-rpc/core_rpc_server_commands_defs.h b/docs/daemon-rpc/core_rpc_server_commands_defs.h new file mode 100644 index 00000000000..7d9b000d289 --- /dev/null +++ b/docs/daemon-rpc/core_rpc_server_commands_defs.h @@ -0,0 +1 @@ +../../src/rpc/core_rpc_server_commands_defs.h \ No newline at end of file diff --git a/docs/daemon-rpc/make-docs.sh b/docs/daemon-rpc/make-docs.sh new file mode 100644 index 00000000000..d19dddf543c --- /dev/null +++ b/docs/daemon-rpc/make-docs.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +if [ "$(basename $(pwd))" != "daemon-rpc" ]; then + echo "Error: you must run this from the docs/daemon-rpc directory" >&2 + exit 1 +fi + +rm -rf api + +docsify init --local api + +rm -f api/README.md + +if [ -n "$NPM_PACKAGES" ]; then + npm_dir="$NPM_PACKAGES/lib/node_modules" +elif [ -n "$NODE_PATH" ]; then + npm_dir="$NODE_PATH" +elif [ -d "$HOME/node_modules" ]; then + npm_dir="$HOME/node_modules" +elif [ -d "/usr/local/lib/node_modules" ]; then + npm_dir="/usr/local/lib/node_modules" +else + echo "Can't determine your node_modules path; set NPM_PACKAGES or NODE_PATH appropriately" >&2 + exit 1 +fi + +cp $npm_dir/docsify/node_modules/prismjs/components/prism-{json,python}.min.js api/vendor + +./rpc-to-markdown.py core_rpc_server_commands_defs.h "$@" + +perl -ni.bak -e ' +BEGIN { $first = 0; } +if (m{^\s*\s*$}) { + if (not $first) { + $first = false; + print qq{ + \n}; + } +} else { + s{.*}{Beldex Daemon RPC}; + s{(name="description" content=)"[^"]*"}{$1"Beldex Daemon RPC endpoint documentation"}; + if (m{^\s*}) { + print qq{ + + \n}; + } + print; +}' api/index.html \ No newline at end of file diff --git a/docs/daemon-rpc/rpc-to-markdown.py b/docs/daemon-rpc/rpc-to-markdown.py new file mode 100644 index 00000000000..89f18c40dcf --- /dev/null +++ b/docs/daemon-rpc/rpc-to-markdown.py @@ -0,0 +1,376 @@ +#!/usr/bin/env python3 + +import sys +import os +import shutil +import re +import fileinput +from enum import Enum, auto +import json +import requests +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument( + "-L", + "--markdown-level", + type=int, + choices=[1, 2, 3, 4], + default=2, + help="Specify a heading level for the top-level endpoints; the default is 2, which means " + "endpoints start in a `## name` section. For example, 3 would start endpoints with `### name` " + "instead.", +) +parser.add_argument("--out", "-o", metavar='DIR', default="api", help="Output directory for generated endpoints") +parser.add_argument("--disable-public", action='store_true', help="disable PUBLIC endpoint detection (and disable marking endpoints as requiring admin)") +parser.add_argument("--disable-no-args", action='store_true', help="disable NO_ARGS enforcement of `Inputs: none`") +parser.add_argument("--dev", action='store_true', help="generate dev mode docs, which include endpoints marked 'Dev-RPC'") +parser.add_argument("--no-sort", "-S", action='store_true', help="disable sorting endpoints by name (use file order)") +parser.add_argument("--no-group", "-G", action='store_true', help="disable grouping endpoints by category") +parser.add_argument("--no-emdash", "-M", action='store_true', help="disable converting ' -- ' to ' — ' (em-dashes)") +parser.add_argument("--rpc", metavar='URL', default="http://public-na.optf.ngo:22023", help="URL to a running beldexd RPC node for live example fetching") +parser.add_argument("filename", nargs="+") +args = parser.parse_args() + +for f in args.filename: + if not os.path.exists(f): + parser.error(f"{f} does not exist!") + + +# We parse the file looking for `///` comment blocks beginning with "RPC: /". +# +# is the RPC endpoint name to use in the documentation (alternative names can be specified +# using "Old names:"; see below). +# +# is the category for grouping endpoints together. +# +# Following comment lines are then a Markdown long description, until we find one or more of: +# +# "Inputs: none." +# "Outputs: none." +# "Inputs:" followed by markdown (typically an unordered list) until the next match from this list. +# "Outputs:" followed by markdown +# "Example input:" followed by a code block (i.e. containing json) +# "Example output:" followed by a code block (i.e. json output) +# "Example-JSON-Fetch" goes and fetches the endpoint (live) with the previous example input as the +# "params" value (or no params if "Inputs: none"). +# "Old names: a, b, c" +# +# subject to the following rules: +# - each section must have exactly one Input; if the type inherits NO_ARGS then it *must* be an +# "Inputs: none". +# - each section must have exactly one Output +# - "Example input:" section must be immediately followed by an "Example output" +# - "Example output:" sections are permitted without a preceding example input only if the endpoint +# takes no inputs. +# - 0 or more example pairs are permitted. +# - Old names is permitted only once, if it occurs at all; the given names will be indicated as +# deprecated, old names for the endpoint. +# +# Immediately following the command we expect to find a not-only-comment line (e.g. `struct +# `) and apply some checks to this: +# - if the line does *not* contain the word `PUBLIC` then we mark the endpoint as requiring admin +# access in its description. +# - if the line contains the word `NO_ARGS` then we double-check that "Inputs: none" was also given +# and error if a more complex Inputs: section was written. + + +hdr = '#' * args.markdown_level +MD_INPUT_HEADER = f"{hdr}# Parameters" +MD_OUTPUT_HEADER = f"{hdr}# Returns" + +MD_EXAMPLES_HEADER = f"{hdr}# Examples" +MD_EXAMPLE_IN_HDR = f"{hdr}## Input" +MD_EXAMPLE_OUT_HDR = f"{hdr}## Output" + +MD_EX_SINGLE_IN_HDR = f"{hdr}# Example Input" +MD_EX_SINGLE_OUT_HDR = f"{hdr}# Example Output" + +MD_NO_INPUT = "This endpoint takes no inputs. _(An optional empty dict/object may be provided, but is not required.)_" +MD_ADMIN = "\n\n> _This endpoint requires admin RPC access; it is not available on public RPC servers._" + +RPC_COMMENT = re.compile(r"^\s*/// ?") +RPC_START = re.compile(r"^RPC:\s*([\w/]+)(.*)$") +DEV_RPC_START = re.compile(r"^Dev-RPC:\s*([\w/]+)(.*)$") +IN_NONE = re.compile(r"^Inputs?: *[nN]one\.?$") +IN_SOME = re.compile(r"^Inputs?:\s*$") +OUT_SOME = re.compile(r"^Outputs?:\s*$") +EXAMPLE_IN = re.compile(r"^Example [iI]nputs?:\s*$") +EXAMPLE_OUT = re.compile(r"^Example [oO]utputs?:\s*$") +EXAMPLE_JSON_FETCH = re.compile(r"^Example-JSON-Fetch\s*$") +OLD_NAMES = re.compile(r"[Oo]ld [nN]ames?:") +PLAIN_NAME = re.compile(r"\w+") +PUBLIC = re.compile(r"\bPUBLIC\b") +NO_ARGS = re.compile(r"\bNO_ARGS\b") + +input = fileinput.input(args.filename) +rpc_name = None + + +def error(msg): + print( + f"\x1b[31;1mERROR\x1b[0m[{input.filename()}:{input.filelineno()}] " + f"while parsing endpoint {rpc_name}:", + file=sys.stderr, + ) + if msg and isinstance(msg, list): + for m in msg: + print(f" - {m}", file=sys.stderr) + else: + print(f" {msg}", file=sys.stderr) + sys.exit(1) + + +class Parsing(Enum): + DESC = auto() + INPUTS = auto() + OUTPUTS = auto() + EX_IN = auto() + EX_OUT = auto() + NONE = auto() + + +cur_file = None +found_some = True + +endpoints = {} + +while True: + line = input.readline() + if not line: + break + + if cur_file is None or cur_file != input.filename(): + if not found_some: + error(f"Found no parseable endpoint descriptions in {cur_file}") + cur_file = input.filename() + found_some = False + + line, removed_comment = re.subn(RPC_COMMENT, "", line, count=1) + if not removed_comment: + continue + + m = re.search(RPC_START, line) + if not m and args.dev: + m = re.search(DEV_RPC_START, line) + if not m: + continue + if m and m[2]: + error(f"found trailing garbage after 'RPC: m[1]': {m[2]}") + if m[1].count('/') != 1: + error(f"Found invalid RPC name: expected 'cat/name', not '{m[1]}'") + + cat, rpc_name = m[1].split('/') + if args.no_group: + cat = '' + description, inputs, outputs = "", "", "" + done_desc = False + no_inputs = False + examples = [] + cur_ex_in = None + old_names = [] + + mode = Parsing.DESC + + while True: + line = input.readline() + line, removed_comment = re.subn(RPC_COMMENT, "", line, count=1) + if not removed_comment: + break + + if re.search(IN_NONE, line): + if inputs: + error("found multiple Inputs:") + inputs, no_inputs, mode = MD_NO_INPUT, True, Parsing.NONE + + elif re.search(IN_SOME, line): + if inputs: + error("found multiple Inputs:") + mode = Parsing.INPUTS + + elif re.search(OUT_SOME, line): + if outputs: + error("found multiple Outputs:") + mode = Parsing.OUTPUTS + + elif re.search(EXAMPLE_IN, line): + if cur_ex_in is not None: + error("found multiple input examples without paired output examples") + cur_ex_in = "" + mode = Parsing.EX_IN + + elif re.search(EXAMPLE_OUT, line): + if not cur_ex_in and not no_inputs: + error( + "found output example without preceding input example (or 'Inputs: none.')" + ) + examples.append([cur_ex_in, ""]) + cur_ex_in = None + mode = Parsing.EX_OUT + + elif re.search(EXAMPLE_JSON_FETCH, line): + if not cur_ex_in and not no_inputs: + error( + "found output example fetch instruction without preceding input (or 'Inputs: none.')" + ) + params = None + if cur_ex_in: + params = cur_ex_in.strip() + if not params.startswith("```json\n"): + error("current example input is not tagged as json for Example-JSON-Fetch") + params = params[8:] + if not params.endswith("\n```"): + error("current example input doesn't look right (expected trailing ```)") + params = params[:-4] + try: + params = json.loads(params) + except Exception as e: + error("failed to parse json example input as json") + + result = requests.post(args.rpc + "/json_rpc", json={"jsonrpc": "2.0", "id": "0", "method": rpc_name, "params": params}).json() + if 'error' in result: + error(f"JSON fetched example returned an error: {result['error']}") + elif 'result' not in result: + error(f"JSON fetched example doesn't contain a \"result\" key: {result}") + ex_out = json.dumps(result["result"], indent=2, sort_keys=True) + + examples.append([cur_ex_in, f"\n```json\n{ex_out}\n```\n"]) + cur_ex_in = None + mode = Parsing.NONE + + elif re.search(OLD_NAMES, line): + old_names = [x.strip() for x in line.split(':', 1)[1].split(',')] + if not old_names or not all(re.fullmatch(PLAIN_NAME, n) for n in old_names): + error(f"found unparseable old names line: {line}") + + elif mode == Parsing.NONE: + if line and not line.isspace(): + error(f"Found unexpected content while looking for a tag: '{line}'") + + elif mode == Parsing.DESC: + description += line + + elif mode == Parsing.INPUTS: + inputs += line + + elif mode == Parsing.OUTPUTS: + outputs += line + + elif mode == Parsing.EX_IN: + cur_ex_in += line + + elif mode == Parsing.EX_OUT: + examples[-1][1] += line + + problems = [] + # We hit the end of the commented section + if not description or inputs.isspace(): + problems.append("endpoint has no description") + if not inputs or inputs.isspace(): + problems.append( + "endpoint has no inputs description; perhaps you need to add 'Inputs: none.'?" + ) + if not outputs or outputs.isspace(): + problems.append("endpoint has no outputs description") + if cur_ex_in is not None: + problems.append( + "endpoint has a trailing example input without a following example output" + ) + if not no_inputs and any(not x[0] or x[0].isspace() for x in examples): + problems.append("found one or more blank input examples") + if any(not x[1] or x[1].isspace() for x in examples): + problems.append("found one or more blank output examples") + + public = args.disable_public or re.search(PUBLIC, line) + if not public: + description += MD_ADMIN + + if old_names: + s = 's' if len(old_names) > 1 else '' + description += f"\n\n> _For backwards compatibility this endpoint is also accessible via the following deprecated endpoint name{s}:_" + for n in old_names: + description += f"\n> - _`{n}`_" + + if not args.disable_no_args: + if re.search(NO_ARGS, line) and not no_inputs: + problems.append("found NO_ARGS, but 'Inputs: none' was specified in description") + + if problems: + error(problems) + + md = f""" +{hdr} `{rpc_name}` + +{description} + +{MD_INPUT_HEADER} + +{inputs} + +{MD_OUTPUT_HEADER} + +{outputs} +""" + + if examples: + if len(examples) > 1: + md += f"\n\n{MD_EXAMPLES_HEADER}\n\n" + for ex in examples: + if ex[0] is not None: + md += f""" +{MD_EXAMPLE_IN_HDR} + +{ex[0]} +""" + md += f""" +{MD_EXAMPLE_OUT_HDR} + +{ex[1]} +""" + + else: + if examples[0][0] is not None: + md += f"\n\n{MD_EX_SINGLE_IN_HDR}\n\n{examples[0][0]}" + md += f"\n\n{MD_EX_SINGLE_OUT_HDR}\n\n{examples[0][1]}" + + if not args.no_emdash: + md = md.replace(" -- ", " — ") + + if cat in endpoints: + endpoints[cat].append((rpc_name, md)) + else: + endpoints[cat] = [(rpc_name, md)] + +if not endpoints: + error(f"Found no parseable endpoint descriptions in {cur_file}") + +if not args.no_sort: + for v in endpoints.values(): + v.sort(key=lambda x: x[0]) + +os.makedirs(args.out, exist_ok=True) + +static_path = os.path.dirname(os.path.realpath(__file__)) + '/static' + +for f in ('index.md', 'sidebar.md'): + shutil.copy(f"{static_path}/{f}", f"{args.out}/{f}") + print(f"Copied static/{f} => {args.out}/{f}") + +preamble_prefix = static_path + '/preamble-' + +for cat, eps in endpoints.items(): + out = f"{args.out}/{cat}.md" + with open(out, "w") as f: + preamble = f"{preamble_prefix}{cat}.md" + if os.path.isfile(preamble): + with open(preamble, "r") as fp: + for line in fp: + f.write(line) + f.write("\n\n") + else: + print(f"Warning: {preamble} doesn't exist, writing generic preamble for {cat}", file=sys.stderr) + f.write(f"# {cat} endpoints\n\n") + + for _, md in eps: + f.write(md) + print(f"Wrote {out}") \ No newline at end of file diff --git a/docs/daemon-rpc/static/index.md b/docs/daemon-rpc/static/index.md new file mode 100644 index 00000000000..9d407932c5c --- /dev/null +++ b/docs/daemon-rpc/static/index.md @@ -0,0 +1,62 @@ +# Beldex Daemon RPC Endpoints + +These pages describe the available RPC endpoints available from a running `beldexd` node. These +endpoints are used for querying blockchain data, submitting transactions, obtaining master node +information, and controlling the running beldexd. + +Many of the endpoints described here are publicly accessible; those that are not are marked +accordingly and can only be used by a local administrator. + +## HTTP JSON access + +Accessing an endpoint using HTTP and JSON can be done by making a JSON RPC request. For example, to +call the `get_info` endpoint on a master node with an HTTP RPC listener on `localhost:22023` (the +default) using JSON RPC you would make a POST request to `http://localhost:22023/json_rpc` with a +JSON body: + +```json +{ + "jsonrpc": "2.0", + "id": "0", + "method": "get_info", + "params": { "foo": 123 } +} +``` + +The pages here describe only the values of the inner "params" value; the outer boilerplate is the +same for all requests. For methods that do not require any input parameters the `"params"` field +may be omitted entirely. + +### Command-line usage + +For example, to make a request using `curl` to the public RPC node `public-na.optf.ngo`, using the +command-line with `jq` to "prettify" the json response: + +``` +curl -sSX POST http://public-na.optf.ngo:22023/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' | jq . +``` + +## OxenMQ RPC access + +All beldexd endpoints are also available via OxenMQ at either the `rpc.ENDPOINT` or `admin.ENDPOINT` +name (the latter if the endpoint is marked admin-only), with an optional additional data part +containing a JSON or bencoded request. + +### Command-line usage: + +The beldex-core source code contains a script (`utils/lmq-rpc.py`) that can invoke such a request: + +``` +./utils/lmq-rpc.py ipc://$HOME/.beldex/beldexd.sock rpc.get_info | jq . +``` +to query a local beldexd, or: +``` +./utils/lmq-rpc.py tcp://public-na.optf.ngo:22027 02ae9aa1bdface3ce32488874d16671b04d44f611d1076033c92f3379f221161 rpc.get_info | jq . +``` +or +``` +./utils/lmq-rpc.py tcp://public-na.optf.ngo:22029 rpc.get_info '{}' | jq . +``` +to query a public node. (The first version uses an encrypted public connection given the remote +beldexd's X25519 pubkey; the second version uses an unencrypted public connection). \ No newline at end of file diff --git a/docs/daemon-rpc/static/preamble-blockchain.md b/docs/daemon-rpc/static/preamble-blockchain.md new file mode 100644 index 00000000000..9eeff202297 --- /dev/null +++ b/docs/daemon-rpc/static/preamble-blockchain.md @@ -0,0 +1,4 @@ +# Blockchain Endpoints + +These endpoints are used for querying various data from the blockchain; unless otherwise indicated, +these are accessible using a public RPC node. \ No newline at end of file diff --git a/docs/daemon-rpc/static/preamble-bns.md b/docs/daemon-rpc/static/preamble-bns.md new file mode 100644 index 00000000000..19f53d1fbb8 --- /dev/null +++ b/docs/daemon-rpc/static/preamble-bns.md @@ -0,0 +1,3 @@ +# Beldex Name System Query Endpoints + +This endpoints allow querying Beldex Name System records. \ No newline at end of file diff --git a/docs/daemon-rpc/static/preamble-daemon.md b/docs/daemon-rpc/static/preamble-daemon.md new file mode 100644 index 00000000000..f99abea3fa1 --- /dev/null +++ b/docs/daemon-rpc/static/preamble-daemon.md @@ -0,0 +1,4 @@ +# Daemon Administration Endpoints + +These endpoints allow for controlling and querying privileged information about the running beldexd. +They require administrator access. \ No newline at end of file diff --git a/docs/daemon-rpc/static/preamble-master_node.md b/docs/daemon-rpc/static/preamble-master_node.md new file mode 100644 index 00000000000..504c8ce21b9 --- /dev/null +++ b/docs/daemon-rpc/static/preamble-master_node.md @@ -0,0 +1,4 @@ +# Master Node Administration Endpoints + +These endpoints allow administering and controlling an active master node. They generally require +the queried beldexd is running as a master node, and require administrator access. \ No newline at end of file diff --git a/docs/daemon-rpc/static/preamble-network.md b/docs/daemon-rpc/static/preamble-network.md new file mode 100644 index 00000000000..69cba179f8d --- /dev/null +++ b/docs/daemon-rpc/static/preamble-network.md @@ -0,0 +1,4 @@ +# Network Information Endpoints + +These endpoints are used for querying various general information about the network that are not +(directly) blockchain data. \ No newline at end of file diff --git a/docs/daemon-rpc/static/sidebar.md b/docs/daemon-rpc/static/sidebar.md new file mode 100644 index 00000000000..5df6f1a2598 --- /dev/null +++ b/docs/daemon-rpc/static/sidebar.md @@ -0,0 +1,5 @@ +- [Network information](network.md) +- [Blockchain data](blockchain.md) +- [Beldex Name System](bns.md) +- [Daemon administration](daemon.md) +- [Master Node administration](master_node.md) \ No newline at end of file diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index a1f33241456..83c90e843ab 100755 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -35,8 +35,8 @@ if(NOT STATIC AND NOT BUILD_STATIC_DEPS) find_package(PkgConfig REQUIRED) - pkg_check_modules(OXENC liboxenc>=1.0.1 IMPORTED_TARGET) - pkg_check_modules(OXENMQ liboxenmq>=1.2.3 IMPORTED_TARGET) + pkg_check_modules(OXENC liboxenc>=1.0.10 IMPORTED_TARGET) + pkg_check_modules(OXENMQ liboxenmq>=1.2.7 IMPORTED_TARGET) endif() if(NOT OXENC_FOUND) @@ -63,6 +63,11 @@ add_subdirectory(db_drivers) add_subdirectory(easylogging++ easyloggingpp) add_subdirectory(randomx EXCLUDE_FROM_ALL) add_subdirectory(fmt) +add_subdirectory(date EXCLUDE_FROM_ALL) + +set(JSON_MultipleHeaders ON CACHE BOOL "") # Allows multi-header nlohmann use +add_subdirectory(nlohmann-json) + # uSockets doesn't really have a proper build system (just a very simple Makefile) so build it # ourselves. if (NOT CMAKE_VERSION VERSION_LESS 3.12) @@ -139,4 +144,6 @@ if(cpr_cmake_head MATCHES "project\\(cpr VERSION ([0-9]+)\.([0-9]+)\.([0-9]+) LA target_include_directories(cpr PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/cpr_generated_includes") else() message(FATAL_ERROR "Could not identify cpr submodule version!") -endif() \ No newline at end of file +endif() + +add_subdirectory(Catch2) \ No newline at end of file diff --git a/external/Catch2 b/external/Catch2 new file mode 160000 index 00000000000..4ff57aba429 --- /dev/null +++ b/external/Catch2 @@ -0,0 +1 @@ +Subproject commit 4ff57aba42938478f8b626c0862409f6eb7548f0 diff --git a/external/date b/external/date new file mode 160000 index 00000000000..5bdb7e6f31f --- /dev/null +++ b/external/date @@ -0,0 +1 @@ +Subproject commit 5bdb7e6f31fac909c090a46dbd9fea27b6e609a4 diff --git a/external/loki-mq b/external/loki-mq index b8bb10eac5b..923a0a80bc5 160000 --- a/external/loki-mq +++ b/external/loki-mq @@ -1 +1 @@ -Subproject commit b8bb10eac5b45846f25f30fe6f26fd2bc671f9c2 +Subproject commit 923a0a80bc533e0d63ae32e2f9f5d4f7f53a258e diff --git a/external/nlohmann-json b/external/nlohmann-json new file mode 160000 index 00000000000..55f93686c01 --- /dev/null +++ b/external/nlohmann-json @@ -0,0 +1 @@ +Subproject commit 55f93686c01528224f448c19128836e7df245f72 diff --git a/external/oxen-encoding b/external/oxen-encoding index 085bbff7741..9dd7fb39d49 160000 --- a/external/oxen-encoding +++ b/external/oxen-encoding @@ -1 +1 @@ -Subproject commit 085bbff774106dfd41c4848ef03e4de259accf4d +Subproject commit 9dd7fb39d49666c20f62c6b63399a711fe3ac7f7 diff --git a/external/uWebSockets b/external/uWebSockets index eea4b7e0f6e..c445faa3812 160000 --- a/external/uWebSockets +++ b/external/uWebSockets @@ -1 +1 @@ -Subproject commit eea4b7e0f6e907646d34909e32f415c2a7dea385 +Subproject commit c445faa38125bf782eed3fec97f83b4733c7fb91 diff --git a/src/beldex_economy.h b/src/beldex_economy.h index 526a1bd9c26..e666592c515 100755 --- a/src/beldex_economy.h +++ b/src/beldex_economy.h @@ -1,32 +1,54 @@ #pragma once -#include +#include "cryptonote_config.h" -constexpr uint64_t COIN = (uint64_t)1000000000; // 1 BELDEX = pow(10, 9) -constexpr uint64_t MONEY_SUPPLY = ((uint64_t)(-1)); // MONEY_SUPPLY - total number coins to be generated -constexpr uint64_t EMISSION_LINEAR_BASE = ((uint64_t)(1) << 58); -constexpr uint64_t EMISSION_SUPPLY_MULTIPLIER = 19; -constexpr uint64_t EMISSION_SUPPLY_DIVISOR = 10; -constexpr uint64_t EMISSION_DIVISOR = 2000000; +namespace beldex { -constexpr uint64_t MODIFIED_STAKING_REQUIREMENT_HEIGHT = 56500; +inline constexpr uint64_t COIN = (uint64_t)1000000000; // 1 BELDEX = pow(10, 9) +inline constexpr size_t DISPLAY_DECIMAL_POINT = 9; +inline constexpr uint64_t MONEY_SUPPLY = ((uint64_t)(-1)); // MONEY_SUPPLY - total number coins to be generated +inline constexpr uint64_t EMISSION_LINEAR_BASE = ((uint64_t)(1) << 58); +inline constexpr uint64_t EMISSION_SUPPLY_MULTIPLIER = 19; +inline constexpr uint64_t EMISSION_SUPPLY_DIVISOR = 10; +inline constexpr uint64_t EMISSION_DIVISOR = 2000000; + +inline constexpr uint64_t MODIFIED_STAKING_REQUIREMENT_HEIGHT = 56500; // HF15 money supply parameters: -constexpr uint64_t BLOCK_REWARD_HF16 = 2 * COIN; -constexpr uint64_t BLOCK_REWARD_HF17_POS = 10 *COIN; -constexpr uint64_t MINER_REWARD_HF16 = BLOCK_REWARD_HF16 * 10 / 100; // Only until HF16 -constexpr uint64_t MN_REWARD_HF16 = BLOCK_REWARD_HF16 * 90 / 100; -constexpr uint64_t MN_REWARD_HF17_POS = BLOCK_REWARD_HF17_POS * 62.5 / 100; // After HF17 MN_REWARD changed about 6.25 BDX for each Block +inline constexpr uint64_t BLOCK_REWARD_HF16 = 2 * COIN; +inline constexpr uint64_t BLOCK_REWARD_HF17_POS = 10 *COIN; +inline constexpr uint64_t MINER_REWARD_HF16 = BLOCK_REWARD_HF16 * 10 / 100; // Only until HF16 +inline constexpr uint64_t MN_REWARD_HF16 = BLOCK_REWARD_HF16 * 90 / 100; +inline constexpr uint64_t MN_REWARD_HF17_POS = BLOCK_REWARD_HF17_POS * 62.5 / 100; // After HF17 MN_REWARD changed about 6.25 BDX for each Block (62.5%) // HF16+ money supply parameters: same as HF16 except the miner fee goes away and is redirected to // LF to be used exclusively for Beldex Chainflip liquidity seeding and incentives. See // https://github.com/beldex-project/beldex-improvement-proposals/issues/24 for more details. This ends // after 6 months. -constexpr uint64_t BLOCK_REWARD_HF17 = BLOCK_REWARD_HF16; -constexpr uint64_t FOUNDATION_REWARD_HF17 = BLOCK_REWARD_HF17_POS * 37.5 /100; //governance reward 3.75 BDX after HF17 +inline constexpr uint64_t BLOCK_REWARD_HF17 = BLOCK_REWARD_HF16; +inline constexpr uint64_t FOUNDATION_REWARD_HF17 = BLOCK_REWARD_HF17_POS * 37.5 /100; //governance reward 3.75 BDX after HF17 (37.5%) static_assert(MINER_REWARD_HF16 + MN_REWARD_HF16 == BLOCK_REWARD_HF16); static_assert(MN_REWARD_HF17_POS + FOUNDATION_REWARD_HF17 == BLOCK_REWARD_HF17_POS); +// ------------------------------------------------------------------------------------------------- +// +// Master Nodes +// +// ------------------------------------------------------------------------------------------------- + +// Fixed staking requirement see +// master_node_rules.cpp): +inline constexpr uint64_t STAKING_REQUIREMENT = 10'000 * COIN; +// testnet/devnet/fakenet have always had a fixed 10000 BDX staking requirement: +inline constexpr uint64_t STAKING_REQUIREMENT_TESTNET = 10'000 * COIN; + +// Max contributors: +inline constexpr size_t MAX_NUMBER_OF_CONTRIBUTORS = 4; + +// // Required operator contribution is 1/4 of the staking requirement +// inline constexpr uint64_t MINIMUM_OPERATOR_DIVISOR = 4; + + // ------------------------------------------------------------------------------------------------- // // Flash @@ -47,6 +69,8 @@ static_assert(FLASH_MINER_TX_FEE_PERCENT >= 100, "flash miner fee cannot be smal static_assert(FLASH_BURN_FIXED >= 0, "fixed flash burn amount cannot be negative"); static_assert(FLASH_BURN_TX_FEE_PERCENT_OLD >= 0, "flash burn tx percent cannot be negative"); +} // namespace beldex + // ------------------------------------------------------------------------------------------------- // // BNS @@ -78,18 +102,20 @@ enum struct mapping_years : uint16_t update_record_internal, }; +constexpr bool is_belnet_type(mapping_type t) { return t >= mapping_type::belnet && t <= mapping_type::belnet_10years; } + constexpr bool is_renewal_type(mapping_years y) { return y >= mapping_years::bns_1year && y <= mapping_years::bns_10years; } // How many days we add per "year" of BNS belnet registration. We slightly extend this to the 368 // days per registration "year" to allow for some blockchain time drift + leap years. constexpr uint64_t REGISTRATION_YEAR_DAYS = 368; -constexpr uint64_t burn_needed(uint8_t hf_version, mapping_years map_years) +constexpr uint64_t burn_needed(cryptonote::hf hf_version, mapping_years map_years) { uint64_t result = 0; - const uint64_t basic_fee = (hf_version >= 18 ? 500 * COIN : // cryptonote::network_version_18_bns -- but don't want to add cryptonote_config.h include - 15 * COIN // cryptonote::network_version_17_POS + const uint64_t basic_fee = (hf_version >= cryptonote::hf::hf18_bns ? 500 * beldex::COIN : // cryptonote::hf::hf18_bns -- but don't want to add cryptonote_config.h include + 15 * beldex::COIN // cryptonote::hf::hf17_POS ); switch (map_years) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index f8bc95eefb9..0f712e48f37 100755 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -27,17 +27,18 @@ // 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/string_util.h" #include "cryptonote_basic/hardfork.h" #include "cryptonote_core/master_node_rules.h" #include "checkpoints/checkpoints.h" #include "epee/string_tools.h" #include "blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" -#include "epee/profile_tools.h" #include "ringct/rctOps.h" #include "common/hex.h" #include "lmdb/db_lmdb.h" +#include #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "blockchain.db" @@ -185,16 +186,15 @@ uint64_t BlockchainDB::add_block( const std::pair& blck if (blk.tx_hashes.size() != txs.size()) throw std::runtime_error("Inconsistent tx/hashes sizes"); - TIME_MEASURE_START(time1); + auto started = std::chrono::steady_clock::now(); crypto::hash blk_hash = get_block_hash(blk); - TIME_MEASURE_FINISH(time1); - time_blk_hash += time1; + time_blk_hash += std::chrono::steady_clock::now() - started; uint64_t prev_height = height(); // call out to add the transactions - time1 = epee::misc_utils::get_tick_count(); + started = std::chrono::steady_clock::now(); uint64_t num_rct_outs = 0; add_transaction(blk_hash, std::make_pair(blk.miner_tx, tx_to_blob(blk.miner_tx))); @@ -214,14 +214,12 @@ uint64_t BlockchainDB::add_block( const std::pair& blck } ++tx_i; } - TIME_MEASURE_FINISH(time1); - time_add_transaction += time1; + time_add_transaction += std::chrono::steady_clock::now() - started; // call out to subclass implementation to add the block & metadata - time1 = epee::misc_utils::get_tick_count(); + started = std::chrono::steady_clock::now(); add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); - TIME_MEASURE_FINISH(time1); - time_add_block1 += time1; + time_add_block1 += std::chrono::steady_clock::now() - started; ++num_calls; @@ -321,11 +319,11 @@ transaction BlockchainDB::get_pruned_tx(const crypto::hash& h) const void BlockchainDB::reset_stats() { num_calls = 0; - time_blk_hash = 0; - time_tx_exists = 0; - time_add_block1 = 0; - time_add_transaction = 0; - time_commit1 = 0; + time_blk_hash = 0ns; + time_tx_exists = 0ns; + time_add_block1 = 0ns; + time_add_transaction = 0ns; + time_commit1 = 0ns; } void BlockchainDB::show_stats() @@ -333,11 +331,11 @@ void BlockchainDB::show_stats() LOG_PRINT_L1("\n" << "*********************************\n" << "num_calls: " << num_calls << "\n" - << "time_blk_hash: " << time_blk_hash << "ms\n" - << "time_tx_exists: " << time_tx_exists << "ms\n" - << "time_add_block1: " << time_add_block1 << "ms\n" - << "time_add_transaction: " << time_add_transaction << "ms\n" - << "time_commit1: " << time_commit1 << "ms\n" + << "time_blk_hash: " << tools::friendly_duration(time_blk_hash) << "\n" + << "time_tx_exists: " << tools::friendly_duration(time_tx_exists) << "\n" + << "time_add_block1: " << tools::friendly_duration(time_add_block1) << "\n" + << "time_add_transaction: " << tools::friendly_duration(time_add_transaction) << "\n" + << "time_commit1: " << tools::friendly_duration(time_commit1) << "\n" << "*********************************\n" ); } @@ -432,8 +430,8 @@ void BlockchainDB::fill_timestamps_and_difficulties_for_pow(cryptonote::network_ return; uint64_t const top_block_height = chain_height - 1; - bool const before_hf16 = !is_hard_fork_at_least(nettype, network_version_17_POS, chain_height); - uint64_t const block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16); + bool const before_hf16 = !is_hard_fork_at_least(nettype, hf::hf17_POS, chain_height); + uint64_t const block_count = old::DIFFICULTY_BLOCKS_COUNT(before_hf16); timestamps.reserve(block_count); difficulties.reserve(block_count); diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 6dad31078ab..d0d50e6957a 100755 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -524,9 +524,9 @@ class BlockchainDB void remove_transaction(const crypto::hash& tx_hash); uint64_t num_calls = 0; //!< a performance metric - uint64_t time_blk_hash = 0; //!< a performance metric - uint64_t time_add_block1 = 0; //!< a performance metric - uint64_t time_add_transaction = 0; //!< a performance metric + std::chrono::nanoseconds time_blk_hash = 0ns; //!< a performance metric + std::chrono::nanoseconds time_add_block1 = 0ns; //!< a performance metric + std::chrono::nanoseconds time_add_transaction = 0ns; //!< a performance metric protected: @@ -544,8 +544,8 @@ class BlockchainDB */ void add_transaction(const crypto::hash& blk_hash, const std::pair& tx, const crypto::hash* tx_hash_ptr = NULL, const crypto::hash* tx_prunable_hash_ptr = NULL); - mutable uint64_t time_tx_exists = 0; //!< a performance metric - uint64_t time_commit1 = 0; //!< a performance metric + mutable std::chrono::nanoseconds time_tx_exists = 0ns; //!< a performance metric + std::chrono::nanoseconds time_commit1 = 0ns; //!< a performance metric bool m_auto_remove_logs = true; //!< whether or not to automatically remove old logs public: diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 25d3031b185..04bf274019d 100755 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -30,12 +30,14 @@ #include #include -#include +#include +#include #include #include #include #include +#include "common/string_util.h" #include "cryptonote_basic/hardfork.h" #include "epee/string_tools.h" #include "common/file.h" @@ -44,7 +46,6 @@ #include "common/median.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" -#include "epee/profile_tools.h" #include "ringct/rctOps.h" #include "checkpoints/checkpoints.h" @@ -57,7 +58,6 @@ using namespace crypto; -using namespace boost::endian; enum struct lmdb_version { @@ -894,12 +894,12 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l bi.bi_diff = cumulative_difficulty; bi.bi_hash = blk_hash; bi.bi_cum_rct = num_rct_outs; - if (blk.major_version >= 4 && m_height > 0) + if (m_height > 0) { uint64_t last_height = m_height-1; MDB_val_set(h, last_height); if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH))) - throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str())); + throw1(BLOCK_DNE(lmdb_error("Failed to get parent block info: ", result).c_str())); const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data; bi.bi_cum_rct += bi_prev->bi_cum_rct; } @@ -1417,11 +1417,11 @@ void BlockchainLMDB::open(const fs::path& filename, cryptonote::network_type net // check for existing LMDB files in base directory auto old_files = filename.parent_path(); - if (fs::exists(old_files / CRYPTONOTE_BLOCKCHAINDATA_FILENAME) - || fs::exists(old_files / CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME)) + if (fs::exists(old_files / BLOCKCHAINDATA_FILENAME) + || fs::exists(old_files / BLOCKCHAINDATA_LOCK_FILENAME)) { LOG_PRINT_L0("Found existing LMDB files in " << old_files.u8string()); - LOG_PRINT_L0("Move " << CRYPTONOTE_BLOCKCHAINDATA_FILENAME << " and/or " << CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME << " to " << filename << ", or delete them, and then restart"); + LOG_PRINT_L0("Move " << BLOCKCHAINDATA_FILENAME << " and/or " << BLOCKCHAINDATA_LOCK_FILENAME << " to " << filename << ", or delete them, and then restart"); throw DB_ERROR("Database could not be opened"); } @@ -1729,14 +1729,14 @@ std::vector BlockchainLMDB::get_filenames() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); std::vector paths; - paths.push_back(m_folder / CRYPTONOTE_BLOCKCHAINDATA_FILENAME); - paths.push_back(m_folder / CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME); + paths.push_back(m_folder / BLOCKCHAINDATA_FILENAME); + paths.push_back(m_folder / BLOCKCHAINDATA_LOCK_FILENAME); return paths; } bool BlockchainLMDB::remove_data_file(const fs::path& folder) const { - auto filename = folder / CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + auto filename = folder / BLOCKCHAINDATA_FILENAME; try { fs::remove(filename); @@ -2052,14 +2052,14 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); const uint32_t log_stripes = tools::get_pruning_log_stripes(pruning_seed); - if (log_stripes && log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES) + if (log_stripes && log_stripes != PRUNING_LOG_STRIPES) throw0(DB_ERROR("Pruning seed not in range")); pruning_seed = tools::get_pruning_stripe(pruning_seed); - if (pruning_seed > (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES)) + if (pruning_seed > (1ul << PRUNING_LOG_STRIPES)) throw0(DB_ERROR("Pruning seed not in range")); check_open(); - TIME_MEASURE_START(t); + auto t = std::chrono::steady_clock::now(); size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0, commit_counter = 0; uint64_t n_bytes = 0; @@ -2084,13 +2084,12 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) if (mode != prune_mode_prune) { txn.abort(); - TIME_MEASURE_FINISH(t); MDEBUG("Pruning not enabled, nothing to do"); return true; } if (pruning_seed == 0) pruning_seed = tools::get_random_stripe(); - pruning_seed = tools::make_pruning_seed(pruning_seed, CRYPTONOTE_PRUNING_LOG_STRIPES); + pruning_seed = tools::make_pruning_seed(pruning_seed, PRUNING_LOG_STRIPES); v.mv_data = &pruning_seed; v.mv_size = sizeof(pruning_seed); result = mdb_put(txn, m_properties, &k, &v, 0); @@ -2108,9 +2107,9 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) pruning_seed = tools::get_pruning_stripe(data); if (tools::get_pruning_stripe(data) != pruning_seed) throw0(DB_ERROR("Blockchain already pruned with different seed")); - if (tools::get_pruning_log_stripes(data) != CRYPTONOTE_PRUNING_LOG_STRIPES) + if (tools::get_pruning_log_stripes(data) != PRUNING_LOG_STRIPES) throw0(DB_ERROR("Blockchain already pruned with different base")); - pruning_seed = tools::make_pruning_seed(pruning_seed, CRYPTONOTE_PRUNING_LOG_STRIPES); + pruning_seed = tools::make_pruning_seed(pruning_seed, PRUNING_LOG_STRIPES); prune_tip_table = (mode == prune_mode_update); } else @@ -2149,7 +2148,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) uint64_t block_height; memcpy(&block_height, v.mv_data, sizeof(block_height)); - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS < blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS < blockchain_height) { ++n_total_records; if (!tools::has_unpruned_block(block_height, blockchain_height, pruning_seed) && !is_v1_tx(c_txs_pruned, &k)) @@ -2217,7 +2216,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) txindex ti; memcpy(&ti, v.mv_data, sizeof(ti)); const uint64_t block_height = ti.data.block_id; - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) { MDB_val_set(kp, ti.data.tx_id); MDB_val_set(vp, block_height); @@ -2322,10 +2321,8 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) txn.commit(); - TIME_MEASURE_FINISH(t); - MINFO((mode == prune_mode_check ? "Checked" : "Pruned") << " blockchain in " << - t << " ms: " << (n_bytes/1024.0f/1024.0f) << " MB (" << db_bytes/1024.0f/1024.0f << " MB) pruned in " << + tools::friendly_duration(std::chrono::steady_clock::now() - t) << ": " << (n_bytes/1024.0f/1024.0f) << " MB (" << db_bytes/1024.0f/1024.0f << " MB) pruned in " << n_pruned_records << " records (" << pages0 - pages1 << "/" << pages0 << " " << db_stats.ms_psize << " byte pages), " << n_prunable_records << "/" << n_total_records << " pruned records"); return true; @@ -2413,14 +2410,14 @@ static_assert(sizeof(blob_header) == 8, "blob_header layout is unexpected, possi static blob_header write_little_endian_blob_header(blob_type type, uint32_t size) { blob_header result = {type, size}; - native_to_little_inplace(result.size); + oxenc::host_to_little_inplace(result.size); return result; } -static blob_header native_endian_blob_header(const blob_header *header) +static blob_header host_endian_blob_header(const blob_header *header) { blob_header result = {header->type, header->size}; - little_to_native_inplace(result.size); + oxenc::little_to_host_inplace(result.size); return result; } @@ -2439,7 +2436,7 @@ static bool read_alt_block_data_from_mdb_val(MDB_val const v, alt_block_data_t * src = reinterpret_cast(alt_data + 1); while (src < end) { - blob_header header = native_endian_blob_header(reinterpret_cast(src)); + blob_header header = host_endian_blob_header(reinterpret_cast(src)); src += sizeof(header); if (header.type == blob_type::block) { @@ -3049,15 +3046,14 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const MDB_val_set(key, h); bool tx_found = false; - TIME_MEASURE_START(time1); + auto time1 = std::chrono::steady_clock::now(); auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &key, MDB_GET_BOTH); if (get_result == 0) tx_found = true; else if (get_result != MDB_NOTFOUND) throw0(DB_ERROR(lmdb_error(std::string("DB error attempting to fetch transaction index from hash ") + tools::type_to_hex(h) + ": ", get_result).c_str())); - TIME_MEASURE_FINISH(time1); - time_tx_exists += time1; + time_tx_exists += std::chrono::steady_clock::now() - time1; if (! tx_found) { @@ -3077,10 +3073,9 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_id) const MDB_val_set(v, h); - TIME_MEASURE_START(time1); + auto time1 = std::chrono::steady_clock::now(); auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); - TIME_MEASURE_FINISH(time1); - time_tx_exists += time1; + time_tx_exists += std::chrono::steady_clock::now() - time1; if (!get_result) { txindex *tip = (txindex *)v.mv_data; tx_id = tip->data.tx_id; @@ -3726,10 +3721,9 @@ void BlockchainLMDB::batch_commit() check_open(); - TIME_MEASURE_START(time1); + auto time1 = std::chrono::steady_clock::now(); m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; + time_commit1 += std::chrono::steady_clock::now() - time1; m_write_txn = nullptr; delete m_write_batch_txn; @@ -3759,12 +3753,11 @@ void BlockchainLMDB::batch_stop() throw1(DB_ERROR("batch transaction owned by other thread")); check_open(); - TIME_MEASURE_START(time1); + auto time1 = std::chrono::steady_clock::now(); try { m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; + time_commit1 += std::chrono::steady_clock::now() - time1; cleanup_batch(); } catch (const std::exception &e) @@ -3899,16 +3892,15 @@ void BlockchainLMDB::block_wtxn_stop() throw0(DB_ERROR_TXN_START((std::string("Attempted to stop write txn from the wrong thread in ")+__FUNCTION__).c_str())); { if (! m_batch_active) - { - TIME_MEASURE_START(time1); + { + auto time1 = std::chrono::steady_clock::now(); m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; + time_commit1 += std::chrono::steady_clock::now() - time1; delete m_write_txn; m_write_txn = nullptr; memset(&m_wcursors, 0, sizeof(m_wcursors)); - } + } } } @@ -3977,8 +3969,8 @@ static bool convert_checkpoint_into_buffer(checkpoint_t const &checkpoint, check header.block_hash = checkpoint.block_hash; header.num_signatures = checkpoint.signatures.size(); - native_to_little_inplace(header.height); - native_to_little_inplace(header.num_signatures); + oxenc::host_to_little_inplace(header.height); + oxenc::host_to_little_inplace(header.num_signatures); size_t const bytes_for_signatures = sizeof(*checkpoint.signatures.data()) * checkpoint.signatures.size(); result.len = sizeof(header) + bytes_for_signatures; @@ -4061,8 +4053,8 @@ static checkpoint_t convert_mdb_val_to_checkpoint(MDB_val const value) auto const *signatures = reinterpret_cast(static_cast(value.mv_data) + sizeof(*header)); - auto num_sigs = little_to_native(header->num_signatures); - result.height = little_to_native(header->height); + auto num_sigs = oxenc::little_to_host(header->num_signatures); + result.height = oxenc::little_to_host(header->height); result.type = (num_sigs > 0) ? checkpoint_type::master_node : checkpoint_type::hardcoded; result.block_hash = header->block_hash; result.signatures.insert(result.signatures.end(), signatures, signatures + num_sigs); @@ -4243,7 +4235,7 @@ void BlockchainLMDB::get_output_key(const epee::span &amounts, c throw0(DB_ERROR("Invalid sizes of amounts and offets")); LOG_PRINT_L3("BlockchainLMDB::" << __func__); - TIME_MEASURE_START(db3); + auto db3 = std::chrono::steady_clock::now(); check_open(); outputs.clear(); outputs.reserve(offsets.size()); @@ -4286,8 +4278,7 @@ void BlockchainLMDB::get_output_key(const epee::span &amounts, c } } - TIME_MEASURE_FINISH(db3); - LOG_PRINT_L3("db3: " << db3); + LOG_PRINT_L3("db3: " << tools::friendly_duration(std::chrono::steady_clock::now() - db3)); } void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const @@ -4317,13 +4308,12 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std:: tx_indices.push_back(okp->output_id); } - TIME_MEASURE_START(db3); + auto db3 = std::chrono::steady_clock::now(); if(tx_indices.size() > 0) { get_output_tx_and_index_from_global(tx_indices, indices); } - TIME_MEASURE_FINISH(db3); - LOG_PRINT_L3("db3: " << db3); + LOG_PRINT_L3("db3: " << tools::friendly_duration(std::chrono::steady_clock::now() - db3)); } std::map> BlockchainLMDB::get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count,cryptonote::network_type nettype) const @@ -4389,10 +4379,10 @@ std::map> BlockchainLMDB::get while (num_elems > 0) { const tx_out_index toi = get_output_tx_and_index(amount, num_elems - 1); const uint64_t height = get_tx_block_height(toi.first); - const uint8_t hf_version = cryptonote::get_network_version(nettype, height); - LOG_PRINT_L2("TX hf_version:" << hf_version); + const auto hf_version = cryptonote::get_network_version(nettype, height); + LOG_PRINT_L2("TX hf_version:" << static_cast(hf_version)); - if ((height + (hf_version>=cryptonote::network_version_17_POS?CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17:CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE)) <= blockchain_height) + if ((height + (hf_version >= cryptonote::hf::hf17_POS ? DEFAULT_TX_SPENDABLE_AGE_V17 : cryptonote::old::DEFAULT_TX_SPENDABLE_AGE)) <= blockchain_height) break; --num_elems; } @@ -4656,7 +4646,7 @@ bool BlockchainLMDB::is_read_only() const uint64_t BlockchainLMDB::get_database_size() const { - return fs::file_size(m_folder / CRYPTONOTE_BLOCKCHAINDATA_FILENAME); + return fs::file_size(m_folder / BLOCKCHAINDATA_FILENAME); } void BlockchainLMDB::fixup(cryptonote::network_type nettype) @@ -4699,7 +4689,7 @@ void BlockchainLMDB::fixup(cryptonote::network_type nettype) add_timestamp_and_difficulty(nettype, curr_chain_height, timestamps, difficulties, curr_timestamp, curr_cumulative_diff); // NOTE: Calculate next block difficulty - if (is_hard_fork_at_least(nettype, cryptonote::network_version_17_POS, curr_height) + if (is_hard_fork_at_least(nettype, hf::hf17_POS, curr_height) && block_header_has_POS_components(get_block_header_from_height(curr_height))) { diff = POS_FIXED_DIFFICULTY; // TARGET_BLOCK_TIME @@ -4708,7 +4698,7 @@ void BlockchainLMDB::fixup(cryptonote::network_type nettype) { diff = next_difficulty_v2(timestamps, difficulties, - tools::to_seconds(TARGET_BLOCK_TIME_OLD), //use OLD BLOCK_TIME + tools::to_seconds(old::TARGET_BLOCK_TIME_12), //use OLD BLOCK_TIME difficulty_mode(nettype, curr_height + 1)); } } @@ -5694,7 +5684,7 @@ void BlockchainLMDB::migrate_3_4() throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); const uint64_t blockchain_height = db_stats.ms_entries; - boost::circular_buffer long_term_block_weights(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE); + boost::circular_buffer long_term_block_weights(LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE); /* the block_info table name is the same but the old version and new version * have incompatible data. Create a new table. We want the name to be similar @@ -5760,8 +5750,8 @@ void BlockchainLMDB::migrate_3_4() throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); if (vb.mv_size == 0) throw0(DB_ERROR("Invalid data from m_blocks")); - const uint8_t block_major_version = *((const uint8_t*)vb.mv_data); - if (block_major_version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + const hf block_major_version {*((const uint8_t*)vb.mv_data)}; + if (block_major_version >= feature::LONG_TERM_BLOCK_WEIGHT) past_long_term_weight = true; } @@ -5769,7 +5759,7 @@ void BlockchainLMDB::migrate_3_4() if (past_long_term_weight) { std::vector weights(long_term_block_weights.begin(), long_term_block_weights.end()); - uint64_t long_term_effective_block_median_weight = std::max(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, tools::median(weights)); + uint64_t long_term_effective_block_median_weight = std::max(BLOCK_GRANTED_FULL_REWARD_ZONE_V5, tools::median(weights)); long_term_block_weight = std::min(bi.bi_weight, long_term_effective_block_median_weight + long_term_effective_block_median_weight * 2 / 5); } else @@ -5939,12 +5929,12 @@ void BlockchainLMDB::migrate_5_6() // unexpected padding auto const *header = static_cast(val.mv_data); - auto num_sigs = little_to_native(header->num_signatures); + auto num_sigs = oxenc::little_to_host(header->num_signatures); auto const *aligned_signatures = reinterpret_cast(static_cast(val.mv_data) + sizeof(*header)); if (num_sigs == 0) continue; // NOTE: Hardcoded checkpoints checkpoint_t checkpoint = {}; - checkpoint.height = little_to_native(header->height); + checkpoint.height = oxenc::little_to_host(header->height); checkpoint.type = (num_sigs > 0) ? checkpoint_type::master_node : checkpoint_type::hardcoded; checkpoint.block_hash = header->block_hash; @@ -6160,15 +6150,15 @@ void BlockchainLMDB::clear_master_node_data() } template -C native_to_little_container(const C& c) { +C host_to_little_container(const C& c) { C result{c}; - for (auto& x : result) native_to_little_inplace(x); + for (auto& x : result) oxenc::host_to_little_inplace(x); return result; } template -C little_to_native_container(const C& c) { +C little_to_host_container(const C& c) { C result{c}; - for (auto& x : result) little_to_native_inplace(x); + for (auto& x : result) oxenc::little_to_host_inplace(x); return result; } @@ -6176,25 +6166,25 @@ struct master_node_proof_serialized_old { master_node_proof_serialized_old() = default; master_node_proof_serialized_old(const master_nodes::proof_info &info) - : timestamp{native_to_little(info.timestamp)}, - ip{native_to_little(info.proof->public_ip)}, - storage_https_port{native_to_little(info.proof->storage_https_port)}, - storage_omq_port{native_to_little(info.proof->storage_omq_port)}, - quorumnet_port{native_to_little(info.proof->qnet_port)}, - version{native_to_little_container(info.proof->version)}, + : timestamp{oxenc::host_to_little(info.timestamp)}, + ip{oxenc::host_to_little(info.proof->public_ip)}, + storage_https_port{oxenc::host_to_little(info.proof->storage_https_port)}, + storage_omq_port{oxenc::host_to_little(info.proof->storage_omq_port)}, + quorumnet_port{oxenc::host_to_little(info.proof->qnet_port)}, + version{host_to_little_container(info.proof->version)}, pubkey_ed25519{info.proof->pubkey_ed25519} {} void update(master_nodes::proof_info &info) const { - info.timestamp = little_to_native(timestamp); + info.timestamp = oxenc::little_to_host(timestamp); if (info.timestamp > info.effective_timestamp) info.effective_timestamp = info.timestamp; - info.proof->public_ip = little_to_native(ip); - info.proof->storage_https_port = little_to_native(storage_https_port); - info.proof->storage_omq_port = little_to_native(storage_omq_port); - info.proof->qnet_port = little_to_native(quorumnet_port); - info.proof->version = little_to_native_container(version); + info.proof->public_ip = oxenc::little_to_host(ip); + info.proof->storage_https_port = oxenc::little_to_host(storage_https_port); + info.proof->storage_omq_port = oxenc::little_to_host(storage_omq_port); + info.proof->qnet_port = oxenc::little_to_host(quorumnet_port); + info.proof->version = little_to_host_container(version); info.proof->storage_server_version = {0, 0, 0}; info.proof->belnet_version = {0, 0, 0}; info.update_pubkey(pubkey_ed25519); @@ -6221,8 +6211,8 @@ struct master_node_proof_serialized : master_node_proof_serialized_old { master_node_proof_serialized() = default; master_node_proof_serialized(const master_nodes::proof_info &info) : master_node_proof_serialized_old{info}, - storage_server_version{native_to_little_container(info.proof->storage_server_version)}, - belnet_version{native_to_little_container(info.proof->belnet_version)} + storage_server_version{host_to_little_container(info.proof->storage_server_version)}, + belnet_version{host_to_little_container(info.proof->belnet_version)} {} std::array storage_server_version{}; std::array belnet_version{}; @@ -6231,8 +6221,8 @@ struct master_node_proof_serialized : master_node_proof_serialized_old { void update(master_nodes::proof_info& info) const { if (!info.proof) info.proof = std::unique_ptr(new uptime_proof::Proof()); master_node_proof_serialized_old::update(info); - info.proof->storage_server_version = little_to_native_container(storage_server_version); - info.proof->belnet_version = little_to_native_container(belnet_version); + info.proof->storage_server_version = little_to_host_container(storage_server_version); + info.proof->belnet_version = little_to_host_container(belnet_version); } operator master_nodes::proof_info() const diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index f2fee7c9f7a..1b3079ca18a 100755 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -43,7 +43,7 @@ namespace cryptonote class BaseTestDB: public cryptonote::BlockchainDB { public: BaseTestDB() {} - virtual void open(const fs::path& filename, network_type nettype = FAKECHAIN, const int db_flags = 0) override { } + virtual void open(const fs::path& filename, network_type nettype = network_type::FAKECHAIN, const int db_flags = 0) override { } virtual void close() override {} virtual void sync() override {} virtual void safesyncmode(const bool onoff) override {} diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index d57d6a9658b..09fe1e931e8 100755 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -87,7 +87,7 @@ target_link_libraries(blockchain_depth PRIVATE blockchain_tools_common_libs) beldex_add_executable(blockchain_stats "beldex-blockchain-stats" blockchain_stats.cpp ) -target_link_libraries(blockchain_stats PRIVATE blockchain_tools_common_libs) +target_link_libraries(blockchain_stats PRIVATE blockchain_tools_common_libs date::date) # TODO(beldex): Blockchain pruning not supported in Beldex yet # beldex_add_executable(blockchain_prune_known_spent_data "beldex-blockchain-prune-known-spent-data" diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index ad836b6289c..16dd215ce69 100755 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -400,7 +400,7 @@ int main(int argc, char* argv[]) std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; std::string opt_txid_string = command_line::get_arg(vm, arg_txid); std::string opt_output_string = command_line::get_arg(vm, arg_output); uint64_t opt_height = command_line::get_arg(vm, arg_height); diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 7a0f6322b57..fb55c45938f 100755 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -100,7 +100,7 @@ int main(int argc, char* argv[]) bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; std::string opt_txid_string = command_line::get_arg(vm, arg_txid); uint64_t opt_height = command_line::get_arg(vm, arg_height); bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase); diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index b6ff123fe5e..859c091bbd9 100755 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -142,7 +142,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Error opening database: " << e.what()); return 1; } - r = core_storage->init(db, nullptr, opt_testnet ? cryptonote::TESTNET : opt_devnet ? cryptonote::DEVNET : cryptonote::MAINNET); + r = core_storage->init(db, nullptr, opt_testnet ? cryptonote::network_type::TESTNET : opt_devnet ? cryptonote::network_type::DEVNET : cryptonote::network_type::MAINNET); if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat) { diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp index fd620282ab7..e0922f31d3f 100755 --- a/src/blockchain_utilities/blockchain_prune.cpp +++ b/src/blockchain_utilities/blockchain_prune.cpp @@ -304,7 +304,7 @@ static void prune(MDB_env *env0, MDB_env *env1) if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); MDB_val k, v; - uint32_t pruning_seed = tools::make_pruning_seed(tools::get_random_stripe(), CRYPTONOTE_PRUNING_LOG_STRIPES); + uint32_t pruning_seed = tools::make_pruning_seed(tools::get_random_stripe(), PRUNING_LOG_STRIPES); static char pruning_seed_key[] = "pruning_seed"; k.mv_data = pruning_seed_key; k.mv_size = strlen("pruning_seed") + 1; @@ -334,7 +334,7 @@ static void prune(MDB_env *env0, MDB_env *env1) const txindex *ti = (const txindex*)v.mv_data; const uint64_t block_height = ti->data.block_id; MDB_val_set(kk, ti->data.tx_id); - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) { MDEBUG(block_height << "/" << blockchain_height << " is in tip"); MDB_val_set(vv, block_height); @@ -494,7 +494,7 @@ int main(int argc, char* argv[]) bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; bool opt_copy_pruned_database = command_line::get_arg(vm, arg_copy_pruned_database); std::string data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); while (tools::ends_with(data_dir, "/") || tools::ends_with(data_dir, "\\")) diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp index c18c8d4bec6..ee5c5391da3 100755 --- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; bool opt_verbose = command_line::get_arg(vm, arg_verbose); bool opt_dry_run = command_line::get_arg(vm, arg_dry_run); diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index ef9d5b8c319..ee0a9591cd6 100755 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -27,6 +27,9 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include + #include "common/command_line.h" #include "common/varint.h" #include "common/signal_handler.h" @@ -35,7 +38,6 @@ #include "blockchain_objects.h" #include "blockchain_db/blockchain_db.h" #include "version.h" -#include "epee/misc_os_dependent.h" #include "cryptonote_core/uptime_proof.h" #undef BELDEX_DEFAULT_LOG_CATEGORY @@ -114,7 +116,7 @@ int main(int argc, char* argv[]) std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; block_start = command_line::get_arg(vm, arg_block_start); block_stop = command_line::get_arg(vm, arg_block_stop); bool do_inputs = command_line::get_arg(vm, arg_inputs); @@ -193,7 +195,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' } std::cout << "\n"; - struct tm prevtm = {0}, currtm; + std::optional prev_ts; uint64_t prevsz = 0, currsz = 0; uint64_t prevtxs = 0, currtxs = 0; uint64_t currblks = 0; @@ -214,20 +216,20 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' LOG_PRINT_L0("Bad block from db"); return 1; } - time_t tt = blk.timestamp; - char timebuf[64]; - epee::misc_utils::get_gmt_time(tt, currtm); - if (!prevtm.tm_year) - prevtm = currtm; + auto ts = std::chrono::system_clock::from_time_t(blk.timestamp); + using namespace date; + year_month_day curr_date{floor(ts)}; + if (!prev_ts) + prev_ts = ts; + year_month_day prev_date{floor(*prev_ts)}; // catch change of day - if (currtm.tm_mday > prevtm.tm_mday || (currtm.tm_mday == 1 && prevtm.tm_mday > 27)) + if (curr_date.day() > prev_date.day() || (curr_date.day() == day{1} && prev_date.day() > day{27})) { // check for timestamp fudging around month ends - if (prevtm.tm_mday == 1 && currtm.tm_mday > 27) + if (curr_date.day() == day{1} && prev_date.day() > day{27}) goto skip; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &prevtm); - prevtm = currtm; - std::cout << timebuf << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz; + prev_ts = ts; + std::cout << format("%Y-%m-%d", prev_date) << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz; prevsz += currsz; currsz = 0; currblks = 0; @@ -277,7 +279,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' currsz += bd.size(); currtxs++; if (do_hours) - txhr[currtm.tm_hour]++; + txhr[hh_mm_ss{ts - floor(ts)}.hours().count()]++; if (do_inputs) { io = tx.vin.size(); if (io < minins) diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 1d6fb3835ab..d8fe52a01c8 100755 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -132,7 +132,7 @@ int main(int argc, char* argv[]) bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); - network_type net_type = opt_testnet ? TESTNET : opt_devnet ? DEVNET : MAINNET; + network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET; bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); // If we wanted to use the memory pool, we would set up a fake_core. diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 4a3d5d77dd5..1faa12d9017 100755 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -55,7 +55,7 @@ namespace cryptonote if (result) MINFO ("CHECKPOINT PASSED FOR HEIGHT " << height << " " << block_hash); else MWARNING("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH " << block_hash << "GIVEN HASH: " << hash); return result; - } + }; height_to_hash const HARDCODED_MAINNET_CHECKPOINTS[] = { @@ -78,10 +78,10 @@ namespace cryptonote { crypto::hash result = crypto::null_hash; *height = 0; - if (nettype != MAINNET && nettype != TESTNET) + if (nettype != network_type::MAINNET && nettype != network_type::TESTNET) return result; - if (nettype == MAINNET) + if (nettype == network_type::MAINNET) { uint64_t last_index = beldex::array_count(HARDCODED_MAINNET_CHECKPOINTS) - 1; height_to_hash const &entry = HARDCODED_MAINNET_CHECKPOINTS[last_index]; @@ -180,7 +180,7 @@ namespace cryptonote void checkpoints::block_add(const block_add_info& info) { uint64_t const height = get_block_height(info.block); - if (height < master_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL || info.block.major_version < network_version_13_checkpointing) + if (height < master_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL || info.block.major_version < hf::hf13_checkpointing) return; uint64_t end_cull_height = 0; @@ -317,12 +317,13 @@ namespace cryptonote return true; #if !defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) - if (nettype == MAINNET) + if (nettype == network_type::MAINNET) { for (size_t i = 0; i < beldex::array_count(HARDCODED_MAINNET_CHECKPOINTS); ++i) { height_to_hash const &checkpoint = HARDCODED_MAINNET_CHECKPOINTS[i]; - ADD_CHECKPOINT(checkpoint.height, checkpoint.hash); + bool added = add_checkpoint(checkpoint.height, checkpoint.hash); + CHECK_AND_ASSERT(added, false); } } #endif diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index f6327649296..d19e30ea5d2 100755 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -38,11 +38,10 @@ #include "cryptonote_basic/cryptonote_basic_impl.h" #include "common/fs.h" -#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false); -#define JSON_HASH_FILE_NAME "checkpoints.json" - namespace cryptonote { + constexpr std::string_view JSON_HASH_FILE_NAME = "checkpoints.json"sv; + enum struct checkpoint_type { hardcoded, @@ -195,7 +194,7 @@ namespace cryptonote bool init(network_type nettype, class BlockchainDB *db); private: - network_type m_nettype = UNDEFINED; + network_type m_nettype = network_type::UNDEFINED; uint64_t m_last_cull_height = 0; uint64_t m_immutable_height = 0; BlockchainDB *m_db; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 803876f661d..265399842a5 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -36,6 +36,7 @@ add_library(common expect.cpp file.cpp i18n.cpp + json_binary_proxy.cpp beldex.cpp notify.cpp password.cpp @@ -62,9 +63,12 @@ endif() target_link_libraries(common PUBLIC cncrypto + oxenc::oxenc oxenmq::oxenmq filesystem fmt::fmt + nlohmann_json::nlohmann_json + date::date PRIVATE sodium extra) diff --git a/src/common/file.cpp b/src/common/file.cpp index 05f94eca27e..9df0cf68916 100755 --- a/src/common/file.cpp +++ b/src/common/file.cpp @@ -254,7 +254,7 @@ namespace tools { } -#ifdef WIN32 +#ifdef _WIN32 fs::path get_special_folder_path(int nfolder, bool iscreate) { WCHAR psz_path[MAX_PATH] = L""; @@ -267,30 +267,28 @@ namespace tools { LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path."); return ""; } +#endif - // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME - // Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME - fs::path get_default_data_dir() - { - return get_special_folder_path(CSIDL_COMMON_APPDATA, true) / fs::u8path(CRYPTONOTE_NAME); - } - fs::path get_depreciated_default_data_dir() - { - return get_special_folder_path(CSIDL_COMMON_APPDATA, true) / fs::u8path("beldex"); - } + // Windows < Vista: C:\Documents and Settings\Username\Application Data\... + // Windows >= Vista: C:\Users\Username\AppData\Roaming\... + // Sane OSes: ~/ + static fs::path get_default_parent_dir() { +#ifdef _WIN32 + return get_special_folder_path(CSIDL_COMMON_APPDATA, true); #else - // Non-windows: ~/.CRYPTONOTE_NAME + char* home = std::getenv("HOME"); + return home && std::strlen(home) ? fs::u8path(home) : fs::current_path(); +#endif + } + fs::path get_default_data_dir() { - char* home = std::getenv("HOME"); - return (home && std::strlen(home) ? fs::u8path(home) : fs::current_path()) / fs::u8path("." CRYPTONOTE_NAME); + return get_default_parent_dir() / fs::u8path(cryptonote::DATA_DIRNAME); } fs::path get_depreciated_default_data_dir() { - char* home = std::getenv("HOME"); - return (home && std::strlen(home) ? fs::u8path(home) : fs::current_path()) / fs::u8path(".beldex"); + return get_default_parent_dir() / fs::u8path(cryptonote::DATA_DIRNAME); } -#endif void set_strict_default_file_permissions(bool strict) { diff --git a/src/common/json_binary_proxy.cpp b/src/common/json_binary_proxy.cpp new file mode 100644 index 00000000000..ac2b1d7fbc3 --- /dev/null +++ b/src/common/json_binary_proxy.cpp @@ -0,0 +1,42 @@ +#include "json_binary_proxy.h" +#include +#include + +namespace tools { + + void load_binary_parameter_impl(std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data) { + if (allow_raw && bytes.size() == raw_size) { + std::memcpy(val_data, bytes.data(), bytes.size()); + return; + } else if (bytes.size() == raw_size * 2) { + if (oxenc::is_hex(bytes)){ + oxenc::from_hex(bytes.begin(), bytes.end(), val_data); + return; + } + } else { + const size_t b64_padded = (raw_size + 2) / 3 * 4; + const size_t b64_padding = raw_size % 3 == 1 ? 2 : raw_size % 3 == 2 ? 1 : 0; + const size_t b64_unpadded = b64_padded - b64_padding; + const std::string_view b64_padding_string = b64_padding == 2 ? "=="sv : b64_padding == 1 ? "="sv : ""sv; + if (bytes.size() == b64_unpadded || + (b64_padding > 0 && bytes.size() == b64_padded && bytes.substr(b64_unpadded) == b64_padding_string)) { + if (oxenc::is_base64(bytes)){ + oxenc::from_base64(bytes.begin(), bytes.end(), val_data); + return; + } + } + } + + throw std::runtime_error{"Invalid binary value: unexpected size and/or encoding"}; + } + + nlohmann::json& json_binary_proxy::operator=(std::string_view binary_data) { + switch (format) { + case fmt::bt: return e = binary_data; + case fmt::hex: return e = oxenc::to_hex(binary_data); + case fmt::base64: return e = oxenc::to_base64(binary_data); + } + throw std::runtime_error{"Internal error: invalid binary encoding"}; + } + +} \ No newline at end of file diff --git a/src/common/json_binary_proxy.h b/src/common/json_binary_proxy.h new file mode 100644 index 00000000000..42f8ddc8bd8 --- /dev/null +++ b/src/common/json_binary_proxy.h @@ -0,0 +1,135 @@ +#pragma once + +#include "ringct/rctTypes.h" +#include "crypto/crypto.h" +#include +#include +#include + +using namespace std::literals; + +namespace tools { + + // Binary types that we support for rpc input/output. For json, these must be specified as hex or + // base64; for bt-encoded requests these can be accepted as binary, hex, or base64. + template + inline constexpr bool json_is_binary = false; + template <> inline constexpr bool json_is_binary = true; + template <> inline constexpr bool json_is_binary = true; + template <> inline constexpr bool json_is_binary = true; + template <> inline constexpr bool json_is_binary = true; + template <> inline constexpr bool json_is_binary = true; + template <> inline constexpr bool json_is_binary = true; + + template + inline constexpr bool json_is_binary_container = false; + template + inline constexpr bool json_is_binary_container> = json_is_binary; + template + inline constexpr bool json_is_binary_container> = json_is_binary; + + // De-referencing wrappers around the above: + template inline constexpr bool json_is_binary = json_is_binary; + template inline constexpr bool json_is_binary = json_is_binary; + template inline constexpr bool json_is_binary_container = json_is_binary_container; + template inline constexpr bool json_is_binary_container = json_is_binary_container; + + void load_binary_parameter_impl(std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data); + + // Loads a binary value from a string_view which may contain hex, base64, and (optionally) raw + // bytes. + template >> + void load_binary_parameter(std::string_view bytes, bool allow_raw, T& val) { + load_binary_parameter_impl(bytes, sizeof(T), allow_raw, reinterpret_cast(&val)); + } + + // Wrapper around a nlohmann::json that assigns a binary value either as binary (for bt-encoding); + // or as hex or base64 (for json-encoding). + class json_binary_proxy { + public: + nlohmann::json& e; + enum class fmt { bt, hex, base64 } format; + explicit json_binary_proxy(nlohmann::json& elem, fmt format) + : e{elem}, format{format} {} + json_binary_proxy() = delete; + + json_binary_proxy(const json_binary_proxy&) = default; + json_binary_proxy(json_binary_proxy&&) = default; + + /// Dereferencing a proxy element accesses the underlying nlohmann::json + nlohmann::json& operator*() { return e; } + nlohmann::json* operator->() { return &e; } + + /// Descends into the json object, returning a new binary value proxy around the child element. + template + json_binary_proxy operator[](T&& key) { + return json_binary_proxy{e[std::forward(key)], format}; + } + + /// Returns a binary value proxy around the first/last element (requires an underlying list) + json_binary_proxy front() { return json_binary_proxy{e.front(), format}; } + json_binary_proxy back() { return json_binary_proxy{e.back(), format}; } + + /// Assigns binary data from a string_view/string/etc. + nlohmann::json& operator=(std::string_view binary_data); + + /// Assigns binary data from a string_view over a 1-byte, non-char type (e.g. unsigned char or + /// uint8_t). + template , int> = 0> + nlohmann::json& operator=(std::basic_string_view binary_data) { + return *this = std::string_view{reinterpret_cast(binary_data.data()), binary_data.size()}; + } + + /// Takes a trivial, no-padding data structure (e.g. a crypto::hash) as the value and dumps its + /// contents as the binary value. + template , int> = 0> + nlohmann::json& operator=(const T& val) { + return *this = std::string_view{reinterpret_cast(&val), sizeof(val)}; + } + /// Takes a vector of some json_binary_proxy-assignable type and builds an array by assigning + /// each one into a new array of binary values. + template , int> = 0> + nlohmann::json& operator=(const T& vals) { + auto a = nlohmann::json::array(); + for (auto& val : vals) + json_binary_proxy{a.emplace_back(), format} = val; + return e = std::move(a); + } + /// Emplaces a new nlohman::json to the end of an underlying list and returns a + /// json_binary_proxy wrapping it. + /// + /// Example: + /// + /// auto child = wrappedelem.emplace_back({"key1": 1}, {"key2": 2}); + /// child["binary-key"] = some_binary_thing; + template + json_binary_proxy emplace_back(Args&&... args) { + return json_binary_proxy{e.emplace_back(std::forward(args)...), format}; + } + + /// Adds an element to an underlying list, then copies or moves the given argument onto it via + /// json_binary_proxy assignment. + template + void push_back(T&& val) { + emplace_back() = std::forward(val); + } + }; + +} + +// Specializations of binary types for deserialization; when receiving these from json we expect +// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so +// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead. +namespace nlohmann { + template + struct adl_serializer>> { + static_assert(std::is_trivially_copyable_v && std::has_unique_object_representations_v); + + static void to_json(json& j, const T&) { + throw std::logic_error{"Internal error: binary types are not directly serializable"}; + } + static void from_json(const json& j, T& val) { + tools::load_binary_parameter(j.get(), false /*no raw*/, val); + } + }; +} diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 4be3ac9a091..e768c856406 100755 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -27,19 +27,17 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include #include -#include "epee/misc_os_dependent.h" #include "perf_timer.h" +using namespace std::literals; + #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "perf" #define PERF_LOG_ALWAYS(level, cat, x) \ el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::FileOnlyLog).construct(cat) << x -#define PERF_LOG(level, cat, x) \ - do { \ - if (ELPP->vRegistry()->allowed(level, cat)) PERF_LOG_ALWAYS(level, cat, x); \ - } while(0) namespace tools { @@ -59,12 +57,10 @@ void set_performance_timer_log_level(el::Level level) performance_timer_log_level = level; } -PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused) +PerformanceTimer::PerformanceTimer(bool paused): started(true) { - if (paused) - ticks = 0; - else - ticks = epee::misc_utils::get_ns_count(); + if (!paused) + since = std::chrono::steady_clock::now(); } LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l) @@ -79,12 +75,12 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std } else { - LoggingPerformanceTimer *pt = performance_timers->back(); - if (!pt->started && !pt->paused) + LoggingPerformanceTimer* pt = performance_timers->back(); + if (!pt->started && pt->since) { if (log) { - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; + size_t size = 0; for (const auto *tmp: *performance_timers) if (tmp->since) ++size; PERF_LOG_ALWAYS(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); } pt->started = true; @@ -101,47 +97,45 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer() if (log) { char s[12]; - snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks / (1000000000 / unit))); - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; + snprintf(s, sizeof(s), "%8llu ", static_cast(elapsed.count() / (1000000000 / unit))); + size_t size = 0; for (const auto *tmp: *performance_timers) if (tmp->since || tmp==this) ++size; PERF_LOG_ALWAYS(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); } if (performance_timers->empty()) { delete performance_timers; - performance_timers = NULL; + performance_timers = nullptr; } } void PerformanceTimer::pause() { - if (paused) + if (!since) return; - ticks = epee::misc_utils::get_ns_count() - ticks; - paused = true; + auto now = std::chrono::steady_clock::now(); + elapsed += now - *since; + since.reset(); } void PerformanceTimer::resume() { - if (!paused) - return; - ticks = epee::misc_utils::get_ns_count() - ticks; - paused = false; + if (!since) + since = std::chrono::steady_clock::now(); } void PerformanceTimer::reset() { - if (paused) - ticks = 0; - else - ticks = epee::misc_utils::get_ns_count(); + elapsed = 0ns; + if (since) + since = std::chrono::steady_clock::now(); } -uint64_t PerformanceTimer::value() const +std::chrono::nanoseconds PerformanceTimer::value() const { - uint64_t v = ticks; - if (!paused) - v = epee::misc_utils::get_ns_count() - v; - return v; + auto ret = elapsed; + if (since) + ret += std::chrono::steady_clock::now() - *since; + return ret; } -} +} // namespace tools diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 22049bf027d..a7ad36a1a22 100755 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -29,7 +29,9 @@ #pragma once +#include #include +#include #include #include #include "epee/misc_log_ex.h" @@ -46,15 +48,13 @@ class PerformanceTimer void pause(); void resume(); void reset(); - uint64_t value() const; - operator uint64_t() const { return value(); } - float milliseconds() const { return value() / 1.0e6; } - float seconds() const { return milliseconds() / 1000.f; } + std::chrono::nanoseconds value() const; + std::chrono::duration seconds() const { return value(); } protected: - uint64_t ticks; + std::optional since; + std::chrono::nanoseconds elapsed; bool started; - bool paused; }; class LoggingPerformanceTimer: public PerformanceTimer diff --git a/src/common/pruning.cpp b/src/common/pruning.cpp index 527c62c4d55..0e62658bb3f 100755 --- a/src/common/pruning.cpp +++ b/src/common/pruning.cpp @@ -34,6 +34,8 @@ namespace tools { +using namespace cryptonote; + uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes) { CHECK_AND_ASSERT_THROW_MES(log_stripes <= PRUNING_SEED_LOG_STRIPES_MASK, "log_stripes out of range"); @@ -53,9 +55,9 @@ bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint3 uint32_t get_pruning_stripe(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes) { - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) return 0; - return ((block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) & (uint64_t)((1ul << log_stripes) - 1)) + 1; + return ((block_height / PRUNING_STRIPE_SIZE) & (uint64_t)((1ul << log_stripes) - 1)) + 1; } uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes) @@ -68,24 +70,24 @@ uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uin uint64_t get_next_unpruned_block_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) { - CHECK_AND_ASSERT_MES(block_height <= CRYPTONOTE_MAX_BLOCK_NUMBER+1, block_height, "block_height too large"); - CHECK_AND_ASSERT_MES(blockchain_height <= CRYPTONOTE_MAX_BLOCK_NUMBER+1, block_height, "blockchain_height too large"); + CHECK_AND_ASSERT_MES(block_height <= MAX_BLOCK_NUMBER+1, block_height, "block_height too large"); + CHECK_AND_ASSERT_MES(blockchain_height <= MAX_BLOCK_NUMBER+1, block_height, "blockchain_height too large"); const uint32_t stripe = get_pruning_stripe(pruning_seed); if (stripe == 0) return block_height; - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) return block_height; const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed); - const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : CRYPTONOTE_PRUNING_LOG_STRIPES; + const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES; const uint64_t mask = (1ul << log_stripes) - 1; - const uint32_t block_pruning_stripe = ((block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) & mask) + 1; + const uint32_t block_pruning_stripe = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1; if (block_pruning_stripe == stripe) return block_height; - const uint64_t cycles = ((block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) >> log_stripes); + const uint64_t cycles = ((block_height / PRUNING_STRIPE_SIZE) >> log_stripes); const uint64_t cycle_start = cycles + ((stripe > block_pruning_stripe) ? 0 : 1); - const uint64_t h = cycle_start * (CRYPTONOTE_PRUNING_STRIPE_SIZE << log_stripes) + (stripe - 1) * CRYPTONOTE_PRUNING_STRIPE_SIZE; - if (h + CRYPTONOTE_PRUNING_TIP_BLOCKS > blockchain_height) - return blockchain_height < CRYPTONOTE_PRUNING_TIP_BLOCKS ? 0 : blockchain_height - CRYPTONOTE_PRUNING_TIP_BLOCKS; + const uint64_t h = cycle_start * (PRUNING_STRIPE_SIZE << log_stripes) + (stripe - 1) * PRUNING_STRIPE_SIZE; + if (h + PRUNING_TIP_BLOCKS > blockchain_height) + return blockchain_height < PRUNING_TIP_BLOCKS ? 0 : blockchain_height - PRUNING_TIP_BLOCKS; CHECK_AND_ASSERT_MES(h >= block_height, block_height, "h < block_height, unexpected"); return h; } @@ -95,12 +97,12 @@ uint64_t get_next_pruned_block_height(uint64_t block_height, uint64_t blockchain const uint32_t stripe = get_pruning_stripe(pruning_seed); if (stripe == 0) return blockchain_height; - if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height) + if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) return blockchain_height; const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed); - const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : CRYPTONOTE_PRUNING_LOG_STRIPES; + const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES; const uint64_t mask = (1ul << log_stripes) - 1; - const uint32_t block_pruning_seed = ((block_height / CRYPTONOTE_PRUNING_STRIPE_SIZE) & mask) + 1; + const uint32_t block_pruning_seed = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1; if (block_pruning_seed != stripe) return block_height; const uint32_t next_stripe = 1 + (block_pruning_seed & mask); @@ -109,7 +111,7 @@ uint64_t get_next_pruned_block_height(uint64_t block_height, uint64_t blockchain uint32_t get_random_stripe() { - return 1 + crypto::rand() % (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES); + return 1 + crypto::rand() % (1ul << PRUNING_LOG_STRIPES); } } diff --git a/src/common/pruning.h b/src/common/pruning.h index f6cbfeffb37..ae431102564 100755 --- a/src/common/pruning.h +++ b/src/common/pruning.h @@ -32,13 +32,19 @@ namespace tools { - static constexpr uint32_t PRUNING_SEED_LOG_STRIPES_SHIFT = 7; - static constexpr uint32_t PRUNING_SEED_LOG_STRIPES_MASK = 0x7; - static constexpr uint32_t PRUNING_SEED_STRIPE_SHIFT = 0; - static constexpr uint32_t PRUNING_SEED_STRIPE_MASK = 0x7f; + inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_SHIFT = 7; + inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_MASK = 0x7; + inline constexpr uint32_t PRUNING_SEED_STRIPE_SHIFT = 0; + inline constexpr uint32_t PRUNING_SEED_STRIPE_MASK = 0x7f; - constexpr inline uint32_t get_pruning_log_stripes(uint32_t pruning_seed) { return (pruning_seed >> PRUNING_SEED_LOG_STRIPES_SHIFT) & PRUNING_SEED_LOG_STRIPES_MASK; } - inline uint32_t get_pruning_stripe(uint32_t pruning_seed) { if (pruning_seed == 0) return 0; return 1 + ((pruning_seed >> PRUNING_SEED_STRIPE_SHIFT) & PRUNING_SEED_STRIPE_MASK); } + inline constexpr uint32_t get_pruning_log_stripes(uint32_t pruning_seed) { + return (pruning_seed >> PRUNING_SEED_LOG_STRIPES_SHIFT) & PRUNING_SEED_LOG_STRIPES_MASK; + } + inline uint32_t get_pruning_stripe(uint32_t pruning_seed) { + if (pruning_seed == 0) + return 0; + return 1 + ((pruning_seed >> PRUNING_SEED_STRIPE_SHIFT) & PRUNING_SEED_STRIPE_MASK); + } uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes); diff --git a/src/common/rules.cpp b/src/common/rules.cpp index 4bafa1cb9ab..e6f308d3109 100755 --- a/src/common/rules.cpp +++ b/src/common/rules.cpp @@ -42,11 +42,11 @@ namespace rules bool is_output_unlocked(uint64_t unlock_time, uint64_t height,network_type nettype) { - if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + if(unlock_time < MAX_BLOCK_NUMBER) { // ND: Instead of calling get_current_blockchain_height(), call m_db->height() // directly as get_current_blockchain_height() locks the recursive mutex. - if(height - 1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + if(height - 1 + LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) return true; else return false; @@ -57,7 +57,7 @@ bool is_output_unlocked(uint64_t unlock_time, uint64_t height,network_type netty auto hf_version = ::cryptonote::get_network_version(nettype, height); //interpret as time uint64_t current_time = static_cast(time(NULL)); - if(current_time + tools::to_seconds((hf_version>=cryptonote::network_version_17_POS?CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V3:CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2)) >= unlock_time) + if(current_time + tools::to_seconds( LOCKED_TX_ALLOWED_DELTA_BLOCKS * (hf_version >= hf::hf17_POS ? TARGET_BLOCK_TIME : old::TARGET_BLOCK_TIME_12)) >= unlock_time) // have to change return true; else return false; diff --git a/src/common/rules.h b/src/common/rules.h index b8d504b7ca7..6b9ccc35076 100755 --- a/src/common/rules.h +++ b/src/common/rules.h @@ -27,16 +27,12 @@ // 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 #include "cryptonote_config.h" -namespace cryptonote -{ - -namespace rules +namespace cryptonote::rules { bool is_output_unlocked(uint64_t unlock_time, uint64_t height,network_type nettype); -} // namespace rules - -} // namespace cryptonote +} // namespace cryptonote::rules \ No newline at end of file diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index a5caf2b31bd..717de79b039 100755 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -97,8 +97,8 @@ std::string friendly_duration(std::chrono::nanoseconds dur) { dur %= 1min; some = true; } - if (some) { - // If we have >= minutes then don't bother with fractional seconds + if (some || dur == 0s) { + // If we have >= minutes or its exactly 0 seconds then don't bother with fractional seconds os << dur / 1s << 's'; } else { double seconds = std::chrono::duration(dur).count(); diff --git a/src/common/string_util.h b/src/common/string_util.h index 945989446a9..265636368bd 100755 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "epee/span.h" // epee namespace tools { @@ -74,6 +75,41 @@ std::string join(std::string_view delimiter, It begin, It end) { template std::string join(std::string_view delimiter, const Container& c) { return join(delimiter, c.begin(), c.end()); } +/// Similar to join(), but first applies a transformation to each element. +template +std::string join_transform(std::string_view delimiter, It begin, It end, UnaryOperation transform) { + std::ostringstream o; + if (begin != end) + o << transform(*begin++); + while (begin != end) + o << delimiter << transform(*begin++); + return o.str(); +} + +/// Wrapper around the above that takes a container and passes c.begin(), c.end(). +template +std::string join_transform(std::string_view delimiter, const Container& c, UnaryOperation&& transform) { + return join_transform(delimiter, c.begin(), c.end(), std::forward(transform)); +} + +/// Concatenates a bunch of random values together with delim as a separator via << operator. +/// Returns the result as a string. +template +std::string join_stuff(std::string_view delim, T&& first, Ts&&... stuff) { + std::ostringstream o; + o << std::forward(first); + ((o << delim << std::forward(stuff)), ...); + return o.str(); +} + +/// Concatenates arguments via << operator, returns as a string. +template +std::string concat(T&&... stuff) { + std::ostringstream o; + (o << ... << std::forward(stuff)); + return o.str(); +} + /// Simple version of whitespace trimming: mutates the given string view to remove leading /// space, \t, \r, \n. (More exotic and locale-dependent whitespace is not removed). void trim(std::string_view& s); @@ -92,6 +128,15 @@ bool parse_int(const std::string_view str, T& value, int base = 10) { return true; } +/// Converts an integer value into a string via std::to_chars (i.e. without locale). +template >> +std::string int_to_string(const T& value, int base = 10) { + char buf[8*sizeof(T) + std::is_signed_v]; // maximum possible size with smallest possible base (2) + auto [p, ec] = std::to_chars(std::begin(buf), std::end(buf), value, base); + assert(ec == std::errc{}); // Our buffer should be big enough for anything + return {buf, p}; +} + /// Returns a string_view that views the data of the given object; this is not something you want to /// do unless the struct is specifically design to be used this way. The value must be a standard /// layout type; it should really require is_trivial, too, but we have classes (like crypto keys) @@ -119,7 +164,7 @@ T make_from_guts(std::string_view s) { if (s.size() != sizeof(T)) throw std::runtime_error("Cannot reconstitute type: wrong type content size"); T x; - std::memcpy(&x, s.data(), sizeof(T)); + std::memcpy(static_cast(&x), s.data(), sizeof(T)); return x; } diff --git a/src/common/util.cpp b/src/common/util.cpp index ec58dbabf5b..6a7396c5181 100755 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -29,15 +29,17 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include #include #include #include +#include + #include "epee/string_tools.h" #include "epee/wipeable_string.h" #include "crypto/crypto.h" #include "util.h" -#include "epee/misc_os_dependent.h" #include "epee/readline_buffer.h" #include "epee/misc_log_ex.h" #include "string_util.h" @@ -192,16 +194,11 @@ namespace tools } #endif - std::string get_human_readable_timestamp(uint64_t ts) + std::string get_human_readable_timestamp(std::time_t t) { - char buffer[64]; - if (ts < 1234567890) + if (t < 1234567890) return ""; - time_t tt = ts; - struct tm tm; - epee::misc_utils::get_gmt_time(tt, tm); - strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S UTC", &tm); - return std::string(buffer); + return date::format("%Y-%m-%d %H:%M:%S UTC", std::chrono::system_clock::from_time_t(t)); } std::string get_human_readable_timespan(std::chrono::seconds seconds) diff --git a/src/common/util.h b/src/common/util.h index 3b28013b7b3..1a3a3a706e0 100755 --- a/src/common/util.h +++ b/src/common/util.h @@ -30,7 +30,7 @@ #pragma once -#include +#include #include #include #include @@ -65,7 +65,7 @@ namespace tools std::string input_line_win(); #endif - std::string get_human_readable_timestamp(uint64_t ts); + std::string get_human_readable_timestamp(std::time_t ts); std::string get_human_readable_timespan(std::chrono::seconds seconds); std::string get_human_readable_bytes(uint64_t bytes); @@ -85,7 +85,7 @@ namespace tools // Copy an integer type, swapping to little-endian if needed template ::value, int> = 0> void memcpy_one(char*& dest, T t) { - boost::endian::native_to_little_inplace(t); + oxenc::host_to_little_inplace(t); std::memcpy(dest, &t, sizeof(T)); dest += sizeof(T); } diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 3d7d8d0f280..17cfd406685 100755 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -110,7 +110,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) for (i = 0; i < rsizw; i++) { uint64_t ina; memcpy(&ina, in + i * 8, 8); - st[i] ^= swap64le(ina); + st[i] ^= SWAP64LE(ina); } keccakf(st, KECCAK_ROUNDS); } @@ -128,7 +128,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) temp[rsiz - 1] |= 0x80; for (i = 0; i < rsizw; i++) - st[i] ^= swap64le(((uint64_t *) temp)[i]); + st[i] ^= SWAP64LE(((uint64_t *) temp)[i]); keccakf(st, KECCAK_ROUNDS); @@ -151,7 +151,7 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) #define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) #define KECCAK_PROCESS_BLOCK(st, block) { \ for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \ - ((st))[i_] ^= swap64le(((block))[i_]); \ + ((st))[i_] ^= SWAP64LE(((block))[i_]); \ }; \ keccakf(st, KECCAK_ROUNDS); } diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index a8490e58516..cb33e359e57 100755 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -473,7 +473,11 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) char * _test = NULL; gettimeofday(&timer, NULL); - gmTimer = gmtime( &timer.tv_sec ); + + // Convert tv_sec to time_t for gmtime + time_t t = (time_t)timer.tv_sec; + gmTimer = gmtime(&t); + _test = (char *) calloc( sizeof( char ), timer.tv_usec/1000 ); sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, @@ -492,7 +496,11 @@ static uint32_t oaes_get_seed(void) uint32_t _ret = 0; gettimeofday(&timer, NULL); - gmTimer = gmtime( &timer.tv_sec ); + + // Convert tv_sec to time_t for gmtime + time_t t = (time_t)timer.tv_sec; + gmTimer = gmtime(&t); + _test = (char *) calloc( sizeof( char ), timer.tv_usec/1000 ); _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.tv_usec/1000 + diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index dfd300481fc..5383369cbbc 100755 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -68,7 +68,7 @@ DISABLE_VS_WARNINGS(4244 4345) static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size"); epee::mlocked> data; memcpy(data.data(), &base_key, sizeof(base_key)); - data[sizeof(base_key)] = config::HASH_KEY_MEMORY; + data[sizeof(base_key)] = cryptonote::hashkey::MEMORY; crypto::generate_chacha_key(data.data(), sizeof(data), key, 1); } //----------------------------------------------------------------- diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index d0c0fff7923..2c9480b4334 100755 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -36,6 +36,7 @@ #include "epee/copyable_atomic.h" #include "crypto/hash.h" +using namespace std::literals; namespace cryptonote { @@ -46,7 +47,6 @@ namespace cryptonote state_before_handshake = 0, //default state state_synchronizing, state_standby, - state_idle, state_normal }; @@ -62,46 +62,31 @@ namespace cryptonote epee::copyable_atomic m_callback_request_count{0}; //in debug purpose: problem with double callback rise crypto::hash m_last_known_hash{crypto::null_hash}; uint32_t m_pruning_seed{0}; - uint16_t m_rpc_port{0}; bool m_anchor{false}; //size_t m_score{0}; TODO: add score calculations }; - inline std::string get_protocol_state_string(cryptonote_connection_context::state s) + constexpr std::string_view get_protocol_state_string(cryptonote_connection_context::state s) { switch (s) { - case cryptonote_connection_context::state_before_handshake: - return "before_handshake"; - case cryptonote_connection_context::state_synchronizing: - return "synchronizing"; - case cryptonote_connection_context::state_standby: - return "standby"; - case cryptonote_connection_context::state_idle: - return "idle"; - case cryptonote_connection_context::state_normal: - return "normal"; - default: - return "unknown"; + case cryptonote_connection_context::state_before_handshake: return "before_handshake"sv; + case cryptonote_connection_context::state_synchronizing: return "synchronizing"sv; + case cryptonote_connection_context::state_standby: return "standby"sv; + case cryptonote_connection_context::state_normal: return "normal"sv; + default: return "unknown"sv; } } - inline char get_protocol_state_char(cryptonote_connection_context::state s) + constexpr char get_protocol_state_char(cryptonote_connection_context::state s) { switch (s) { - case cryptonote_connection_context::state_before_handshake: - return 'h'; - case cryptonote_connection_context::state_synchronizing: - return 's'; - case cryptonote_connection_context::state_standby: - return 'w'; - case cryptonote_connection_context::state_idle: - return 'i'; - case cryptonote_connection_context::state_normal: - return 'n'; - default: - return 'u'; + case cryptonote_connection_context::state_before_handshake: return 'h'; + case cryptonote_connection_context::state_synchronizing: return 's'; + case cryptonote_connection_context::state_standby: return 'w'; + case cryptonote_connection_context::state_normal: return 'n'; + default: return 'u'; } } diff --git a/src/cryptonote_basic/cryptonote_basic.cpp b/src/cryptonote_basic/cryptonote_basic.cpp index b8f8492ef8d..00e6e26ead0 100644 --- a/src/cryptonote_basic/cryptonote_basic.cpp +++ b/src/cryptonote_basic/cryptonote_basic.cpp @@ -1,4 +1,5 @@ #include "cryptonote_basic.h" +#include namespace cryptonote { diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index fcfc4bd6d71..2571d4c89e0 100755 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -36,7 +36,6 @@ #include "serialization/variant.h" #include "serialization/vector.h" #include "serialization/binary_archive.h" -#include "serialization/json_archive.h" #include "serialization/crypto.h" #include "epee/serialization/keyvalue_serialization.h" // eepe named serialization #include "cryptonote_config.h" @@ -176,9 +175,9 @@ namespace cryptonote static char const* version_to_string(txversion v); static char const* type_to_string(txtype type); - static constexpr txversion get_min_version_for_hf(uint8_t hf_version); - static txversion get_max_version_for_hf(uint8_t hf_version); - static constexpr txtype get_max_type_for_hf (uint8_t hf_version); + static constexpr txversion get_min_version_for_hf(hf hf_version); + static txversion get_max_version_for_hf(hf hf_version); + static constexpr txtype get_max_type_for_hf (hf hf_version); // tx information txversion version; @@ -275,16 +274,18 @@ namespace cryptonote set_blob_size_valid(false); } - const unsigned int start_pos = Binary ? ar.streampos() : 0; + unsigned int start_pos = 0; + if constexpr (Binary) + start_pos = ar.streampos(); serialization::value(ar, static_cast(*this)); - if (Binary) + if constexpr (Binary) prefix_size = ar.streampos() - start_pos; if (version == txversion::v1) { - if (Binary) + if constexpr (Binary) unprunable_size = ar.streampos() - start_pos; ar.tag("signatures"); @@ -311,7 +312,7 @@ namespace cryptonote else if (signature_size != signatures[i].size()) throw std::invalid_argument{"Invalid signature size (expected " + std::to_string(signature_size) + ", have " + std::to_string(signatures[i].size()) + ")"}; - value(arr.element(), signatures[i]); + value(ar, signatures[i]); } } else @@ -324,7 +325,7 @@ namespace cryptonote rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size()); } - if (Binary) + if constexpr (Binary) unprunable_size = ar.streampos() - start_pos; if (!pruned && rct_signatures.type != rct::RCTType::Null) @@ -392,22 +393,12 @@ namespace cryptonote struct block_header { - uint8_t major_version = cryptonote::network_version_7; - uint8_t minor_version = cryptonote::network_version_7; // now used as a voting mechanism, rather than how this particular block is built + hf major_version = hf::hf7; + uint8_t minor_version = 0; uint64_t timestamp; crypto::hash prev_id; uint32_t nonce; POS_header POS = {}; - - BEGIN_SERIALIZE() - VARINT_FIELD(major_version) - VARINT_FIELD(minor_version) - VARINT_FIELD(timestamp) - FIELD(prev_id) - FIELD(nonce) - if (major_version >= cryptonote::network_version_17_POS) - FIELD(POS) - END_SERIALIZE() }; struct block: public block_header @@ -433,21 +424,34 @@ namespace cryptonote // hash cache mutable crypto::hash hash; std::vector signatures; - - BEGIN_SERIALIZE_OBJECT() - if (Archive::is_deserializer) - set_hash_valid(false); - - FIELDS(static_cast(*this)) - FIELD(miner_tx) - FIELD(tx_hashes) - if (tx_hashes.size() > CRYPTONOTE_MAX_TX_PER_BLOCK) - throw std::invalid_argument{"too many txs in block"}; - if (major_version >= cryptonote::network_version_17_POS) - FIELD(signatures) - END_SERIALIZE() }; + template + void serialize_value(Archive& ar, block_header& b) { + using namespace serialization; + field(ar, "major_version", b.major_version); + field_varint(ar, "minor_version", b.minor_version); + field_varint(ar, "timestamp", b.timestamp); + field(ar, "prev_id", b.prev_id); + field(ar, "nonce", b.nonce); + if (b.major_version >= hf::hf17_POS) + field(ar, "POS", b.POS); + } + + template + void serialize_value(Archive& ar, block& b) { + auto _obj = ar.begin_object(); + if constexpr (Archive::is_deserializer) + b.set_hash_valid(false); + + serialization::value(ar, static_cast(b)); + field(ar, "miner_tx", b.miner_tx); + field(ar, "tx_hashes", b.tx_hashes); + if (b.tx_hashes.size() > MAX_TX_PER_BLOCK) + throw std::invalid_argument{"too many txs in block"}; + if (b.major_version >= hf::hf17_POS) + field(ar, "signatures", b.signatures); + } /************************************************************************/ /* */ @@ -502,9 +506,9 @@ namespace cryptonote using byte_and_output_fees = std::pair; //--------------------------------------------------------------- - constexpr txversion transaction_prefix::get_min_version_for_hf(uint8_t hf_version) + constexpr txversion transaction_prefix::get_min_version_for_hf(hf hf_version) { - if (hf_version >= cryptonote::network_version_7 && hf_version <= cryptonote::network_version_10_bulletproofs) + if (hf_version >= hf::hf7 && hf_version <= hf::hf10_bulletproofs) return txversion::v1; return txversion::v4_tx_types; } @@ -513,27 +517,27 @@ namespace cryptonote // tests can still use particular hard forks without needing to actually generate pre-v4 txes. namespace hack { inline bool test_suite_permissive_txes = false; } - inline txversion transaction_prefix::get_max_version_for_hf(uint8_t hf_version) + inline txversion transaction_prefix::get_max_version_for_hf(hf hf_version) { if (!hack::test_suite_permissive_txes) { - if (hf_version >= cryptonote::network_version_7 && hf_version <= cryptonote::network_version_8) + if (hf_version >= hf::hf7 && hf_version <= hf::hf8) return txversion::v2_ringct; - if (hf_version >= cryptonote::network_version_9_master_nodes && hf_version <= cryptonote::network_version_10_bulletproofs) + if (hf_version >= hf::hf9_master_nodes && hf_version <= hf::hf10_bulletproofs) return txversion::v3_per_output_unlock_times; } return txversion::v4_tx_types; } - constexpr txtype transaction_prefix::get_max_type_for_hf(uint8_t hf_version) + constexpr txtype transaction_prefix::get_max_type_for_hf(hf hf_version) { txtype result = txtype::standard; - if (hf_version >= network_version_18_bns) result = txtype::coin_burn; - else if (hf_version >= network_version_16) result = txtype::beldex_name_system; - else if (hf_version >= network_version_15_flash) result = txtype::stake; - else if (hf_version >= network_version_11_infinite_staking) result = txtype::key_image_unlock; - else if (hf_version >= network_version_9_master_nodes) result = txtype::state_change; + if (hf_version >= hf::hf18_bns) result = txtype::coin_burn; + else if (hf_version >= hf::hf16) result = txtype::beldex_name_system; + else if (hf_version >= hf::hf15_flash) result = txtype::stake; + else if (hf_version >= hf::hf11_infinite_staking) result = txtype::key_image_unlock; + else if (hf_version >= hf::hf9_master_nodes) result = txtype::state_change; return result; } @@ -564,12 +568,25 @@ namespace cryptonote } } - inline std::ostream &operator<<(std::ostream &os, txtype t) { + inline std::ostream& operator<<(std::ostream& os, txtype t) { return os << transaction::type_to_string(t); } - inline std::ostream &operator<<(std::ostream &os, txversion v) { + inline std::ostream& operator<<(std::ostream& os, txversion v) { return os << transaction::version_to_string(v); } + + inline std::ostream& operator<<(std::ostream& os, hf v) = delete;/*{ + return os << "HF" << static_cast(v); + }*/ + + // Serialization for the `hf` type; this is simply writing/reading the underlying uint8_t value + template + void serialize_value(Archive& ar, hf& x) { + auto val = static_cast>(x); + serialization::value(ar, val); + if constexpr (Archive::is_deserializer) + x = static_cast(val); + } } namespace std { diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 48bd65f8d8e..828952d73e5 100755 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -65,40 +65,35 @@ namespace cryptonote { constexpr cryptonote::POS_random_value empty_random_value = {}; bool bitset = blk_header.POS.validator_bitset > 0; bool random_value = !(blk_header.POS.random_value == empty_random_value); - uint8_t hf_version = blk_header.major_version; - bool result = hf_version >= cryptonote::network_version_17_POS && (bitset || random_value); + auto hf_version = blk_header.major_version; + bool result = hf_version >= hf::hf17_POS && (bitset || random_value); return result; } //----------------------------------------------------------------------------------------------- bool block_has_POS_components(block const &blk) { bool signatures = blk.signatures.size(); - uint8_t hf_version = blk.major_version; + auto hf_version = blk.major_version; bool result = - (hf_version >= cryptonote::network_version_17_POS && signatures) || block_header_has_POS_components(blk); + (hf_version >= hf::hf17_POS && signatures) || block_header_has_POS_components(blk); return result; } //----------------------------------------------------------------------------------------------- - size_t get_min_block_weight(uint8_t version) + size_t get_min_block_weight(hf version) { - return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; - } - //----------------------------------------------------------------------------------------------- - size_t get_max_tx_size() - { - return CRYPTONOTE_MAX_TX_SIZE; + return BLOCK_GRANTED_FULL_REWARD_ZONE_V5; } //----------------------------------------------------------------------------------------------- // TODO(beldex): Move into beldex_economy, this will require access to beldex::exp2 - uint64_t block_reward_unpenalized_formula_v7(uint8_t version, uint64_t already_generated_coins, uint64_t height) + uint64_t block_reward_unpenalized_formula_v7(hf version, uint64_t already_generated_coins, uint64_t height) { - const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + const int target = version <= hf::hf1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; const int target_minutes = target / 60; const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - (target_minutes-1); - uint64_t result = (MONEY_SUPPLY - already_generated_coins) >> emission_speed_factor; + uint64_t result = (beldex::MONEY_SUPPLY - already_generated_coins) >> emission_speed_factor; - if (version < network_version_7 ) + if (version < hf::hf7) { if (result < FINAL_SUBSIDY_PER_MINUTE*target_minutes) { @@ -119,26 +114,26 @@ namespace cryptonote { return result; } - bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint64_t &reward_unpenalized, uint8_t version, uint64_t height) + bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint64_t &reward_unpenalized, hf version, uint64_t height) { //premine reward if (height == 1) { - reward = 1400000000000000000; + reward = 1400000000 * beldex::COIN; return true; } - if((height>=56500) && (version=56500) && (version< hf::hf17_POS)) { - reward = COIN * 2; + reward = 2 * beldex::COIN; return true; } - static_assert(TARGET_BLOCK_TIME_OLD % 1 == 0s, "difficulty targets must be a multiple of 60"); + static_assert(old::TARGET_BLOCK_TIME_12 % 1 == 0s, "difficulty targets must be a multiple of 60"); static_assert(TARGET_BLOCK_TIME % 1 == 0s, "difficulty targets must be a multiple of 60"); uint64_t base_reward = - version >= network_version_17_POS ? BLOCK_REWARD_HF17_POS : - version >= network_version_16 ? BLOCK_REWARD_HF16 : + version >= hf::hf17_POS ? beldex::BLOCK_REWARD_HF17_POS : + version >= hf::hf16 ? beldex::BLOCK_REWARD_HF16 : block_reward_unpenalized_formula_v7(version, already_generated_coins, height); uint64_t full_reward_zone = get_min_block_weight(version); @@ -206,8 +201,8 @@ namespace cryptonote { , account_public_address const & adr ) { - uint64_t address_prefix = subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - + auto& conf = get_config(nettype); + uint64_t address_prefix = subaddress ? conf.PUBLIC_SUBADDRESS_BASE58_PREFIX : conf.PUBLIC_ADDRESS_BASE58_PREFIX; return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr)); } //----------------------------------------------------------------------- @@ -217,7 +212,7 @@ namespace cryptonote { , crypto::hash8 const & payment_id ) { - uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = get_config(nettype).PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; integrated_address iadr = { adr, payment_id @@ -236,9 +231,10 @@ namespace cryptonote { , const std::string_view str ) { - uint64_t address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; - uint64_t subaddress_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; + auto& conf = get_config(nettype); + uint64_t address_prefix = conf.PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = conf.PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t subaddress_prefix = conf.PUBLIC_SUBADDRESS_BASE58_PREFIX; blobdata data; uint64_t prefix{0}; diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 400f8de054b..7f811789aa4 100755 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -107,11 +107,10 @@ namespace cryptonote { /************************************************************************/ bool block_header_has_POS_components(block_header const &blk_header); bool block_has_POS_components(block const &blk); - size_t get_min_block_weight(uint8_t version); - size_t get_max_tx_size(); + size_t get_min_block_weight(hf version); uint64_t block_reward_unpenalized_formula_v7(uint64_t already_generated_coins, uint64_t height); uint64_t block_reward_unpenalized_formula_v8(uint64_t height); - bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint64_t &reward_unpenalized, uint8_t version, uint64_t height); + bool get_base_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint64_t &reward_unpenalized, hf version, uint64_t height); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 9523ef4b3ea..97b75e6b412 100755 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -254,6 +254,20 @@ namespace boost a & x.t; } + template + inline void serialize(Archive &a, rct::BulletproofPlus &x, const boost::serialization::version_type ver) + { + a & x.V; + a & x.A; + a & x.A1; + a & x.B; + a & x.r1; + a & x.s1; + a & x.d1; + a & x.L; + a & x.R; + } + template inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver) { @@ -332,7 +346,7 @@ namespace boost a & x.type; if (x.type == rct::RCTType::Null) return; - if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG)) + if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG, rct::RCTType::BulletproofPlus)) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -348,7 +362,11 @@ namespace boost { a & x.rangeSigs; if (x.rangeSigs.empty()) + { a & x.bulletproofs; + if (ver >= 2u) + a & x.bulletproofs_plus; + } a & x.MGs; if (ver >= 1u) a & x.CLSAGs; @@ -362,7 +380,7 @@ namespace boost a & x.type; if (x.type == rct::RCTType::Null) return; - if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG)) + if (!tools::equals_any(x.type, rct::RCTType::Full, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG, rct::RCTType::BulletproofPlus)) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -374,11 +392,15 @@ namespace boost //-------------- a & x.p.rangeSigs; if (x.p.rangeSigs.empty()) + { a & x.p.bulletproofs; + if (ver >= 2u) + a & x.p.bulletproofs_plus; + } a & x.p.MGs; if (ver >= 1u) a & x.p.CLSAGs; - if (rct::is_rct_bulletproof(x.type)) + if (x.type == rct::RCTType::Bulletproof || x.type == rct::RCTType::Bulletproof2 || x.type == rct::RCTType::CLSAG || x.type == rct::RCTType::BulletproofPlus) a & x.p.pseudoOuts; } @@ -391,6 +413,6 @@ namespace boost } } -BOOST_CLASS_VERSION(rct::rctSigPrunable, 1) -BOOST_CLASS_VERSION(rct::rctSig, 1) +BOOST_CLASS_VERSION(rct::rctSigPrunable, 2) +BOOST_CLASS_VERSION(rct::rctSig, 2) BOOST_CLASS_VERSION(rct::multisig_out, 1) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index a663fd92784..a35f2100d70 100755 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -82,7 +82,8 @@ namespace cryptonote uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs) { const rct::rctSig &rv = tx.rct_signatures; - const uint64_t bp_base = 368; + const bool plus = rv.type == rct::RCTType::BulletproofPlus; + const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2) const size_t n_outputs = tx.vout.size(); if (n_padded_outputs <= 2) return 0; @@ -90,8 +91,8 @@ namespace cryptonote while ((1u << nlr) < n_padded_outputs) ++nlr; nlr += 6; - const size_t bp_size = 32 * (9 + 2 * nlr); - CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction"); + const size_t bp_size = 32 * ((plus ? 6 : 9) + 2 * nlr); + CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= TX_BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(TX_BULLETPROOF_MAX_OUTPUTS) + " per transaction"); CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs " + std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size)); const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5; @@ -149,7 +150,33 @@ namespace cryptonote if (!base_only) { const bool bulletproof = rct::is_rct_bulletproof(rv.type); - if (bulletproof) + const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); + + if (bulletproof_plus) + { + if (rv.p.bulletproofs_plus.size() != 1) + { + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus size in tx " << get_transaction_hash(tx)); + return false; + } + if (rv.p.bulletproofs_plus[0].L.size() < 6) + { + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus L size in tx " << get_transaction_hash(tx)); + return false; + } + const size_t max_outputs = rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus[0]); + if (max_outputs < tx.vout.size()) + { + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus max outputs in tx " << get_transaction_hash(tx)); + return false; + } + const size_t n_amounts = tx.vout.size(); + CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs_plus[0].V.resize(n_amounts); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs_plus[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); + } + else if (bulletproof) { if (rv.p.bulletproofs.size() != 1) { @@ -295,6 +322,7 @@ namespace cryptonote //--------------------------------------------------------------- bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { + // This tries to compute the key image using a hardware device, if this succeeds return immediately, otherwise continue if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki)) { return true; @@ -392,11 +420,11 @@ namespace cryptonote // // TODO: get rid of the user-configurable default_decimal_point nonsense and just multiply // this value by the `COIN` constant. - for (size_t i = 0; i < CRYPTONOTE_DISPLAY_DECIMAL_POINT; i++) + for (size_t i = 0; i < beldex::DISPLAY_DECIMAL_POINT; i++) { if (amount > std::numeric_limits::max() / 10) return false; // would overflow - amount *= 10; + amount *= 10; // have to change } } @@ -407,10 +435,10 @@ namespace cryptonote return false; // fractional part contains non-digit // If too long, but with insignificant 0's, trim them off - while (parts[1].size() > CRYPTONOTE_DISPLAY_DECIMAL_POINT && parts[1].back() == '0') + while (parts[1].size() > beldex::DISPLAY_DECIMAL_POINT && parts[1].back() == '0') parts[1].remove_suffix(1); - if (parts[1].size() > CRYPTONOTE_DISPLAY_DECIMAL_POINT) + if (parts[1].size() > beldex::DISPLAY_DECIMAL_POINT) return false; // fractional part has too many significant digits uint64_t fractional; @@ -419,7 +447,7 @@ namespace cryptonote // Scale up the value if it wasn't a full fractional value, e.g. if we have "10.45" then we // need to convert the 45 we just parsed to 450'000'000. - for (size_t i = parts[1].size(); i < CRYPTONOTE_DISPLAY_DECIMAL_POINT; i++) + for (size_t i = parts[1].size(); i < beldex::DISPLAY_DECIMAL_POINT; i++) fractional *= 10; if (fractional > std::numeric_limits::max() - amount) @@ -435,9 +463,11 @@ namespace cryptonote if (tx.version < txversion::v2_ringct) return blob_size; const rct::rctSig &rv = tx.rct_signatures; - if (!rct::is_rct_bulletproof(rv.type)) + const bool bulletproof = rct::is_rct_bulletproof(rv.type); + const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); + if (!bulletproof && !bulletproof_plus) return blob_size; - const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs); + const size_t n_padded_outputs = bulletproof_plus ? rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus) : rct::n_bulletproof_max_amounts(rv.p.bulletproofs); uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs); CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits::max() - blob_size, "Weight overflow"); return blob_size + bp_clawback; @@ -447,6 +477,8 @@ namespace cryptonote { CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits::max(), "get_pruned_transaction_weight does not support non pruned txes"); CHECK_AND_ASSERT_MES(tx.version >= txversion::v2_ringct, std::numeric_limits::max(), "get_pruned_transaction_weight does not support v1 txes"); + CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTType::Bulletproof2 || tx.rct_signatures.type == rct::RCTType::CLSAG || tx.rct_signatures.type == rct::RCTType::BulletproofPlus, + std::numeric_limits::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight"); CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTType::Bulletproof2, std::numeric_limits::max(), "get_pruned_transaction_weight does not support older range proof types"); CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits::max(), "empty vin"); @@ -463,12 +495,12 @@ namespace cryptonote while ((n_padded_outputs = (1u << nrl)) < tx.vout.size()) ++nrl; nrl += 6; - uint64_t extra = 32 * (9 + 2 * nrl) + 2; + uint64_t extra = 32 * ((rct::is_rct_bulletproof_plus(tx.rct_signatures.type) ? 6 : 9) + 2 * nrl) + 2; weight += extra; // calculate deterministic CLSAG/MLSAG data size const size_t ring_size = var::get(tx.vin[0]).key_offsets.size(); - if (tx.rct_signatures.type == rct::RCTType::CLSAG) + if (rct::is_rct_clsag(tx.rct_signatures.type)) extra = tx.vin.size() * (ring_size + 2) * 32; else extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */); @@ -637,10 +669,10 @@ namespace cryptonote return true; } - bool add_master_node_state_change_to_tx_extra(std::vector& tx_extra, const tx_extra_master_node_state_change& state_change, const uint8_t hf_version) + bool add_master_node_state_change_to_tx_extra(std::vector& tx_extra, const tx_extra_master_node_state_change& state_change, const hf hf_version) { tx_extra_field field; - if (hf_version < network_version_13_checkpointing) + if (hf_version < hf::hf13_checkpointing) { CHECK_AND_ASSERT_MES(state_change.state == master_nodes::new_state::deregister, false, "internal error: cannot construct an old deregistration for a non-deregistration state change (before hardfork v12)"); @@ -809,9 +841,9 @@ namespace cryptonote add_tx_extra(tx_extra, winner); } //--------------------------------------------------------------- - bool get_master_node_state_change_from_tx_extra(const std::vector& tx_extra, tx_extra_master_node_state_change &state_change, const uint8_t hf_version) + bool get_master_node_state_change_from_tx_extra(const std::vector& tx_extra, tx_extra_master_node_state_change &state_change, const hf hf_version) { - if (hf_version >= cryptonote::network_version_13_checkpointing) { + if (hf_version >= hf::hf13_checkpointing) { // Look for a new-style state change field: return get_field_from_tx_extra(tx_extra, state_change); } @@ -1100,37 +1132,28 @@ namespace cryptonote cn_fast_hash(blob.data(), blob.size(), res); } //--------------------------------------------------------------- - std::string get_unit(unsigned int decimal_point) - { - if (decimal_point == (unsigned int)-1) - decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; - switch (decimal_point) - { - case 9: - return "beldex"; - case 6: - return "megarok"; - case 3: - return "kilorok"; - case 0: - return "rok"; - default: - ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); + std::string print_money(uint64_t amount, bool strip_zeros) { + constexpr unsigned int decimal_point = beldex::DISPLAY_DECIMAL_POINT; + std::string s = std::to_string(amount); + if (s.size() < decimal_point + 1) { + s.insert(0, decimal_point + 1 - s.size(), '0'); + } + s.insert(s.size() - decimal_point, "."); + if (strip_zeros) { + while (s.back() == '0') + s.pop_back(); + if (s.back() == '.') + s.pop_back(); } + return s; } //--------------------------------------------------------------- - std::string print_money(uint64_t amount, unsigned int decimal_point) + std::string format_money(uint64_t amount, bool strip_zeros) { - if (decimal_point == (unsigned int)-1) - decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; - std::string s = std::to_string(amount); - if(s.size() < decimal_point+1) - { - s.insert(0, decimal_point+1 - s.size(), '0'); - } - if (decimal_point > 0) - s.insert(s.size() - decimal_point, "."); - return s; + auto value = print_money(amount, strip_zeros); + value += ' '; + value += get_unit(); + return value; } //--------------------------------------------------------------- std::string print_tx_verification_context(tx_verification_context const &tvc, transaction const *tx) @@ -1142,7 +1165,7 @@ namespace cryptonote if (tvc.m_verifivation_failed) os << "Verification failed, connection should be dropped, "; //bad tx, should drop connection if (tvc.m_verifivation_impossible) os << "Verification impossible, related to alt chain, "; //the transaction is related with an alternative blockchain - if (tvc.m_should_be_relayed) os << "TX should be relayed, "; + if (!tvc.m_should_be_relayed) os << "TX should NOT be relayed, "; if (tvc.m_added_to_pool) os << "TX added to pool, "; if (tvc.m_low_mixin) os << "Insufficient mixin, "; if (tvc.m_double_spend) os << "Double spend TX, "; @@ -1167,6 +1190,28 @@ namespace cryptonote return buf; } //--------------------------------------------------------------- + std::unordered_set tx_verification_failure_codes(const tx_verification_context& tvc) { + std::unordered_set reasons; + + if (tvc.m_verifivation_failed) reasons.insert("failed"); + if (tvc.m_verifivation_impossible) reasons.insert("altchain"); + if (tvc.m_low_mixin) reasons.insert("mixin"); + if (tvc.m_double_spend) reasons.insert("double_spend"); + if (tvc.m_invalid_input) reasons.insert("invalid_input"); + if (tvc.m_invalid_output) reasons.insert("invalid_output"); + if (tvc.m_too_few_outputs) reasons.insert("too_few_outputs"); + if (tvc.m_too_big) reasons.insert("too_big"); + if (tvc.m_overspend) reasons.insert("overspend"); + if (tvc.m_fee_too_low) reasons.insert("fee_too_low"); + if (tvc.m_invalid_version) reasons.insert("invalid_version"); + if (tvc.m_invalid_type) reasons.insert("invalid_type"); + if (tvc.m_key_image_locked_by_mnode) reasons.insert("mnode_locked"); + if (tvc.m_key_image_blacklisted) reasons.insert("blacklisted"); + if (!tvc.m_should_be_relayed) reasons.insert("not_relayed"); + + return reasons; + } + //--------------------------------------------------------------- std::string print_vote_verification_context(vote_verification_context const &vvc, master_nodes::quorum_vote_t const *vote) { std::ostringstream os; @@ -1386,12 +1431,12 @@ namespace cryptonote LOG_ERROR("get_registration_hash addresses.size() != portions.size()"); return false; } - uint64_t portions_left = STAKING_PORTIONS; + uint64_t portions_left = old::STAKING_PORTIONS; for (uint64_t portion : portions) { if (portion > portions_left) { - LOG_ERROR(tr("Your registration has more than ") << STAKING_PORTIONS << tr(" portions, this registration is invalid!")); + LOG_ERROR(tr("Your registration has more than ") << old::STAKING_PORTIONS << tr(" portions, this registration is invalid!")); return false; } portions_left -= portion; @@ -1493,9 +1538,11 @@ namespace cryptonote std::vector absolute_output_offsets_to_relative(const std::vector& off) { std::vector res = off; - if(!off.size()) - return res; - std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + CHECK_AND_ASSERT_THROW_MES(not off.empty(), "absolute index to relative offset, no indices provided"); + + // vector must be sorted before calling this, else an index' offset would be negative + CHECK_AND_ASSERT_THROW_MES(std::is_sorted(res.begin(), res.end()), "absolute index to relative offset, indices not sorted"); + for(size_t i = res.size()-1; i != 0; i--) res[i] -= res[i-1]; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index aa9f2756e50..8318711455a 100755 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -39,6 +39,7 @@ #include "common/meta.h" #include "common/string_util.h" #include "serialization/binary_utils.h" +#include "serialization/json_archive.h" #include namespace epee @@ -129,8 +130,8 @@ namespace cryptonote crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra, size_t pk_index = 0); crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0); - bool add_master_node_state_change_to_tx_extra(std::vector& tx_extra, const tx_extra_master_node_state_change& state_change, uint8_t hf_version); - bool get_master_node_state_change_from_tx_extra(const std::vector& tx_extra, tx_extra_master_node_state_change& state_change, uint8_t hf_version); + bool add_master_node_state_change_to_tx_extra(std::vector& tx_extra, const tx_extra_master_node_state_change& state_change, hf hf_version); + bool get_master_node_state_change_from_tx_extra(const std::vector& tx_extra, tx_extra_master_node_state_change& state_change, hf hf_version); bool get_master_node_pubkey_from_tx_extra(const std::vector& tx_extra, crypto::public_key& pubkey); bool get_master_node_contributor_from_tx_extra(const std::vector& tx_extra, cryptonote::account_public_address& address); @@ -219,11 +220,37 @@ namespace cryptonote uint64_t get_block_height(const block& b); std::vector relative_output_offsets_to_absolute(const std::vector& off); std::vector absolute_output_offsets_to_relative(const std::vector& off); - std::string get_unit(unsigned int decimal_point = -1); - std::string print_money(uint64_t amount, unsigned int decimal_point = -1); + constexpr std::string_view get_unit() { return "BDX"sv; } + // Returns a monetary value with a decimal point; optionally strips insignificant trailing 0s. + std::string print_money(uint64_t amount, bool strip_zeros = false); + // Returns a formatted monetary value including the unit, e.g. "1.234567 BDX"; strips + // insignificant trailing 0s by default (unlike the above) but can be overridden to not do that. + std::string format_money(uint64_t amount, bool strip_zeros = true); std::string print_tx_verification_context (tx_verification_context const &tvc, transaction const *tx = nullptr); std::string print_vote_verification_context(vote_verification_context const &vvc, master_nodes::quorum_vote_t const *vote = nullptr); + + // Returns code strings for various tx verification failures: + // - "failed" -- general "bad transaction" code + // - "altchain" -- the transaction is spending outputs that exist on an altchain. + // - "mixin" -- the transaction has the wrong number of decoys + // - "double_spend" -- the transaction is spending outputs that are already spent + // - "invalid_input" -- one or more inputs in the transaction are invalid + // - "invalid_output" -- out or more outputs in the transaction are invalid + // - "too_few_outputs" -- the transaction does not create enough outputs (at least two are + // required, currently). + // - "too_big" -- the transaction is too large + // - "overspend" -- the transaction spends (via outputs + fees) more than the inputs + // - "fee_too_low" -- the transaction fee is insufficient + // - "invalid_version" -- the transaction version is invalid (the wallet likely needs an update). + // - "invalid_type" -- the transaction type is invalid + // - "mnode_locked" -- one or more outputs are currently staked to a registred master node and + // thus are not currently spendable on the blockchain. + // - "blacklisted" -- the outputs are currently blacklisted (from being in the 30-day penalty + // period following a master node deregistration). + // - "not_relayed" -- the transaction cannot be relayed to the network for some reason but may + // still have been accepted by this node. + std::unordered_set tx_verification_failure_codes(const tx_verification_context& tvc); bool is_valid_address(const std::string address, cryptonote::network_type nettype, bool allow_subaddress = true, bool allow_integrated = true); @@ -253,7 +280,8 @@ namespace cryptonote return true; } catch (const std::exception& e) { LOG_ERROR("Serialization of " << tools::type_name(typeid(T)) << " failed: " << e.what()); - return false; + // return false; + throw; } } //--------------------------------------------------------------- @@ -292,15 +320,12 @@ namespace cryptonote template std::string obj_to_json_str(T&& obj, bool indent = false) { - std::ostringstream ss; - serialization::json_archiver ar{ss, indent}; try { - serialize(ar, obj); + return serialization::dump_json(obj, indent ? 2 : -1); } catch (const std::exception& e) { LOG_ERROR("obj_to_json_str failed: serialization failed: " << e.what()); - return ""s; } - return ss.str(); + return ""s; } //--------------------------------------------------------------- blobdata block_to_blob(const block& b); diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 6745c8e36b6..552ea6ac772 100755 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -47,52 +47,6 @@ namespace cryptonote { using std::uint64_t; using std::vector; -#if defined(__x86_64__) - static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - low = mul128(a, b, &high); - } - -#else - - static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - // __int128 isn't part of the standard, so the previous function wasn't portable. mul128() in Windows is fine, - // but this portable function should be used elsewhere. Credit for this function goes to latexi95. - - uint64_t aLow = a & 0xFFFFFFFF; - uint64_t aHigh = a >> 32; - uint64_t bLow = b & 0xFFFFFFFF; - uint64_t bHigh = b >> 32; - - uint64_t res = aLow * bLow; - uint64_t lowRes1 = res & 0xFFFFFFFF; - uint64_t carry = res >> 32; - - res = aHigh * bLow + carry; - uint64_t highResHigh1 = res >> 32; - uint64_t highResLow1 = res & 0xFFFFFFFF; - - res = aLow * bHigh; - uint64_t lowRes2 = res & 0xFFFFFFFF; - carry = res >> 32; - - res = aHigh * bHigh + carry; - uint64_t highResHigh2 = res >> 32; - uint64_t highResLow2 = res & 0xFFFFFFFF; - - //Addition - - uint64_t r = highResLow1 + lowRes2; - carry = r >> 32; - low = (r << 32) | lowRes1; - r = highResHigh1 + highResLow2 + carry; - uint64_t d3 = r & 0xFFFFFFFF; - carry = r >> 32; - r = highResHigh2 + carry; - high = d3 | (r << 32); - } - -#endif - static inline bool cadd(uint64_t a, uint64_t b) { return a + b < a; } @@ -104,15 +58,15 @@ namespace cryptonote { bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. - mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); + top = mul128(SWAP64LE(((const uint64_t *) &hash)[3]), difficulty, &high); if (high != 0) { return false; } - mul(swap64le(((const uint64_t *) &hash)[0]), difficulty, low, cur); - mul(swap64le(((const uint64_t *) &hash)[1]), difficulty, low, high); + low = mul128(SWAP64LE(((const uint64_t *) &hash)[0]), difficulty, &cur); + low = mul128(SWAP64LE(((const uint64_t *) &hash)[1]), difficulty, &high); bool carry = cadd(cur, low); cur = high; - mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high); + low = mul128(SWAP64LE(((const uint64_t *) &hash)[2]), difficulty, &high); carry = cadc(cur, low, carry); carry = cadc(high, top, carry); return !carry; @@ -128,13 +82,13 @@ namespace cryptonote { timestamps.push_back(timestamp); difficulties.push_back(cumulative_difficulty); - bool before_hf16 = !is_hard_fork_at_least(nettype, network_version_17_POS, chain_height); + bool before_hf16 = !is_hard_fork_at_least(nettype, hf::hf17_POS, chain_height); // Trim down arrays - while (timestamps.size() > DIFFICULTY_BLOCKS_COUNT(before_hf16)) + while (timestamps.size() > old::DIFFICULTY_BLOCKS_COUNT(before_hf16)) timestamps.erase(timestamps.begin()); - while (difficulties.size() > DIFFICULTY_BLOCKS_COUNT(before_hf16)) + while (difficulties.size() > old::DIFFICULTY_BLOCKS_COUNT(before_hf16)) difficulties.erase(difficulties.begin()); } @@ -167,15 +121,15 @@ namespace cryptonote { { auto result = difficulty_calc_mode::normal; - if (!is_hard_fork_at_least(nettype, cryptonote::network_version_10_bulletproofs, height)) + if (!is_hard_fork_at_least(nettype, hf::hf10_bulletproofs, height)) result = difficulty_calc_mode::use_old_lwma; // HF12 switches to RandomX with a likely drastically reduced hashrate versus Turtle, so override // difficulty for the first difficulty window blocks: - else if (auto randomx_start_height = get_hard_fork_heights(nettype, network_version_13_checkpointing).first; - randomx_start_height && height >= *randomx_start_height && height <= *randomx_start_height + DIFFICULTY_WINDOW) + else if (auto randomx_start_height = get_hard_fork_heights(nettype, hf::hf13_checkpointing).first; + randomx_start_height && height >= *randomx_start_height && height <= *randomx_start_height + old::DIFFICULTY_WINDOW) result = difficulty_calc_mode::hf12_override; - else if (auto POS_start_height = get_hard_fork_heights(nettype, network_version_17_POS).first; - nettype == MAINNET && POS_start_height && height >= *POS_start_height && height <= *POS_start_height + DIFFICULTY_WINDOW) + else if (auto POS_start_height = get_hard_fork_heights(nettype, hf::hf17_POS).first; + nettype == MAINNET && POS_start_height && height >= *POS_start_height && height <= *POS_start_height + old::DIFFICULTY_WINDOW) result = difficulty_calc_mode::hf16_override; return result; @@ -187,7 +141,7 @@ namespace cryptonote { difficulty_calc_mode mode) { const int64_t T = static_cast(target_seconds); - size_t N = DIFFICULTY_WINDOW; + size_t N = old::DIFFICULTY_WINDOW; // Return a difficulty of 1 for first 4 blocks if it's the start of the chain. if (timestamps.size() < 4) { diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 3818139a1a8..1ff8ce357f1 100755 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -36,39 +36,41 @@ namespace cryptonote { // version 7 from the start of the blockchain, inhereted from Monero mainnet static constexpr std::array mainnet_hard_forks = { - hard_fork{1, 0, 1, 1548750273 }, // Beldex 0.1: Beldex is born - hard_fork{7, 0, 10, 1548750283 }, - hard_fork{8, 0, 40000, 1559474448 }, - hard_fork{11, 0, 56240, 1577836800 }, - hard_fork{12, 0, 126874, 1578704502 }, - hard_fork{15, 0, 742420, 1636320320 }, //Friday, December 10, 2021 6:00:00 PM (GMT) - hard_fork{17, 0, 742421, 1636320540 }, - hard_fork{18, 0, 2986890, 1706506200 }, // Monday, January 29, 2024 5:30:00 AM (UTC) - hard_fork{19, 0, 3546545, 1725514200 }, // Thursday, September 5, 2024 5:30:00 AM (UTC) + hard_fork{hf::hf1, 0, 1, 1548750273 }, // Beldex 1.0: Beldex is born + hard_fork{hf::hf7, 0, 10, 1548750283 }, + hard_fork{hf::hf8, 0, 40000, 1559474448 }, + hard_fork{hf::hf11_infinite_staking, 0, 56240, 1577836800 }, + hard_fork{hf::hf12_security_signature,0, 126874, 1578704502 }, + hard_fork{hf::hf15_flash, 0, 742420, 1636320320 }, //Friday, December 10, 2021 6:00:00 PM (GMT) + hard_fork{hf::hf17_POS, 0, 742421, 1636320540 }, + hard_fork{hf::hf18_bns, 0, 2986890, 1706506200 }, // Monday, January 29, 2024 5:30:00 AM (UTC) + hard_fork{hf::hf19_enhance_bns, 0, 3546545, 1725514200 }, // Thursday, September 5, 2024 5:30:00 AM (UTC) + hard_fork{hf::hf20_bulletproof_plus, 0, 4939540, 1765105200 }, // Sunday, December 7, 2025 4:30:00 AM (UTC) }; static constexpr std::array testnet_hard_forks = { - hard_fork{1, 0, 1, 1548474440 }, - hard_fork{7, 0, 10, 1559474448 }, - hard_fork{8, 0, 40000, 1559474448 }, - hard_fork{11, 0, 54288, 1628224369 }, - hard_fork{12, 0, 104832, 1629012232 }, // Sunday, August 15, 2021 7:23:52 AM - hard_fork{15, 0, 169950, 1636391396 }, // Monday, November 8, 2021 5:09:56 PM - hard_fork{17, 0, 169960, 1636391696 }, // Monday, November 8, 2021 5:14:56 PM - hard_fork{18, 0, 1251330, 1701063000 }, // Monday, November 27, 2023 5:30:00 AM - hard_fork{19, 0, 1997558, 1723447800 }, // Monday, Aug 12, 2024 7:30:00 AM + hard_fork{hf::hf1, 0, 1, 1548474440 }, + hard_fork{hf::hf7, 0, 10, 1559474448 }, + hard_fork{hf::hf8, 0, 40000, 1559474448 }, + hard_fork{hf::hf11_infinite_staking, 0, 54288, 1628224369 }, + hard_fork{hf::hf12_security_signature,0, 104832, 1629012232 }, // Sunday, August 15, 2021 7:23:52 AM + hard_fork{hf::hf15_flash, 0, 169950, 1636391396 }, // Monday, November 8, 2021 5:09:56 PM + hard_fork{hf::hf17_POS, 0, 169960, 1636391696 }, // Monday, November 8, 2021 5:14:56 PM + hard_fork{hf::hf18_bns, 0, 1251330, 1701063000 }, // Monday, November 27, 2023 5:30:00 AM + hard_fork{hf::hf19_enhance_bns, 0, 1997558, 1723447800 }, // Monday, Aug 12, 2024 7:30:00 AM + hard_fork{hf::hf20_bulletproof_plus, 0, 3262180, 1761388200 }, // Saturday, Oct 25, 2025 10:30:00 AM }; static constexpr std::array devnet_hard_forks = { - hard_fork{ 7, 0, 0, 1599848400 }, - hard_fork{ 17, 0, 2, 1599848400 }, + hard_fork{hf::hf7, 0, 0, 1599848400 }, + hard_fork{hf::hf17_POS, 0, 2, 1599848400 }, }; template static constexpr bool is_ordered(const std::array& forks) { - if (N == 0 || forks[0].version < 1) + if (N == 0 || forks[0].version < hf::hf1) return false; for (size_t i = 1; i < N; i++) { auto& hf = forks[i]; @@ -102,7 +104,7 @@ std::pair get_hard_forks(network_type type) std::pair, std::optional> -get_hard_fork_heights(network_type nettype, uint8_t version) { +get_hard_fork_heights(network_type nettype, hf version) { std::pair, std::optional> found; for (auto [it, end] = get_hard_forks(nettype); it != end; it++) { if (it->version > version) { // This (and anything else) are in the future @@ -116,7 +118,7 @@ get_hard_fork_heights(network_type nettype, uint8_t version) { return found; } -uint8_t hard_fork_ceil(network_type nettype, uint8_t version) { +hf hard_fork_ceil(network_type nettype, hf version) { auto [it, end] = get_hard_forks(nettype); for (; it != end; it++) if (it->version >= version) @@ -125,9 +127,9 @@ uint8_t hard_fork_ceil(network_type nettype, uint8_t version) { return version; } -std::pair +std::pair get_network_version_revision(network_type nettype, uint64_t height) { - std::pair result; + std::pair result; for (auto [it, end] = get_hard_forks(nettype); it != end; it++) { if (it->height <= height) result = {it->version, it->mnode_revision}; @@ -137,18 +139,22 @@ get_network_version_revision(network_type nettype, uint64_t height) { return result; } -bool is_hard_fork_at_least(network_type type, uint8_t version, uint64_t height) { +bool is_hard_fork_at_least(network_type type, hf version, uint64_t height) { return get_network_version(type, height) >= version; } -std::pair +std::pair get_ideal_block_version(network_type nettype, uint64_t height) { - std::pair result; + std::pair result; for (auto [it, end] = get_hard_forks(nettype); it != end; it++) { - if (it->height <= height) + if (it->height <= height){ result.first = it->version; - result.second = it->version; + result.second = it->mnode_revision; + } + if (result.first < hf::hf20_bulletproof_plus) + result.second = static_cast(it->version); + } return result; } diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index 568a8ee55f2..4dd2571ed70 100755 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -36,7 +36,7 @@ namespace cryptonote // Defines where hard fork (i.e. new minimum network versions) begin struct hard_fork { - uint8_t version; // Blockchain major version + hf version; // Blockchain major version uint8_t mnode_revision; // Mnode revision for enforcing non-blockchain-breaking mandatory mester node updates uint64_t height; time_t time; @@ -54,7 +54,7 @@ namespace cryptonote // outdated), and returns nullopt for B if the version indicates that top network version we know // about (i.e. there is no subsequent hardfork scheduled). std::pair, std::optional> - get_hard_fork_heights(network_type type, uint8_t version); + get_hard_fork_heights(network_type type, hf version); // Returns the lowest network version >= the given version, that is, it rounds up missing hf table // entries to the next largest entry. Typically this returns the network version itself, but if @@ -69,18 +69,18 @@ namespace cryptonote // ... // hard_fork_ceil(14) == 14 // hard_fork_ceil(15) == 15 - uint8_t hard_fork_ceil(network_type type, uint8_t version); + hf hard_fork_ceil(network_type type, hf version); // Returns true if the given height is sufficiently high to be at or after the given hard fork // version. - bool is_hard_fork_at_least(network_type type, uint8_t version, uint64_t height); + bool is_hard_fork_at_least(network_type type, hf version, uint64_t height); // Returns the active network version and mnode revision for the given height. - std::pair + std::pair get_network_version_revision(network_type nettype, uint64_t height); // Returns the network (i.e. block) version for the given height. - inline uint8_t get_network_version(network_type nettype, uint64_t height) { + inline hf get_network_version(network_type nettype, uint64_t height) { return get_network_version_revision(nettype, height).first; } @@ -88,14 +88,14 @@ namespace cryptonote // a shortcut for `get_hard_fork_heights(type, hard_fork_ceil(type, version)).first`, i.e. it // returns the first height at which `version` rules become active (even if they became active at // a hard fork > the given value). - inline std::optional hard_fork_begins(network_type type, uint8_t version) { + inline std::optional hard_fork_begins(network_type type, hf version) { return get_hard_fork_heights(type, hard_fork_ceil(type, version)).first; } // Returns the "ideal" network version that we want to use on blocks we create, which is to use // the required major version for major version and the maximum major version we know about as // minor version. If this seems a bit silly, it is, and will be changed in the future. - std::pair get_ideal_block_version(network_type nettype, uint64_t height); + std::pair get_ideal_block_version(network_type nettype, uint64_t height); } // namespace cryptonote diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 84f4277d684..0ce2ee6c308 100755 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -33,7 +33,6 @@ #include #include #include "cryptonote_basic/cryptonote_format_utils.h" -#include "epee/misc_os_dependent.h" #include "common/command_line.h" #include "common/util.h" #include "common/file.h" @@ -57,30 +56,15 @@ namespace cryptonote namespace { - const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; - const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; + const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; } miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1), m_template{}, - m_template_no(0), - m_diffic(0), - m_thread_index(0), m_phandler(phandler), - m_gbh(gbh), - m_height(0), - m_pausers_count(0), - m_threads_total(0), - m_starter_nonce(0), - m_last_hr_merge_time(0), - m_hashes(0), - m_total_hashes(0), - m_do_print_hashrate(false), - m_do_mining(false), - m_current_hash_rate(0), - m_block_reward(0) + m_gbh(gbh) {} //----------------------------------------------------------------------------------------------------- miner::~miner() @@ -116,13 +100,7 @@ namespace cryptonote uint64_t height{}; uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? - cryptonote::blobdata extra_nonce; - if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) - { - extra_nonce = m_extra_messages[m_config.current_extra_message_index]; - } - - if(!m_phandler->create_next_miner_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce)) + if(!m_phandler->create_next_miner_block_template(bl, m_mine_address, di, height, expected_reward, ""s)) { LOG_ERROR("Failed to get_block_template(), stopping mining"); return false; @@ -138,143 +116,17 @@ namespace cryptonote return true; }); - m_update_merge_hr_interval.do_call([&](){ - merge_hr(); - return true; - }); - - m_autodetect_interval.do_call([&](){ - update_autodetection(); - return true; - }); - return true; } //----------------------------------------------------------------------------------------------------- - void miner::do_print_hashrate(bool do_hr) - { - m_do_print_hashrate = do_hr; - } - //----------------------------------------------------------------------------------------------------- - void miner::merge_hr() - { - if(m_last_hr_merge_time && is_mining()) - { - m_current_hash_rate = m_hashes * 1000 / ((epee::misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); - std::unique_lock lock{m_last_hash_rates_lock}; - m_last_hash_rates.push_back(m_current_hash_rate); - if(m_last_hash_rates.size() > 19) - m_last_hash_rates.pop_front(); - if(m_do_print_hashrate) - { - uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); - float hr = static_cast(total_hr)/static_cast(m_last_hash_rates.size()); - const auto flags = std::cout.flags(); - const auto precision = std::cout.precision(); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << std::setiosflags(flags) << std::setprecision(precision) << std::endl; - } - } - m_last_hr_merge_time = epee::misc_utils::get_tick_count(); - m_hashes = 0; - } - //----------------------------------------------------------------------------------------------------- - void miner::update_autodetection() - { - if (m_threads_autodetect.empty()) - return; - - uint64_t now = epee::misc_utils::get_ns_count(); - uint64_t dt = now - m_threads_autodetect.back().first; - if (dt < AUTODETECT_WINDOW * 1000000000ull) - return; - - // work out how many more hashes we got - m_threads_autodetect.back().first = dt; - uint64_t dh = m_total_hashes - m_threads_autodetect.back().second; - m_threads_autodetect.back().second = dh; - float hs = dh / (dt / (float)1000000000); - MGINFO("Mining autodetection: " << m_threads_autodetect.size() << " threads: " << hs << " H/s"); - - // when we don't increase by at least 2%, stop, otherwise check next - // if N and N+1 have mostly the same hash rate, we want to "lighter" one - bool found = false; - if (m_threads_autodetect.size() > 1) - { - int previdx = m_threads_autodetect.size() - 2; - float previous_hs = m_threads_autodetect[previdx].second / (m_threads_autodetect[previdx].first / (float)1000000000); - if (previous_hs > 0 && hs / previous_hs < AUTODETECT_GAIN_THRESHOLD) - { - m_threads_total = m_threads_autodetect.size() - 1; - m_threads_autodetect.clear(); - MGINFO("Optimal number of threads seems to be " << m_threads_total); - found = true; - } - } - - if (!found) - { - // setup one more thread - m_threads_autodetect.push_back({now, m_total_hashes}); - m_threads_total = m_threads_autodetect.size(); - } - - // restart all threads - std::unique_lock lock{m_threads_lock}; - m_stop = true; - for (auto& th : m_threads) - if (th.joinable()) - th.join(); - m_threads.clear(); - m_stop = false; - m_thread_index = 0; - for(size_t i = 0; i != m_threads_total; i++) - m_threads.emplace_back([this] { return worker_thread(false); }); - } - //----------------------------------------------------------------------------------------------------- void miner::init_options(boost::program_options::options_description& desc) { - command_line::add_arg(desc, arg_extra_messages); command_line::add_arg(desc, arg_start_mining); command_line::add_arg(desc, arg_mining_threads); } //----------------------------------------------------------------------------------------------------- bool miner::init(const boost::program_options::variables_map& vm, network_type nettype) { - if(command_line::has_arg(vm, arg_extra_messages)) - { - std::string buff; - bool r = tools::slurp_file(fs::u8path(command_line::get_arg(vm, arg_extra_messages)), buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); - auto extra_vec = tools::split_any(buff, "\n"sv, true); - m_extra_messages.resize(extra_vec.size()); - for(size_t i = 0; i != extra_vec.size(); i++) - { - tools::trim(extra_vec[i]); - if(!extra_vec[i].size()) - continue; - if (!oxenc::is_base64(extra_vec[i])) - { - MWARNING("Invalid (non-base64) extra message `" << extra_vec[i] << "'"); - continue; - } - - std::string buff = oxenc::from_base64(extra_vec[i]); - if(buff != "0") - m_extra_messages[i] = buff; - } - m_config_dir = fs::u8path(command_line::get_arg(vm, arg_extra_messages)).parent_path(); - m_config = {}; - fs::path filename = m_config_dir / MINER_CONFIG_FILE_NAME; - if (std::string contents; - !tools::slurp_file(filename, contents) || - !epee::serialization::load_t_from_json(m_config, contents)) - { - MERROR("Failed to load data from " << filename); - return false; - } - MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); - } - if(command_line::has_arg(vm, arg_start_mining)) { address_parse_info info; @@ -309,16 +161,10 @@ namespace cryptonote return m_threads_total; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, uint64_t stop_after, bool slow_mining) + bool miner::start(const account_public_address& adr, int threads_count, int stop_after, bool slow_mining) { m_mine_address = adr; - m_threads_total = static_cast(threads_count); - if (threads_count == 0) - { - m_threads_autodetect.clear(); - m_threads_autodetect.push_back({epee::misc_utils::get_ns_count(), m_total_hashes}); - m_threads_total = 1; - } + m_threads_total = std::max(threads_count, 1); m_starter_nonce = crypto::rand(); std::unique_lock lock{m_threads_lock}; if(is_mining()) @@ -336,32 +182,24 @@ namespace cryptonote request_block_template();//lets update block template m_stop = false; - m_thread_index = 0; - m_stop_height = stop_after ? m_height + stop_after : std::numeric_limits::max(); - if (stop_after) + m_stop_height = stop_after > 0 ? m_height + stop_after : std::numeric_limits::max(); + if (stop_after > 0) MGINFO("Mining until height " << m_stop_height); - for(size_t i = 0; i != m_threads_total; i++) - { - m_threads.emplace_back([=] { return worker_thread(slow_mining); }); - } + for (int i = 0; i < m_threads_total; i++) + m_threads.emplace_back([=] { return worker_thread(i, slow_mining); }); - if (threads_count == 0) - MINFO("Mining has started, autodetecting optimal number of threads, good luck!" ); - else - MINFO("Mining has started with " << threads_count << " threads, good luck!" ); + MINFO("Mining has started with " << m_threads_total << " threads, good luck!" ); return true; } //----------------------------------------------------------------------------------------------------- - uint64_t miner::get_speed() const + double miner::get_speed() const { - if(is_mining()) { + if (is_mining()) { return m_current_hash_rate; } - else { - return 0; - } + return 0.0; } //----------------------------------------------------------------------------------------------------- extern "C" void rx_stop_mining(void); @@ -385,7 +223,6 @@ namespace cryptonote MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); m_threads.clear(); - m_threads_autodetect.clear(); rx_stop_mining(); return true; } @@ -417,7 +254,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- void miner::pause() { - std::unique_lock lock{m_miners_count_lock}; + std::unique_lock lock{m_miners_count_mutex}; MDEBUG("miner::pause: " << m_pausers_count << " -> " << (m_pausers_count + 1)); ++m_pausers_count; if(m_pausers_count == 1 && is_mining()) @@ -426,7 +263,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- void miner::resume() { - std::unique_lock lock{m_miners_count_lock}; + std::unique_lock lock{m_miners_count_mutex}; MDEBUG("miner::resume: " << m_pausers_count << " -> " << (m_pausers_count - 1)); --m_pausers_count; if(m_pausers_count < 0) @@ -438,12 +275,11 @@ namespace cryptonote MDEBUG("MINING RESUMED"); } //----------------------------------------------------------------------------------------------------- - bool miner::worker_thread(bool slow_mining) + bool miner::worker_thread(uint32_t index, bool slow_mining) { - uint32_t th_local_index = m_thread_index++; - MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); - MGINFO("Miner thread was started ["<< th_local_index << "]"); - uint32_t nonce = m_starter_nonce + th_local_index; + MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(index) + "]"); + MGINFO("Miner thread was started ["<< index << "]"); + uint32_t nonce = m_starter_nonce + index; uint64_t height = 0; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; @@ -468,7 +304,7 @@ namespace cryptonote height = m_height; } local_template_ver = m_template_no; - nonce = m_starter_nonce + th_local_index; + nonce = m_starter_nonce + index; } if(!local_template_ver)//no any set_block_template call @@ -493,23 +329,16 @@ namespace cryptonote if(check_hash(h, local_diff)) { //we lucky! - ++m_config.current_extra_message_index; MGINFO_GREEN("Found block " << get_block_hash(b) << " at height " << height << " for difficulty: " << local_diff); cryptonote::block_verification_context bvc; - if(!m_phandler->handle_block_found(b, bvc) || !bvc.m_added_to_main_chain) - --m_config.current_extra_message_index; - else if (!m_config_dir.empty()) - //success update, lets update config - if (std::string json; epee::serialization::store_t_to_json(m_config, json)) - tools::dump_file(m_config_dir / fs::u8path(MINER_CONFIG_FILE_NAME), json); + m_phandler->handle_block_found(b, bvc); } - nonce+=m_threads_total; + nonce += static_cast(m_threads_total); ++m_hashes; - ++m_total_hashes; } rx_slow_hash_free_state(); - MGINFO("Miner thread stopped ["<< th_local_index << "]"); + MGINFO("Miner thread stopped ["<< index << "]"); if (call_stop) // Call in a detached thread because the thread calling stop() needs to be able to join this // worker thread. diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 6ce919b2c46..90b3608cdd1 100755 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -70,8 +70,8 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, uint64_t stop_after = 0, bool slow_mining = false); - uint64_t get_speed() const; + bool start(const account_public_address& adr, int threads_count, int stop_after = 0, bool slow_mining = false); + double get_speed() const; uint32_t get_threads_count() const; bool stop(); bool is_mining() const; @@ -82,14 +82,11 @@ namespace cryptonote static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height); void pause(); void resume(); - void do_print_hashrate(bool do_hr); uint64_t get_block_reward() const { return m_block_reward; } private: - bool worker_thread(bool slow_mining = false); + bool worker_thread(uint32_t index, bool slow_mining = false); bool request_block_template(); - void merge_hr(); - void update_autodetection(); struct miner_config { @@ -105,14 +102,14 @@ namespace cryptonote uint64_t m_stop_height = std::numeric_limits::max(); std::mutex m_template_lock; block m_template; - std::atomic m_template_no; + std::atomic m_template_no = 0; std::atomic m_starter_nonce; - difficulty_type m_diffic; - uint64_t m_height; + difficulty_type m_diffic = 0; + uint64_t m_height = 0; std::atomic m_thread_index; - std::atomic m_threads_total; - std::atomic m_pausers_count; - std::mutex m_miners_count_lock; + std::atomic m_threads_total; + std::atomic m_pausers_count; + std::mutex m_miners_count_mutex; std::list m_threads; std::mutex m_threads_lock; @@ -120,20 +117,14 @@ namespace cryptonote get_block_hash_t m_gbh; account_public_address m_mine_address; tools::periodic_task m_update_block_template_interval{5s}; - tools::periodic_task m_update_merge_hr_interval{2s}; - tools::periodic_task m_autodetect_interval{1s}; - std::vector m_extra_messages; - miner_config m_config; - fs::path m_config_dir; - std::atomic m_last_hr_merge_time; - std::atomic m_hashes; - std::atomic m_total_hashes; - std::atomic m_current_hash_rate; - std::mutex m_last_hash_rates_lock; - std::list m_last_hash_rates; - bool m_do_print_hashrate; - bool m_do_mining; - std::vector> m_threads_autodetect; - std::atomic m_block_reward; + tools::periodic_task m_update_hashrate_interval{2s}; + + mutable std::mutex m_hashrate_mutex; + std::optional m_last_hr_update; + std::atomic m_hashes = 0; + double m_current_hash_rate = 0.0; + + bool m_do_mining = false; + std::atomic m_block_reward = 0; }; } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6ac596fdaff..0fd15bfff30 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -30,197 +30,279 @@ #pragma once + +#include #include #include #include -#include #include #include #include #include +#include using namespace std::literals; -#define EMISSION_SPEED_FACTOR_PER_MINUTE (28) - -#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 -#define CRYPTONOTE_MAX_TX_SIZE 1000000 -#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000 -#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 -#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 -#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 60*10 -#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10 -#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17 2 -#define CRYPTONOTE_DEFAULT_TX_MIXIN 9 -#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)500000000) // 3 * pow(10, 7) - -#define STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS 20 -#define STAKING_PORTIONS UINT64_C(0xfffffffffffffffc) -#define MAX_NUMBER_OF_CONTRIBUTORS 4 -#define MIN_PORTIONS (STAKING_PORTIONS / MAX_NUMBER_OF_CONTRIBUTORS) - -static_assert(STAKING_PORTIONS % 12 == 0, "Use a multiple of twelve, so that it divides evenly by two, three, or four contributors."); - -#define STAKING_AUTHORIZATION_EXPIRATION_WINDOW (60*60*24*7*2) // 2 weeks - -#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 11 - -#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 -#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 // NOTE(BDX): For testing suite, //size of block (bytes) after which reward for block calculated using block size - before first fork -#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 -#define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE 100000 // size in blocks of the long term block weight median window -#define CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR 50 -#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 -#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 9 - -#define FEE_PER_KB ((uint64_t)2000000000) // 2 BDX (= 2 * pow(10, 9)) -#define FEE_PER_BYTE ((uint64_t)215) // Fallback used in wallet if no fee is available from RPC -#define FEE_PER_BYTE_V12 ((uint64_t)17200) // Higher fee (and fallback) in v12 (only, v13 switches back) -#define FEE_PER_OUTPUT ((uint64_t)20000000) // 0.02 BDX per tx output (in addition to the per-byte fee), starting in v13 -#define FEE_PER_OUTPUT_V17 ((uint64_t)100000) // 0.0001 BDX per tx output -#define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000) // 10 * pow(10,12) -#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)400000000) -#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT ((uint64_t)300000) -#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT_V17 ((uint64_t)30000) // Only v17 - -#define DIFFICULTY_TARGET_V2 120 // seconds -#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork -constexpr auto TARGET_BLOCK_TIME_OLD = 2min; -constexpr uint64_t BLOCKS_PER_HOUR_OLD = 1h / TARGET_BLOCK_TIME_OLD; -constexpr uint64_t BLOCKS_PER_DAY_OLD = 24h / TARGET_BLOCK_TIME_OLD; - -constexpr auto TARGET_BLOCK_TIME = 30s; -constexpr uint64_t BLOCKS_PER_HOUR = 1h / TARGET_BLOCK_TIME; -constexpr uint64_t BLOCKS_PER_DAY = 24h / TARGET_BLOCK_TIME; - -constexpr uint64_t DIFFICULTY_WINDOW = 59; -constexpr uint64_t DIFFICULTY_BLOCKS_COUNT(bool before_hf16) -{ - // NOTE: We used to have a different setup here where, - // DIFFICULTY_WINDOW = 60 - // DIFFICULTY_BLOCKS_COUNT = 61 - // next_difficulty_v2's N = DIFFICULTY_WINDOW - 1 - // - // And we resized timestamps/difficulties to (N+1) (chopping off the latest timestamp). - // - // Now we re-adjust DIFFICULTY_WINDOW to 59. To preserve the old behaviour we - // add +2. After HF16 we avoid trimming the top block and just add +1. - // - // Ideally, we just set DIFFICULTY_BLOCKS_COUNT to DIFFICULTY_WINDOW - // + 1 for before and after HF16 (having one unified constant) but this - // requires some more investigation to get it working with pre HF16 blocks and - // alt chain code without bugs. - uint64_t result = (before_hf16) ? DIFFICULTY_WINDOW + 2 : DIFFICULTY_WINDOW + 1; - return result; +namespace cryptonote { + +/// Cryptonote protocol related constants: + +inline constexpr uint64_t EMISSION_SPEED_FACTOR_PER_MINUTE = 28; + +inline constexpr uint64_t MAX_BLOCK_NUMBER = 500000000; +inline constexpr size_t MAX_TX_SIZE = 1000000; +inline constexpr uint64_t MAX_TX_PER_BLOCK = 0x10000000; +inline constexpr uint64_t MINED_MONEY_UNLOCK_WINDOW = 60; +inline constexpr uint64_t DEFAULT_TX_SPENDABLE_AGE_V17 = 2; +inline constexpr uint64_t TX_OUTPUT_DECOYS = 9; +inline constexpr size_t TX_BULLETPROOF_MAX_OUTPUTS = 16; +inline constexpr size_t TX_BULLETPROOF_PLUS_MAX_OUTPUTS = 16; +inline constexpr uint64_t PUBLIC_ADDRESS_TEXTBLOB_VER = 0; + +inline constexpr uint64_t FINAL_SUBSIDY_PER_MINUTE = 500000000; // 3 * pow(10, 7) + +inline constexpr uint64_t BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW = 11; + + // #define MAX_NUMBER_OF_CONTRIBUTORS 4 + // #define MIN_PORTIONS (STAKING_PORTIONS / MAX_NUMBER_OF_CONTRIBUTORS) + + // static_assert(STAKING_PORTIONS % 12 == 0, "Use a multiple of twelve, so that it divides evenly by two, three, or four contributors."); + + +inline constexpr uint64_t REWARD_BLOCKS_WINDOW = 100; +inline constexpr uint64_t BLOCK_GRANTED_FULL_REWARD_ZONE_V1 = 20000; // NOTE(oxen): For testing suite, //size of block (bytes) after which reward for block calculated using block size - before first fork +inline constexpr uint64_t BLOCK_GRANTED_FULL_REWARD_ZONE_V5 = 300000; //size of block (bytes) after which reward for block calculated using block size - second change, from v5 +inline constexpr uint64_t LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE = 100000; // size in blocks of the long term block weight median window +inline constexpr uint64_t SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR = 50; +inline constexpr uint64_t COINBASE_BLOB_RESERVED_SIZE = 600; + +inline constexpr uint64_t LOCKED_TX_ALLOWED_DELTA_BLOCKS = 1; + + // #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 9 + #define DIFFICULTY_TARGET_V2 120 // seconds + #define DIFFICULTY_TARGET_V1 60 // seconds - before first fork + +inline constexpr auto TARGET_BLOCK_TIME = 30s; +inline constexpr uint64_t BLOCKS_PER_HOUR = 1h / TARGET_BLOCK_TIME; +inline constexpr uint64_t BLOCKS_PER_DAY = 24h / TARGET_BLOCK_TIME; + +inline constexpr auto MEMPOOL_TX_LIVETIME = 3 * 24h; +inline constexpr auto MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 7 * 24h; +inline constexpr auto MEMPOOL_PRUNE_NON_STANDARD_TX_LIFETIME = 2h; +inline constexpr size_t DEFAULT_MEMPOOL_MAX_WEIGHT = 72h / TARGET_BLOCK_TIME * 300'000; // 3 days worth of full 300kB blocks + +inline constexpr uint64_t FEE_PER_BYTE = 215; // Fallback used in wallet if no fee is available from RPC +inline constexpr uint64_t FEE_PER_OUTPUT_V17 = 100000; // 0.0001 BDX per tx output +inline constexpr uint64_t DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT = 300000; +inline constexpr uint64_t FEE_QUANTIZATION_DECIMALS = 8; + +inline constexpr size_t BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT = 10000; // by default, blocks ids count in synchronizing +inline constexpr size_t BLOCKS_SYNCHRONIZING_DEFAULT_COUNT = 100; // by default, blocks count in blocks downloading +inline constexpr size_t BLOCKS_SYNCHRONIZING_MAX_COUNT = 2048; //must be a power of 2, greater than 128, equal to SEEDHASH_EPOCH_BLOCKS in rx-slow-hash.c + +inline constexpr size_t HASH_OF_HASHES_STEP = 256; + +// Hash domain separators +namespace hashkey { + inline constexpr std::string_view BULLETPROOF_EXPONENT = "bulletproof"sv; + inline constexpr std::string_view BULLETPROOF_PLUS_EXPONENT = "bulletproof_plus"sv; + inline constexpr std::string_view BULLETPROOF_PLUS_TRANSCRIPT = "bulletproof_plus_transcript"sv; + inline constexpr std::string_view RINGDB = "ringdsb\0"sv; + inline constexpr std::string_view SUBADDRESS = "SubAddr\0"sv; + inline constexpr unsigned char ENCRYPTED_PAYMENT_ID = 0x8d; + inline constexpr unsigned char WALLET = 0x8c; + inline constexpr unsigned char WALLET_CACHE = 0x8d; + inline constexpr unsigned char RPC_PAYMENT_NONCE = 0x58; + inline constexpr unsigned char MEMORY = 'k'; + inline constexpr std::string_view MULTISIG = "Multisig\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"sv; + inline constexpr std::string_view CLSAG_ROUND = "CLSAG_round"sv; + inline constexpr std::string_view CLSAG_AGG_0 = "CLSAG_agg_0"sv; + inline constexpr std::string_view CLSAG_AGG_1 = "CLSAG_agg_1"sv; } +// Maximum allowed stake contribution, as a fraction of the available contribution room. This +// should generally be slightly larger than 1. This is used to disallow large overcontributions +// which can happen when there are competing stakes submitted at the same time for the same +// master node. +using MAXIMUM_ACCEPTABLE_STAKE = std::ratio<101, 100>; + // #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 + // #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 old::TARGET_BLOCK_TIME_12 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS + // #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V3 TARGET_BLOCK_TIME * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS -#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 TARGET_BLOCK_TIME_OLD * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS -#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V3 TARGET_BLOCK_TIME * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS -#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 - +// see src/cryptonote_protocol/levin_notify.cpp +inline constexpr auto NOISE_MIN_EPOCH = 5min; +inline constexpr auto NOISE_EPOCH_RANGE = 30s; +inline constexpr auto NOISE_MIN_DELAY = 10s; +inline constexpr auto NOISE_DELAY_RANGE = 5s; +inline constexpr uint64_t NOISE_BYTES = 3 * 1024; // 3 kiB +inline constexpr size_t NOISE_CHANNELS = 2; +inline constexpr size_t MAX_FRAGMENTS = 20; // ~20 * NOISE_BYTES max payload size for covert/noise send + + +// p2p-specific constants: +namespace p2p { + + inline constexpr size_t LOCAL_WHITE_PEERLIST_LIMIT = 1000; + inline constexpr size_t LOCAL_GRAY_PEERLIST_LIMIT = 5000; + + inline constexpr int64_t DEFAULT_CONNECTIONS_COUNT_OUT = 8; + inline constexpr int64_t DEFAULT_CONNECTIONS_COUNT_IN = 32; + inline constexpr auto DEFAULT_HANDSHAKE_INTERVAL = 60s; + inline constexpr uint32_t DEFAULT_PACKET_MAX_SIZE = 50000000; + inline constexpr uint32_t DEFAULT_PEERS_IN_HANDSHAKE = 250; + inline constexpr auto DEFAULT_CONNECTION_TIMEOUT = 5s; + inline constexpr auto DEFAULT_SOCKS_CONNECT_TIMEOUT = 45s; + inline constexpr auto DEFAULT_PING_CONNECTION_TIMEOUT = 2s; + inline constexpr auto DEFAULT_INVOKE_TIMEOUT = 2min; + inline constexpr auto DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5s; + inline constexpr int DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; + inline constexpr size_t DEFAULT_ANCHOR_CONNECTIONS_COUNT = 2; + inline constexpr size_t DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT = 2; + inline constexpr int64_t DEFAULT_LIMIT_RATE_UP = 2048; // kB/s + inline constexpr int64_t DEFAULT_LIMIT_RATE_DOWN = 8192; // kB/s + inline constexpr auto FAILED_ADDR_FORGET = 1h; + inline constexpr auto IP_BLOCK_TIME = 24h; + inline constexpr size_t IP_FAILS_BEFORE_BLOCK = 10; + inline constexpr auto IDLE_CONNECTION_KILL_INTERVAL = 5min; +} // namespace p2p + +// filename constants: +inline constexpr auto DATA_DIRNAME = +#ifdef _WIN32 + "beldex"sv; // Buried in some windows filesystem maze location +#else + ".beldex"sv; // ~/.beldex +#endif + +inline constexpr auto CONF_FILENAME = "beldex.conf"sv; +inline constexpr auto SOCKET_FILENAME = "beldexd.sock"sv; +inline constexpr auto LOG_FILENAME = "beldex.log"sv; +inline constexpr auto POOLDATA_FILENAME = "poolstate.bin"sv; +inline constexpr auto BLOCKCHAINDATA_FILENAME = "data.mdb"sv; +inline constexpr auto BLOCKCHAINDATA_LOCK_FILENAME = "lock.mdb"sv; +inline constexpr auto P2P_NET_DATA_FILENAME = "p2pstate.bin"sv; + +inline constexpr uint64_t PRUNING_STRIPE_SIZE = 4096; // the smaller, the smoother the increase +inline constexpr uint64_t PRUNING_LOG_STRIPES = 3; // the higher, the more space saved +inline constexpr uint64_t PRUNING_TIP_BLOCKS = 5500; // the smaller, the more space saved +inline constexpr bool PRUNING_DEBUG_SPOOF_SEED = false; // For debugging only + +// Constants for hardfork versions: +enum class hf : uint8_t +{ hf1 = 1, + hf7 = 7, + hf8, + hf9_master_nodes, // Proof Of Stake w/ Master Nodes + hf10_bulletproofs, // Bulletproofs, Master Node Grace Registration Period, + hf11_infinite_staking, // Infinite Staking, CN-Turtle + hf12_security_signature, + hf13_checkpointing, // Checkpointing, RandomXL + hf14_enforce_checkpoints, + hf15_flash, // Beldex Storage Server, Belnet + hf16, + hf17_POS, // Proof Of Stake, Batched Governance + hf18_bns, + hf19_enhance_bns, // provided EVM address in BNS + hf20_bulletproof_plus, + + _next, + none = 0 + + // `hf` serialization is in cryptonote_basic/cryptonote_basic.h +}; + +constexpr auto hf_max = static_cast(static_cast(hf::_next) - 1); +constexpr auto hf_prev(hf x) { + if (x <= hf::hf7 || x > hf_max) return hf::none; + return static_cast(static_cast(x) - 1); +} -#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 100 //by default, blocks count in blocks downloading -#define BLOCKS_SYNCHRONIZING_MAX_COUNT 2048 //must be a power of 2, greater than 128, equal to SEEDHASH_EPOCH_BLOCKS +// Constants for which hardfork activates various features: +namespace feature { + constexpr auto PER_BYTE_FEE = hf::hf10_bulletproofs; + constexpr auto SMALLER_BP = hf::hf11_infinite_staking; + constexpr auto LONG_TERM_BLOCK_WEIGHT = hf::hf11_infinite_staking; + constexpr auto PER_OUTPUT_FEE = hf::hf15_flash; + constexpr auto ED25519_KEY = hf::hf15_flash; + constexpr auto FEE_BURNING = hf::hf15_flash; + constexpr auto FLASH = hf::hf15_flash; + constexpr auto REDUCE_FEE = hf::hf17_POS; + constexpr auto MIN_2_OUTPUTS = hf::hf17_POS; + constexpr auto REJECT_SIGS_IN_COINBASE = hf::hf17_POS; + constexpr auto ENFORCE_MIN_AGE = hf::hf17_POS; + constexpr auto EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY = hf::hf17_POS; + constexpr auto POS = hf::hf17_POS; + constexpr auto CLSAG = hf::hf15_flash; + constexpr auto PROOF_BTENC = hf::hf18_bns; + constexpr auto BULLETPROOF_PLUS = hf::hf20_bulletproof_plus; +} -#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days -#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME (86400*7) //seconds, one week +enum network_type : uint8_t +{ + MAINNET = 0, + TESTNET, + DEVNET, + FAKECHAIN, + UNDEFINED = 255 +}; + +// Constants for older hard-forks that are mostly irrelevant now, but are still needed to sync the +// older parts of the blockchain: +namespace old { + + // block time future time limit used in the mining difficulty algorithm: + inline constexpr uint64_t BLOCK_FUTURE_TIME_LIMIT_V2 = 60*10; + // Re-registration grace period (not used since HF11 infinite staking): + inline constexpr uint64_t STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS = 20; + // Before HF19, staking portions and fees (in SN registrations) are encoded as a numerator value + // with this implied denominator: + inline constexpr uint64_t STAKING_PORTIONS = UINT64_C(0xfffffffffffffffc); + // Before HF19 signed registrations were only valid for two weeks: + // TODO: After HF19 we eliminate the window-checking code entirely (as long as no expired + // registration has ever been sent to the blockchain then it should still sync fine). + inline constexpr std::chrono::seconds STAKING_AUTHORIZATION_EXPIRATION_WINDOW = 14 * 24h; + + inline constexpr uint64_t DEFAULT_TX_SPENDABLE_AGE = 10; + + inline constexpr uint64_t FEE_PER_BYTE_V12 = 17200; // Higher fee (and fallback) in v12 (only, v13 switches back) + inline constexpr uint64_t FEE_PER_OUTPUT = 20000000; // 0.02 BDX per tx output (in addition to the per-byte fee), starting in v13 + inline constexpr uint64_t DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT_V17 = 30000; // Only v17 (v18 switches back) + + // Dynamic fee calculations used before HF10: + inline constexpr uint64_t DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD = UINT64_C(10000000000); // 10 * pow(10,9) + inline constexpr uint64_t DYNAMIC_FEE_PER_KB_BASE_FEE_V5 = 400000000; + + inline constexpr uint64_t DIFFICULTY_WINDOW = 59; + inline constexpr uint64_t DIFFICULTY_BLOCKS_COUNT(bool before_hf16) + { + // NOTE: We used to have a different setup here where, + // DIFFICULTY_WINDOW = 60 + // DIFFICULTY_BLOCKS_COUNT = 61 + // next_difficulty_v2's N = DIFFICULTY_WINDOW - 1 + // + // And we resized timestamps/difficulties to (N+1) (chopping off the latest timestamp). + // + // Now we re-adjust DIFFICULTY_WINDOW to 59. To preserve the old behaviour we + // add +2. After HF16 we avoid trimming the top block and just add +1. + // + // Ideally, we just set DIFFICULTY_BLOCKS_COUNT to DIFFICULTY_WINDOW + // + 1 for before and after HF16 (having one unified constant) but this + // requires some more investigation to get it working with pre HF16 blocks and + // alt chain code without bugs. + uint64_t result = (before_hf16) ? DIFFICULTY_WINDOW + 2 : DIFFICULTY_WINDOW + 1; + return result; + } + + inline constexpr auto TARGET_BLOCK_TIME_12 = 2min; + inline constexpr uint64_t BLOCKS_PER_HOUR_12 = 1h / TARGET_BLOCK_TIME_12; + inline constexpr uint64_t BLOCKS_PER_DAY_12 = 24h / TARGET_BLOCK_TIME_12; -#define MEMPOOL_PRUNE_NON_STANDARD_TX_LIFETIME (2 * 60 * 60) // seconds, 2 hours -// see src/cryptonote_protocol/levin_notify.cpp -#define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes -#define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds -#define CRYPTONOTE_NOISE_MIN_DELAY 10 // seconds -#define CRYPTONOTE_NOISE_DELAY_RANGE 5 // seconds -#define CRYPTONOTE_NOISE_BYTES 3*1024 // 3 KiB -#define CRYPTONOTE_NOISE_CHANNELS 2 // Max outgoing connections per zone used for noise/covert sending - -#define CRYPTONOTE_MAX_FRAGMENTS 20 // ~20 * NOISE_BYTES max payload size for covert/noise send - - -#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 -#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 - -#define P2P_DEFAULT_CONNECTIONS_COUNT_OUT 8 -#define P2P_DEFAULT_CONNECTIONS_COUNT_IN 32 -#define P2P_DEFAULT_HANDSHAKE_INTERVAL 60 //secondes -#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size -#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 -#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds -#define P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT 45 // seconds -#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds -#define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes -#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds -#define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 -#define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 -#define P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT 2 -#define P2P_DEFAULT_LIMIT_RATE_UP 2048 // kB/s -#define P2P_DEFAULT_LIMIT_RATE_DOWN 8192 // kB/s - -#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour -#define P2P_IP_BLOCKTIME (60*60*24) //24 hour -#define P2P_IP_FAILS_BEFORE_BLOCK 10 -#define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes - -// TODO(doyle): Deprecate after checkpointing hardfork, remove notion of being -// able to sync non-fluffy blocks, keep here so we can still accept blocks -// pre-hardfork -#define P2P_SUPPORT_FLAG_FLUFFY_BLOCKS 0x01 -#define P2P_SUPPORT_FLAGS P2P_SUPPORT_FLAG_FLUFFY_BLOCKS - -#define CRYPTONOTE_NAME "beldex" -#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" -#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" -#define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb" -#define P2P_NET_DATA_FILENAME "p2pstate.bin" -#define MINER_CONFIG_FILE_NAME "miner_conf.json" - -#define THREAD_STACK_SIZE 5 * 1024 * 1024 - -#define HF_VERSION_PER_BYTE_FEE cryptonote::network_version_10_bulletproofs -#define HF_VERSION_SMALLER_BP cryptonote::network_version_11_infinite_staking -#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT cryptonote::network_version_11_infinite_staking -#define HF_VERSION_PER_OUTPUT_FEE cryptonote::network_version_14_enforce_checkpoints -#define HF_VERSION_ED25519_KEY cryptonote::network_version_14_enforce_checkpoints -#define HF_VERSION_FEE_BURNING cryptonote::network_version_15_flash -#define HF_VERSION_FLASH cryptonote::network_version_15_flash -#define HF_VERSION_REDUCE_FEE cryptonote::network_version_17_POS -#define HF_VERSION_MIN_2_OUTPUTS cryptonote::network_version_17_POS -#define HF_VERSION_REJECT_SIGS_IN_COINBASE cryptonote::network_version_17_POS -#define HF_VERSION_ENFORCE_MIN_AGE cryptonote::network_version_17_POS -#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY cryptonote::network_version_17_POS -#define HF_VERSION_POS cryptonote::network_version_17_POS -#define HF_VERSION_CLSAG cryptonote::network_version_15_flash -#define HF_VERSION_PROOF_BTENC cryptonote::network_version_18_bns - -#define PER_KB_FEE_QUANTIZATION_DECIMALS 8 - -#define HASH_OF_HASHES_STEP 256 - -#define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes - -#define BULLETPROOF_MAX_OUTPUTS 16 - -#define CRYPTONOTE_PRUNING_STRIPE_SIZE 4096 // the smaller, the smoother the increase -#define CRYPTONOTE_PRUNING_LOG_STRIPES 3 // the higher, the more space saved -#define CRYPTONOTE_PRUNING_TIP_BLOCKS 5500 // the smaller, the more space saved -//#define CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED +} // namespace old // New constants are intended to go here namespace config { - inline constexpr auto DNS_TIMEOUT = 20s; - inline constexpr uint64_t DEFAULT_FEE_ATOMIC_XMR_PER_KB = 500; // Just a placeholder! Change me! - inline constexpr uint8_t FEE_CALCULATION_MAX_RETRIES = 10; inline constexpr uint64_t DEFAULT_DUST_THRESHOLD = 2000000000; // 2 * pow(10, 9) - inline constexpr uint64_t BASE_REWARD_CLAMP_THRESHOLD = 100000000; // pow(10, 8) - - // Maximum allowed stake contribution, as a fraction of the available contribution room. This - // should generally be slightly larger than 1. This is used to disallow large overcontributions - // which can happen when there are competing stakes submitted at the same time for the same - // master node. - using MAXIMUM_ACCEPTABLE_STAKE = std::ratio<101, 100>; // Used to estimate the blockchain height from a timestamp, with some grace time. This can drift // slightly over time (because average block time is not typically *exactly* @@ -229,24 +311,24 @@ namespace config inline constexpr uint64_t BNS_VALIDATION_HEIGHT = 2068850; inline constexpr time_t HEIGHT_ESTIMATE_TIMESTAMP = 1639187815; - inline constexpr uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0xd1; - inline constexpr uint64_t CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; - inline constexpr uint64_t CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; + inline constexpr uint64_t PUBLIC_ADDRESS_BASE58_PREFIX = 0xd1; + inline constexpr uint64_t PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; + inline constexpr uint64_t PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; inline constexpr uint16_t P2P_DEFAULT_PORT = 19090; inline constexpr uint16_t RPC_DEFAULT_PORT = 19091; inline constexpr uint16_t ZMQ_RPC_DEFAULT_PORT = 19092; inline constexpr uint16_t QNET_DEFAULT_PORT = 19095; - inline constexpr boost::uuids::uuid const NETWORK_ID = { { + inline constexpr std::array const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x17, 0xA1, 0xB5, 0x90 } }; // Bender's nightmare inline constexpr std::string_view GENESIS_TX = "013c01ff0005978c390224a302c019c844f7141f35bf7f0fc5b02ada055e4ba897557b17ac6ccf88f0a2c09fab030276d443549feee11fe325048eeea083fcb7535312572d255ede1ecb58f84253b480e89226023b7d7c5e6eff4da699393abf12b6e3d04eae7909ae21932520fb3166b8575bb180cab5ee0102e93beb645ce7d5574d6a5ed5d9b8aadec7368342d08a7ca7b342a428353a10df80e497d01202b6e6844c1e9a478d0e4f7f34e455b26077a51f0005357aa19a49ca16eb373f622101f7c2a3a2ed7011b61998b1cd4f45b4d3c1daaa82908a10ca191342297eef1cf8"sv; inline constexpr uint32_t GENESIS_NONCE = 11011; - inline constexpr uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 7 * BLOCKS_PER_DAY_OLD;//Governance added from V17 + inline constexpr uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 7 * old::BLOCKS_PER_DAY_12;//Governance added from V17 inline constexpr std::array GOVERNANCE_WALLET_ADDRESS = { - "bxcguQiBhYaDW5wAdPLSwRHA6saX1nCEYUF89SPKZfBY1BENdLQWjti59aEtAEgrVZjnCJEVFoCDrG1DCoz2HeeN2pxhxL9xa"sv, // const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x17, 0xA1, 0xB6, 0x91 } }; // Bender's daydream inline constexpr std::string_view GENESIS_TX = "023c01ff0001d7c1c4e81402a4b3be74714906edf0d798d22083d36983e80086d62436302684ca5bea0f312b420195937f9cb7005504052c96bf73d65d55f611c141876e5e519cef59fcb041d90872000000000000000000000000000000000000000000000000000000000000000000"sv; @@ -303,20 +371,20 @@ namespace config inline constexpr uint64_t HEIGHT_ESTIMATE_HEIGHT = 0; inline constexpr uint64_t BNS_VALIDATION_HEIGHT = 0; inline constexpr time_t HEIGHT_ESTIMATE_TIMESTAMP = 1668622463; - inline constexpr uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 24; // ~ dV1 .. dV3 - inline constexpr uint64_t CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 25; // ~ dVA .. dVC - inline constexpr uint64_t CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 36; // ~dVa .. dVc + inline constexpr uint64_t PUBLIC_ADDRESS_BASE58_PREFIX = 24; // ~ dV1 .. dV3 + inline constexpr uint64_t PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 25; // ~ dVA .. dVC + inline constexpr uint64_t PUBLIC_SUBADDRESS_BASE58_PREFIX = 36; // ~dVa .. dVc inline constexpr uint16_t P2P_DEFAULT_PORT = 39090; inline constexpr uint16_t RPC_DEFAULT_PORT = 39091; inline constexpr uint16_t ZMQ_RPC_DEFAULT_PORT = 39092; inline constexpr uint16_t QNET_DEFAULT_PORT = 39095; - inline constexpr boost::uuids::uuid const NETWORK_ID = { { + inline constexpr std::array const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x17, 0xA1, 0xB7, 0x92 } }; inline constexpr std::string_view GENESIS_TX = "023c01ff0001d7c1c4e81402a25ba172ed7bca3b35e0be2f097b743973cf3c26777342032bed1036b19ab7a4420145706ec71eec5d57962c225b0615c172f8429984ec4954ba8b05bdad3f454f0472000000000000000000000000000000000000000000000000000000000000000000"sv; inline constexpr uint32_t GENESIS_NONCE = 11013; - inline constexpr uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 7 * BLOCKS_PER_DAY_OLD;//governance added from V17 + inline constexpr uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS = 7 * old::BLOCKS_PER_DAY_12;//governance added from V17 inline constexpr std::array GOVERNANCE_WALLET_ADDRESS = { "59XZKiAFwAKVyWN1CuuyFqMTTFLu9PEjpb3WhXfVuStgdoCZM1MtyJ2C41qijqfbdnY844F3boaW29geb8pT3mfrV9QQSRB"sv, // hardfork v7-9 @@ -332,50 +400,7 @@ namespace config inline constexpr auto UPTIME_PROOF_FREQUENCY = 1min; inline constexpr auto UPTIME_PROOF_VALIDITY = 2min + 5s; } -} - -namespace cryptonote -{ - enum network_version - { - network_version_7 = 7, - network_version_8, - network_version_9_master_nodes, // Proof Of Stake w/ Service Nodes - network_version_10_bulletproofs, // Bulletproofs, Service Node Grace Registration Period, Batched Governance - network_version_11_infinite_staking, // Infinite Staking, CN-Turtle - network_version_12_security_signature, - network_version_13_checkpointing, // Checkpointing, Relaxed Deregistration, RandomXL, Beldex Storage Server - network_version_14_enforce_checkpoints, - network_version_15_flash, - network_version_16, - network_version_17_POS, - network_version_18_bns, - network_version_19, - - network_version_count, - }; - - enum network_type : uint8_t - { - MAINNET = 0, - TESTNET, - DEVNET, - FAKECHAIN, - UNDEFINED = 255 - }; - - inline constexpr std::string_view network_type_str(network_type nettype) - { - switch(nettype) - { - case MAINNET: return "Mainnet"sv; - case TESTNET: return "Testnet"sv; - case DEVNET: return "Devnet"sv; - case FAKECHAIN: return "Fakenet"sv; - case UNDEFINED: return "Undefined Net"sv; - } - return "Unhandled Net"sv; - } +} // namespace config struct network_config { @@ -383,14 +408,14 @@ namespace cryptonote uint64_t HEIGHT_ESTIMATE_HEIGHT; uint64_t BNS_VALIDATION_HEIGHT; time_t HEIGHT_ESTIMATE_TIMESTAMP; - uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - uint64_t CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; - uint64_t CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; + uint64_t PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t PUBLIC_SUBADDRESS_BASE58_PREFIX; uint16_t P2P_DEFAULT_PORT; uint16_t RPC_DEFAULT_PORT; uint16_t ZMQ_RPC_DEFAULT_PORT; uint16_t QNET_DEFAULT_PORT; - boost::uuids::uuid NETWORK_ID; + const std::array NETWORK_ID; std::string_view GENESIS_TX; uint32_t GENESIS_NONCE; uint64_t GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; @@ -402,27 +427,28 @@ namespace cryptonote std::chrono::seconds UPTIME_PROOF_FREQUENCY; std::chrono::seconds UPTIME_PROOF_VALIDITY; - inline constexpr std::string_view governance_wallet_address(int hard_fork_version) const { - return GOVERNANCE_WALLET_ADDRESS[hard_fork_version >= cryptonote::network_version_17_POS ? 1 : 0]; + inline constexpr std::string_view governance_wallet_address(hf hard_fork_version) const { + return GOVERNANCE_WALLET_ADDRESS[hard_fork_version >= hf::hf17_POS ? 1 : 0]; } }; + inline constexpr network_config mainnet_config{ MAINNET, - ::config::HEIGHT_ESTIMATE_HEIGHT, - ::config::BNS_VALIDATION_HEIGHT, - ::config::HEIGHT_ESTIMATE_TIMESTAMP, - ::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, - ::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, - ::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, - ::config::P2P_DEFAULT_PORT, - ::config::RPC_DEFAULT_PORT, - ::config::ZMQ_RPC_DEFAULT_PORT, - ::config::QNET_DEFAULT_PORT, - ::config::NETWORK_ID, - ::config::GENESIS_TX, - ::config::GENESIS_NONCE, - ::config::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, - ::config::GOVERNANCE_WALLET_ADDRESS, + config::HEIGHT_ESTIMATE_HEIGHT, + config::BNS_VALIDATION_HEIGHT, + config::HEIGHT_ESTIMATE_TIMESTAMP, + config::PUBLIC_ADDRESS_BASE58_PREFIX, + config::PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + config::PUBLIC_SUBADDRESS_BASE58_PREFIX, + config::P2P_DEFAULT_PORT, + config::RPC_DEFAULT_PORT, + config::ZMQ_RPC_DEFAULT_PORT, + config::QNET_DEFAULT_PORT, + config::NETWORK_ID, + config::GENESIS_TX, + config::GENESIS_NONCE, + config::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, + config::GOVERNANCE_WALLET_ADDRESS, config::UPTIME_PROOF_TOLERANCE, config::UPTIME_PROOF_STARTUP_DELAY, config::UPTIME_PROOF_CHECK_INTERVAL, @@ -431,21 +457,21 @@ namespace cryptonote }; inline constexpr network_config testnet_config{ TESTNET, - ::config::testnet::HEIGHT_ESTIMATE_HEIGHT, - ::config::testnet::BNS_VALIDATION_HEIGHT, - ::config::testnet::HEIGHT_ESTIMATE_TIMESTAMP, - ::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, - ::config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, - ::config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, - ::config::testnet::P2P_DEFAULT_PORT, - ::config::testnet::RPC_DEFAULT_PORT, - ::config::testnet::ZMQ_RPC_DEFAULT_PORT, - ::config::testnet::QNET_DEFAULT_PORT, - ::config::testnet::NETWORK_ID, - ::config::testnet::GENESIS_TX, - ::config::testnet::GENESIS_NONCE, - ::config::testnet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, - ::config::testnet::GOVERNANCE_WALLET_ADDRESS, + config::testnet::HEIGHT_ESTIMATE_HEIGHT, + config::testnet::BNS_VALIDATION_HEIGHT, + config::testnet::HEIGHT_ESTIMATE_TIMESTAMP, + config::testnet::PUBLIC_ADDRESS_BASE58_PREFIX, + config::testnet::PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + config::testnet::PUBLIC_SUBADDRESS_BASE58_PREFIX, + config::testnet::P2P_DEFAULT_PORT, + config::testnet::RPC_DEFAULT_PORT, + config::testnet::ZMQ_RPC_DEFAULT_PORT, + config::testnet::QNET_DEFAULT_PORT, + config::testnet::NETWORK_ID, + config::testnet::GENESIS_TX, + config::testnet::GENESIS_NONCE, + config::testnet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, + config::testnet::GOVERNANCE_WALLET_ADDRESS, config::UPTIME_PROOF_TOLERANCE, config::UPTIME_PROOF_STARTUP_DELAY, config::UPTIME_PROOF_CHECK_INTERVAL, @@ -454,21 +480,21 @@ namespace cryptonote }; inline constexpr network_config devnet_config{ DEVNET, - ::config::devnet::HEIGHT_ESTIMATE_HEIGHT, - ::config::devnet::BNS_VALIDATION_HEIGHT, - ::config::devnet::HEIGHT_ESTIMATE_TIMESTAMP, - ::config::devnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, - ::config::devnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, - ::config::devnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, - ::config::devnet::P2P_DEFAULT_PORT, - ::config::devnet::RPC_DEFAULT_PORT, - ::config::devnet::ZMQ_RPC_DEFAULT_PORT, - ::config::devnet::QNET_DEFAULT_PORT, - ::config::devnet::NETWORK_ID, - ::config::devnet::GENESIS_TX, - ::config::devnet::GENESIS_NONCE, - ::config::devnet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, - ::config::devnet::GOVERNANCE_WALLET_ADDRESS, + config::devnet::HEIGHT_ESTIMATE_HEIGHT, + config::devnet::BNS_VALIDATION_HEIGHT, + config::devnet::HEIGHT_ESTIMATE_TIMESTAMP, + config::devnet::PUBLIC_ADDRESS_BASE58_PREFIX, + config::devnet::PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + config::devnet::PUBLIC_SUBADDRESS_BASE58_PREFIX, + config::devnet::P2P_DEFAULT_PORT, + config::devnet::RPC_DEFAULT_PORT, + config::devnet::ZMQ_RPC_DEFAULT_PORT, + config::devnet::QNET_DEFAULT_PORT, + config::devnet::NETWORK_ID, + config::devnet::GENESIS_TX, + config::devnet::GENESIS_NONCE, + config::devnet::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, + config::devnet::GOVERNANCE_WALLET_ADDRESS, config::UPTIME_PROOF_TOLERANCE, config::UPTIME_PROOF_STARTUP_DELAY, config::UPTIME_PROOF_CHECK_INTERVAL, @@ -477,21 +503,21 @@ namespace cryptonote }; inline constexpr network_config fakenet_config{ FAKECHAIN, - ::config::HEIGHT_ESTIMATE_HEIGHT, - ::config::BNS_VALIDATION_HEIGHT, - ::config::HEIGHT_ESTIMATE_TIMESTAMP, - ::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, - ::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, - ::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, - ::config::P2P_DEFAULT_PORT, - ::config::RPC_DEFAULT_PORT, - ::config::ZMQ_RPC_DEFAULT_PORT, - ::config::QNET_DEFAULT_PORT, - ::config::NETWORK_ID, - ::config::GENESIS_TX, - ::config::GENESIS_NONCE, - 100, //::config::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, - ::config::GOVERNANCE_WALLET_ADDRESS, + config::HEIGHT_ESTIMATE_HEIGHT, + config::BNS_VALIDATION_HEIGHT, + config::HEIGHT_ESTIMATE_TIMESTAMP, + config::PUBLIC_ADDRESS_BASE58_PREFIX, + config::PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + config::PUBLIC_SUBADDRESS_BASE58_PREFIX, + config::P2P_DEFAULT_PORT, + config::RPC_DEFAULT_PORT, + config::ZMQ_RPC_DEFAULT_PORT, + config::QNET_DEFAULT_PORT, + config::NETWORK_ID, + config::GENESIS_TX, + config::GENESIS_NONCE, + 100, //config::GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS, + config::GOVERNANCE_WALLET_ADDRESS, config::UPTIME_PROOF_TOLERANCE, config::fakechain::UPTIME_PROOF_STARTUP_DELAY, config::fakechain::UPTIME_PROOF_CHECK_INTERVAL, @@ -499,15 +525,16 @@ namespace cryptonote config::fakechain::UPTIME_PROOF_VALIDITY, }; - inline constexpr const network_config& get_config(network_type nettype) +inline constexpr const network_config& get_config(network_type nettype) +{ + switch (nettype) { - switch (nettype) - { - case MAINNET: return mainnet_config; - case TESTNET: return testnet_config; - case DEVNET: return devnet_config; - case FAKECHAIN: return fakenet_config; - default: throw std::runtime_error{"Invalid network type"}; - } + case MAINNET: return mainnet_config; + case TESTNET: return testnet_config; + case DEVNET: return devnet_config; + case FAKECHAIN: return fakenet_config; + default: throw std::runtime_error{"Invalid network type"}; } } + +} \ No newline at end of file diff --git a/src/cryptonote_core/beldex_name_system.cpp b/src/cryptonote_core/beldex_name_system.cpp index 60caac947e8..d19e73fcfd3 100755 --- a/src/cryptonote_core/beldex_name_system.cpp +++ b/src/cryptonote_core/beldex_name_system.cpp @@ -4,6 +4,7 @@ #include #include #include "common/hex.h" +#include "cryptonote_config.h" #include "beldex_name_system.h" #include "common/beldex.h" @@ -36,6 +37,8 @@ extern "C" #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "bns" +using cryptonote::hf; + namespace bns { @@ -653,14 +656,14 @@ sqlite3 *init_beldex_name_system(const fs::path& file_path, bool read_only) return result; } -std::vector all_mapping_types(uint8_t hf_version) { +std::vector all_mapping_types(hf hf_version) { std::vector result; result.reserve(2); - if (hf_version >= cryptonote::network_version_16) + if (hf_version >= hf::hf16) result.push_back(mapping_type::bchat); - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) result.push_back(mapping_type::belnet); - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) result.push_back(mapping_type::wallet); return result; } @@ -669,7 +672,7 @@ std::optional expiry_blocks(cryptonote::network_type nettype, mapping_ { std::optional result; - result = BLOCKS_PER_DAY * REGISTRATION_YEAR_DAYS * ( + result = cryptonote::BLOCKS_PER_DAY * REGISTRATION_YEAR_DAYS * ( map_years == mapping_years::bns_1year ? 1 : map_years == mapping_years::bns_2years ? 2 : map_years == mapping_years::bns_5years ? 5 : @@ -677,10 +680,10 @@ std::optional expiry_blocks(cryptonote::network_type nettype, mapping_ assert(result && *result); - if (nettype == cryptonote::TESTNET) // For testnet we shorten 1/2/5/10 years renewals to 1/2/5 days. + if (nettype == cryptonote::network_type::TESTNET) // For testnet we shorten 1/2/5/10 years renewals to 1/2/5 days. *result /= REGISTRATION_YEAR_DAYS; - else if (nettype == cryptonote::FAKECHAIN) // For fakenet testing we shorten 1/2/5/10 years to 2/4/10/20 blocks - *result /= (BLOCKS_PER_DAY * REGISTRATION_YEAR_DAYS / 2); + else if (nettype == cryptonote::network_type::FAKECHAIN) // For fakenet testing we shorten 1/2/5/10 years to 2/4/10/20 blocks + *result /= (cryptonote::BLOCKS_PER_DAY * REGISTRATION_YEAR_DAYS / 2); return result; } @@ -1229,7 +1232,7 @@ static bool validate_against_previous_mapping(bns::name_system_db &bns_db, uint6 // Sanity check value to disallow the empty name hash static const crypto::hash null_name_hash = name_to_hash(""); -bool name_system_db::validate_bns_tx(uint8_t hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_beldex_name_system &bns_extra, std::string *reason) +bool name_system_db::validate_bns_tx(hf hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_beldex_name_system &bns_extra, std::string *reason) { // ----------------------------------------------------------------------------------------------- // Pull out BNS Extra from TX @@ -1320,7 +1323,7 @@ bool name_system_db::validate_bns_tx(uint8_t hf_version, uint64_t blockchain_hei if (bns_extra.field_is_set(bns::extra_field::encrypted_eth_addr_value)) { // BNS Allowed type Validation - if (check_condition(hf_version < cryptonote::network_version_19, reason, tx, ", ", bns_extra_string(nettype, bns_extra)," specifying eth_addr is disallowed in HF", +static_cast(hf_version))) + if (check_condition(hf_version < cryptonote::hf::hf19_enhance_bns, reason, tx, ", ", bns_extra_string(nettype, bns_extra)," specifying eth_addr is disallowed in HF", +static_cast(hf_version))) return false; if (!mapping_value::validate_encrypted(mapping_type::eth_addr, bns_extra.encrypted_eth_addr_value, nullptr, reason)) @@ -1356,7 +1359,7 @@ bool name_system_db::validate_bns_tx(uint8_t hf_version, uint64_t blockchain_hei return true; } -bool validate_mapping_type(std::string_view mapping_type_str, uint8_t hf_version, bns::mapping_type *mapping_type, std::string *reason) +bool validate_mapping_type(std::string_view mapping_type_str, hf hf_version, bns::mapping_type *mapping_type, std::string *reason) { std::string mapping = tools::lowercase_ascii_string(mapping_type_str); std::optional mapping_type_; @@ -2195,7 +2198,7 @@ bool name_system_db::add_block(const cryptonote::block &block, const std::vector return false; bool bns_parsed_from_block = false; - if (block.major_version >= cryptonote::network_version_18_bns) + if (block.major_version >= hf::hf18_bns) { for (cryptonote::transaction const &tx : txs) { @@ -2330,11 +2333,19 @@ bool name_system_db::save_settings(uint64_t top_height, crypto::hash const &top_ bool name_system_db::prune_db(uint64_t height) { - if (!bind_and_run(bns_sql_type::pruning, prune_mappings_sql, nullptr, height)) return false; - if (!sql_run_statement(bns_sql_type::pruning, prune_owners_sql, nullptr)) return false; + bool result = false; + if (db) { + if (bind_and_run(bns_sql_type::pruning, prune_mappings_sql, nullptr, height)) + if (sql_run_statement(bns_sql_type::pruning, prune_owners_sql, nullptr)) + result = true; - this->last_processed_height = (height - 1); - return true; + MDEBUG("Detach request for BNS (last processed is" << this->last_processed_height << "), " << (result ? "detached" : "failed to detach") << " to " << height); + + if (result) + this->last_processed_height = (height - 1); + } + + return result; } owner_record name_system_db::get_owner_by_key(bns::generic_owner const &owner) diff --git a/src/cryptonote_core/beldex_name_system.h b/src/cryptonote_core/beldex_name_system.h index 4bcb91bdad3..2200e0ae8b8 100755 --- a/src/cryptonote_core/beldex_name_system.h +++ b/src/cryptonote_core/beldex_name_system.h @@ -132,13 +132,13 @@ inline std::string_view mapping_type_str(mapping_type type) } inline std::ostream &operator<<(std::ostream &os, mapping_type type) { return os << mapping_type_str(type); } -constexpr bool mapping_type_allowed(uint8_t hf_version, mapping_type type) { - return (type == mapping_type::bchat && hf_version >= cryptonote::network_version_16) - || (type == mapping_type::belnet && hf_version >= cryptonote::network_version_17_POS) || (type == mapping_type::wallet && hf_version >= cryptonote::network_version_17_POS); +constexpr bool mapping_type_allowed(cryptonote::hf hf_version, mapping_type type) { + return (type == mapping_type::bchat && hf_version >= cryptonote::hf::hf16) + || (type == mapping_type::belnet && hf_version >= cryptonote::hf::hf17_POS) || (type == mapping_type::wallet && hf_version >= cryptonote::hf::hf17_POS); } // Returns all mapping types supported for lookup as of the given hardfork. -std::vector all_mapping_types(uint8_t hf_version); +std::vector all_mapping_types(cryptonote::hf hf_version); sqlite3 *init_beldex_name_system(const fs::path& file_path, bool read_only); @@ -149,6 +149,9 @@ constexpr uint16_t db_mapping_type(bns::mapping_type type) { return static_cast(type); } constexpr std::string_view db_mapping_value(bns::mapping_type type) { + if(is_belnet_type(type)) + type = mapping_type::belnet; + switch(type) { case mapping_type::bchat: return "encrypted_bchat_value"sv; @@ -191,7 +194,7 @@ enum struct bns_tx_type { lookup, buy, update, renew }; // Currently accepts "bchat" or "belnet" for lookups, buys, updates, and renewals; for buys and renewals also accepts "belnet_Ny[ear]" for N=2,5,10 // Lookups are implied by none of buy/update/renew. // mapping_type: (optional) if function returns true, the uint16_t value of the 'type' will be set -bool validate_mapping_type(std::string_view type, uint8_t hf_version, mapping_type *mapping_type, std::string *reason); +bool validate_mapping_type(std::string_view type, cryptonote::hf hf_version, mapping_type *mapping_type, std::string *reason); // Hashes an BNS name. The name must already be lower-case (but this is only checked in debug builds). crypto::hash name_to_hash(std::string_view name, const std::optional& key = std::nullopt); // Takes a human readable name and hashes it. Takes an optional value to use as a key to produce a keyed hash. @@ -320,7 +323,7 @@ struct name_system_db // Validates an BNS transaction. If the function returns true then entry will be populated with // the BNS details. On a false return, `reason` is instead populated with the failure reason. - bool validate_bns_tx(uint8_t hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_beldex_name_system &entry, std::string *reason); + bool validate_bns_tx(cryptonote::hf hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_beldex_name_system &entry, std::string *reason); // Destructor; closes the sqlite3 database if one is open ~name_system_db(); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e122470db9f..c470f68859b 100755 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -32,10 +32,12 @@ #include #include #include -#include +#include +#include #include "common/rules.h" #include "common/hex.h" +#include "common/string_util.h" #include "common/median.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -48,8 +50,8 @@ #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "cryptonote_basic/miner.h" -#include "epee/profile_tools.h" #include "epee/int-util.h" +#include "epee/time_helper.h" #include "epee/string_tools.h" #include "common/threadpool.h" #include "common/boost_serialization_helper.h" @@ -113,10 +115,10 @@ Blockchain::block_extended_info::block_extended_info(const alt_block_data_t &src Blockchain::Blockchain(tx_memory_pool& tx_pool, master_nodes::master_node_list& master_node_list): m_db(), m_tx_pool(tx_pool), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), - m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), + m_long_term_block_weights_window(LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_long_term_effective_median_block_weight(0), m_long_term_block_weights_cache_tip_hash(crypto::null_hash), - m_long_term_block_weights_cache_rolling_median(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), + m_long_term_block_weights_cache_rolling_median(LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_master_node_list(master_node_list), m_btc_valid(false), m_batch_success(true), @@ -300,8 +302,8 @@ uint64_t Blockchain::get_current_blockchain_height(bool lock) const //------------------------------------------------------------------ bool Blockchain::load_missing_blocks_into_beldex_subsystems() { - uint64_t const mnl_height = std::max(hard_fork_begins(m_nettype, network_version_9_master_nodes).value_or(0), m_master_node_list.height() + 1); - uint64_t const bns_height = std::max(hard_fork_begins(m_nettype, network_version_18_bns).value_or(0), m_bns_db.height() + 1); + uint64_t const mnl_height = std::max(hard_fork_begins(m_nettype, hf::hf9_master_nodes).value_or(0), m_master_node_list.height() + 1); + uint64_t const bns_height = std::max(hard_fork_begins(m_nettype, hf::hf18_bns).value_or(0), m_bns_db.height() + 1); uint64_t const end_height = m_db->height(); uint64_t const start_height = std::min(end_height, std::min(bns_height, mnl_height)); @@ -311,26 +313,26 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() MGINFO("Loading blocks into beldex subsystems, scanning blockchain from height: " << start_height << " to: " << end_height << " (mnl: " << mnl_height << ", bns: " << bns_height << ")"); using clock = std::chrono::steady_clock; - using work_time = std::chrono::duration; + using dseconds = std::chrono::duration; int64_t constexpr BLOCK_COUNT = 1000; auto work_start = clock::now(); auto scan_start = work_start; - work_time bns_duration{}, mnl_duration{}, bns_iteration_duration{}, mnl_iteration_duration{}; - - std::vector blocks; - std::vector txs; - std::vector missed_txs; + dseconds bns_duration{}, mnl_duration{}, bns_iteration_duration{}, mnl_iteration_duration{}; for (int64_t block_count = total_blocks, index = 0; block_count > 0; block_count -= BLOCK_COUNT, index++) { - if (index > 0 && (index % 10 == 0)) + auto duration = dseconds{clock::now() - work_start}; + if (duration >= 10s) { m_master_node_list.store(); - auto duration = work_time{clock::now() - work_start}; - MGINFO("... scanning height " << start_height + (index * BLOCK_COUNT) << " (" << duration.count() << "s) (mnl: " << mnl_iteration_duration.count() << "s; bns: " << bns_iteration_duration.count() << "s)"); + MGINFO(fmt::format("... scanning height {} ({:.3f}s) (mnl: {:.3f}s, bns: {:.3f}s)", + start_height + (index * BLOCK_COUNT), + duration.count(), + mnl_iteration_duration.count(), + bns_iteration_duration.count())); #ifdef ENABLE_SYSTEMD // Tell systemd that we're doing something so that it should let us continue starting up // (giving us 120s until we have to send the next notification): @@ -343,7 +345,7 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() bns_iteration_duration = mnl_iteration_duration = {}; } - blocks.clear(); + std::vector blocks; uint64_t height = start_height + (index * BLOCK_COUNT); if (!get_blocks_only(height, static_cast(BLOCK_COUNT), blocks)) { @@ -355,9 +357,8 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() { uint64_t block_height = get_block_height(blk); - txs.clear(); - missed_txs.clear(); - if (!get_transactions(blk.tx_hashes, txs, missed_txs)) + std::vector txs; + if (!get_transactions(blk.tx_hashes, txs)) { MERROR("Unable to get transactions for block for updating BNS DB: " << cryptonote::get_block_hash(blk)); return false; @@ -369,7 +370,7 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() checkpoint_t *checkpoint_ptr = nullptr; checkpoint_t checkpoint; - if (blk.major_version >= cryptonote::network_version_14_enforce_checkpoints && get_checkpoint(block_height, checkpoint)) + if (blk.major_version >= hf::hf15_flash && get_checkpoint(block_height, checkpoint)) checkpoint_ptr = &checkpoint; try { @@ -396,8 +397,8 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() if (total_blocks > 1) { - auto duration = work_time{clock::now() - scan_start}; - MGINFO("Done recalculating beldex subsystems (" << duration.count() << "s) (mnl: " << mnl_duration.count() << "s; bns: " << bns_duration.count() << "s)"); + MGINFO(fmt::format("Done recalculating beldex subsystems in {:.2f}s ({:.2f}s mnl; {:.2f}s bns)", + dseconds{clock::now() - scan_start}.count(), mnl_duration.count(), bns_duration.count())); } if (total_blocks > 0) @@ -405,6 +406,24 @@ bool Blockchain::load_missing_blocks_into_beldex_subsystems() return true; } + +static bool exec_detach_hooks( + Blockchain& blockchain, + uint64_t detach_height, + std::vector hooks, // have to change + bool by_pop_blocks, + bool load_missing_blocks = true) { + + detached_info hook_data{detach_height, by_pop_blocks}; + for (const auto& hook : hooks) + hook(hook_data); + + bool result = true; + if (load_missing_blocks) + result = blockchain.load_missing_blocks_into_beldex_subsystems(); + return result; +} + //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer @@ -412,7 +431,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett { LOG_PRINT_L3("Blockchain::" << __func__); - CHECK_AND_ASSERT_MES(nettype != FAKECHAIN || test_options, false, "fake chain network type used without options"); + CHECK_AND_ASSERT_MES(nettype != network_type::FAKECHAIN || test_options, false, "fake chain network type used without options"); auto lock = tools::unique_locks(m_tx_pool, *this); @@ -439,7 +458,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett // in integration mode or --regtest m_nettype = nettype; #else - m_nettype = test_options != NULL ? FAKECHAIN : nettype; + m_nettype = test_options != NULL ? network_type::FAKECHAIN : nettype; #endif if (!m_checkpoints.init(m_nettype, m_db)) @@ -471,7 +490,7 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett { } - if (m_nettype != FAKECHAIN) + if (m_nettype != network_type::FAKECHAIN) m_db->fixup(m_nettype); db_rtxn_guard rtxn_guard(m_db); @@ -486,11 +505,11 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett // create general purpose async service queue - m_async_work_idle = std::unique_ptr < boost::asio::io_service::work > (new boost::asio::io_service::work(m_async_service)); + m_async_work_idle = std::make_unique(m_async_service.get_executor()); m_async_thread = std::thread{[this] { m_async_service.run(); }}; #if defined(PER_BLOCK_CHECKPOINT) - if (m_nettype != FAKECHAIN) + if (m_nettype != network_type::FAKECHAIN) load_compiled_in_block_hashes(get_checkpoints); #endif @@ -503,8 +522,8 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett uint64_t top_height; const crypto::hash top_id = m_db->top_block_hash(&top_height); const block top_block = m_db->get_top_block(); - const uint8_t ideal_hf_version = get_network_version(top_height); - if (ideal_hf_version <= 1 || ideal_hf_version == top_block.major_version) + const auto ideal_hf_version = get_network_version(top_height); + if (ideal_hf_version <= hf::hf1 || ideal_hf_version == top_block.major_version) { if (num_popped_blocks > 0) MGINFO("Initial popping done, top block: " << top_id << ", top height: " << top_height << ", block version: " << (uint64_t)top_block.major_version); @@ -566,10 +585,18 @@ bool Blockchain::init(BlockchainDB* db, sqlite3 *bns_db, const network_type nett for (const auto& hook : m_init_hooks) hook(); - if (!m_db->is_read_only() && !load_missing_blocks_into_beldex_subsystems()) + if (!m_db->is_read_only()) { - MERROR("Failed to load blocks into beldex subsystems"); - return false; + if (!exec_detach_hooks( + *this, + m_db->height(), + m_blockchain_detached_hooks, + /*by_pop_blocks*/ false, + /*load_missing_blocks_into_beldex_subsystems*/ true)) + { + MERROR("Failed to load blocks into beldex subsystems"); + return false; + } } return true; @@ -581,7 +608,7 @@ bool Blockchain::store_blockchain() // lock because the rpc_thread command handler also calls this std::unique_lock lock{*m_db}; - TIME_MEASURE_START(save); + auto save = std::chrono::steady_clock::now(); // TODO: make sure sync(if this throws that it is not simply ignored higher // up the call stack try @@ -599,9 +626,8 @@ bool Blockchain::store_blockchain() throw; } - TIME_MEASURE_FINISH(save); if(m_show_time_stats) - MINFO("Blockchain stored OK, took: " << save << " ms"); + MINFO("Blockchain stored OK, took: " << tools::friendly_duration(std::chrono::steady_clock::now() - save)); return true; } //------------------------------------------------------------------ @@ -649,8 +675,8 @@ void Blockchain::pop_blocks(uint64_t nblocks) auto lock = tools::unique_locks(m_tx_pool, *this); bool stop_batch = m_db->batch_start(); - uint8_t hf_version = get_network_version(); - uint64_t blocks_expected_per_day = (hf_version>=cryptonote::network_version_17_POS) ? BLOCKS_PER_DAY : BLOCKS_PER_DAY_OLD ; + auto hf_version = get_network_version(); + uint64_t blocks_expected_per_day = (hf_version >= hf::hf17_POS) ? BLOCKS_PER_DAY : old::BLOCKS_PER_DAY_12 ; try { const uint64_t blockchain_height = m_db->height(); @@ -665,7 +691,7 @@ void Blockchain::pop_blocks(uint64_t nblocks) { if (nblocks >= blocks_expected_per_day && (i != 0 && (i % blocks_per_update == 0))) { - MGINFO("... popping blocks " << (++progress * PERCENT_PER_PROGRESS_UPDATE) << "% completed, height: " << (blockchain_height - i) << " (" << timer.seconds() << "s)"); + MGINFO("... popping blocks " << (++progress * PERCENT_PER_PROGRESS_UPDATE) << "% completed, height: " << (blockchain_height - i) << " (" << tools::friendly_duration(timer.value()) << "s)"); timer.reset(); } @@ -680,11 +706,7 @@ void Blockchain::pop_blocks(uint64_t nblocks) return; } - detached_info hook_data{m_db->height(), /*by_pop_blocks=*/true}; - for (const auto& hook : m_blockchain_detached_hooks) - hook(hook_data); - load_missing_blocks_into_beldex_subsystems(); - + exec_detach_hooks(*this, m_db->height(), m_blockchain_detached_hooks, /*by_pop_blocks=*/true); if (stop_batch) m_db->batch_stop(); } @@ -736,7 +758,7 @@ block Blockchain::pop_block_from_blockchain() { cryptonote::tx_verification_context tvc{}; - uint8_t version = get_network_version(m_db->height()); + hf version = get_network_version(m_db->height()); // We assume that if they were in a block, the transactions are already // known to the network as a whole. However, if we had mined that block, @@ -933,7 +955,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block(bool POS) if (POS) return POS_FIXED_DIFFICULTY; - uint8_t const hf_version = get_network_version(); + const auto hf_version = get_network_version(); crypto::hash top_hash = get_tail_id(); { std::unique_lock diff_lock{m_cache.m_difficulty_lock}; @@ -954,7 +976,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block(bool POS) m_nettype, m_cache.m_timestamps, m_cache.m_difficulties, chain_height, m_cache.m_timestamps_and_difficulties_height); uint64_t diff = next_difficulty_v2(m_cache.m_timestamps, m_cache.m_difficulties, - tools::to_seconds((hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)), + tools::to_seconds((hf_version >= hf::hf17_POS ? TARGET_BLOCK_TIME: old::TARGET_BLOCK_TIME_12)), difficulty_mode(m_nettype, chain_height)); m_cache.m_timestamps_and_difficulties_height = chain_height; @@ -998,11 +1020,7 @@ bool Blockchain::rollback_blockchain_switching(const std::listheight(); - detached_info split_hook_data{split_height, /*by_pop_blocks=*/false}; - for (const auto& hook : m_blockchain_detached_hooks) - hook(split_hook_data); - load_missing_blocks_into_beldex_subsystems(); + exec_detach_hooks(*this, split_height, m_blockchain_detached_hooks, /*by_pop_blocks=*/false); //connecting new alternative chain for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) @@ -1154,11 +1169,11 @@ difficulty_type Blockchain::get_difficulty_for_alternative_chain(const std::list { bool before_hf16 = true; if (alt_chain.size()) - before_hf16 = alt_chain.back().bl.major_version < network_version_17_POS; + before_hf16 = alt_chain.back().bl.major_version < hf::hf17_POS; else - before_hf16 = !is_hard_fork_at_least(m_nettype, cryptonote::network_version_17_POS, get_current_blockchain_height()); + before_hf16 = !is_hard_fork_at_least(m_nettype, hf::hf17_POS, get_current_blockchain_height()); - block_count = DIFFICULTY_BLOCKS_COUNT(before_hf16); + block_count = old::DIFFICULTY_BLOCKS_COUNT(before_hf16); } std::vector timestamps; @@ -1219,7 +1234,7 @@ difficulty_type Blockchain::get_difficulty_for_alternative_chain(const std::list auto hf_version = cryptonote::get_network_version(m_nettype, height); return next_difficulty_v2(timestamps, cumulative_difficulties, - tools::to_seconds((hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)), + tools::to_seconds((hf_version>=hf::hf17_POS?TARGET_BLOCK_TIME:old::TARGET_BLOCK_TIME_12)), difficulty_mode(m_nettype, height)); } //------------------------------------------------------------------ @@ -1228,7 +1243,7 @@ difficulty_type Blockchain::get_difficulty_for_alternative_chain(const std::list // one input, of type txin_gen, with height set to the block's height // correct miner tx unlock time // a non-overflowing tx amount (dubious necessity on this check) -bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version) +bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, hf hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); @@ -1239,9 +1254,9 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, return false; } MDEBUG("Miner tx hash: " << get_transaction_hash(b.miner_tx)); - CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + MINED_MONEY_UNLOCK_WINDOW, false, "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + MINED_MONEY_UNLOCK_WINDOW); - if (hf_version >= cryptonote::network_version_13_checkpointing) + if (hf_version >= hf::hf13_checkpointing) { if (b.miner_tx.type != txtype::standard) { @@ -1253,12 +1268,12 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, txversion max_version = transaction::get_min_version_for_hf(hf_version); if (b.miner_tx.version < min_version || b.miner_tx.version > max_version) { - MERROR_VER("Coinbase invalid version: " << b.miner_tx.version << " for hardfork: " << hf_version << " min/max version: " << min_version << "/" << max_version); + MERROR_VER("Coinbase invalid version: " << b.miner_tx.version << " for hardfork: " << static_cast(hf_version) << " min/max version: " << min_version << "/" << max_version); return false; } } - if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE) // Enforce empty rct signatures for miner transactions, + if (hf_version >= feature::REJECT_SIGS_IN_COINBASE) // Enforce empty rct signatures for miner transactions, CHECK_AND_ASSERT_MES(b.miner_tx.rct_signatures.type == rct::RCTType::Null, false, "RingCT signatures not allowed in coinbase transactions"); //check outs overflow @@ -1275,7 +1290,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, } //------------------------------------------------------------------ // This function validates the miner transaction reward -bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, uint8_t version) +bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, hf version) { LOG_PRINT_L3("Blockchain::" << __func__); //validate reward @@ -1286,14 +1301,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } uint64_t median_weight; - if (version >= HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY) + if (version >= feature::EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY) { median_weight = m_current_block_cumul_weight_median; } else { std::vector last_blocks_weights; - get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_blocks_weights(last_blocks_weights, REWARD_BLOCKS_WINDOW); median_weight = tools::median(std::move(last_blocks_weights)); } @@ -1301,7 +1316,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl beldex_block_reward_context block_reward_context = {}; block_reward_context.fee = fee; block_reward_context.height = height; - block_reward_context.testnet_override = nettype() == TESTNET && height < 386000; + block_reward_context.testnet_override = nettype() == network_type::TESTNET && height < 386000; if (!calc_batched_governance_reward(height, block_reward_context.batched_governance)) { MERROR_VER("Failed to calculate batched governance reward"); @@ -1327,9 +1342,9 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - if (already_generated_coins != 0 && block_has_governance_output(nettype(), b)) + if (already_generated_coins != 0 && block_has_governance_output(nettype(), b) && version > hf::hf20_bulletproof_plus) { - if (version >= network_version_17_POS && reward_parts.governance_paid == 0) + if (version >= hf::hf17_POS && reward_parts.governance_paid == 0) { MERROR("Governance reward should not be 0 after hardfork v17 if this height has a governance output because it is the batched payout height"); return false; @@ -1357,7 +1372,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl // TODO(beldex): eliminate all floating point math in reward calculations. uint64_t max_base_reward = reward_parts.base_miner + reward_parts.governance_paid + reward_parts.master_node_total + 1; uint64_t max_money_in_use = max_base_reward + reward_parts.miner_fee; - if (money_in_use > max_money_in_use) + if (money_in_use > max_money_in_use && version > hf::hf20_bulletproof_plus) { MERROR_VER("coinbase transaction spends too much money (" << print_money(money_in_use) << "). Maximum block reward is " << print_money(max_money_in_use) << " (= " << print_money(max_base_reward) << " base + " << print_money(reward_parts.miner_fee) << " fees)"); @@ -1573,7 +1588,7 @@ bool Blockchain::create_block_template_internal(block& b, const crypto::hash *fr block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight - uint8_t hf_version = b.major_version; + auto hf_version = b.major_version; auto miner_tx_context = info.is_miner ? beldex_miner_tx_context::miner_block(m_nettype, info.miner_address, m_master_node_list.get_block_leader()) @@ -1585,7 +1600,7 @@ bool Blockchain::create_block_template_internal(block& b, const crypto::hash *fr } crypto::signature security_signature; - if ((hf_version >= network_version_12_security_signature) && info.is_miner){ + if ((hf_version >= hf::hf12_security_signature) && info.is_miner){ crypto::hash hash = cryptonote::make_security_hash_from(height, b); const std::string skey_string = "8616b3fbc071ba5ed64e50cd4350691fa8fb07610fb61b698f2c989d1b30ea08"; @@ -1960,7 +1975,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id uint64_t block_reward = get_outs_money_amount(b.miner_tx); const uint64_t prev_generated_coins = alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(blk_height - 1); - alt_data.already_generated_coins = (block_reward < (MONEY_SUPPLY - prev_generated_coins)) ? prev_generated_coins + block_reward : MONEY_SUPPLY; + alt_data.already_generated_coins = (block_reward < (beldex::MONEY_SUPPLY - prev_generated_coins)) ? prev_generated_coins + block_reward : beldex::MONEY_SUPPLY; m_db->add_alt_block(id, alt_data, cryptonote::block_to_blob(b), checkpoint_blob.empty() ? nullptr : &checkpoint_blob); // Check current height for pre-existing checkpoint @@ -1993,8 +2008,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // NOTE: Execute Alt Block Hooks { std::vector txs; - std::vector missed; - if (!get_transactions(b.tx_hashes, txs, missed)) + std::unordered_set missed; + if (!get_transactions(b.tx_hashes, txs, &missed)) { bvc.m_verifivation_failed = true; return false; @@ -2038,7 +2053,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id bool const alt_chain_has_more_checkpoints = (num_checkpoints_on_alt_chain > num_checkpoints_on_chain); bool const alt_chain_has_equal_checkpoints = (num_checkpoints_on_alt_chain == num_checkpoints_on_chain); - if (b.major_version >= cryptonote::network_version_17_POS) + if (b.major_version >= hf::hf17_POS) { // In POS, we move away from the concept of difficulty to solve ties // between chains. We calculate the preferred chain using a simpler system. @@ -2110,7 +2125,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id difficulty_type const main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1); bool const alt_chain_has_greater_pow = alt_data.cumulative_difficulty > main_chain_cumulative_difficulty; - if (b.major_version >= network_version_14_enforce_checkpoints) + if (b.major_version >= hf::hf15_flash) { if (alt_chain_has_more_checkpoints || (alt_chain_has_greater_pow && alt_chain_has_equal_checkpoints)) { @@ -2188,8 +2203,8 @@ bool Blockchain::get_blocks_only(uint64_t start_offset, size_t count, std::vecto { for(const auto& blk : blocks) { - std::vector missed_ids; - get_transactions_blobs(blk.tx_hashes, *txs, missed_ids); + std::unordered_set missed_ids; + get_transactions_blobs(blk.tx_hashes, *txs, &missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); } } @@ -2211,8 +2226,8 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector missed_ids; - get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids); + std::unordered_set missed_ids; + get_transactions_blobs(blk.second.tx_hashes, txs, &missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); } @@ -2258,7 +2273,11 @@ bool Blockchain::handle_get_blocks(NOTIFY_REQUEST_GET_BLOCKS::request& arg, NOTI db_rtxn_guard rtxn_guard (m_db); rsp.current_blockchain_height = get_current_blockchain_height(); std::vector> blocks; - get_blocks(arg.blocks, blocks, rsp.missed_ids); + { + std::unordered_set missed_ids; + get_blocks(arg.blocks, blocks, &missed_ids); + rsp.missed_ids.insert(rsp.missed_ids.end(), missed_ids.begin(), missed_ids.end()); + } uint64_t const top_height = (m_db->height() - 1); uint64_t const earliest_height_to_sync_checkpoints_granularly = @@ -2296,8 +2315,8 @@ bool Blockchain::handle_get_blocks(NOTIFY_REQUEST_GET_BLOCKS::request& arg, NOTI // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. - std::vector missed_tx_ids; - get_transactions_blobs(block.tx_hashes, block_entry.txs, missed_tx_ids); + std::unordered_set missed_tx_ids; + get_transactions_blobs(block.tx_hashes, block_entry.txs, &missed_tx_ids); for (auto &h : block.tx_hashes) { @@ -2339,10 +2358,10 @@ bool Blockchain::handle_get_txs(NOTIFY_REQUEST_GET_TXS::request& arg, NOTIFY_NEW std::lock(blockchain_lock, flash_lock); db_rtxn_guard rtxn_guard (m_db); - std::vector missed; + std::unordered_set missed; // First check the blockchain for any txs: - get_transactions_blobs(arg.txs, rsp.txs, missed); + get_transactions_blobs(arg.txs, rsp.txs, &missed); // Look for any missed txes in the mempool: m_tx_pool.find_transactions(missed, rsp.txs); @@ -2402,8 +2421,8 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const { const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1); const uint64_t height = m_db->get_tx_block_height(toi.first); - const uint8_t hf_version = get_network_version(height); - if ((height + (hf_version>=cryptonote::network_version_17_POS?CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17:CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE) )<= blockchain_height) + const hf hf_version = get_network_version(height); + if ((height + (hf_version>=hf::hf17_POS?DEFAULT_TX_SPENDABLE_AGE_V17:old::DEFAULT_TX_SPENDABLE_AGE) )<= blockchain_height) break; --num_outs; } @@ -2591,7 +2610,7 @@ uint64_t Blockchain::block_difficulty(uint64_t i) const //------------------------------------------------------------------ //TODO: return type should be void, throw on exception // alternatively, return true only if no blocks missed -bool Blockchain::get_blocks(const std::vector& block_ids, std::vector>& blocks, std::vector& missed_bs) const +bool Blockchain::get_blocks(const std::vector& block_ids, std::vector>& blocks, std::unordered_set* missed_bs) const { LOG_PRINT_L3("Blockchain::" << __func__); std::unique_lock lock{*this}; @@ -2609,11 +2628,11 @@ bool Blockchain::get_blocks(const std::vector& block_ids, std::vec { LOG_ERROR("Invalid block: " << block_hash); blocks.pop_back(); - missed_bs.push_back(block_hash); + if (missed_bs) missed_bs->insert(block_hash); } } else - missed_bs.push_back(block_hash); + if (missed_bs) missed_bs->insert(block_hash); } catch (const std::exception& e) { @@ -2625,7 +2644,7 @@ bool Blockchain::get_blocks(const std::vector& block_ids, std::vec //------------------------------------------------------------------ //TODO: return type should be void, throw on exception // alternatively, return true only if no transactions missed -bool Blockchain::get_transactions_blobs(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs, bool pruned) const +bool Blockchain::get_transactions_blobs(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs, bool pruned) const { LOG_PRINT_L3("Blockchain::" << __func__); std::unique_lock lock{*this}; @@ -2640,8 +2659,8 @@ bool Blockchain::get_transactions_blobs(const std::vector& txs_ids txs.push_back(std::move(tx)); else if (!pruned && m_db->get_tx_blob(tx_hash, tx)) txs.push_back(std::move(tx)); - else - missed_txs.push_back(tx_hash); + else if (missed_txs) + missed_txs->insert(tx_hash); } catch (const std::exception& e) { @@ -2675,7 +2694,7 @@ size_t get_transaction_version(const cryptonote::blobdata &bd) return version; } //------------------------------------------------------------------ -bool Blockchain::get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::vector& missed_txs) const +bool Blockchain::get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::unordered_set* missed_txs) const { LOG_PRINT_L3("Blockchain::" << __func__); std::unique_lock lock{*this}; @@ -2697,8 +2716,8 @@ bool Blockchain::get_split_transactions_blobs(const std::vector& t if (!m_db->get_prunable_tx_blob(tx_hash, prunable)) prunable.clear(); } - else - missed_txs.push_back(tx_hash); + else if (missed_txs) + missed_txs->insert(tx_hash); } catch (const std::exception& e) { @@ -2708,7 +2727,7 @@ bool Blockchain::get_split_transactions_blobs(const std::vector& t return true; } //------------------------------------------------------------------ -bool Blockchain::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const +bool Blockchain::get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs) const { LOG_PRINT_L3("Blockchain::" << __func__); std::unique_lock lock{*this}; @@ -2729,8 +2748,8 @@ bool Blockchain::get_transactions(const std::vector& txs_ids, std: return false; } } - else - missed_txs.push_back(tx_hash); + else if (missed_txs) + missed_txs->insert(tx_hash); } catch (const std::exception& e) { @@ -2763,7 +2782,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed); } size_t count = 0; - hashes.reserve(std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); + hashes.reserve(std::min((size_t)(stop_height - start_height), BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); for(size_t i = start_height; i < stop_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { hashes.push_back(m_db->get_block_hash_from_height(i)); @@ -2829,9 +2848,9 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } else { - std::vector mis; - get_transactions_blobs(b.tx_hashes, txs, mis, pruned); - CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + std::unordered_set mis; + get_transactions_blobs(b.tx_hashes, txs, &mis, pruned); + CHECK_AND_ASSERT_MES(mis.empty(), false, "internal error, transaction from block not found"); } size += blocks.back().first.first.size(); for (const auto &t: txs) @@ -2857,7 +2876,7 @@ bool Blockchain::add_block_as_invalid(cryptonote::block const &block) return true; } -uint8_t Blockchain::get_network_version(std::optional height) const { +hf Blockchain::get_network_version(std::optional height) const { if (!height) height = get_current_blockchain_height(); return cryptonote::get_network_version(m_nettype, *height); } @@ -2986,15 +3005,15 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx) // check if we're doing per-block checkpointing if (m_db->height() < m_blocks_hash_check.size()) { - TIME_MEASURE_START(a); + auto a = std::chrono::steady_clock::now(); m_blocks_txs_check.push_back(get_transaction_hash(tx)); - TIME_MEASURE_FINISH(a); if(m_show_time_stats) { size_t ring_size = 0; if (!tx.vin.empty() && std::holds_alternative(tx.vin[0])) ring_size = var::get(tx.vin[0]).key_offsets.size(); - MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a); + MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << + tools::friendly_duration(std::chrono::steady_clock::now() - a)); } } #endif @@ -3023,16 +3042,16 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh } #endif - TIME_MEASURE_START(a); + auto a = std::chrono::steady_clock::now(); bool res = check_tx_inputs(tx, tvc, &max_used_block_height, key_image_conflicts); - TIME_MEASURE_FINISH(a); if(m_show_time_stats) { size_t ring_size = 0; if (!tx.vin.empty() && std::holds_alternative(tx.vin[0])) ring_size = var::get(tx.vin[0]).key_offsets.size(); MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << - " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx) << " W: " << get_transaction_weight(tx)); + " H: " << max_used_block_height << " time: " << tools::friendly_duration(std::chrono::steady_clock::now() - a + m_fake_scan_time) << + " B: " << get_object_blobsize(tx) << " W: " << get_transaction_weight(tx)); } if (!res) return false; @@ -3068,8 +3087,8 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // from v10, allow bulletproofs auto height = get_current_blockchain_height(); - const uint8_t hf_version = get_network_version(height); - if (hf_version < network_version_8) { + const hf hf_version = get_network_version(height); + if (hf_version < hf::hf8) { const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) { @@ -3083,7 +3102,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // The HF10 block height itself was allowed to (and did) have a Borromean tx as an exception // to the HF10 rules so that a borderline tx didn't end up unmineable, hence the strict `>` // here: - if (auto hf10_height = hard_fork_begins(m_nettype, network_version_10_bulletproofs); + if (auto hf10_height = hard_fork_begins(m_nettype, hf::hf10_bulletproofs); hf10_height && height > *hf10_height) { MERROR_VER("Borromean range proofs are not allowed after v10"); @@ -3092,21 +3111,21 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - if (hf_version < HF_VERSION_SMALLER_BP) { + if (hf_version < feature::SMALLER_BP) { if (tx.rct_signatures.type == rct::RCTType::Bulletproof2) { - MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof2 << " is not allowed before v" << HF_VERSION_SMALLER_BP); + MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof2 << " is not allowed before v" << static_cast(feature::SMALLER_BP)); tvc.m_invalid_output = true; return false; } } - if (hf_version > HF_VERSION_SMALLER_BP) { + if (hf_version > feature::SMALLER_BP) { if (tx.version >= txversion::v4_tx_types && tx.is_transfer()) { if (tx.rct_signatures.type == rct::RCTType::Bulletproof) { - MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof << " is not allowed from v" << (HF_VERSION_SMALLER_BP + 1)); + MERROR_VER("Ringct type " << (unsigned)rct::RCTType::Bulletproof << " is not allowed after v" << static_cast(feature::SMALLER_BP)); tvc.m_invalid_output = true; return false; } @@ -3114,11 +3133,11 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // Disallow CLSAGs before the CLSAG hardfork - if (hf_version < HF_VERSION_CLSAG) { + if (hf_version < feature::CLSAG) { if (tx.version >= txversion::v4_tx_types && tx.is_transfer()) { if (tx.rct_signatures.type == rct::RCTType::CLSAG) { - MERROR_VER("Ringct type " << (unsigned)rct::RCTType::CLSAG << " is not allowed before v" << HF_VERSION_CLSAG); + MERROR_VER("Ringct type " << (unsigned)rct::RCTType::CLSAG << " is not allowed before v" << static_cast(feature::CLSAG)); tvc.m_invalid_output = true; return false; } @@ -3128,16 +3147,42 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // Require CLSAGs starting 10 blocks after the CLSAG-enabling hard fork (the 10 block buffer is to // allow staggling txes around fork time to still make it into a block). // NB: there *are* such txes on mainnet in this 10-block window so this code has to stay. - if (hf_version >= HF_VERSION_CLSAG + if (hf_version >= feature::CLSAG && tx.rct_signatures.type < rct::RCTType::CLSAG && tx.version >= txversion::v4_tx_types && tx.is_transfer() - && (hf_version > HF_VERSION_CLSAG || height >= 10 + *hard_fork_begins(m_nettype, HF_VERSION_CLSAG))) + && (hf_version > feature::CLSAG || height >= 10 + *hard_fork_begins(m_nettype, feature::CLSAG))) { - MERROR_VER("Ringct type " << (unsigned)tx.rct_signatures.type << " is not allowed from v" << HF_VERSION_CLSAG); + MERROR_VER("Ringct type " << (unsigned)tx.rct_signatures.type << " is not allowed from v" << static_cast(feature::CLSAG)); tvc.m_invalid_output = true; return false; } + // Don't allow bulletproofs plus before V20 + if (hf_version < cryptonote::feature::BULLETPROOF_PLUS) { + if (tx.version >= txversion::v4_tx_types && tx.is_transfer()) { + const bool bulletproof_plus = rct::is_rct_bulletproof_plus(tx.rct_signatures.type); + if (bulletproof_plus || !tx.rct_signatures.p.bulletproofs_plus.empty()) + { + MERROR_VER("Bulletproofs plus are not allowed before v" << std::to_string(static_cast(cryptonote::feature::BULLETPROOF_PLUS))); + tvc.m_invalid_output = true; + return false; + } + } + } + + // forbid bulletproofs from v20 + if (hf_version >= cryptonote::feature::BULLETPROOF_PLUS) { + if (tx.version >= txversion::v4_tx_types && tx.is_transfer()) { + const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); + if (bulletproof) + { + MERROR_VER("Bulletproof range proofs are not allowed after v" << std::to_string(static_cast(cryptonote::feature::BULLETPROOF_PLUS))); + tvc.m_invalid_output = true; + return false; + } + } + } + return true; } //------------------------------------------------------------------ @@ -3178,7 +3223,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (tools::equals_any(rv.type, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG)) + else if (tools::equals_any(rv.type, rct::RCTType::Simple, rct::RCTType::Bulletproof, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG, rct::RCTType::BulletproofPlus)) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -3213,7 +3258,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr rv.p.MGs[n].II[0] = rct::ki2rct(var::get(tx.vin[n]).k_image); } } - else if (rv.type == rct::RCTType::CLSAG) + else if (rv.type == rct::RCTType::CLSAG || rv.type == rct::RCTType::BulletproofPlus) { if (!tx.pruned) { @@ -3267,9 +3312,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (tx.is_transfer()) { - if (tx.type != txtype::beldex_name_system && tx.type != txtype::coin_burn && hf_version >= HF_VERSION_MIN_2_OUTPUTS && tx.vout.size() < 2) + if (tx.type != txtype::beldex_name_system && tx.type != txtype::coin_burn && hf_version >= feature::MIN_2_OUTPUTS && tx.vout.size() < 2) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has fewer than two outputs, which is not allowed as of hardfork " << +HF_VERSION_MIN_2_OUTPUTS); + MERROR_VER("Tx " << get_transaction_hash(tx) << " has fewer than two outputs, which is not allowed as of hardfork " << static_cast(feature::MIN_2_OUTPUTS)); tvc.m_too_few_outputs = true; return false; } @@ -3294,11 +3339,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); // Mixin Check, from hard fork 7, we require mixin at least 9, always. - if (((hf_version <=7) && (in_to_key.key_offsets.size() - 1 < 6) && tx.version == txversion::v2_ringct) || - ((hf_version ==8) && (in_to_key.key_offsets.size() - 1 < 7) ) || - ((hf_version >8 ) && (in_to_key.key_offsets.size() - 1 != CRYPTONOTE_DEFAULT_TX_MIXIN))) + if (((hf_version <= hf::hf7 ) && (in_to_key.key_offsets.size() - 1 < 6) && tx.version == txversion::v2_ringct) || + ((hf_version == hf::hf8) && (in_to_key.key_offsets.size() - 1 < 7) ) || + ((hf_version > hf::hf8 ) && (in_to_key.key_offsets.size() - 1 != cryptonote::TX_OUTPUT_DECOYS))) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has incorrect ring size (" << in_to_key.key_offsets.size() - 1 << ", expected (" << CRYPTONOTE_DEFAULT_TX_MIXIN << ")"); + MERROR_VER("Tx " << get_transaction_hash(tx) << " has incorrect ring size (" << in_to_key.key_offsets.size() - 1 << ", expected (" << cryptonote::TX_OUTPUT_DECOYS << ")"); tvc.m_low_mixin = true; return false; } @@ -3343,7 +3388,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, // // Master Node Checks // - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) { const auto &blacklist = m_master_node_list.get_blacklisted_key_images(); for (const auto &entry : blacklist) @@ -3366,9 +3411,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (hf_version >= HF_VERSION_ENFORCE_MIN_AGE) + if (hf_version >= feature::ENFORCE_MIN_AGE) { - const uint8_t spendable_age = (hf_version>=cryptonote::network_version_17_POS?CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17:CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); + const uint8_t spendable_age = (hf_version >= hf::hf17_POS ? DEFAULT_TX_SPENDABLE_AGE_V17: old::DEFAULT_TX_SPENDABLE_AGE); CHECK_AND_ASSERT_MES((*pmax_used_block_height + spendable_age) <= m_db->height(), false, "Transaction spends at least one output which is too young"); } @@ -3395,6 +3440,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) case rct::RCTType::Bulletproof: case rct::RCTType::Bulletproof2: case rct::RCTType::CLSAG: + case rct::RCTType::BulletproofPlus: { // check all this, either reconstructed (so should really pass), or not { @@ -3430,7 +3476,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) } } - const size_t n_sigs = rv.type == rct::RCTType::CLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size(); + const size_t n_sigs = rct::is_rct_clsag(rv.type) ? rv.p.CLSAGs.size() : rv.p.MGs.size(); if (n_sigs != tx.vin.size()) { MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes"); @@ -3439,7 +3485,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) for (size_t n = 0; n < tx.vin.size(); ++n) { bool error; - if (rv.type == rct::RCTType::CLSAG) + if (rct::is_rct_clsag(rv.type)) error = memcmp(&var::get(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32); else error = rv.p.MGs[n].II.empty() || memcmp(&var::get(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32); @@ -3522,7 +3568,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) } // for bulletproofs, check they're only multi-output after v8 - if (rct::is_rct_bulletproof(rv.type) && hf_version < network_version_8) + if (rct::is_rct_bulletproof(rv.type) && hf_version < hf::hf8) { for (const rct::Bulletproof &proof: rv.p.bulletproofs) { @@ -3537,7 +3583,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) if (tx.type == txtype::beldex_name_system) { uint64_t height = get_current_blockchain_height(); - if (hf_version >= network_version_18_bns) + if (hf_version >= hf::hf18_bns) { cryptonote::tx_extra_beldex_name_system data; std::string fail_reason; @@ -3618,7 +3664,7 @@ if (tx.version >= cryptonote::txversion::v2_ringct) if (master_node_array.empty()) { MERROR_VER("Master Node no longer exists on the network, state change can be ignored"); - return hf_version < cryptonote::network_version_13_checkpointing; // NOTE: Used to be allowed pre HF12. + return hf_version < hf::hf13_checkpointing; // NOTE: Used to be allowed pre HF12. } master_nodes::master_node_info const &master_node_info = *master_node_array[0].info; @@ -3693,14 +3739,14 @@ uint64_t Blockchain::get_fee_quantization_mask() if (mask == 0) { mask = 1; - for (size_t n = PER_KB_FEE_QUANTIZATION_DECIMALS; n < CRYPTONOTE_DISPLAY_DECIMAL_POINT; ++n) + for (size_t n = FEE_QUANTIZATION_DECIMALS; n < beldex::DISPLAY_DECIMAL_POINT; ++n) mask *= 10; } return mask; } //------------------------------------------------------------------ -byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version) +byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, hf version) { const uint64_t min_block_weight = get_min_block_weight(version); if (median_block_weight < min_block_weight) @@ -3708,7 +3754,7 @@ byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, siz byte_and_output_fees fees{0, 0}; uint64_t hi, &lo = fees.first; - if (version >= HF_VERSION_PER_BYTE_FEE) + if (version >= feature::PER_BYTE_FEE) { // fee = block_reward * DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / min_block_weight / median_block_weight / 5 // (but done in 128-bit math). Note that the wallet uses FEE_PER_BYTE as a fallback if it can't @@ -3724,30 +3770,30 @@ byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, siz // This calculation was painful for large txes (in particular sweeps and MN stakes), which // wasn't intended, so in v13 we reduce the reference tx fee back to what it was before and // introduce a per-output fee instead. (This is why this is an hard == instead of a >=). - const uint64_t reference_fee = version == HF_VERSION_REDUCE_FEE ? DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT_V17 : DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT; + const uint64_t reference_fee = version != feature::REDUCE_FEE ? DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT : old::DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT_V17; lo = mul128(block_reward, reference_fee, &hi); div128_32(hi, lo, min_block_weight, &hi, &lo); div128_32(hi, lo, median_block_weight, &hi, &lo); assert(hi == 0); lo /= 5; - if (version >= cryptonote::network_version_17_POS) + if (version >= hf::hf17_POS) fees.second = FEE_PER_OUTPUT_V17; - else if (version >= HF_VERSION_PER_OUTPUT_FEE) - fees.second = FEE_PER_OUTPUT; + else if (version >= feature::PER_OUTPUT_FEE) + fees.second = old::FEE_PER_OUTPUT; return fees; } - constexpr uint64_t fee_base = DYNAMIC_FEE_PER_KB_BASE_FEE_V5; + constexpr uint64_t fee_base = old::DYNAMIC_FEE_PER_KB_BASE_FEE_V5; uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight); lo = mul128(unscaled_fee_base, block_reward, &hi); - static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); - static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); + static_assert(old::DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); + static_assert(old::DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); - // divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't - div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); + // divide in two steps, since the divisor must be 32 bits, but old::DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't + div128_32(hi, lo, old::DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); div128_32(hi, lo, 1000000, &hi, &lo); assert(hi == 0); @@ -3763,7 +3809,7 @@ byte_and_output_fees Blockchain::get_dynamic_base_fee(uint64_t block_reward, siz //------------------------------------------------------------------ bool Blockchain::check_fee(size_t tx_weight, size_t tx_outs, uint64_t fee, uint64_t burned, const tx_pool_options &opts) const { - const uint8_t version = get_network_version(); + const hf version = get_network_version(); const uint64_t blockchain_height = get_current_blockchain_height(); uint64_t median = m_current_block_cumul_weight_limit / 2; @@ -3773,9 +3819,9 @@ bool Blockchain::check_fee(size_t tx_weight, size_t tx_outs, uint64_t fee, uint6 return false; uint64_t needed_fee; - if (version >= HF_VERSION_PER_BYTE_FEE) + if (version >= feature::PER_BYTE_FEE) { - const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + const bool use_long_term_median_in_fee = version >= feature::LONG_TERM_BLOCK_WEIGHT; auto fees = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min(median, m_long_term_effective_median_block_weight) : median, version); MDEBUG("Using " << print_money(fees.first) << "/byte + " << print_money(fees.second) << "/out fee"); needed_fee = tx_weight * fees.first + tx_outs * fees.second; @@ -3822,15 +3868,15 @@ bool Blockchain::check_fee(size_t tx_weight, size_t tx_outs, uint64_t fee, uint6 //------------------------------------------------------------------ byte_and_output_fees Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const { - const uint8_t version = get_network_version(); + const hf version = get_network_version(); const uint64_t db_height = m_db->height(); - if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) - grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; + if (grace_blocks >= REWARD_BLOCKS_WINDOW) + grace_blocks = REWARD_BLOCKS_WINDOW - 1; const uint64_t min_block_weight = get_min_block_weight(version); std::vector weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); + get_last_n_blocks_weights(weights, REWARD_BLOCKS_WINDOW - grace_blocks); weights.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) weights.push_back(min_block_weight); @@ -3847,10 +3893,10 @@ byte_and_output_fees Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_bl base_reward = BLOCK_REWARD_OVERESTIMATE; } - const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + const bool use_long_term_median_in_fee = version >= feature::LONG_TERM_BLOCK_WEIGHT; const uint64_t use_median_value = use_long_term_median_in_fee ? std::min(median, m_long_term_effective_median_block_weight) : median; auto fee = get_dynamic_base_fee(base_reward, use_median_value, version); - const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; + const bool per_byte = version < feature::PER_BYTE_FEE; MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee.first) << "/" << (per_byte ? "byte" : "kB") << " + " << print_money(fee.second) << "/out"); return fee; @@ -3954,8 +4000,7 @@ bool Blockchain::check_block_timestamp(std::vector timestamps, const b bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const { LOG_PRINT_L3("Blockchain::" << __func__); - uint64_t cryptonote_block_future_time_limit = CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2; - if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit) + if(b.timestamp > get_adjusted_time() + old::BLOCK_FUTURE_TIME_LIMIT_V2) { MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); return false; @@ -3984,7 +4029,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons //------------------------------------------------------------------ void Blockchain::return_tx_to_pool(std::vector> &txs) { - uint8_t version = get_network_version(); + hf version = get_network_version(); for (auto& tx : txs) { cryptonote::tx_verification_context tvc{}; @@ -4035,7 +4080,7 @@ Blockchain::block_pow_verified Blockchain::verify_block_pow(cryptonote::block co if (alt_block) { randomx_longhash_context randomx_context = {}; - if (blk.major_version >= cryptonote::network_version_13_checkpointing) + if (blk.major_version >= hf::hf13_checkpointing) { randomx_context.current_blockchain_height = chain_height; randomx_context.seed_height = rx_seedheight(blk_height); @@ -4112,7 +4157,7 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block const crypto::hash blk_hash = cryptonote::get_block_hash(blk); const uint64_t blk_height = cryptonote::get_block_height(blk); const uint64_t chain_height = get_current_blockchain_height(); - const uint8_t hf_version = get_network_version(); + const hf hf_version = get_network_version(); if (alt_block) { @@ -4129,11 +4174,11 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block } // this is a cheap test - // HF19 TODO: remove the requirement that minor_version must be >= network version - if (auto v = get_network_version(blk_height); (v>0) && (blk.major_version != v || blk.minor_version < v)) + // HF20 TODO: remove the requirement that minor_version must be >= network version + if (auto v = get_network_version(blk_height); (v > hf::none) && (blk.major_version != v || (blk.major_version < hf::hf20_bulletproof_plus && blk.minor_version < static_cast(v)))) { - LOG_PRINT_L1("Block with id: " << blk_hash << ", has invalid version " << +blk.major_version << "." << +blk.minor_version << - "; current: " << +v << "." << +v << " for height " << blk_height); + LOG_PRINT_L1("Block with id: " << blk_hash << ", has invalid version " << static_cast(blk.major_version) << "." << +blk.minor_version << + "; current: " << static_cast(v) << "." << static_cast(v) << " for height " << blk_height); return false; } } @@ -4171,11 +4216,14 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block } } - // HF19 TODO: remove the requirement that minor_version must be >= network version - if (required_major_version>0 && (blk.major_version != required_major_version || blk.minor_version < required_major_version)) + // HF20 TODO: remove the requirement that minor_version must be >= network version + if ((required_major_version > hf::none) && + (blk.major_version != required_major_version || + (blk.major_version < hf::hf20_bulletproof_plus && + blk.minor_version < static_cast(required_major_version)))) { - MGINFO_RED("Block with id: " << blk_hash << ", has invalid version " << +blk.major_version << "." << +blk.minor_version << - "; current: " << +required_major_version << "." << +required_major_version << " for height " << blk_height); + MGINFO_RED("Block with id: " << blk_hash << ", has invalid version " << static_cast(blk.major_version) << "." << +blk.minor_version << + "; current: " << static_cast(required_major_version) << "." << static_cast(required_major_version) << " for height " << blk_height); return false; } @@ -4186,7 +4234,7 @@ bool Blockchain::basic_block_checks(cryptonote::block const &blk, bool alt_block bool master_node_checkpoint = false; if(!m_checkpoints.check_block(chain_height, blk_hash, nullptr, &master_node_checkpoint)) { - if (!master_node_checkpoint || (master_node_checkpoint && blk.major_version >= cryptonote::network_version_14_enforce_checkpoints)) + if (!master_node_checkpoint || (master_node_checkpoint && blk.major_version >= hf::hf15_flash)) { MGINFO_RED("CHECKPOINT VALIDATION FAILED"); return false; @@ -4222,22 +4270,21 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L3("Blockchain::" << __func__); - TIME_MEASURE_START(block_processing_time); + auto block_processing_start = std::chrono::steady_clock::now(); std::unique_lock lock{*this}; db_rtxn_guard rtxn_guard(m_db); - TIME_MEASURE_START(t1); + auto t1 = std::chrono::steady_clock::now(); if (!basic_block_checks(bl, false /*alt_block*/)) { bvc.m_verifivation_failed = true; return false; } - TIME_MEASURE_FINISH(t1); + auto t1_elapsed = std::chrono::steady_clock::now() - t1; struct { - uint64_t verify_pow_time; - uint64_t difficulty_calc_time; + std::chrono::nanoseconds verify_pow_time; block_pow_verified blk_pow = {}; } miner = {}; @@ -4253,12 +4300,9 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& } else // check proof of work { - miner.difficulty_calc_time = epee::misc_utils::get_tick_count(); - miner.difficulty_calc_time = epee::misc_utils::get_tick_count() - miner.difficulty_calc_time; - - miner.verify_pow_time = epee::misc_utils::get_tick_count(); + auto verify_pow_start = std::chrono::steady_clock::now(); miner.blk_pow = verify_block_pow(bl, current_diffic, chain_height, false /*alt_block*/); - miner.verify_pow_time = epee::misc_utils::get_tick_count() - miner.verify_pow_time; + miner.verify_pow_time = std::chrono::steady_clock::now() - verify_pow_start; if (!miner.blk_pow.valid) { @@ -4277,10 +4321,10 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& key_images_container keys; uint64_t fee_summary = 0; - uint64_t t_checktx = 0; - uint64_t t_exists = 0; - uint64_t t_pool = 0; - uint64_t t_dblspnd = 0; + auto t_checktx = 0ns; + auto t_exists = 0ns; + auto t_pool = 0ns; + auto t_dblspnd = 0ns; // XXX old code adds miner tx here @@ -4296,7 +4340,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& size_t tx_weight = 0; uint64_t fee = 0; bool relayed = false, do_not_relay = false, double_spend_seen = false; - TIME_MEASURE_START(aa); + auto aa = std::chrono::steady_clock::now(); // XXX old code does not check whether tx exists if (m_db->tx_exists(tx_id)) @@ -4307,9 +4351,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& return false; } - TIME_MEASURE_FINISH(aa); - t_exists += aa; - TIME_MEASURE_START(bb); + auto bb = std::chrono::steady_clock::now(); + t_exists += bb - aa; // get transaction with hash from tx_pool if(!m_tx_pool.take_tx(tx_id, tx_tmp, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen)) @@ -4320,14 +4363,13 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& return false; } - TIME_MEASURE_FINISH(bb); - t_pool += bb; + auto dd = std::chrono::steady_clock::now(); + t_pool += dd - bb; // add the transaction to the temp list of transactions, so we can either // store the list of transactions all at once or return the ones we've // taken from the tx_pool back to it if the block fails verification. txs.push_back(std::make_pair(std::move(tx_tmp), std::move(txblob))); transaction &tx = txs.back().first; - TIME_MEASURE_START(dd); // FIXME: the storage should not be responsible for validation. // If it does any, it is merely a sanity check. @@ -4342,9 +4384,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // break; // } - TIME_MEASURE_FINISH(dd); - t_dblspnd += dd; - TIME_MEASURE_START(cc); + auto cc = std::chrono::steady_clock::now(); + t_dblspnd += cc - dd; #if defined(PER_BLOCK_CHECKPOINT) if (!miner.blk_pow.per_block_checkpointed) @@ -4381,15 +4422,14 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& } } #endif - TIME_MEASURE_FINISH(cc); - t_checktx += cc; + t_checktx += std::chrono::steady_clock::now() - cc; fee_summary += fee; cumulative_block_weight += tx_weight; } m_blocks_txs_check.clear(); - TIME_MEASURE_START(vmt); + auto vmt = std::chrono::steady_clock::now(); uint64_t base_reward = 0; uint64_t already_generated_coins = chain_height ? m_db->get_block_already_generated_coins(chain_height - 1) : 0; if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, get_network_version())) @@ -4400,25 +4440,25 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& return false; } - TIME_MEASURE_FINISH(vmt); + auto vmt_elapsed = std::chrono::steady_clock::now() - vmt; // populate various metadata about the block to be stored alongside it. size_t block_weight = cumulative_block_weight; difficulty_type cumulative_difficulty = current_diffic; // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of - // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins - // at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a + // coins will eventually exceed beldex::MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins + // at beldex::MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and beldex::MONEY_SUPPLY yields a // subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state. - already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY; + already_generated_coins = base_reward < (beldex::MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : beldex::MONEY_SUPPLY; if(chain_height) cumulative_difficulty += m_db->get_block_cumulative_difficulty(chain_height - 1); - TIME_MEASURE_FINISH(block_processing_time); + auto block_processing_time = std::chrono::steady_clock::now() - block_processing_start; if(miner.blk_pow.precomputed) block_processing_time += m_fake_pow_calc_time; rtxn_guard.stop(); - TIME_MEASURE_START(addblock); + auto addblock = std::chrono::steady_clock::now(); uint64_t new_height = 0; if (!bvc.m_verifivation_failed) { @@ -4453,9 +4493,12 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& auto abort_block = beldex::defer([&]() { pop_block_from_blockchain(); - detached_info hook_data{m_db->height(), false /*by_pop_blocks*/}; - for (const auto& hook : m_blockchain_detached_hooks) - hook(hook_data); + exec_detach_hooks( + *this, + m_db->height(), + m_blockchain_detached_hooks, + /*by_pop_blocks=*/false, + /*load_missing_blocks=*/false); }); // TODO(beldex): Not nice, making the hook take in a vector of pair(bl.major_version) << '.' << +bl.minor_version << "\n\tblock reward: " << print_money(fee_after_penalty + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_after_penalty) << ")" ", coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << - ", " << block_processing_time << "ms"); + ", " << tools::friendly_duration(block_processing_time)); } else { @@ -4524,20 +4567,25 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& MINFO("+++++ MINER BLOCK SUCCESSFULLY ADDED\n" "\n\tid: " << id << "\n\tPoW: " << miner.blk_pow.proof_of_work << - "\n\tHEIGHT: " << new_height - 1 << ", v" << +bl.major_version << '.' << +bl.minor_version << ", difficulty: " << current_diffic << + "\n\tHEIGHT: " << new_height - 1 << ", v" << static_cast(bl.major_version) << '.' << +bl.minor_version << ", difficulty: " << current_diffic << "\n\tblock reward: " << print_money(fee_after_penalty + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_after_penalty) << ")" ", coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << - ", " << block_processing_time << "(" << miner.difficulty_calc_time << "/" << miner.verify_pow_time << ")ms"); + ", " << tools::friendly_duration(block_processing_time) << "(" << tools::friendly_duration(miner.verify_pow_time) << ")"); } if(m_show_time_stats) { MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: " - << cumulative_block_weight << " p/t: " << block_processing_time << " (" - << miner.difficulty_calc_time << "/" << miner.verify_pow_time << "/" - << t1 << "/" << t_exists << "/" << t_pool - << "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms"); + << cumulative_block_weight << " p/t: " << tools::friendly_duration(block_processing_time) << " (" + << "/" << tools::friendly_duration(miner.verify_pow_time) + << "/" << tools::friendly_duration(t1_elapsed) + << "/" << tools::friendly_duration(t_exists) + << "/" << tools::friendly_duration(t_pool) + << "/" << tools::friendly_duration(t_checktx) + << "/" << tools::friendly_duration(t_dblspnd) + << "/" << tools::friendly_duration(vmt_elapsed) + << "/" << tools::friendly_duration(addblock_elapsed) << ")"); } @@ -4582,11 +4630,11 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons const uint64_t db_height = m_db->height(); const uint64_t nblocks = std::min(m_long_term_block_weights_window, db_height); - if (!is_hard_fork_at_least(m_nettype, HF_VERSION_LONG_TERM_BLOCK_WEIGHT, get_current_blockchain_height())) + if (!is_hard_fork_at_least(m_nettype, feature::LONG_TERM_BLOCK_WEIGHT, get_current_blockchain_height())) return block_weight; uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks); - uint64_t long_term_effective_median_block_weight = std::max(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + uint64_t long_term_effective_median_block_weight = std::max(BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); uint64_t short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5; uint64_t long_term_block_weight = std::min(block_weight, short_term_constraint); @@ -4602,13 +4650,13 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti // when we reach this, the last hf version is not yet written to the db const uint64_t db_height = m_db->height(); - const uint8_t hf_version = get_network_version(); + const hf hf_version = get_network_version(); uint64_t full_reward_zone = get_min_block_weight(hf_version); - if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + if (hf_version < feature::LONG_TERM_BLOCK_WEIGHT) { std::vector weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_blocks_weights(weights, REWARD_BLOCKS_WINDOW); m_current_block_cumul_weight_median = tools::median(std::move(weights)); } else @@ -4618,7 +4666,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti uint64_t long_term_median; if (db_height == 1) { - long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + long_term_median = BLOCK_GRANTED_FULL_REWARD_ZONE_V5; } else { @@ -4628,7 +4676,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti long_term_median = get_long_term_block_weight_median(db_height - nblocks - 1, nblocks); } - m_long_term_effective_median_block_weight = std::max(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + m_long_term_effective_median_block_weight = std::max(BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5; uint64_t long_term_block_weight = std::min(block_weight, short_term_constraint); @@ -4643,13 +4691,13 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti m_long_term_block_weights_cache_rolling_median.insert(long_term_block_weight); long_term_median = m_long_term_block_weights_cache_rolling_median.median(); } - m_long_term_effective_median_block_weight = std::max(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + m_long_term_effective_median_block_weight = std::max(BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); std::vector weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_blocks_weights(weights, REWARD_BLOCKS_WINDOW); uint64_t short_term_median = tools::median(std::move(weights)); - uint64_t effective_median_block_weight = std::min(std::max(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight); + uint64_t effective_median_block_weight = std::min(std::max(BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight); m_current_block_cumul_weight_median = effective_median_block_weight; } @@ -4683,9 +4731,9 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc, return false; } - const int hf_version = get_network_version(); + const hf hf_version = get_network_version(); bool const POS_block = cryptonote::block_has_POS_components(bl); - if (hf_version >= network_version_12_security_signature and !POS_block){ + if (hf_version >= hf::hf12_security_signature and !POS_block){ crypto::signature security_signature; const bool has_security_signature = cryptonote::get_security_signature_from_tx_extra(bl.miner_tx.extra, security_signature); @@ -4833,8 +4881,6 @@ bool Blockchain::get_checkpoint(uint64_t height, checkpoint_t &checkpoint) const //------------------------------------------------------------------ void Blockchain::block_longhash_worker(uint64_t height, const epee::span &blocks, std::unordered_map &map) const { - TIME_MEASURE_START(t); - for (const auto & block : blocks) { if (m_cancel) @@ -4843,8 +4889,6 @@ void Blockchain::block_longhash_worker(uint64_t height, const epee::span{}); } else if(m_db_sync_mode == db_sync) { @@ -4896,7 +4940,6 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) } } - TIME_MEASURE_FINISH(t1); m_blocks_longhash_table.clear(); m_scan_table.clear(); m_blocks_txs_check.clear(); @@ -5034,7 +5077,7 @@ bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &rewar { reward = 0; auto hard_fork_version = get_network_version(height); - if (hard_fork_version <= network_version_9_master_nodes) + if (hard_fork_version <= hf::hf9_master_nodes) { return true; } @@ -5046,7 +5089,7 @@ bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &rewar if(height == 742425) { - reward = 8500000000 * COIN; //mint 8.5 billion bdx governance in this block + reward = 8500000000 * beldex::COIN; //mint 8.5 billion bdx governance in this block return true; } // Ignore governance reward and payout instead the last @@ -5058,9 +5101,9 @@ bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &rewar size_t num_blocks = cryptonote::get_config(nettype()).GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; // governance reward starting at HF17 - if (hard_fork_version >= network_version_17_POS) + if (hard_fork_version >= hf::hf17_POS) { - reward = num_blocks * FOUNDATION_REWARD_HF17; + reward = num_blocks * beldex::FOUNDATION_REWARD_HF17; return true; } @@ -5080,7 +5123,7 @@ bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &rewar for (const auto &block : blocks) { - if (block.major_version >= network_version_10_bulletproofs) + if (block.major_version >= hf::hf10_bulletproofs) reward += derive_governance_from_block_reward(nettype(), block, hard_fork_version); } @@ -5097,7 +5140,7 @@ bool Blockchain::calc_batched_governance_reward(uint64_t height, uint64_t &rewar bool Blockchain::prepare_handle_incoming_blocks(const std::vector &blocks_entry, std::vector &blocks) { MTRACE("Blockchain::" << __func__); - TIME_MEASURE_START(prepare); + auto prepare = std::chrono::steady_clock::now(); uint64_t bytes = 0; size_t total_txs = 0; blocks.clear(); @@ -5243,18 +5286,18 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector 1 && threads > 1 && m_show_time_stats) - MDEBUG("Prepare blocks took: " << prepare << " ms"); + MDEBUG("Prepare blocks took: " << tools::friendly_duration(prepare_elapsed)); - TIME_MEASURE_START(scantable); + auto scantable = std::chrono::steady_clock::now(); // [input] stores all unique amounts found std::vector < uint64_t > amounts; @@ -5272,7 +5315,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector 0) { - m_fake_scan_time = scantable / total_txs; + auto scantable_elapsed = std::chrono::steady_clock::now() - scantable; + m_fake_scan_time = scantable_elapsed / total_txs; if(m_show_time_stats) - MDEBUG("Prepare scantable took: " << scantable << " ms"); + MDEBUG("Prepare scantable took: " << tools::friendly_duration(scantable_elapsed)); } return true; @@ -5594,7 +5636,7 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get if (!checkpoints.empty()) { MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)"); - if (m_nettype == MAINNET) + if (m_nettype == network_type::MAINNET) { // first check hash crypto::hash hash; @@ -5623,9 +5665,7 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get if (checkpoints.size() > 4) { - uint32_t nblocks; - std::memcpy(&nblocks, checkpoints.data(), 4); - boost::endian::little_to_native_inplace(nblocks); + auto nblocks = oxenc::load_little_to_host(checkpoints.data()); if (nblocks > (std::numeric_limits::max() - 4) / sizeof(hash)) { MERROR("Block hash data is too large"); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 55db47f7456..3597991e9a6 100755 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -29,7 +29,8 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include +#include +#include #include #include @@ -39,6 +40,10 @@ #include #endif +namespace boost::asio { +using io_service = io_context; +} + #include #include #include @@ -57,6 +62,7 @@ #include "common/util.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "rpc/core_rpc_server_binary_commands.h" #include "cryptonote_basic/difficulty.h" #include "cryptonote_tx_utils.h" #include "cryptonote_basic/verification_context.h" @@ -639,7 +645,7 @@ namespace cryptonote * * @return pair of {per-size, per-output} fees */ - static byte_and_output_fees get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version); + static byte_and_output_fees get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, hf version); /** * @brief get dynamic per kB or byte fee estimate for the next few blocks @@ -717,25 +723,25 @@ namespace cryptonote * * @param block_ids a vector of block hashes for which to get the corresponding blocks * @param blocks return-by-reference a vector to store result blocks in - * @param missed_bs return-by-reference a vector to store missed blocks in + * @param missed_bs optional pointer to an unordered_set to add missed blocks ids to * * @return false if an unexpected exception occurs, else true */ - bool get_blocks(const std::vector& block_ids, std::vector>& blocks, std::vector& missed_bs) const; + bool get_blocks(const std::vector& block_ids, std::vector>& blocks, std::unordered_set* missed_bs = nullptr) const; /** * @brief gets transactions based on a list of transaction hashes * * @param txs_ids a vector of hashes for which to get the corresponding transactions * @param txs return-by-reference a vector to store result transactions in - * @param missed_txs return-by-reference a vector to store missed transactions in + * @param missed_txs optional pointer to an unordered set to add missed transactions ids to * @param pruned whether to return full or pruned blobs * * @return false if an unexpected exception occurs, else true */ - bool get_transactions_blobs(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs, bool pruned = false) const; - bool get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::vector& missed_txs) const; - bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; + bool get_transactions_blobs(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs = nullptr, bool pruned = false) const; + bool get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::unordered_set* missed_txs = nullptr) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs = nullptr) const; /** * @brief looks up transactions based on a list of transaction hashes and returns the block @@ -801,7 +807,7 @@ namespace cryptonote * * @return the version */ - uint8_t get_network_version(std::optional height = std::nullopt) const; + hf get_network_version(std::optional height = std::nullopt) const; /** * @brief remove transactions from the transaction pool (if present) @@ -1022,6 +1028,8 @@ namespace cryptonote */ void flush_invalid_blocks(); + bool load_missing_blocks_into_beldex_subsystems(); + #ifndef IN_UNIT_TESTS private: #endif @@ -1049,8 +1057,6 @@ namespace cryptonote bool create_block_template_internal(block& b, const crypto::hash *from_block, block_template_info const &info, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); - bool load_missing_blocks_into_beldex_subsystems(); - // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage typedef std::unordered_set key_images_container; @@ -1087,8 +1093,8 @@ namespace cryptonote bool m_db_sync_on_blocks; uint64_t m_db_sync_threshold; uint64_t m_max_prepare_blocks_threads; - uint64_t m_fake_pow_calc_time; - uint64_t m_fake_scan_time; + std::chrono::nanoseconds m_fake_pow_calc_time; + std::chrono::nanoseconds m_fake_scan_time; uint64_t m_sync_counter; uint64_t m_bytes_to_sync; @@ -1118,7 +1124,8 @@ namespace cryptonote boost::asio::io_service m_async_service; std::thread m_async_thread; - std::unique_ptr m_async_work_idle; + using work_type = boost::asio::executor_work_guard; + std::unique_ptr m_async_work_idle; // some invalid blocks std::set m_invalid_blocks; @@ -1312,7 +1319,7 @@ namespace cryptonote * * @return false if anything is found wrong with the miner transaction, otherwise true */ - bool prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version); + bool prevalidate_miner_transaction(const block& b, uint64_t height, hf hf_version); /** * @brief validates a miner (coinbase) transaction @@ -1329,7 +1336,7 @@ namespace cryptonote * * @return false if anything is found wrong with the miner transaction, otherwise true */ - bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, uint8_t version); + bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, hf version); /** * @brief reverts the blockchain to its previous state following a failed switch diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 0ff4213f17b..353fdf71ade 100755 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -30,7 +30,6 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include -#include #include "epee/string_tools.h" @@ -177,7 +176,7 @@ namespace cryptonote static const command_line::arg_descriptor arg_max_txpool_weight = { "max-txpool-weight" , "Set maximum txpool weight in bytes." - , DEFAULT_TXPOOL_MAX_WEIGHT + , DEFAULT_MEMPOOL_MAX_WEIGHT }; static const command_line::arg_descriptor arg_master_node = { "master-node" @@ -272,7 +271,7 @@ namespace cryptonote , m_starter_message_showed(false) , m_target_blockchain_height(0) , m_last_json_checkpoints_update(0) - , m_nettype(UNDEFINED) + , m_nettype(network_type::UNDEFINED) , m_last_storage_server_ping(0) , m_last_belnet_ping(0) , m_pad_transactions(false) @@ -362,11 +361,11 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) { - if (m_nettype != FAKECHAIN) + if (m_nettype != network_type::FAKECHAIN) { const bool testnet = command_line::get_arg(vm, arg_testnet_on); const bool devnet = command_line::get_arg(vm, arg_devnet_on); - m_nettype = testnet ? TESTNET : devnet ? DEVNET : MAINNET; + m_nettype = testnet ? network_type::TESTNET : devnet ? network_type::DEVNET : network_type::MAINNET; } m_check_uptime_proof_interval.interval(get_net_config().UPTIME_PROOF_CHECK_INTERVAL); @@ -390,7 +389,7 @@ namespace cryptonote bool args_okay = true; if (m_quorumnet_port == 0) { - MERROR("Quorumnet port cannot be 0; please specify a valid port to listen on with: '--" << arg_quorumnet_port.name << " '"); + MFATAL("Quorumnet port cannot be 0; please specify a valid port to listen on with: '--" << arg_quorumnet_port.name << " '"); args_okay = false; } @@ -398,7 +397,7 @@ namespace cryptonote if (pub_ip.size()) { if (!epee::string_tools::get_ip_int32_from_string(m_mn_public_ip, pub_ip)) { - MERROR("Unable to parse IPv4 public address from: " << pub_ip); + MFATAL("Unable to parse IPv4 public address from: " << pub_ip); args_okay = false; } @@ -406,19 +405,19 @@ namespace cryptonote if (m_master_node_list.debug_allow_local_ips) { MWARNING("Address given for public-ip is not public; allowing it because dev-allow-local-ips was specified. This master node WILL NOT WORK ON THE PUBLIC BELDEX NETWORK!"); } else { - MERROR("Address given for public-ip is not public: " << epee::string_tools::get_ip_string_from_int32(m_mn_public_ip)); + MFATAL("Address given for public-ip is not public: " << epee::string_tools::get_ip_string_from_int32(m_mn_public_ip)); args_okay = false; } } } else { - MERROR("Please specify an IPv4 public address which the master node & storage server is accessible from with: '--" << arg_public_ip.name << " '"); + MFATAL("Please specify an IPv4 public address which the master node & storage server is accessible from with: '--" << arg_public_ip.name << " '"); args_okay = false; } if (!args_okay) { - MERROR("IMPORTANT: One or more required master node-related configuration settings/options were omitted or invalid; " + MFATAL("IMPORTANT: One or more required master node-related configuration settings/options were omitted or invalid; " << "please fix them and restart beldexd."); return false; } @@ -432,9 +431,11 @@ namespace cryptonote return m_blockchain_storage.get_current_blockchain_height(); } //----------------------------------------------------------------------------------------------- - void core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const + std::pair core::get_blockchain_top() const { - top_id = m_blockchain_storage.get_tail_id(height); + std::pair result; + result.second = m_blockchain_storage.get_tail_id(result.first); + return result; } //----------------------------------------------------------------------------------------------- bool core::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const @@ -452,17 +453,22 @@ namespace cryptonote return m_blockchain_storage.get_blocks_only(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const + bool core::get_blocks(const std::vector& block_ids, std::vector> blocks, std::unordered_set* missed_bs) const + { + return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs) const { return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::vector& missed_txs) const + bool core::get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::unordered_set* missed_txs) const { return m_blockchain_storage.get_split_transactions_blobs(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs) const { return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } @@ -594,7 +600,7 @@ namespace cryptonote const bool regtest = command_line::get_arg(vm, arg_regtest_on); if (test_options != NULL || regtest) { - m_nettype = FAKECHAIN; + m_nettype = network_type::FAKECHAIN; } bool r = handle_command_line(vm); @@ -620,13 +626,13 @@ namespace cryptonote } auto folder = m_config_folder; - if (m_nettype == FAKECHAIN) + if (m_nettype == network_type::FAKECHAIN) folder /= "fake"; // make sure the data directory exists, and try to lock it if (std::error_code ec; !fs::is_directory(folder, ec) && !fs::create_directories(folder, ec) && ec) { - MERROR("Failed to create directory " + folder.u8string() + (ec ? ": " + ec.message() : ""s)); + MFATAL("Failed to create directory " + folder.u8string() + (ec ? ": " + ec.message() : ""s)); return false; } @@ -648,12 +654,12 @@ namespace cryptonote uint64_t sync_threshold = 1; #if !defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) // In integration mode, don't delete the DB. This should be explicitly done in the tests. Otherwise the more likely behaviour is persisting the DB across multiple daemons in the same test. - if (m_nettype == FAKECHAIN && !keep_fakechain) + if (m_nettype == network_type::FAKECHAIN && !keep_fakechain) { // reset the db by removing the database file before opening it if (!db->remove_data_file(folder)) { - MERROR("Failed to remove data file in " << folder); + MFATAL("Failed to remove data file in " << folder); return false; } fs::remove(bns_db_file_path); @@ -888,7 +894,7 @@ namespace cryptonote try { generate_pair(privkey, pubkey); } catch (const std::exception& e) { - MERROR("failed to generate keypair " << e.what()); + MFATAL("failed to generate keypair " << e.what()); return false; } @@ -975,7 +981,7 @@ namespace cryptonote MGINFO_YELLOW("- x25519: " << tools::type_to_hex(keys.pub_x25519)); } else { // Only print the x25519 version because it's the only thing useful for a non-MN (for - // encrypted LMQ RPC connections). + // encrypted OMQ RPC connections). MGINFO_YELLOW("x25519 public key: " << tools::type_to_hex(keys.pub_x25519)); } @@ -1142,7 +1148,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- void core::parse_incoming_tx_pre(tx_verification_batch_info &tx_info) { - if(tx_info.blob->size() > get_max_tx_size()) + if(tx_info.blob->size() > MAX_TX_SIZE) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_info.blob->size() << ", rejected"); tx_info.tvc.m_verifivation_failed = true; @@ -1196,7 +1202,17 @@ namespace cryptonote if (proofs.size() != 1) return false; const size_t sz = proofs[0].V.size(); - if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS) + if (sz == 0 || sz > TX_BULLETPROOF_MAX_OUTPUTS) + return false; + return true; + } + //----------------------------------------------------------------------------------------------- + static bool is_canonical_bulletproof_plus_layout(const std::vector &proofs) + { + if (proofs.size() != 1) + return false; + const size_t sz = proofs[0].V.size(); + if (sz == 0 || sz > cryptonote::TX_BULLETPROOF_PLUS_MAX_OUTPUTS) return false; return true; } @@ -1267,6 +1283,17 @@ namespace cryptonote } rvv.push_back(&rv); // delayed batch verification break; + case rct::RCTType::BulletproofPlus: + if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus)) + { + MERROR_VER("Bulletproof_plus does not have canonical form"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + } + rvv.push_back(&rv); // delayed batch verification + break; default: MERROR_VER("Unknown rct type: " << (int)rv.type); set_semantics_failed(tx_info[n].tx_hash); @@ -1283,7 +1310,7 @@ namespace cryptonote { if (!tx_info[n].result || tx_info[n].already_have) continue; - if (!rct::is_rct_bulletproof(tx_info[n].tx.rct_signatures.type)) + if (tx_info[n].tx.rct_signatures.type != rct::RCTType::Bulletproof && tx_info[n].tx.rct_signatures.type != rct::RCTType::Bulletproof2 && tx_info[n].tx.rct_signatures.type != rct::RCTType::CLSAG && tx_info[n].tx.rct_signatures.type != rct::RCTType::BulletproofPlus) continue; if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx.rct_signatures)) { @@ -1312,7 +1339,7 @@ namespace cryptonote } catch (const std::exception &e) { - MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what()); + MERROR_VER("Exception in parse_incoming_tx_pre: " << e.what()); info.tvc.m_verifivation_failed = true; } }); @@ -1346,7 +1373,7 @@ namespace cryptonote { // Caller needs to do this around both this *and* parse_incoming_txs //auto lock = incoming_tx_lock(); - uint8_t version = m_blockchain_storage.get_network_version(); + hf version = m_blockchain_storage.get_network_version(); bool ok = true; if (flash_rollback_height) *flash_rollback_height = 0; @@ -1413,7 +1440,7 @@ namespace cryptonote auto &new_flashes = results.first; auto &missing_txs = results.second; - if (m_blockchain_storage.get_network_version() < HF_VERSION_FLASH) + if (m_blockchain_storage.get_network_version() < feature::FLASH) return results; std::vector want(flashes.size(), false); // Really bools, but std::vector is broken. @@ -1624,9 +1651,9 @@ namespace cryptonote } } - if(!keeped_by_block && get_transaction_weight(tx) >= m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + if(!keeped_by_block && get_transaction_weight(tx) >= m_blockchain_storage.get_current_cumulative_block_weight_limit() - COINBASE_BLOB_RESERVED_SIZE) { - MERROR_VER("tx is too large " << get_transaction_weight(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + MERROR_VER("tx is too large " << get_transaction_weight(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_block_weight_limit() - COINBASE_BLOB_RESERVED_SIZE); return false; } @@ -1687,9 +1714,7 @@ namespace cryptonote m_mn_times.add(entry); // Counts the number of times we have been out of sync - uint8_t num_mn_out_of_sync = std::count_if(m_mn_times.begin(), m_mn_times.end(), - [](const master_nodes::timesync_entry entry) { return !entry.in_sync; }); - if (num_mn_out_of_sync > (m_mn_times.array.size() * master_nodes::MAXIMUM_EXTERNAL_OUT_OF_SYNC/100)) { + if (m_mn_times.failures() > (m_mn_times.size() * master_nodes::MAXIMUM_EXTERNAL_OUT_OF_SYNC/100)) { MWARNING("master node time might be out of sync"); // If we are out of sync record the other master node as in sync m_master_node_list.record_timesync_status(pubkey, true); @@ -1723,9 +1748,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- size_t core::get_block_sync_size(uint64_t height) const { - if (block_sync_size > 0) - return block_sync_size; - return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; + return block_sync_size > 0 ? block_sync_size : BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } //----------------------------------------------------------------------------------------------- bool core::are_key_images_spent_in_pool(const std::vector& key_im, std::vector &spent) const @@ -1814,14 +1837,13 @@ namespace cryptonote [this, &cache_to, &result, &cache_build_started](uint64_t height, const crypto::hash& hash, const block& b){ auto& [emission_amount, total_fee_amount, burnt_beldex] = *result; std::vector txs; - std::vector missed_txs; auto coinbase_amount = static_cast(get_outs_money_amount(b.miner_tx)); - get_transactions(b.tx_hashes, txs, missed_txs); + get_transactions(b.tx_hashes, txs); int64_t tx_fee_amount = 0; for(const auto& tx: txs) { - tx_fee_amount += static_cast(get_tx_miner_fee(tx, b.major_version >= HF_VERSION_FEE_BURNING)); - if(b.major_version >= HF_VERSION_FEE_BURNING) + tx_fee_amount += static_cast(get_tx_miner_fee(tx, b.major_version >= feature::FEE_BURNING)); + if(b.major_version >= feature::FEE_BURNING) { burnt_beldex += static_cast(get_burned_amount_from_tx_extra(tx.extra)); } @@ -1867,17 +1889,14 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const { - const uint8_t version = m_blockchain_storage.get_network_version(); - if (version >= 6) + for(const auto& in: tx.vin) { - for(const auto& in: tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, txin_to_key, tokey_in, false); - for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n) - if (tokey_in.key_offsets[n] == 0) - return false; - } + CHECKED_GET_SPECIFIC_VARIANT(in, txin_to_key, tokey_in, false); + for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n) + if (tokey_in.key_offsets[n] == 0) + return false; } + return true; } //----------------------------------------------------------------------------------------------- @@ -2121,9 +2140,9 @@ namespace cryptonote } else if(bvc.m_added_to_main_chain) { - std::vector missed_txs; + std::unordered_set missed_txs; std::vector txs; - m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs); + m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, &missed_txs); if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block"); @@ -2296,8 +2315,10 @@ namespace cryptonote { std::vector const states = get_master_node_list_state({ m_master_keys.pub }); - // wait one block before starting uptime proofs. - if (!states.empty() && (states[0].info->registration_height + 1) < get_current_blockchain_height()) + // wait one block before starting uptime proofs (but not on testnet/devnet, where we sometimes + // have mass registrations/deregistrations where the waiting causes problems). + uint64_t delay_blocks = m_nettype == network_type::MAINNET ? 1 : 0; + if (!states.empty() && (states[0].info->registration_height + delay_blocks) < get_current_blockchain_height()) { m_check_uptime_proof_interval.do_call([this]() { // This timer is not perfectly precise and can leak seconds slightly, so send the uptime @@ -2334,13 +2355,13 @@ namespace cryptonote MGINFO_RED( "Another master node (" << pk << ") is broadcasting the same public IP and ports as this master node (" << epee::string_tools::get_ip_string_from_int32(m_mn_public_ip) << ":" << proof.proof->qnet_port << "[qnet], :" << - proof.proof->storage_https_port << "[SS-HTTP], :" << proof.proof->storage_omq_port << "[SS-LMQ]). " + proof.proof->storage_https_port << "[SS-HTTP], :" << proof.proof->storage_omq_port << "[SS-OMQ]). " "This will lead to deregistration of one or both master nodes if not corrected. " "(Do both master nodes have the correct IP for the master-node-public-ip setting?)"); }); } - if (m_nettype != DEVNET) + if (m_nettype != network_type::DEVNET) { if (!check_external_ping(m_last_storage_server_ping, get_net_config().UPTIME_PROOF_FREQUENCY, "the storage server")) { @@ -2461,7 +2482,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_block_rate() { - if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height() || m_target_blockchain_height == 0) + if (m_offline || m_nettype == network_type::FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height() || m_target_blockchain_height == 0) { MDEBUG("Not checking block rate, offline or syncing"); return true; @@ -2474,7 +2495,7 @@ namespace cryptonote const auto hf_version = get_network_version(m_nettype, m_target_blockchain_height); - static double threshold = 1. / ((24h * 10) / (hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)); // one false positive every 10 days + static double threshold = 1. / ((24h * 10) / (hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)); // one false positive every 10 days static constexpr unsigned int max_blocks_checked = 150; const time_t now = time(NULL); @@ -2486,7 +2507,7 @@ namespace cryptonote unsigned int b = 0; const time_t time_boundary = now - static_cast(seconds[n]); for (time_t ts: timestamps) b += ts >= time_boundary; - const double p = probability(b, seconds[n] / tools::to_seconds((hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD))); + const double p = probability(b, seconds[n] / tools::to_seconds((hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12))); MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); if (p < threshold) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 85cdcf4f6e8..ccddda5d495 100755 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -481,10 +481,9 @@ namespace cryptonote /** * @brief get the hash and height of the most recent block * - * @param height return-by-reference height of the block - * @param top_id return-by-reference hash of the block + * @return height and hash of the top block on the chain */ - void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; + std::pair get_blockchain_top() const; /** * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&, std::vector&) const @@ -512,11 +511,7 @@ namespace cryptonote * * @note see Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const */ - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const - { - return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); - } + bool get_blocks(const std::vector& block_ids, std::vector> blocks, std::unordered_set* missed_bs = nullptr) const; /** * @copydoc Blockchain::get_block_id_by_height @@ -530,21 +525,21 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs = nullptr) const; /** * @copydoc Blockchain::get_transactions * * @note see Blockchain::get_transactions */ - bool get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::vector& missed_txs) const; + bool get_split_transactions_blobs(const std::vector& txs_ids, std::vector>& txs, std::unordered_set* missed_txs = nullptr) const; /** * @copydoc Blockchain::get_transactions * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::unordered_set* missed_txs = nullptr) const; /** * @copydoc Blockchain::get_block_by_hash @@ -992,7 +987,7 @@ namespace cryptonote /// Time point at which the storage server and belnet last pinged us std::atomic m_last_storage_server_ping, m_last_belnet_ping; - std::atomic m_storage_https_port, m_storage_omq_port; + std::atomic m_storage_https_port{0}, m_storage_omq_port{0}; uint32_t mn_public_ip() const { return m_mn_public_ip; } uint16_t storage_https_port() const { return m_storage_https_port; } @@ -1210,8 +1205,8 @@ namespace cryptonote // avoid linking issues (protocol does not link against core). void* m_quorumnet_state = nullptr; - /// Stores x25519 -> access level for LMQ authentication. - /// Not to be modified after the LMQ listener starts. + /// Stores x25519 -> access level for OMQ authentication. + /// Not to be modified after the OMQ listener starts. std::unordered_map m_omq_auth; size_t block_sync_size; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index fc3c9fdab0e..d874d89a8d7 100755 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -133,14 +133,14 @@ namespace cryptonote const uint64_t MASTER_NODE_BASE_REWARD_PERCENTAGE = 95; - uint64_t governance_reward_formula(uint64_t base_reward, uint8_t hf_version) + uint64_t governance_reward_formula(uint64_t base_reward, hf hf_version) { - return hf_version >= network_version_17_POS ? FOUNDATION_REWARD_HF17 : 0;// governance planned at V17 + return hf_version >= hf::hf17_POS ? beldex::FOUNDATION_REWARD_HF17 : 0;// governance planned at V17 } - uint64_t derive_governance_from_block_reward(network_type nettype, const cryptonote::block &block, uint8_t hf_version) + uint64_t derive_governance_from_block_reward(network_type nettype, const cryptonote::block &block, hf hf_version) { - if (hf_version >= network_version_17_POS) + if (hf_version >= hf::hf17_POS) return governance_reward_formula(0, hf_version); uint64_t result = 0; uint64_t mnode_reward = 0; @@ -175,9 +175,9 @@ namespace cryptonote return result; } - bool height_has_governance_output(network_type nettype, uint8_t hard_fork_version, uint64_t height) + bool height_has_governance_output(network_type nettype, hf hard_fork_version, uint64_t height) { - if (hard_fork_version < network_version_17_POS) + if (hard_fork_version < hf::hf17_POS) return false; if(height == 742425) @@ -193,11 +193,11 @@ namespace cryptonote } - uint64_t master_node_reward_formula(uint64_t base_reward, uint8_t hard_fork_version) + uint64_t master_node_reward_formula(uint64_t base_reward, hf hard_fork_version) { return - hard_fork_version >= network_version_17_POS ? MN_REWARD_HF17_POS : - hard_fork_version >= network_version_11_infinite_staking ? (base_reward / 10) * (MASTER_NODE_BASE_REWARD_PERCENTAGE/10) : // 90% of base reward up until HF15's fixed payout + hard_fork_version >= hf::hf17_POS ? beldex::MN_REWARD_HF17_POS : + hard_fork_version >= hf::hf11_infinite_staking ? (base_reward / 10) * (MASTER_NODE_BASE_REWARD_PERCENTAGE/10) : // 90% of base reward up until HF15's fixed payout 0; } @@ -205,7 +205,7 @@ namespace cryptonote { uint64_t hi, lo, rewardhi, rewardlo; lo = mul128(total_master_node_reward, portions, &hi); - div128_64(hi, lo, STAKING_PORTIONS, &rewardhi, &rewardlo); + div128_64(hi, lo, old::STAKING_PORTIONS, &rewardhi, &rewardlo); return rewardlo; } @@ -263,7 +263,7 @@ namespace cryptonote transaction& tx, const beldex_miner_tx_context &miner_tx_context, const blobdata& extra_nonce, - uint8_t hard_fork_version, + hf hard_fork_version, const crypto::signature security_signature) { tx.vin.clear(); @@ -347,7 +347,7 @@ namespace cryptonote size_t rewards_length = 0; std::array rewards = {}; - if (hard_fork_version >= cryptonote::network_version_9_master_nodes) + if (hard_fork_version >= hf::hf9_master_nodes) CHECK_AND_ASSERT_MES(miner_tx_context.block_leader.payouts.size(), false, "Constructing a block leader reward for block but no payout entries specified"); // NOTE: Add Block Producer Reward @@ -356,7 +356,7 @@ namespace cryptonote { CHECK_AND_ASSERT_MES(miner_tx_context.POS_block_producer.payouts.size(), false, "Constructing a reward for block produced by POS but no payout entries specified"); CHECK_AND_ASSERT_MES(miner_tx_context.POS_block_producer.key, false, "Null Key given for POS Block Producer"); - CHECK_AND_ASSERT_MES(hard_fork_version >= cryptonote::network_version_17_POS, false, "POS Block Producer is not valid until HF16, current HF" << hard_fork_version); + CHECK_AND_ASSERT_MES(hard_fork_version >= hf::hf17_POS , false, "POS Block Producer is not valid until HF16, current HF" << static_cast(hard_fork_version)); uint64_t leader_reward = reward_parts.master_node_total; if (miner_tx_context.block_leader.key == miner_tx_context.POS_block_producer.key) @@ -385,23 +385,23 @@ namespace cryptonote if (uint64_t miner_amount = reward_parts.base_miner + reward_parts.miner_fee; miner_amount) rewards[rewards_length++] = {reward_type::miner, miner_tx_context.miner_block_producer, miner_amount}; - if (hard_fork_version >= cryptonote::network_version_9_master_nodes) + if (hard_fork_version >= hf::hf9_master_nodes) { std::vector split_rewards = distribute_reward_by_portions(leader.payouts, reward_parts.master_node_total, - hard_fork_version >= cryptonote::network_version_17_POS /*distribute_remainder*/); + hard_fork_version >= hf::hf17_POS /*distribute_remainder*/); for (size_t i = 0; i < leader.payouts.size(); i++) rewards[rewards_length++] = {reward_type::mnode, leader.payouts[i].address, split_rewards[i]}; } } // NOTE: Add Governance Payout - if (hard_fork_version >= network_version_17_POS && already_generated_coins != 0) + if (hard_fork_version >= hf::hf17_POS && already_generated_coins != 0) { if (reward_parts.governance_paid == 0) { - CHECK_AND_ASSERT_MES(hard_fork_version >= network_version_17_POS, false, "Governance reward can NOT be 0 before hardfork 17, hard_fork_version: " << hard_fork_version); + CHECK_AND_ASSERT_MES(hard_fork_version >= hf::hf17_POS, false, "Governance reward can NOT be 0 after hardfork 17, hard_fork_version: " << static_cast(hard_fork_version)); } else { @@ -441,12 +441,12 @@ namespace cryptonote out.target = tk; out.amount = amount; tx.vout.push_back(out); - tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + tx.output_unlock_times.push_back(height + MINED_MONEY_UNLOCK_WINDOW); summary_amounts += amount; } uint64_t expected_amount = 0; - if (hard_fork_version <= cryptonote::network_version_16) + if (hard_fork_version <= hf::hf16) { // NOTE: Use the amount actually paid out when we split the master node // reward (across up to 4 recipients) which may actually pay out less than @@ -469,21 +469,21 @@ namespace cryptonote CHECK_AND_ASSERT_MES(tx.vout.size() == rewards_length, false, "TX output mis-match with rewards expected: " << rewards_length << ", tx outputs: " << tx.vout.size()); //lock - tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.unlock_time = height + MINED_MONEY_UNLOCK_WINDOW; tx.vin.push_back(txin_gen{height}); tx.invalidate_hashes(); //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); - if ((hard_fork_version>=network_version_12_security_signature) && !miner_tx_context.POS) { + if ((hard_fork_version >= hf::hf12_security_signature) && !miner_tx_context.POS) { add_security_signature_to_tx_extra(tx.extra, security_signature); } LOG_PRINT_L2("MINER_TX generated ok"); return true; } - bool get_beldex_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, int hard_fork_version, block_reward_parts &result, const beldex_block_reward_context &beldex_context) + bool get_beldex_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, hf hard_fork_version, block_reward_parts &result, const beldex_block_reward_context &beldex_context) { result = {}; uint64_t base_reward, base_reward_unpenalized; @@ -508,19 +508,19 @@ namespace cryptonote // We base governance fees and MN rewards based on the block reward formula. (Prior to HF13, // however, they were accidentally based on the block reward formula *after* subtracting a // potential penalty if the block producer includes txes beyond the median size limit). - //result.original_base_reward = hard_fork_version >= network_version_14_enforce_checkpoints ? base_reward_unpenalized : base_reward; + //result.original_base_reward = hard_fork_version >= hf::hf15_flash ? base_reward_unpenalized : base_reward; result.original_base_reward = base_reward; // There is a goverance fee due every block. Beginning in hardfork 10 this is still subtracted // from the block reward as if it was paid, but the actual payments get batched into rare, large // accumulated payments. (Before hardfork 10 they are included in every block, unbatched). result.governance_due = governance_reward_formula(result.original_base_reward, hard_fork_version); - result.governance_paid = hard_fork_version >= network_version_10_bulletproofs + result.governance_paid = hard_fork_version >= hf::hf10_bulletproofs ? beldex_context.batched_governance : result.governance_due; uint64_t const master_node_reward = master_node_reward_formula(result.original_base_reward, hard_fork_version); - if (hard_fork_version < cryptonote::network_version_17_POS) + if (hard_fork_version < hf::hf17_POS) { result.master_node_total = calculate_sum_of_portions(beldex_context.block_leader_payouts, master_node_reward); // The base_miner amount is everything left in the base reward after subtracting off the master @@ -880,7 +880,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(key_image_proofs.proofs.size() >= 1, false, "No key image proofs were generated for staking tx"); add_tx_key_image_proofs_to_tx_extra(tx.extra, key_image_proofs); - if (tx_params.hf_version <= cryptonote::network_version_14_enforce_checkpoints) + if (tx_params.hf_version <= hf::hf15_flash) tx.type = txtype::standard; } @@ -1131,8 +1131,8 @@ namespace cryptonote // they are now for our fake networks (which we need to do because we no longer have pre-CLSAG // tx generation code). rct::RCTConfig rct_config{ - tx_params.hf_version < network_version_10_bulletproofs ? rct::RangeProofType::Borromean : rct::RangeProofType::PaddedBulletproof, - tx_params.hf_version >= HF_VERSION_CLSAG ? 3 : tx_params.hf_version >= HF_VERSION_SMALLER_BP ? 2 : 1 + tx_params.hf_version < hf::hf10_bulletproofs ? rct::RangeProofType::Borromean : rct::RangeProofType::PaddedBulletproof, + tx_params.hf_version >= feature::CLSAG ? 3 : tx_params.hf_version >= feature::SMALLER_BP ? 2 : 1 }; return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct_config, NULL, tx_params); @@ -1148,12 +1148,12 @@ namespace cryptonote std::string tx_bl = oxenc::from_hex(conf.GENESIS_TX); bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = 1; + bl.major_version = hf::hf1; bl.minor_version = 0; bl.timestamp = 0; bl.nonce = conf.GENESIS_NONCE; miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ - hash = cryptonote::get_block_longhash(UNDEFINED, cryptonote::randomx_longhash_context(NULL, b, height), b, height, threads); + hash = cryptonote::get_block_longhash(network_type::UNDEFINED, cryptonote::randomx_longhash_context(NULL, b, height), b, height, threads); return true; }, bl, 1, 0); bl.invalidate_hashes(); @@ -1163,7 +1163,7 @@ namespace cryptonote crypto::hash get_altblock_longhash(cryptonote::network_type nettype, randomx_longhash_context const &randomx_context, const block& b, uint64_t height) { crypto::hash result = {}; - if (nettype == FAKECHAIN || b.major_version < network_version_13_checkpointing) + if (nettype == network_type::FAKECHAIN || b.major_version < hf::hf13_checkpointing) { result = get_block_longhash(nettype, randomx_context, b, height, 0); } @@ -1181,7 +1181,7 @@ namespace cryptonote const uint64_t height) { *this = {}; - if (b.major_version >= network_version_13_checkpointing) + if (b.major_version >= hf::hf13_checkpointing) { if (pbc) // null only happens when generating genesis block, 0 init randomx is ok { @@ -1196,20 +1196,20 @@ namespace cryptonote { crypto::hash result = {}; const blobdata bd = get_block_hashing_blob(b); - const uint8_t hf_version = b.major_version; + const auto hf_version = b.major_version; #if defined(BELDEX_INTEGRATION_TESTS) miners = 0; #endif crypto::cn_slow_hash_type cn_type = cn_slow_hash_type::heavy_v1; - if (nettype == FAKECHAIN) + if (nettype == network_type::FAKECHAIN) { cn_type = cn_slow_hash_type::turtle_lite_v2; } else { - if (hf_version >= network_version_13_checkpointing) + if (hf_version >= hf::hf13_checkpointing) { rx_slow_hash(randomx_context.current_blockchain_height, randomx_context.seed_height, @@ -1222,9 +1222,9 @@ namespace cryptonote return result; } - if (hf_version >= network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) cn_type = cn_slow_hash_type::turtle_lite_v2; - else if (hf_version >= network_version_7) + else if (hf_version >= hf::hf7) cn_type = crypto::cn_slow_hash_type::heavy_v2; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 4afb43be06d..2eb10a4b127 100755 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -42,14 +42,14 @@ namespace cryptonote bool get_deterministic_output_key (const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key); bool validate_governance_reward_key (uint64_t height, std::string_view governance_wallet_address_str, size_t output_index, const crypto::public_key& output_key, const cryptonote::network_type nettype); - uint64_t governance_reward_formula (uint64_t base_reward, uint8_t hf_version); + uint64_t governance_reward_formula (uint64_t base_reward, hf hf_version); bool block_has_governance_output (network_type nettype, cryptonote::block const &block); - bool height_has_governance_output (network_type nettype, uint8_t hard_fork_version, uint64_t height); - uint64_t derive_governance_from_block_reward (network_type nettype, const cryptonote::block &block, uint8_t hf_version); + bool height_has_governance_output (network_type nettype, hf hard_fork_version, uint64_t height); + uint64_t derive_governance_from_block_reward (network_type nettype, const cryptonote::block &block, hf hf_version); std::vector distribute_reward_by_portions(const std::vector& payout, uint64_t total_reward, bool distribute_remainder); uint64_t get_portion_of_reward (uint64_t portions, uint64_t total_master_node_reward); - uint64_t master_node_reward_formula (uint64_t base_reward, uint8_t hard_fork_version); + uint64_t master_node_reward_formula (uint64_t base_reward, hf hard_fork_version); struct beldex_miner_tx_context { @@ -76,7 +76,7 @@ namespace cryptonote return result; } - network_type nettype = MAINNET; + network_type nettype = network_type::MAINNET; bool POS; // If true, POS_.* varables are set, otherwise miner_block_producer is set, determining who should get the coinbase reward. master_nodes::payout POS_block_producer; // Can be different from the leader in POS if the original leader fails to complete the round, the block producer changes. @@ -95,7 +95,7 @@ namespace cryptonote transaction& tx, const beldex_miner_tx_context &miner_context, const blobdata& extra_nonce = blobdata(), - uint8_t hard_fork_version = 1, + hf hard_fork_version = hf::hf1, const crypto::signature security_signature={} ); struct block_reward_parts @@ -129,7 +129,7 @@ namespace cryptonote // cryptonote_core since it would have a circular dependency on Blockchain // NOTE: Block reward function that should be called after hard fork v10 - bool get_beldex_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, int hard_fork_version, block_reward_parts &result, const beldex_block_reward_context &beldex_context); + bool get_beldex_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, hf hard_fork_version, block_reward_parts &result, const beldex_block_reward_context &beldex_context); struct tx_source_entry { @@ -165,9 +165,9 @@ namespace cryptonote struct tx_destination_entry { - std::string original; - uint64_t amount; //money - account_public_address addr; //destination address + std::string original; // Cached string of the address. Access using address() + uint64_t amount; // Money + account_public_address addr; // Destination Address bool is_subaddress; bool is_integrated; @@ -206,7 +206,7 @@ namespace cryptonote struct beldex_construct_tx_params { - uint8_t hf_version = cryptonote::network_version_7; + hf hf_version = hf::hf7; txtype tx_type = txtype::standard; // Can be set to non-zero values to have the tx be constructed specifying required burn amounts diff --git a/src/cryptonote_core/master_node_list.cpp b/src/cryptonote_core/master_node_list.cpp index 8d467dbdc85..816f3723ae2 100755 --- a/src/cryptonote_core/master_node_list.cpp +++ b/src/cryptonote_core/master_node_list.cpp @@ -27,12 +27,14 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "cryptonote_config.h" +#include "beldex_economy.h" #include "ringct/rctTypes.h" #include #include #include #include -#include +#include +#include extern "C" { #include @@ -51,7 +53,6 @@ extern "C" { #include "common/random.h" #include "common/lock.h" #include "common/hex.h" -#include "epee/misc_os_dependent.h" #include "blockchain.h" #include "master_node_quorum_cop.h" @@ -65,18 +66,20 @@ extern "C" { #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "master_nodes" +using cryptonote::hf; + namespace master_nodes { size_t constexpr STORE_LONG_TERM_STATE_INTERVAL = 10000; constexpr auto X25519_MAP_PRUNING_INTERVAL = 5min; constexpr auto X25519_MAP_PRUNING_LAG = 24h; - static_assert(X25519_MAP_PRUNING_LAG > config::UPTIME_PROOF_VALIDITY, "x25519 map pruning lag is too short!"); + static_assert(X25519_MAP_PRUNING_LAG > cryptonote::config::UPTIME_PROOF_VALIDITY, "x25519 map pruning lag is too short!"); - static uint64_t short_term_state_cull_height(uint8_t hf_version, uint64_t block_height) + static uint64_t short_term_state_cull_height(hf hf_version, uint64_t block_height) { size_t constexpr DEFAULT_SHORT_TERM_STATE_HISTORY = 6 * STATE_CHANGE_TX_LIFETIME_IN_BLOCKS; - static_assert(DEFAULT_SHORT_TERM_STATE_HISTORY >= 12 * BLOCKS_PER_HOUR, // Arbitrary, but raises a compilation failure if it gets shortened. + static_assert(DEFAULT_SHORT_TERM_STATE_HISTORY >= 12 * cryptonote::BLOCKS_PER_HOUR, // Arbitrary, but raises a compilation failure if it gets shortened. "not enough short term state storage for blink quorum retrieval!"); uint64_t result = (block_height < DEFAULT_SHORT_TERM_STATE_HISTORY) ? 0 : block_height - DEFAULT_SHORT_TERM_STATE_HISTORY; @@ -93,7 +96,7 @@ namespace master_nodes void master_node_list::init() { std::lock_guard lock(m_mn_mutex); - if (m_blockchain.get_network_version() < cryptonote::network_version_9_master_nodes) + if (m_blockchain.get_network_version() < hf::hf9_master_nodes) { reset(true); return; @@ -120,7 +123,7 @@ namespace master_nodes std::sort(result.begin(), result.end(), [](const pubkey_and_mninfo &a, const pubkey_and_mninfo &b) { - return memcmp(reinterpret_cast(&a), reinterpret_cast(&b), sizeof(a)) < 0; + return a.first < b.first; }); return result; } @@ -327,15 +330,15 @@ namespace master_nodes return result; } - void validate_contributor_args(uint8_t hf_version, contributor_args_t const &contributor_args) + void validate_contributor_args(hf hf_version, contributor_args_t const &contributor_args) { if (contributor_args.portions.empty()) throw invalid_contributions{"No portions given"}; if (contributor_args.portions.size() != contributor_args.addresses.size()) throw invalid_contributions{"Number of portions (" + std::to_string(contributor_args.portions.size()) + ") doesn't match the number of addresses (" + std::to_string(contributor_args.portions.size()) + ")"}; - if (contributor_args.portions.size() > MAX_NUMBER_OF_CONTRIBUTORS) + if (contributor_args.portions.size() > beldex::MAX_NUMBER_OF_CONTRIBUTORS) throw invalid_contributions{"Too many contributors"}; - if (contributor_args.portions_for_operator > STAKING_PORTIONS) + if (contributor_args.portions_for_operator > cryptonote::old::STAKING_PORTIONS) throw invalid_contributions{"Operator portions are too high"}; if (!check_master_node_portions(hf_version, contributor_args.portions)) @@ -391,6 +394,7 @@ namespace master_nodes case rct::RCTType::Bulletproof: case rct::RCTType::Bulletproof2: case rct::RCTType::CLSAG: + case rct::RCTType::BulletproofPlus: money_transferred = rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev); break; case rct::RCTType::Full: @@ -436,7 +440,7 @@ namespace master_nodes } bool tx_get_staking_components_and_amounts(cryptonote::network_type nettype, - uint8_t hf_version, + hf hf_version, cryptonote::transaction const &tx, uint64_t block_height, staking_components *contribution) @@ -471,7 +475,7 @@ namespace master_nodes hw::device &hwdev = hw::get_device("default"); contribution->transferred = 0; bool stake_decoded = true; - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) { // In Infinite Staking, we lock the key image that would be generated if // you tried to send your stake and prevent it from being transacted on @@ -552,7 +556,7 @@ namespace master_nodes } } - if (hf_version < cryptonote::network_version_11_infinite_staking) + if (hf_version < hf::hf11_infinite_staking) { // Pre Infinite Staking, we only need to prove the amount sent is // sufficient to become a contributor to the Master Node and that there @@ -566,7 +570,7 @@ namespace master_nodes unlock_time = tx.output_unlock_times[i]; uint64_t min_height = block_height + staking_num_lock_blocks(nettype,hf_version); - has_correct_unlock_time = unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && unlock_time >= min_height; + has_correct_unlock_time = unlock_time < cryptonote::MAX_BLOCK_NUMBER && unlock_time >= min_height; } if (has_correct_unlock_time) @@ -599,7 +603,7 @@ namespace master_nodes if (tx.type != cryptonote::txtype::state_change) return false; - uint8_t const hf_version = block.major_version; + const auto hf_version = block.major_version; cryptonote::tx_extra_master_node_state_change state_change; if (!cryptonote::get_master_node_state_change_from_tx_extra(tx.extra, state_change, hf_version)) { @@ -670,7 +674,7 @@ namespace master_nodes else LOG_PRINT_L1("Deregistration for master node: " << key); - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) { for (const auto &contributor : info.contributors) { @@ -689,7 +693,7 @@ namespace master_nodes return true; case new_state::decommission: - if (hf_version < cryptonote::network_version_13_checkpointing) { + if (hf_version < hf::hf13_checkpointing) { MERROR("Invalid decommission transaction seen before network v12"); return false; } @@ -710,7 +714,7 @@ namespace master_nodes info.last_decommission_reason_consensus_any = state_change.reason_consensus_any; info.decommission_count++; - if (hf_version >= cryptonote::network_version_14_enforce_checkpoints) { + if (hf_version >= hf::hf15_flash) { // Assigning invalid swarm id effectively kicks the node off // its current swarm; it will be assigned a new swarm id when it // gets recommissioned. Prior to HF13 this step was incorrectly @@ -727,7 +731,7 @@ namespace master_nodes return true; case new_state::recommission: { - if (hf_version < cryptonote::network_version_13_checkpointing) { + if (hf_version < hf::hf13_checkpointing) { MERROR("Invalid recommission transaction seen before network v12"); return false; } @@ -772,7 +776,7 @@ namespace master_nodes return true; } case new_state::ip_change_penalty: - if (hf_version < cryptonote::network_version_13_checkpointing) { + if (hf_version < hf::hf13_checkpointing) { MERROR("Invalid ip_change_penalty transaction seen before network v12"); return false; } @@ -801,7 +805,7 @@ namespace master_nodes } } - bool master_node_list::state_t::process_key_image_unlock_tx(cryptonote::network_type nettype, uint64_t block_height, const cryptonote::transaction &tx,uint8_t version) + bool master_node_list::state_t::process_key_image_unlock_tx(cryptonote::network_type nettype, uint64_t block_height, const cryptonote::transaction &tx,hf version) { crypto::public_key mnode_key; if (!cryptonote::get_master_node_pubkey_from_tx_extra(tx.extra, mnode_key)) @@ -839,9 +843,9 @@ namespace master_nodes if (cit != contributor.locked_contributions.end()) { // NOTE(beldex): This should be checked in blockchain check_tx_inputs already - if(version >= cryptonote::network_version_18_bns) + if(version >= hf::hf18_bns) { - if (cit->amount < (master_nodes::SMALL_CONTRIBUTOR_THRESHOLD*COIN) && (block_height - node_info.registration_height) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) + if (cit->amount < (master_nodes::SMALL_CONTRIBUTOR_THRESHOLD * beldex::COIN) && (block_height - node_info.registration_height) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) { LOG_PRINT_L1("Unlock TX: small contributor trying to unlock node before " << std::to_string(master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) @@ -875,7 +879,7 @@ namespace master_nodes return *it->second; } - bool is_registration_tx(cryptonote::network_type nettype, uint8_t hf_version, const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, master_node_info& info) + bool is_registration_tx(cryptonote::network_type nettype, hf hf_version, const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, master_node_info& info) { contributor_args_t contributor_args = {}; crypto::public_key master_node_key; @@ -917,7 +921,7 @@ namespace master_nodes return false; } - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) { // In HF16 we start enforcing three things that were always done but weren't actually enforced: // 1. the staked amount in the tx must be a single output. @@ -954,10 +958,10 @@ namespace master_nodes // Don't need this check for HF16+ because the number of reserved spots is already checked in // the registration details, and we disallow a non-operator registration. - if (total_num_of_addr > MAX_NUMBER_OF_CONTRIBUTORS) + if (total_num_of_addr > beldex::MAX_NUMBER_OF_CONTRIBUTORS) { LOG_PRINT_L1("Register TX: Number of participants: " << total_num_of_addr << - " exceeded the max number of contributors: " << MAX_NUMBER_OF_CONTRIBUTORS << + " exceeded the max number of contributors: " << beldex::MAX_NUMBER_OF_CONTRIBUTORS << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx)); return false; @@ -978,7 +982,7 @@ namespace master_nodes info.swarm_id = UNASSIGNED_SWARM_ID; info.last_ip_change_height = block_height; - if(hf_version >= cryptonote::network_version_18_bns) + if(hf_version >= hf::hf18_bns) info.recommission_credit = DECOMMISSION_INITIAL_CREDIT_V18; for (size_t i = 0; i < contributor_args.addresses.size(); i++) @@ -993,7 +997,7 @@ namespace master_nodes uint64_t hi, lo, resulthi, resultlo; lo = mul128(info.staking_requirement, contributor_args.portions[i], &hi); - div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo); + div128_64(hi, lo, cryptonote::old::STAKING_PORTIONS, &resulthi, &resultlo); info.contributors.emplace_back(); auto &contributor = info.contributors.back(); @@ -1005,7 +1009,7 @@ namespace master_nodes // In HF16 we require that the amount staked in the registration tx be at least the amount // reserved for the operator. Before HF16 it only had to be >= 25%, even if the operator // reserved amount was higher (though wallets would never actually do this). - if (hf_version >= cryptonote::network_version_17_POS && stake.transferred < info.contributors[0].reserved) + if (hf_version >= hf::hf17_POS && stake.transferred < info.contributors[0].reserved) { LOG_PRINT_L1("Register TX rejected: TX does not have sufficient operator stake"); return false; @@ -1016,7 +1020,7 @@ namespace master_nodes bool master_node_list::state_t::process_registration_tx(cryptonote::network_type nettype, const cryptonote::block &block, const cryptonote::transaction& tx, uint32_t index, const master_node_keys *my_keys) { - uint8_t const hf_version = block.major_version; + const auto hf_version = block.major_version; uint64_t const block_timestamp = block.timestamp; uint64_t const block_height = cryptonote::get_block_height(block); @@ -1026,7 +1030,7 @@ namespace master_nodes if (!is_registration_tx(nettype, hf_version, tx, block_timestamp, block_height, index, key, info)) return false; - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) { // NOTE(beldex): Grace period is not used anymore with infinite staking. So, if someone somehow reregisters, we just ignore it const auto iter = master_nodes_infos.find(key); @@ -1053,7 +1057,7 @@ namespace master_nodes const auto iter = master_nodes_infos.find(key); if (iter != master_nodes_infos.end()) { - if (hf_version >= cryptonote::network_version_10_bulletproofs) + if (hf_version >= hf::hf10_bulletproofs) { master_node_info const &old_info = *iter->second; uint64_t expiry_height = old_info.registration_height + staking_num_lock_blocks(nettype,hf_version); @@ -1095,7 +1099,7 @@ namespace master_nodes bool master_node_list::state_t::process_contribution_tx(cryptonote::network_type nettype, const cryptonote::block &block, const cryptonote::transaction& tx, uint32_t index) { uint64_t const block_height = cryptonote::get_block_height(block); - uint8_t const hf_version = block.major_version; + const auto hf_version = block.major_version; staking_components stake = {}; if (!tx_get_staking_components_and_amounts(nettype, hf_version, tx, block_height, &stake)) @@ -1151,7 +1155,7 @@ namespace master_nodes other_reservations++; } - if (hf_version >= cryptonote::network_version_17_POS && stake.locked_contributions.size() != 1) + if (hf_version >= hf::hf17_POS && stake.locked_contributions.size() != 1) { // Nothing has ever created stake txes with multiple stake outputs, but we start enforcing // that in HF16. @@ -1162,20 +1166,20 @@ namespace master_nodes // Check node contributor counts { bool too_many_contributions = false; - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) // Before HF16 we didn't properly take into account unfilled reservation spots - too_many_contributions = existing_contributions + other_reservations + 1 > MAX_NUMBER_OF_CONTRIBUTORS; - else if (hf_version >= cryptonote::network_version_11_infinite_staking) + too_many_contributions = existing_contributions + other_reservations + 1 > beldex::MAX_NUMBER_OF_CONTRIBUTORS; + else if (hf_version >= hf::hf11_infinite_staking) // As of HF11 we allow up to 4 stakes total (except for the loophole closed above) - too_many_contributions = existing_contributions + stake.locked_contributions.size() > MAX_NUMBER_OF_CONTRIBUTORS; + too_many_contributions = existing_contributions + stake.locked_contributions.size() > beldex::MAX_NUMBER_OF_CONTRIBUTORS; else // Before HF11 we allowed up to 4 contributors, but each can contribute multiple times - too_many_contributions = new_contributor && contributors.size() >= MAX_NUMBER_OF_CONTRIBUTORS; + too_many_contributions = new_contributor && contributors.size() >= beldex::MAX_NUMBER_OF_CONTRIBUTORS; if (too_many_contributions) { LOG_PRINT_L1("TX: Already hit the max number of contributions: " - << MAX_NUMBER_OF_CONTRIBUTORS + << beldex::MAX_NUMBER_OF_CONTRIBUTORS << " for contributor: " << cryptonote::get_account_address_as_str(nettype, false, stake.address) << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx)); return false; @@ -1184,11 +1188,11 @@ namespace master_nodes // Check that the contribution is large enough uint64_t min_contribution; - if (!new_contributor && hf_version < cryptonote::network_version_11_infinite_staking) + if (!new_contributor && hf_version < hf::hf11_infinite_staking) { // Follow-up contributions from an existing contributor could be any size before HF11 min_contribution = 1; } - else if (hf_version < cryptonote::network_version_17_POS) + else if (hf_version < hf::hf17_POS) { // The implementation before HF16 was a bit broken w.r.t. properly handling reserved amounts min_contribution = get_min_node_contribution(hf_version, curinfo.staking_requirement, curinfo.total_reserved, existing_contributions); @@ -1249,7 +1253,7 @@ namespace master_nodes info.last_reward_block_height = block_height; info.last_reward_transaction_index = index; - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) for (const auto &contribution : stake.locked_contributions) contributor.locked_contributions.push_back(contribution); @@ -1342,7 +1346,7 @@ namespace master_nodes // TODO(doyle): Core tests need to generate coherent timestamps with // POS. So we relax the rules here for now. - if (nettype != cryptonote::FAKECHAIN) + if (nettype != cryptonote::network_type::FAKECHAIN) { auto round_begin_timestamp = timings.r0_timestamp + (block.POS.round * POS_ROUND_TIME); auto round_end_timestamp = round_begin_timestamp + POS_ROUND_TIME; @@ -1476,7 +1480,7 @@ namespace master_nodes void master_node_list::verify_block(const cryptonote::block &block, bool alt_block, cryptonote::checkpoint_t const *checkpoint) { - if (block.major_version < cryptonote::network_version_9_master_nodes) + if (block.major_version < hf::hf9_master_nodes) return; std::string_view block_type = alt_block ? "alt block "sv : "block "sv; @@ -1484,7 +1488,7 @@ namespace master_nodes // // NOTE: Verify the checkpoint given on this height that locks in a block in the past. // - if (block.major_version >= cryptonote::network_version_14_enforce_checkpoints && checkpoint) + if (block.major_version >= hf::hf15_flash && checkpoint) { std::vector> alt_quorums; std::shared_ptr quorum = get_quorum(quorum_type::checkpointing, checkpoint->height, false, alt_block ? &alt_quorums : nullptr); @@ -1516,7 +1520,7 @@ namespace master_nodes // POS::timings timings = {}; uint64_t height = cryptonote::get_block_height(block); - if (block.major_version >= cryptonote::network_version_17_POS) + if (block.major_version >= hf::hf17_POS) { uint64_t prev_timestamp = 0; if (alt_block) @@ -1546,8 +1550,8 @@ namespace master_nodes // std::shared_ptr POS_quorum; std::vector> alt_POS_quorums; - bool POS_hf = block.major_version >= cryptonote::network_version_17_POS; - + bool POS_hf = block.major_version >= hf::hf17_POS; + if (POS_hf) { POS_quorum = get_quorum(quorum_type::POS, @@ -1556,7 +1560,7 @@ namespace master_nodes alt_block ? &alt_POS_quorums : nullptr); } - if (m_blockchain.nettype() != cryptonote::FAKECHAIN) + if (m_blockchain.nettype() != cryptonote::network_type::FAKECHAIN) { // TODO(doyle): Core tests don't generate proper timestamps for detecting // timeout yet. So we don't do a timeout check and assume all blocks @@ -1585,6 +1589,7 @@ namespace master_nodes // NOTE: No POS quorums are generated when the network has insufficient nodes to generate quorums // Or, block specifies time after all the rounds have timed out bool miner_block = !POS_hf || !POS_quorum; + // std::cout << "miner_block : " << miner_block << std::endl; result = verify_block_components(m_blockchain.nettype(), block, @@ -1602,7 +1607,7 @@ namespace master_nodes void master_node_list::block_add(const cryptonote::block& block, const std::vector& txs, cryptonote::checkpoint_t const *checkpoint) { - if (block.major_version < cryptonote::network_version_9_master_nodes) + if (block.major_version < hf::hf9_master_nodes) return; std::lock_guard lock(m_mn_mutex); @@ -1617,8 +1622,8 @@ namespace master_nodes bool newest_block = m_blockchain.get_current_blockchain_height() == (block_height + 1); auto now = POS::clock::now().time_since_epoch(); - auto earliest_time = std::chrono::seconds(block.timestamp) - (block.major_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD); - auto latest_time = std::chrono::seconds(block.timestamp) + (block.major_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD); + auto earliest_time = std::chrono::seconds(block.timestamp) - (block.major_version >= hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12); + auto latest_time = std::chrono::seconds(block.timestamp) + (block.major_version >= hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12); if (newest_block && (now >= earliest_time && now <= latest_time)) { @@ -1638,14 +1643,14 @@ namespace master_nodes } } - static std::mt19937_64 quorum_rng(uint8_t hf_version, crypto::hash const &hash, quorum_type type) + static std::mt19937_64 quorum_rng(hf hf_version, crypto::hash const &hash, quorum_type type) { std::mt19937_64 result; - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) { std::array src = {static_cast(type)}; std::memcpy(&src[1], &hash, sizeof(hash)); - for (uint32_t &val : src) boost::endian::little_to_native_inplace(val); + for (uint32_t &val : src) oxenc::little_to_host_inplace(val); std::seed_seq sequence(src.begin(), src.end()); result.seed(sequence); } @@ -1653,7 +1658,7 @@ namespace master_nodes { uint64_t seed = 0; std::memcpy(&seed, hash.data, sizeof(seed)); - boost::endian::little_to_native_inplace(seed); + oxenc::little_to_host_inplace(seed); seed += static_cast(type); result.seed(seed); } @@ -1662,7 +1667,7 @@ namespace master_nodes } static std::vector generate_shuffled_master_node_index_list( - uint8_t hf_version, + hf hf_version, size_t list_size, crypto::hash const &block_hash, quorum_type type, @@ -1710,7 +1715,7 @@ namespace master_nodes { cryptonote::block const &block = *it; crypto::hash hash = {}; - if (block.major_version >= cryptonote::network_version_17_POS && + if (block.major_version >= hf::hf17_POS && cryptonote::block_has_POS_components(block)) { std::array src = {POS_round}; @@ -1795,7 +1800,7 @@ namespace master_nodes master_nodes::quorum generate_POS_quorum(cryptonote::network_type nettype, crypto::public_key const &block_leader, - uint8_t hf_version, + hf hf_version, std::vector const &active_mnode_list, std::vector const &POS_entropy, uint8_t POS_round) @@ -1874,7 +1879,7 @@ namespace master_nodes return result; } - static void generate_other_quorums(master_node_list::state_t &state, std::vector const &active_mnode_list, cryptonote::network_type nettype, uint8_t hf_version) + static void generate_other_quorums(master_node_list::state_t &state, std::vector const &active_mnode_list, cryptonote::network_type nettype, hf hf_version) { assert(state.block_hash != crypto::null_hash); @@ -1883,7 +1888,7 @@ namespace master_nodes // (i.e. the nodes to be tested) also include decommissioned master nodes. (Prior to v12 there // are no decommissioned nodes, so this distinction is irrelevant for network concensus). std::vector decomm_mnode_list; - if (hf_version >= cryptonote::network_version_13_checkpointing) + if (hf_version >= hf::hf13_checkpointing) decomm_mnode_list = state.decommissioned_master_nodes_infos(); quorum_type const max_quorum_type = max_quorum_type_for_hf(hf_version); @@ -1921,7 +1926,7 @@ namespace master_nodes size_t total_nodes = active_mnode_list.size(); // TODO(beldex): Soft fork, remove when testnet gets reset - if (nettype == cryptonote::TESTNET && state.height < 85357) + if (nettype == cryptonote::network_type::TESTNET && state.height < 85357) total_nodes = active_mnode_list.size() + decomm_mnode_list.size(); if (total_nodes >= CHECKPOINT_QUORUM_SIZE) @@ -2004,7 +2009,7 @@ namespace master_nodes assert(height == block_height); quorums = {}; block_hash = cryptonote::get_block_hash(block); - uint8_t const hf_version = block.major_version; + const auto hf_version = block.major_version; // // Generate POS Quorum before any MN changes are applied to the list because, @@ -2013,7 +2018,7 @@ namespace master_nodes // i.e. before any deregistrations, registrations, decommissions, recommissions. // crypto::public_key winner_pubkey = cryptonote::get_master_node_winner_from_tx_extra(block.miner_tx.extra); - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) { std::vector entropy = get_POS_entropy_for_next_block(db, block.prev_id, block.POS.round); quorum POS_quorum = generate_POS_quorum(nettype, winner_pubkey, hf_version, active_master_nodes_infos(), entropy, block.POS.round); @@ -2036,7 +2041,7 @@ namespace master_nodes // // Remove expired blacklisted key images // - if (hf_version >= cryptonote::network_version_11_infinite_staking) + if (hf_version >= hf::hf11_infinite_staking) { for (auto entry = key_image_blacklist.begin(); entry != key_image_blacklist.end();) { @@ -2132,9 +2137,9 @@ namespace master_nodes void master_node_list::process_block(const cryptonote::block& block, const std::vector& txs) { uint64_t block_height = cryptonote::get_block_height(block); - uint8_t hf_version = block.major_version; + auto hf_version = block.major_version; - if (hf_version < cryptonote::network_version_9_master_nodes) + if (hf_version < hf::hf9_master_nodes) return; // Cull old history @@ -2226,7 +2231,7 @@ namespace master_nodes std::vector master_node_list::state_t::get_expired_nodes(cryptonote::BlockchainDB const &db, cryptonote::network_type nettype, - uint8_t hf_version, + cryptonote::hf hf_version, uint64_t block_height) const { std::vector expired_nodes; @@ -2234,7 +2239,7 @@ namespace master_nodes // TODO(beldex): This should really use the registration height instead of getting the block and expiring nodes. // But there's something subtly off when using registration height causing syncing problems. - if (hf_version == cryptonote::network_version_9_master_nodes) + if (hf_version == hf::hf9_master_nodes) { if (block_height <= lock_blocks) return expired_nodes; @@ -2251,7 +2256,7 @@ namespace master_nodes return expired_nodes; } - if (block.major_version < cryptonote::network_version_9_master_nodes) + if (block.major_version < hf::hf9_master_nodes) return expired_nodes; for (crypto::hash const &hash : block.tx_hashes) @@ -2266,7 +2271,7 @@ namespace master_nodes uint32_t index = 0; crypto::public_key key; master_node_info info = {}; - if (is_registration_tx(nettype, cryptonote::network_version_9_master_nodes, tx, block.timestamp, expired_nodes_block_height, index, key, info)) + if (is_registration_tx(nettype, hf::hf9_master_nodes, tx, block.timestamp, expired_nodes_block_height, index, key, info)) expired_nodes.push_back(key); index++; } @@ -2278,7 +2283,7 @@ namespace master_nodes { crypto::public_key const &mnode_key = it->first; const master_node_info &info = *it->second; - if (info.registration_hf_version >= cryptonote::network_version_11_infinite_staking) + if (info.registration_hf_version >= hf::hf11_infinite_staking) { if (info.requested_unlock_height != KEY_IMAGE_AWAITING_UNLOCK_HEIGHT && block_height > info.requested_unlock_height) expired_nodes.push_back(mnode_key); @@ -2289,7 +2294,7 @@ namespace master_nodes /// registered in hardfork 9 and was scheduled for deregistration in hardfork 10 /// will have its life is slightly prolonged by the "grace period", although it might /// look like we use the registration height to determine the expiry height. - uint64_t node_expiry_height = info.registration_height + lock_blocks + STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS; + uint64_t node_expiry_height = info.registration_height + lock_blocks + cryptonote::old::STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS; if (block_height > node_expiry_height) expired_nodes.push_back(mnode_key); } @@ -2381,8 +2386,8 @@ namespace master_nodes { const auto& block = info.block; const auto& reward_parts = info.reward_parts; - uint8_t const hf_version = block.major_version; - if (hf_version < cryptonote::network_version_9_master_nodes) + const auto hf_version = block.major_version; + if (hf_version < hf::hf9_master_nodes) return; std::lock_guard lock(m_mn_mutex); @@ -2481,7 +2486,7 @@ namespace master_nodes throw std::runtime_error{fmt::format("Expected {} block, the miner TX specifies a different amount of outputs vs the expected: {}, miner tx outputs: {}",type ,expected_vouts_size , miner_tx.vout.size())}; } - if (hf_version >= cryptonote::network_version_17_POS) + if (hf_version >= hf::hf17_POS) { if (reward_parts.base_miner != 0) { @@ -2503,7 +2508,7 @@ namespace master_nodes std::vector split_rewards = cryptonote::distribute_reward_by_portions(block_leader.payouts, reward_parts.master_node_total, - hf_version >= cryptonote::network_version_17_POS /*distribute_remainder*/); + hf_version >= hf::hf17_POS /*distribute_remainder*/); for (size_t i = 0; i < block_leader.payouts.size(); i++) { @@ -2579,7 +2584,7 @@ namespace master_nodes // store into the alt-chain until it gathers enough blocks to cause // a reorganization (more checkpoints/PoW than the main chain). auto& block = info.block; - if (block.major_version < cryptonote::network_version_9_master_nodes) + if (block.major_version < hf::hf9_master_nodes) return; uint64_t block_height = cryptonote::get_block_height(block); @@ -2628,7 +2633,7 @@ namespace master_nodes ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - static master_node_list::quorum_for_serialization serialize_quorum_state(uint8_t hf_version, uint64_t height, quorum_manager const &quorums) + static master_node_list::quorum_for_serialization serialize_quorum_state(hf hf_version, uint64_t height, quorum_manager const &quorums) { master_node_list::quorum_for_serialization result = {}; result.height = height; @@ -2637,7 +2642,7 @@ namespace master_nodes return result; } - static master_node_list::state_serialized serialize_master_node_state_object(uint8_t hf_version, master_node_list::state_t const &state, bool only_serialize_quorums = false) + static master_node_list::state_serialized serialize_master_node_state_object(hf hf_version, master_node_list::state_t const &state, bool only_serialize_quorums = false) { master_node_list::state_serialized result = {}; result.version = master_node_list::state_serialized::get_version(hf_version); @@ -2662,8 +2667,8 @@ namespace master_nodes if (!m_blockchain.has_db()) return false; // Haven't been initialized yet - uint8_t hf_version = m_blockchain.get_network_version(); - if (hf_version < cryptonote::network_version_9_master_nodes) + auto hf_version = m_blockchain.get_network_version(); + if (hf_version < hf::hf9_master_nodes) return true; data_for_serialization *data[] = {&m_transient.cache_long_term_data, &m_transient.cache_short_term_data}; @@ -2903,8 +2908,8 @@ namespace master_nodes bool master_node_list::handle_uptime_proof(cryptonote::NOTIFY_UPTIME_PROOF::request const &proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey) { auto vers = get_network_version_revision(m_blockchain.nettype(), m_blockchain.get_current_blockchain_height()); - if (vers >= std::pair{cryptonote::network_version_17_POS, 1}) - REJECT_PROOF("Old format (non-bt) proofs are not acceptable from v18+1 onwards"); + if (vers >= std::make_pair(hf::hf17_POS, uint8_t{1})) + REJECT_PROOF("Old format (non-bt) proofs are not acceptable from v17+1 onwards"); auto& netconf = get_config(m_blockchain.nettype()); auto now = std::chrono::system_clock::now(); @@ -2916,7 +2921,7 @@ namespace master_nodes for (auto const &min : MIN_UPTIME_PROOF_VERSIONS) if (vers >= min.hardfork_revision && proof.mnode_version < min.beldexd) - REJECT_PROOF("v" << tools::join(".", min.beldexd) << "+ beldexd version is required for v" << +vers.first << "." << +vers.second << "+ network proofs"); + REJECT_PROOF("v" << tools::join(".", min.beldexd) << "+ beldexd version is required for v" << static_cast(vers.first) << "." << +vers.second << "+ network proofs"); if (!debug_allow_local_ips && !epee::net_utils::is_ip_public(proof.public_ip)) REJECT_PROOF("public_ip is not actually public"); @@ -3010,11 +3015,11 @@ namespace master_nodes for (auto const &min : MIN_UPTIME_PROOF_VERSIONS) { if (vers >= min.hardfork_revision) { if (proof->version < min.beldexd) - REJECT_PROOF("v" << tools::join(".", min.beldexd) << "+ beldexd version is required for v" << +vers.first << "." << +vers.second << "+ network proofs"); + REJECT_PROOF("v" << tools::join(".", min.beldexd) << "+ beldexd version is required for v" << static_cast(vers.first) << "." << +vers.second << "+ network proofs"); if (proof->belnet_version < min.belnet) - REJECT_PROOF("v" << tools::join(".", min.belnet) << "+ belnet version is required for v" << +vers.first << "." << +vers.second << "+ network proofs"); + REJECT_PROOF("v" << tools::join(".", min.belnet) << "+ belnet version is required for v" << static_cast(vers.first) << "." << +vers.second << "+ network proofs"); if (proof->storage_server_version < min.storage_server) - REJECT_PROOF("v" << tools::join(".", min.storage_server) << "+ storage server version is required for v" << +vers.first << "." << +vers.second << "+ network proofs"); + REJECT_PROOF("v" << tools::join(".", min.storage_server) << "+ storage server version is required for v" << static_cast(vers.first) << "." << +vers.second << "+ network proofs"); } } @@ -3185,63 +3190,35 @@ namespace master_nodes void master_node_list::record_checkpoint_participation(crypto::public_key const &pubkey, uint64_t height, bool participated) { std::lock_guard lock(m_mn_mutex); - if (!m_state.master_nodes_infos.count(pubkey)) - return; - - participation_entry entry = {}; - entry.height = height; - entry.voted = participated; - - auto &info = proofs[pubkey]; - info.checkpoint_participation.add(entry); + if (m_state.master_nodes_infos.count(pubkey)) + proofs[pubkey].checkpoint_participation.add({height, participated}); } void master_node_list::record_POS_participation(crypto::public_key const &pubkey, uint64_t height, uint8_t round, bool participated) { std::lock_guard lock(m_mn_mutex); - if (!m_state.master_nodes_infos.count(pubkey)) - return; - - participation_entry entry = {}; - entry.is_POS = true; - entry.height = height; - entry.voted = participated; - entry.POS.round = round; - - auto &info = proofs[pubkey]; - info.POS_participation.add(entry); + if (m_state.master_nodes_infos.count(pubkey)) + proofs[pubkey].POS_participation.add({height, round, participated}); } void master_node_list::record_timestamp_participation(crypto::public_key const &pubkey, bool participated) { std::lock_guard lock(m_mn_mutex); - if (!m_state.master_nodes_infos.count(pubkey)) - return; - - timestamp_participation_entry entry = {}; - entry.participated = participated; - - auto &info = proofs[pubkey]; - info.timestamp_participation.add(entry); + if (m_state.master_nodes_infos.count(pubkey)) + proofs[pubkey].timestamp_participation.add({participated}); } void master_node_list::record_timesync_status(crypto::public_key const &pubkey, bool synced) { std::lock_guard lock(m_mn_mutex); - if (!m_state.master_nodes_infos.count(pubkey)) - return; - - timesync_entry entry = {}; - entry.in_sync = synced; - - auto &info = proofs[pubkey]; - info.timesync_status.add(entry); + if (m_state.master_nodes_infos.count(pubkey)) + proofs[pubkey].timesync_status.add({synced}); } std::optional proof_info::reachable_stats::reachable(const std::chrono::steady_clock::time_point& now) const { if (last_reachable >= last_unreachable) return true; - if (last_unreachable > now - config::REACHABLE_MAX_FAILURE_VALIDITY) + if (last_unreachable > now - cryptonote::config::REACHABLE_MAX_FAILURE_VALIDITY) return false; // Last result was a failure, but it was a while ago, so we don't know for sure that it isn't // reachable now: @@ -3557,7 +3534,7 @@ namespace master_nodes m_blockchain.get_db().clear_master_node_data(); } - m_state.height = hard_fork_begins(m_blockchain.nettype(), cryptonote::network_version_9_master_nodes).value_or(1) - 1; + m_state.height = hard_fork_begins(m_blockchain.nettype(), hf::hf9_master_nodes).value_or(1) - 1; } size_t master_node_info::total_num_locked_contributions() const @@ -3571,7 +3548,7 @@ namespace master_nodes contributor_args_t convert_registration_args(cryptonote::network_type nettype, const std::vector &args, uint64_t staking_requirement, - uint8_t hf_version) + hf hf_version) { contributor_args_t result = {}; if (args.size() % 2 == 0 || args.size() < 3) @@ -3580,24 +3557,24 @@ namespace master_nodes return result; } - if ((args.size()-1)/ 2 > MAX_NUMBER_OF_CONTRIBUTORS) + if ((args.size()-1)/ 2 > beldex::MAX_NUMBER_OF_CONTRIBUTORS) { - result.err_msg = tr("Exceeds the maximum number of contributors, which is ") + std::to_string(MAX_NUMBER_OF_CONTRIBUTORS); + result.err_msg = tr("Exceeds the maximum number of contributors, which is ") + std::to_string(beldex::MAX_NUMBER_OF_CONTRIBUTORS); return result; } try { result.portions_for_operator = boost::lexical_cast(args[0]); - if (result.portions_for_operator > STAKING_PORTIONS) + if (result.portions_for_operator > cryptonote::old::STAKING_PORTIONS) { - result.err_msg = tr("Invalid portion amount: ") + args[0] + tr(". Must be between 0 and ") + std::to_string(STAKING_PORTIONS); + result.err_msg = tr("Invalid portion amount: ") + args[0] + tr(". Must be between 0 and ") + std::to_string(cryptonote::old::STAKING_PORTIONS); return result; } } catch (const std::exception &e) { - result.err_msg = tr("Invalid portion amount: ") + args[0] + tr(". Must be between 0 and ") + std::to_string(STAKING_PORTIONS); + result.err_msg = tr("Invalid portion amount: ") + args[0] + tr(". Must be between 0 and ") + std::to_string(cryptonote::old::STAKING_PORTIONS); return result; } @@ -3649,8 +3626,8 @@ namespace master_nodes // This is temporary code to redistribute the insufficient portion dust // amounts between contributors. It should be removed in HF12. // - std::array excess_portions; - std::array min_contributions; + std::array excess_portions; + std::array min_contributions; { // NOTE: Calculate excess portions from each contributor uint64_t beldex_reserved = 0; @@ -3670,7 +3647,7 @@ namespace master_nodes } } - uint64_t portions_left = STAKING_PORTIONS; + uint64_t portions_left = cryptonote::old::STAKING_PORTIONS; uint64_t total_reserved = 0; for (size_t i = 0; i < addr_to_portions.size(); ++i) { @@ -3684,7 +3661,7 @@ namespace master_nodes // the minimum by a dust amount. uint64_t needed = min_portions - addr_to_portion.portions; const uint64_t FUDGE_FACTOR = 10; - const uint64_t DUST_UNIT = (STAKING_PORTIONS / staking_requirement); + const uint64_t DUST_UNIT = (cryptonote::old::STAKING_PORTIONS / staking_requirement); const uint64_t DUST = DUST_UNIT * FUDGE_FACTOR; if (needed > DUST) continue; @@ -3708,7 +3685,7 @@ namespace master_nodes // NOTE: Operator is sending in the minimum amount and it falls below // the minimum by dust, just increase the portions so it passes - if (needed > 0 && addr_to_portions.size() < MAX_NUMBER_OF_CONTRIBUTORS) + if (needed > 0 && addr_to_portions.size() < beldex::MAX_NUMBER_OF_CONTRIBUTORS) addr_to_portion.portions += needed; } @@ -3720,7 +3697,7 @@ namespace master_nodes if (min_portions == UINT64_MAX) { - result.err_msg = tr("Too many contributors specified, you can only split a node with up to: ") + std::to_string(MAX_NUMBER_OF_CONTRIBUTORS) + tr(" people."); + result.err_msg = tr("Too many contributors specified, you can only split a node with up to: ") + std::to_string(beldex::MAX_NUMBER_OF_CONTRIBUTORS) + tr(" people."); return result; } @@ -3737,7 +3714,7 @@ namespace master_nodes } bool make_registration_cmd(cryptonote::network_type nettype, - uint8_t hf_version, + hf hf_version, uint64_t staking_requirement, const std::vector& args, const master_node_keys &keys, @@ -3752,7 +3729,7 @@ namespace master_nodes return false; } - uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW; + uint64_t exp_timestamp = time(nullptr) + tools::to_seconds(cryptonote::old::STAKING_AUTHORIZATION_EXPIRATION_WINDOW); crypto::hash hash; bool hashed = cryptonote::get_registration_hash(contributor_args.addresses, contributor_args.portions_for_operator, contributor_args.portions, exp_timestamp, hash); @@ -3766,9 +3743,11 @@ namespace master_nodes crypto::generate_signature(hash, keys.pub, keys.key, signature); std::stringstream stream; - if (make_friendly) + if (make_friendly) //TODO have to fix { - stream << tr("Run this command in the wallet that will fund this registration:\n\n"); + stream << tr("Run this command in the operator wallet") << " (" << + cryptonote::get_account_address_as_str(nettype, false, contributor_args.addresses[0]) + << "):\n\n"; } stream << "register_master_node"; @@ -3782,14 +3761,8 @@ namespace master_nodes if (make_friendly) { stream << "\n\n"; - time_t tt = exp_timestamp; - - struct tm tm; - epee::misc_utils::get_gmt_time(tt, tm); - - char buffer[128]; - strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M:%S %p UTC", &tm); - stream << tr("This registration expires at ") << buffer << tr(".\n"); + auto exp = std::chrono::system_clock::from_time_t(exp_timestamp); + stream << tr("This registration expires at ") << date::format("%Y-%m-%d %I:%M:%S %p UTC", exp) << tr(".\n"); stream << tr("This should be in about 2 weeks, if it isn't, check this computer's clock.\n"); stream << tr("Please submit your registration into the blockchain before this time or it will be invalid."); } @@ -3822,9 +3795,9 @@ namespace master_nodes return true; } - bool master_node_info::can_transition_to_state(uint8_t hf_version, uint64_t height, new_state proposed_state) const + bool master_node_info::can_transition_to_state(hf hf_version, uint64_t height, new_state proposed_state) const { - if (hf_version >= cryptonote::network_version_14_enforce_checkpoints) { + if (hf_version >= hf::hf15_flash) { if (!can_be_voted_on(height)) { MDEBUG("MN state transition invalid: " << height << " is not a valid vote height"); return false; @@ -3874,7 +3847,7 @@ namespace master_nodes // Add contributors and their portions to winners. result.payouts.reserve(info.contributors.size()); - const uint64_t remaining_portions = STAKING_PORTIONS - info.portions_for_operator; + const uint64_t remaining_portions = cryptonote::old::STAKING_PORTIONS - info.portions_for_operator; for (const auto& contributor : info.contributors) { uint64_t hi, lo, resulthi, resultlo; diff --git a/src/cryptonote_core/master_node_list.h b/src/cryptonote_core/master_node_list.h index 44f7f53de24..26a642acc0a 100755 --- a/src/cryptonote_core/master_node_list.h +++ b/src/cryptonote_core/master_node_list.h @@ -32,6 +32,7 @@ #include #include #include +#include "cryptonote_basic/hardfork.h" #include "serialization/serialization.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_core/master_node_rules.h" @@ -55,89 +56,60 @@ namespace master_nodes { constexpr uint64_t INVALID_HEIGHT = static_cast(-1); - BELDEX_RPC_DOC_INTROSPECT - struct participation_entry + struct checkpoint_participation_entry { - bool is_POS = false; uint64_t height = INVALID_HEIGHT; bool voted = true; - struct - { - uint8_t round = 0; - } POS; + bool pass() const { return voted; }; + }; - bool pass() const { - return voted; - }; + struct POS_participation_entry + { + uint64_t height = INVALID_HEIGHT; + uint8_t round = 0; + bool voted = true; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height); - KV_SERIALIZE(voted); - KV_SERIALIZE(is_POS); - if (this_ref.is_POS) - { - KV_SERIALIZE_N(POS.round, "POS_round"); - } - END_KV_SERIALIZE_MAP() + bool pass() const { return voted; } }; - struct timestamp_participation_entry { - bool participated = true; - bool pass() const { - return participated; - }; + bool participated = true; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(participated); - END_KV_SERIALIZE_MAP() + bool pass() const { return participated; }; }; struct timesync_entry { - bool in_sync = true; - bool pass() const { - return in_sync; - }; + bool in_sync = true; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(in_sync); - END_KV_SERIALIZE_MAP() + bool pass() const { return in_sync; } }; template struct participation_history { - std::array array; - size_t write_index; + std::array history; + size_t write_index = 0; void reset() { write_index = 0; } - void add(ValueType &entry) - { - size_t real_write_index = write_index % array.size(); - array[real_write_index] = entry; - write_index++; - } + void add(const ValueType& entry) { history[write_index++ % history.size()] = entry; } + void add(ValueType&& entry) { history[write_index++ % history.size()] = std::move(entry); } - bool check_participation(uint16_t threshold) - { - if (this->write_index >= Count) - { - int failed_counter = 0; - for (ValueType &entry : array) - if (!entry.pass()) failed_counter++; - - if (failed_counter > threshold) - return false; - } - return true; + // Returns the number of failures we have stored (of the last Count records). + size_t failures() const { + return std::count_if(begin(), end(), [](auto& e) { return !e.pass(); }); } + size_t passes() const { return size() - failures(); } + + bool empty() const { return write_index == 0; } + size_t size() const { return std::min(history.size(), write_index); } + constexpr size_t max_size() const noexcept { return Count; } - ValueType *begin() { return array.data(); } - ValueType *end() { return array.data() + std::min(array.size(), write_index); } - ValueType const *begin() const { return array.data(); } - ValueType const *end() const { return array.data() + std::min(array.size(), write_index); } + ValueType* begin() { return history.data(); } + ValueType* end() { return history.data() + size(); } + const ValueType* begin() const { return history.data(); } + const ValueType* end() const { return history.data() + size(); } }; inline constexpr auto NEVER = std::chrono::steady_clock::time_point::min(); @@ -146,10 +118,10 @@ namespace master_nodes { proof_info(); - participation_history POS_participation{}; - participation_history checkpoint_participation{}; - participation_history timestamp_participation{}; - participation_history timesync_status{}; + participation_history POS_participation; + participation_history checkpoint_participation; + participation_history timestamp_participation; + participation_history timesync_status; uint64_t timestamp = 0; // The actual time we last received an uptime proof (serialized) uint64_t effective_timestamp = 0; // Typically the same, but on recommissions it is set to the recommission block time to fend off instant obligation checks @@ -308,7 +280,7 @@ namespace master_nodes cryptonote::account_public_address operator_address{}; uint64_t last_ip_change_height = 0; // The height of the last quorum penalty for changing IPs version_t version = tools::enum_top; - uint8_t registration_hf_version = 0; + cryptonote::hf registration_hf_version = cryptonote::hf::none; POS_sort_key POS_sorter; master_node_info() = default; @@ -316,7 +288,7 @@ namespace master_nodes bool is_decommissioned() const { return active_since_height < 0; } bool is_active() const { return is_fully_funded() && !is_decommissioned(); } - bool can_transition_to_state(uint8_t hf_version, uint64_t block_height, new_state proposed_state) const; + bool can_transition_to_state(cryptonote::hf hf_version, uint64_t block_height, new_state proposed_state) const; bool can_be_voted_on (uint64_t block_height) const; size_t total_num_locked_contributions() const; @@ -627,7 +599,7 @@ namespace master_nodes struct state_serialized { enum struct version_t : uint8_t { version_0, version_1_serialize_hash, count, }; - static version_t get_version(uint8_t /*hf_version*/) { return version_t::version_1_serialize_hash; } + static version_t get_version(cryptonote::hf /*hf_version*/) { return version_t::version_1_serialize_hash; } version_t version; uint64_t height; @@ -653,7 +625,7 @@ namespace master_nodes struct data_for_serialization { enum struct version_t : uint8_t { version_0, count, }; - static version_t get_version(uint8_t /*hf_version*/) { return version_t::version_0; } + static version_t get_version(cryptonote::hf /*hf_version*/) { return version_t::version_0; } version_t version; std::vector quorum_states; @@ -689,7 +661,7 @@ namespace master_nodes std::vector active_master_nodes_infos() const; std::vector decommissioned_master_nodes_infos() const; // return: All nodes that are fully funded *and* decommissioned. - std::vector get_expired_nodes(cryptonote::BlockchainDB const &db, cryptonote::network_type nettype, uint8_t hf_version, uint64_t block_height) const; + std::vector get_expired_nodes(cryptonote::BlockchainDB const &db, cryptonote::network_type nettype, cryptonote::hf hf_version, uint64_t block_height) const; void update_from_block( cryptonote::BlockchainDB const &db, cryptonote::network_type nettype, @@ -713,7 +685,7 @@ namespace master_nodes const cryptonote::block &block, const cryptonote::transaction& tx, const master_node_keys *my_keys); - bool process_key_image_unlock_tx(cryptonote::network_type nettype, uint64_t block_height, const cryptonote::transaction &tx,uint8_t version); + bool process_key_image_unlock_tx(cryptonote::network_type nettype, uint64_t block_height, const cryptonote::transaction &tx,cryptonote::hf version); payout get_block_leader() const; payout get_block_producer(uint8_t POS_round) const; master_node_info get_master_node_details(crypto::public_key mnode_key); @@ -780,7 +752,7 @@ namespace master_nodes }; bool tx_get_staking_components (cryptonote::transaction_prefix const &tx_prefix, staking_components *contribution, crypto::hash const &txid); bool tx_get_staking_components (cryptonote::transaction const &tx, staking_components *contribution); - bool tx_get_staking_components_and_amounts(cryptonote::network_type nettype, uint8_t hf_version, cryptonote::transaction const &tx, uint64_t block_height, staking_components *contribution); + bool tx_get_staking_components_and_amounts(cryptonote::network_type nettype, cryptonote::hf hf_version, cryptonote::transaction const &tx, uint64_t block_height, staking_components *contribution); struct contributor_args_t { @@ -791,22 +763,22 @@ namespace master_nodes std::string err_msg; // if (success == false), this is set to the err msg otherwise empty }; - bool is_registration_tx (cryptonote::network_type nettype, uint8_t hf_version, const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, master_node_info& info); + bool is_registration_tx (cryptonote::network_type nettype, cryptonote::hf hf_version, const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, master_node_info& info); bool reg_tx_extract_fields(const cryptonote::transaction& tx, contributor_args_t &contributor_args, uint64_t& expiration_timestamp, crypto::public_key& master_node_key, crypto::signature& signature, crypto::public_key& tx_pub_key); uint64_t offset_testing_quorum_height(quorum_type type, uint64_t height); contributor_args_t convert_registration_args(cryptonote::network_type nettype, const std::vector &args, uint64_t staking_requirement, - uint8_t hf_version); + cryptonote::hf hf_version); // validate_contributors_* functions throws invalid_contributions exception struct invalid_contributions : std::invalid_argument { using std::invalid_argument::invalid_argument; }; - void validate_contributor_args(uint8_t hf_version, contributor_args_t const &contributor_args); + void validate_contributor_args(cryptonote::hf hf_version, contributor_args_t const &contributor_args); void validate_contributor_args_signature(contributor_args_t const &contributor_args, uint64_t const expiration_timestamp, crypto::public_key const &master_node_key, crypto::signature const &signature); bool make_registration_cmd(cryptonote::network_type nettype, - uint8_t hf_version, + cryptonote::hf hf_version, uint64_t staking_requirement, const std::vector& args, const master_node_keys &keys, @@ -815,7 +787,7 @@ namespace master_nodes master_nodes::quorum generate_POS_quorum(cryptonote::network_type nettype, crypto::public_key const &leader, - uint8_t hf_version, + cryptonote::hf hf_version, std::vector const &active_mnode_list, std::vector const &POS_entropy, uint8_t POS_round); @@ -829,6 +801,6 @@ namespace master_nodes payout master_node_info_to_payout(crypto::public_key const &key, master_node_info const &info); - const static payout_entry null_payout_entry = {cryptonote::null_address, STAKING_PORTIONS}; + const static payout_entry null_payout_entry = {cryptonote::null_address, cryptonote::old::STAKING_PORTIONS}; const static payout null_payout = {crypto::null_pkey, {null_payout_entry}}; } diff --git a/src/cryptonote_core/master_node_quorum_cop.cpp b/src/cryptonote_core/master_node_quorum_cop.cpp index d5fcf9a9340..a22cdfec869 100755 --- a/src/cryptonote_core/master_node_quorum_cop.cpp +++ b/src/cryptonote_core/master_node_quorum_cop.cpp @@ -37,13 +37,14 @@ #include "common/beldex.h" #include "common/util.h" #include "epee/net/local_ip.h" -#include +#include #include "common/beldex_integration_test_hooks.h" #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "quorum_cop" +using cryptonote::hf; namespace master_nodes { std::optional> master_node_test_results::why() const @@ -77,7 +78,7 @@ namespace master_nodes // Perform master node tests -- this returns true if the server node is in a good state, that is, // has submitted uptime proofs, participated in required quorums, etc. - master_node_test_results quorum_cop::check_master_node(std::map>> multi_mns_list, uint8_t hf_version, const crypto::public_key &pubkey, const master_node_info &info) const + master_node_test_results quorum_cop::check_master_node(std::map>> multi_mns_list, hf hf_version, const crypto::public_key &pubkey, const master_node_info &info) const { const auto& netconf = m_core.get_net_config(); @@ -86,8 +87,8 @@ namespace master_nodes uint64_t timestamp = 0; decltype(std::declval().public_ips) ips{}; - master_nodes::participation_history checkpoint_participation{}; - master_nodes::participation_history POS_participation{}; + master_nodes::participation_history checkpoint_participation{}; + master_nodes::participation_history POS_participation{}; master_nodes::participation_history timestamp_participation{}; master_nodes::participation_history timesync_status{}; @@ -97,18 +98,17 @@ namespace master_nodes m_core.get_master_node_list().access_proof(pubkey, [&](const proof_info &proof) { ss_reachable = !proof.ss_reachable.unreachable_for(unreachable_threshold); - belnet_reachable = !proof.belnet_reachable.unreachable_for(unreachable_threshold); + belnet_reachable = !proof.belnet_reachable.unreachable_for(unreachable_threshold); timestamp = std::max(proof.timestamp, proof.effective_timestamp); ips = proof.public_ips; checkpoint_participation = proof.checkpoint_participation; - POS_participation = proof.POS_participation; - + POS_participation = proof.POS_participation; timestamp_participation = proof.timestamp_participation; timesync_status = proof.timesync_status; }); - if (hf_version >= cryptonote::network_version_19) { + if (hf_version >= hf::hf19_enhance_bns) { std::vector> multi_mns = multi_mns_list[ips[0].first]; @@ -155,7 +155,7 @@ namespace master_nodes - if (hf_version > cryptonote::network_version_12_security_signature) { + if (hf_version > hf::hf12_security_signature) { if (!ss_reachable) { @@ -163,7 +163,7 @@ namespace master_nodes result.storage_server_reachable = false; } // TODO: perhaps come back and make this activate on some "soft fork" height before HF19? - if (!belnet_reachable && hf_version >= cryptonote::network_version_18_bns) { + if (!belnet_reachable && hf_version >= hf::hf18_bns) { LOG_PRINT_L1("Master Node belnet is not reachable for node: " << pubkey); result.belnet_reachable = false; } @@ -185,22 +185,21 @@ namespace master_nodes if (!info.is_decommissioned()) { - if (check_checkpoint_obligation && - !checkpoint_participation.check_participation(CHECKPOINT_MAX_MISSABLE_VOTES)) { + if (check_checkpoint_obligation && checkpoint_participation.failures() > CHECKPOINT_MAX_MISSABLE_VOTES) { LOG_PRINT_L1("Master Node: " << pubkey << ", failed checkpoint obligation check"); result.checkpoint_participation = false; } - if (!POS_participation.check_participation(POS_MAX_MISSABLE_VOTES)) { + if (POS_participation.failures() > POS_MAX_MISSABLE_VOTES) { LOG_PRINT_L1("Master Node: " << pubkey << ", failed pulse obligation check"); result.POS_participation = false; } - if (!timestamp_participation.check_participation(TIMESTAMP_MAX_MISSABLE_VOTES)) { + if (timestamp_participation.failures() > TIMESTAMP_MAX_MISSABLE_VOTES) { LOG_PRINT_L1("Master Node: " << pubkey << ", failed timestamp obligation check"); result.timestamp_participation = false; } - if (!timesync_status.check_participation(TIMESYNC_MAX_UNSYNCED_VOTES)) { + if (timesync_status.failures() > TIMESYNC_MAX_UNSYNCED_VOTES) { LOG_PRINT_L1("Master Node: " << pubkey << ", failed timesync obligation check"); result.timesync_status = false; } @@ -213,8 +212,8 @@ namespace master_nodes void quorum_cop::blockchain_detached(uint64_t height, bool by_pop_blocks) { - uint8_t hf_version = get_network_version(m_core.get_nettype(), height); - uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= cryptonote::network_version_13_checkpointing) + auto hf_version = get_network_version(m_core.get_nettype(), height); + uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= hf::hf13_checkpointing) ? REORG_SAFETY_BUFFER_BLOCKS_POST_HF12 : REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12; if (m_obligations_height >= height) @@ -236,7 +235,7 @@ namespace master_nodes m_last_checkpointed_height = height - (height % CHECKPOINT_INTERVAL); } - m_vote_pool.remove_expired_votes(height,hf_version); + m_vote_pool.remove_expired_votes(height); } void quorum_cop::set_votes_relayed(std::vector const &relayed_votes) @@ -244,7 +243,7 @@ namespace master_nodes m_vote_pool.set_relayed(relayed_votes); } - std::vector quorum_cop::get_relayable_votes(uint64_t current_height, uint8_t hf_version, bool quorum_relay) + std::vector quorum_cop::get_relayable_votes(uint64_t current_height, hf hf_version, bool quorum_relay) { return m_vote_pool.get_relayable_votes(current_height, hf_version, quorum_relay); } @@ -258,7 +257,7 @@ namespace master_nodes return result; } - void quorum_cop::handling_master_nodes_states(std::map>> multi_mns_list, uint8_t const obligations_height_hf_version_,uint8_t const hf_version,std::shared_ptr quorum,int index_in_group,uint64_t const latest_height) + void quorum_cop::handling_master_nodes_states(std::map>> multi_mns_list, const cryptonote::hf obligations_height_hf_version_, const cryptonote::hf hf_version,std::shared_ptr quorum,int index_in_group,uint64_t const latest_height) { // // NOTE: I am in the quorum @@ -364,7 +363,7 @@ namespace master_nodes LOG_PRINT_L3(good << " of " << total << " master nodes are active and passing checks; no state change votes required"); } - void quorum_cop::handling_my_master_node_states(std::map>> multi_mns_list, uint8_t const obligations_height_hf_version,uint8_t const hf_version,bool &tested_myself_once_per_block,std::chrono::seconds live_time) + void quorum_cop::handling_my_master_node_states(std::map>> multi_mns_list, const cryptonote::hf obligations_height_hf_version, const cryptonote::hf hf_version, bool &tested_myself_once_per_block, std::chrono::seconds live_time) { const auto& my_keys = m_core.get_master_keys(); const auto states_array = m_core.get_master_node_list_state({my_keys.pub}); @@ -399,9 +398,9 @@ namespace master_nodes } } - void quorum_cop::quorum_checkpoint_handle(uint64_t const start_voting_from_height_,uint64_t const height,uint8_t const hf_version) + void quorum_cop::quorum_checkpoint_handle(uint64_t const start_voting_from_height_,uint64_t const height, const cryptonote::hf hf_version) { - uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= cryptonote::network_version_13_checkpointing) + uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= hf::hf13_checkpointing) ? REORG_SAFETY_BUFFER_BLOCKS_POST_HF12 : REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12; const auto& my_keys = m_core.get_master_keys(); @@ -412,11 +411,11 @@ namespace master_nodes m_last_checkpointed_height = std::max(start_checkpointing_height, m_last_checkpointed_height); for (; - m_last_checkpointed_height <= height; + m_last_checkpointed_height < height; m_last_checkpointed_height += CHECKPOINT_INTERVAL) { - uint8_t checkpointed_height_hf_version = get_network_version(m_core.get_nettype(), m_last_checkpointed_height); - if (checkpointed_height_hf_version <= cryptonote::network_version_11_infinite_staking) + auto checkpointed_height_hf_version = get_network_version(m_core.get_nettype(), m_last_checkpointed_height); + if (checkpointed_height_hf_version <= hf::hf11_infinite_staking) continue; if (m_last_checkpointed_height < REORG_SAFETY_BUFFER_BLOCKS) @@ -446,8 +445,8 @@ namespace master_nodes void quorum_cop::process_quorums(cryptonote::block const &block) { - uint8_t const hf_version = block.major_version; - if (hf_version < cryptonote::network_version_9_master_nodes) + const auto hf_version = block.major_version; + if (hf_version < hf::hf9_master_nodes) return; auto mn_infos = m_core.get_master_node_list_state(); @@ -462,7 +461,7 @@ namespace master_nodes } const auto& netconf = m_core.get_net_config(); - uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= cryptonote::network_version_13_checkpointing) + uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= hf::hf13_checkpointing) ? REORG_SAFETY_BUFFER_BLOCKS_POST_HF12 : REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12; const auto& my_keys = m_core.get_master_keys(); @@ -477,12 +476,12 @@ namespace master_nodes if (height < start_voting_from_height) return; - master_nodes::quorum_type const max_quorum_type = master_nodes::max_quorum_type_for_hf(hf_version); + const auto max_quorum_type = master_nodes::max_quorum_type_for_hf(hf_version); bool tested_myself_once_per_block = false; time_t start_time = m_core.get_start_time(); std::chrono::seconds live_time{time(nullptr) - start_time}; - for (int i = 0; i <= (int)max_quorum_type; i++) + for (int i = 0; i <= static_cast(max_quorum_type); i++) { quorum_type const type = static_cast(i); @@ -505,14 +504,14 @@ namespace master_nodes m_obligations_height = std::max(m_obligations_height, start_voting_from_height); for (; m_obligations_height < (height - REORG_SAFETY_BUFFER_BLOCKS); m_obligations_height++) { - uint8_t const obligations_height_hf_version = get_network_version(m_core.get_nettype(), m_obligations_height); - if (obligations_height_hf_version < cryptonote::network_version_9_master_nodes) continue; + const auto obligations_height_hf_version = get_network_version(m_core.get_nettype(), m_obligations_height); + if (obligations_height_hf_version < hf::hf9_master_nodes) continue; // NOTE: Count checkpoints for other nodes, irrespective of being // a master node or not for statistics. Also count checkpoints // before the minimum lifetime for same purposes, note, we still // don't vote for the first 2 hours so this is purely cosmetic - if (obligations_height_hf_version >= cryptonote::network_version_13_checkpointing) + if (obligations_height_hf_version >= hf::hf13_checkpointing) { master_nodes::master_node_list &node_list = m_core.get_master_node_list(); @@ -593,7 +592,7 @@ namespace master_nodes { process_quorums(block); uint64_t const height = cryptonote::get_block_height(block) + 1; // chain height = new top block height + 1 - m_vote_pool.remove_expired_votes(height,block.major_version); + m_vote_pool.remove_expired_votes(height); m_vote_pool.remove_used_votes(txs, block.major_version); } @@ -639,7 +638,7 @@ namespace master_nodes } using version_t = cryptonote::tx_extra_master_node_state_change::version_t; LOG_PRINT_L3("State not reachable A!?"); - auto ver = net >= HF_VERSION_PROOF_BTENC ? version_t::v4_reasons : version_t::v0; + auto ver = net >= cryptonote::feature::PROOF_BTENC ? version_t::v4_reasons : version_t::v0; cryptonote::tx_extra_master_node_state_change state_change{ ver, @@ -713,7 +712,7 @@ namespace master_nodes std::unique_lock lock{blockchain}; - bool update_checkpoint; + bool update_checkpoint = false; if (blockchain.get_checkpoint(vote.block_height, checkpoint) && checkpoint.block_hash == vote.checkpoint.block_hash) { @@ -745,7 +744,7 @@ namespace master_nodes } } } - else + else if (vote.block_height < core.get_current_blockchain_height()) // Don't accept checkpoints for blocks we don't have yet { update_checkpoint = true; checkpoint = make_empty_master_node_checkpoint(vote.checkpoint.block_hash, vote.block_height); @@ -760,10 +759,10 @@ namespace master_nodes return true; } - bool quorum_cop::handle_vote(quorum_vote_t const &vote, cryptonote::vote_verification_context &vvc,uint8_t hf_version) + bool quorum_cop::handle_vote(quorum_vote_t const &vote, cryptonote::vote_verification_context &vvc, hf hf_version) { vvc = {}; - if (!verify_vote_age(vote, m_core.get_current_blockchain_height(), vvc,hf_version)) { + if (!verify_vote_age(vote, m_core.get_current_blockchain_height(), vvc, hf_version)) { LOG_PRINT_L1("failed verify_vote_age"); return false; } @@ -809,7 +808,7 @@ namespace master_nodes // Calculate the decommission credit for a master node. If the MN is current decommissioned this // accumulated blocks. - int64_t quorum_cop::calculate_decommission_credit(const master_node_info &info, uint64_t current_height,uint8_t hf_version) + int64_t quorum_cop::calculate_decommission_credit(const master_node_info &info, uint64_t current_height, hf hf_version) { // If currently decommissioned, we need to know how long it was up before being decommissioned; // otherwise we need to know how long since it last become active until now (or 0 if not staked @@ -826,7 +825,7 @@ namespace master_nodes int64_t credit = info.recommission_credit; if (blocks_up > 0) { - credit += blocks_up * DECOMMISSION_CREDIT_PER_DAY / BLOCKS_PER_DAY; + credit += blocks_up * DECOMMISSION_CREDIT_PER_DAY / cryptonote::BLOCKS_PER_DAY; } if (credit > DECOMMISSION_MAX_CREDIT) @@ -857,7 +856,7 @@ namespace master_nodes std::memcpy(local.data(), pkdata + offset, prewrap); std::memcpy(local.data() + prewrap, pkdata, sizeof(uint64_t) - prewrap); } - sum += boost::endian::little_to_native(*reinterpret_cast(local.data())); + sum += oxenc::little_to_host(*reinterpret_cast(local.data())); ++offset; } return sum; diff --git a/src/cryptonote_core/master_node_quorum_cop.h b/src/cryptonote_core/master_node_quorum_cop.h index 623fd1e8092..16a6da4ff5d 100755 --- a/src/cryptonote_core/master_node_quorum_cop.h +++ b/src/cryptonote_core/master_node_quorum_cop.h @@ -122,17 +122,17 @@ namespace master_nodes void blockchain_detached(uint64_t height, bool by_pop_blocks); void set_votes_relayed (std::vector const &relayed_votes); - std::vector get_relayable_votes(uint64_t current_height, uint8_t hf_version, bool quorum_relay); - bool handle_vote (quorum_vote_t const &vote, cryptonote::vote_verification_context &vvc,uint8_t hf_version); + std::vector get_relayable_votes(uint64_t current_height, cryptonote::hf hf_version, bool quorum_relay); + bool handle_vote (quorum_vote_t const &vote, cryptonote::vote_verification_context &vvc, cryptonote::hf hf_version); - static int64_t calculate_decommission_credit(const master_node_info &info, uint64_t current_height,uint8_t hf_version); + static int64_t calculate_decommission_credit(const master_node_info &info, uint64_t current_height, cryptonote::hf hf_version); private: void process_quorums(cryptonote::block const &block); - void quorum_checkpoint_handle(uint64_t const start_voting_from_height_,uint64_t const height,uint8_t const hf_version); - void handling_master_nodes_states(std::map>> multi_mns_list, uint8_t const obligations_height_hf_version_,uint8_t const hf_version,std::shared_ptr quorum,int index_in_group,uint64_t const latest_height); - void handling_my_master_node_states(std::map>> multi_mns_list, uint8_t const obligations_height_hf_version,uint8_t const hf_version,bool &tested_myself_once_per_block,std::chrono::seconds live_time); - master_node_test_results check_master_node(std::map>> multi_mns_list, uint8_t hf_version, const crypto::public_key &pubkey, const master_node_info &info) const; + void quorum_checkpoint_handle(uint64_t const start_voting_from_height_,uint64_t const height, cryptonote::hf hf_version); + void handling_master_nodes_states(std::map>> multi_mns_list, const cryptonote::hf obligations_height_hf_version_, const cryptonote::hf hf_version, std::shared_ptr quorum, int index_in_group, uint64_t const latest_height); + void handling_my_master_node_states(std::map>> multi_mns_list, const cryptonote::hf obligations_height_hf_version,const cryptonote::hf hf_version, bool &tested_myself_once_per_block, std::chrono::seconds live_time); + master_node_test_results check_master_node(std::map>> multi_mns_list, cryptonote::hf hf_version, const crypto::public_key &pubkey, const master_node_info &info) const; cryptonote::core& m_core; voting_pool m_vote_pool; diff --git a/src/cryptonote_core/master_node_rules.cpp b/src/cryptonote_core/master_node_rules.cpp index 7bb1ab8a057..7ccc8ca0538 100755 --- a/src/cryptonote_core/master_node_rules.cpp +++ b/src/cryptonote_core/master_node_rules.cpp @@ -1,21 +1,23 @@ #include "cryptonote_config.h" #include "common/beldex.h" #include "epee/int-util.h" -#include +#include #include #include #include #include +#include "beldex_economy.h" #include "master_node_rules.h" +using cryptonote::hf; namespace master_nodes { // TODO(beldex): Move to beldex_economy, this will also need access to beldex::exp2 uint64_t get_staking_requirement(uint64_t height) { - uint64_t result = COIN * 100000; - if(height>=MODIFIED_STAKING_REQUIREMENT_HEIGHT) result = COIN * 10000; + uint64_t result = 100000 * beldex::COIN; + if(height >= beldex::MODIFIED_STAKING_REQUIREMENT_HEIGHT) result = 10000 * beldex::COIN; return result; } @@ -23,36 +25,36 @@ uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement) { uint64_t hi, lo, resulthi, resultlo; lo = mul128(staking_requirement, portions, &hi); - div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo); + div128_64(hi, lo, cryptonote::old::STAKING_PORTIONS, &resulthi, &resultlo); return resultlo; } -bool check_master_node_portions(uint8_t hf_version, const std::vector& portions) +bool check_master_node_portions(hf hf_version, const std::vector& portions) { - if (portions.size() > MAX_NUMBER_OF_CONTRIBUTORS) return false; + if (portions.size() > beldex::MAX_NUMBER_OF_CONTRIBUTORS) return false; uint64_t reserved = 0; for (auto i = 0u; i < portions.size(); ++i) { - const uint64_t min_portions = get_min_node_contribution(hf_version, STAKING_PORTIONS, reserved, i); + const uint64_t min_portions = get_min_node_contribution(hf_version, cryptonote::old::STAKING_PORTIONS, reserved, i); if (portions[i] < min_portions) return false; reserved += portions[i]; } - return reserved <= STAKING_PORTIONS; + return reserved <= cryptonote::old::STAKING_PORTIONS; } crypto::hash generate_request_stake_unlock_hash(uint32_t nonce) { static_assert(sizeof(crypto::hash) == 8 * sizeof(uint32_t) && alignof(crypto::hash) >= alignof(uint32_t)); crypto::hash result; - boost::endian::native_to_little_inplace(nonce); + oxenc::host_to_little_inplace(nonce); for (size_t i = 0; i < 8; i++) reinterpret_cast(result.data)[i] = nonce; return result; } -uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, uint64_t curr_height,uint8_t version) +uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, uint64_t curr_height, hf version) { uint64_t blocks_to_lock = staking_num_lock_blocks(nettype,version); uint64_t result = curr_height + (blocks_to_lock / 2); @@ -61,31 +63,31 @@ uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, ui static uint64_t get_min_node_contribution_pre_v11(uint64_t staking_requirement, uint64_t total_reserved) { - return std::min(staking_requirement - total_reserved, staking_requirement / MAX_NUMBER_OF_CONTRIBUTORS); + return std::min(staking_requirement - total_reserved, staking_requirement / beldex::MAX_NUMBER_OF_CONTRIBUTORS); } -uint64_t get_max_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved) +uint64_t get_max_node_contribution(hf version, uint64_t staking_requirement, uint64_t total_reserved) { - if (version >= cryptonote::network_version_17_POS) - return (staking_requirement - total_reserved) * config::MAXIMUM_ACCEPTABLE_STAKE::num - / config::MAXIMUM_ACCEPTABLE_STAKE::den; + if (version >= hf::hf17_POS) + return (staking_requirement - total_reserved) * cryptonote::MAXIMUM_ACCEPTABLE_STAKE::num + / cryptonote::MAXIMUM_ACCEPTABLE_STAKE::den; return std::numeric_limits::max(); } -uint64_t get_min_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions) +uint64_t get_min_node_contribution(hf version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions) { - if (version < cryptonote::network_version_11_infinite_staking) + if (version < hf::hf11_infinite_staking) return get_min_node_contribution_pre_v11(staking_requirement, total_reserved); const uint64_t needed = staking_requirement - total_reserved; - assert(MAX_NUMBER_OF_CONTRIBUTORS > num_contributions); - if (MAX_NUMBER_OF_CONTRIBUTORS <= num_contributions) return UINT64_MAX; + assert(beldex::MAX_NUMBER_OF_CONTRIBUTORS > num_contributions); + if (beldex::MAX_NUMBER_OF_CONTRIBUTORS <= num_contributions) return UINT64_MAX; - const size_t num_contributions_remaining_avail = MAX_NUMBER_OF_CONTRIBUTORS - num_contributions; + const size_t num_contributions_remaining_avail = beldex::MAX_NUMBER_OF_CONTRIBUTORS - num_contributions; return needed / num_contributions_remaining_avail; } -uint64_t get_min_node_contribution_in_portions(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions) +uint64_t get_min_node_contribution_in_portions(hf version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions) { uint64_t atomic_amount = get_min_node_contribution(version, staking_requirement, total_reserved, num_contributions); uint64_t result = (atomic_amount == UINT64_MAX) ? UINT64_MAX : (get_portions_to_make_amount(staking_requirement, atomic_amount)); @@ -109,11 +111,11 @@ static bool get_portions_from_percent(double cur_percent, uint64_t& portions) { // Fix for truncation issue when operator cut = 100 for a pool Master Node. if (cur_percent == 100.0) { - portions = STAKING_PORTIONS; + portions = cryptonote::old::STAKING_PORTIONS; } else { - portions = (cur_percent / 100.0) * (double)STAKING_PORTIONS; + portions = (cur_percent / 100.0) * (double)cryptonote::old::STAKING_PORTIONS; } return true; diff --git a/src/cryptonote_core/master_node_rules.h b/src/cryptonote_core/master_node_rules.h index 2cabac14103..53fe3a9c3bf 100755 --- a/src/cryptonote_core/master_node_rules.h +++ b/src/cryptonote_core/master_node_rules.h @@ -6,34 +6,34 @@ #include namespace master_nodes { - constexpr size_t POS_QUORUM_ENTROPY_LAG = 21; // How many blocks back from the tip of the Blockchain to source entropy for the POS quorums. + inline constexpr size_t POS_QUORUM_ENTROPY_LAG = 21; // How many blocks back from the tip of the Blockchain to source entropy for the POS quorums. #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) - constexpr auto POS_ROUND_TIME = 20s; - constexpr auto POS_WAIT_FOR_HANDSHAKES_DURATION = 3s; - constexpr auto POS_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 3s; - constexpr auto POS_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 3s; - constexpr auto POS_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 3s; - constexpr auto POS_WAIT_FOR_RANDOM_VALUE_DURATION = 3s; - constexpr auto POS_WAIT_FOR_SIGNED_BLOCK_DURATION = 5s; - - constexpr size_t POS_QUORUM_NUM_VALIDATORS = 7; - constexpr size_t POS_BLOCK_REQUIRED_SIGNATURES = 6; // A block must have exactly N signatures to be considered properly + inline constexpr auto POS_ROUND_TIME = 20s; + inline constexpr auto POS_WAIT_FOR_HANDSHAKES_DURATION = 3s; + inline constexpr auto POS_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 3s; + inline constexpr auto POS_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 3s; + inline constexpr auto POS_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 3s; + inline constexpr auto POS_WAIT_FOR_RANDOM_VALUE_DURATION = 3s; + inline constexpr auto POS_WAIT_FOR_SIGNED_BLOCK_DURATION = 5s; + + inline constexpr size_t POS_QUORUM_NUM_VALIDATORS = 7; + inline constexpr size_t POS_BLOCK_REQUIRED_SIGNATURES = 6; // A block must have exactly N signatures to be considered properly #else - constexpr auto POS_ROUND_TIME = 60s; - constexpr auto POS_WAIT_FOR_HANDSHAKES_DURATION = 10s; - constexpr auto POS_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 10s; - constexpr auto POS_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 10s; - constexpr auto POS_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 10s; - constexpr auto POS_WAIT_FOR_RANDOM_VALUE_DURATION = 10s; - constexpr auto POS_WAIT_FOR_SIGNED_BLOCK_DURATION = 10s; - - constexpr size_t POS_QUORUM_NUM_VALIDATORS = 11; - constexpr size_t POS_BLOCK_REQUIRED_SIGNATURES = 7; // A block must have exactly N signatures to be considered properly + inline constexpr auto POS_ROUND_TIME = 60s; + inline constexpr auto POS_WAIT_FOR_HANDSHAKES_DURATION = 10s; + inline constexpr auto POS_WAIT_FOR_OTHER_VALIDATOR_HANDSHAKES_DURATION = 10s; + inline constexpr auto POS_WAIT_FOR_BLOCK_TEMPLATE_DURATION = 10s; + inline constexpr auto POS_WAIT_FOR_RANDOM_VALUE_HASH_DURATION = 10s; + inline constexpr auto POS_WAIT_FOR_RANDOM_VALUE_DURATION = 10s; + inline constexpr auto POS_WAIT_FOR_SIGNED_BLOCK_DURATION = 10s; + + inline constexpr size_t POS_QUORUM_NUM_VALIDATORS = 11; + inline constexpr size_t POS_BLOCK_REQUIRED_SIGNATURES = 7; // A block must have exactly N signatures to be considered properly #endif - constexpr auto POS_MIN_TARGET_BLOCK_TIME = TARGET_BLOCK_TIME - 15s; - constexpr auto POS_MAX_TARGET_BLOCK_TIME = TARGET_BLOCK_TIME + 15s; - constexpr size_t POS_QUORUM_SIZE = POS_QUORUM_NUM_VALIDATORS + 1 /*Leader*/; + inline constexpr auto POS_MIN_TARGET_BLOCK_TIME = cryptonote::TARGET_BLOCK_TIME - 15s; + inline constexpr auto POS_MAX_TARGET_BLOCK_TIME = cryptonote::TARGET_BLOCK_TIME + 15s; + inline constexpr size_t POS_QUORUM_SIZE = POS_QUORUM_NUM_VALIDATORS + 1 /*Leader*/; static_assert(POS_ROUND_TIME >= POS_WAIT_FOR_HANDSHAKES_DURATION + @@ -48,10 +48,10 @@ namespace master_nodes { constexpr size_t POS_min_master_nodes(cryptonote::network_type nettype) { - return (nettype == cryptonote::MAINNET) ? 50 : POS_QUORUM_SIZE; + return (nettype == cryptonote::network_type::MAINNET) ? 50 : POS_QUORUM_SIZE; } - static_assert(POS_min_master_nodes(cryptonote::MAINNET) >= POS_QUORUM_SIZE); - static_assert(POS_min_master_nodes(cryptonote::TESTNET) >= POS_QUORUM_SIZE); + static_assert(POS_min_master_nodes(cryptonote::network_type::MAINNET) >= POS_QUORUM_SIZE); + static_assert(POS_min_master_nodes(cryptonote::network_type::TESTNET) >= POS_QUORUM_SIZE); constexpr uint16_t POS_validator_bit_mask() { @@ -82,11 +82,11 @@ namespace master_nodes { // decommission time: the first quorum test after the credit expires determines whether the server // gets recommissioned or decommissioned). - inline constexpr int64_t DECOMMISSION_CREDIT_PER_DAY = BLOCKS_PER_DAY / 30; - inline constexpr int64_t DECOMMISSION_INITIAL_CREDIT = BLOCKS_PER_HOUR * 2; - inline constexpr int64_t DECOMMISSION_MAX_CREDIT = BLOCKS_PER_DAY * 2; - inline constexpr int64_t DECOMMISSION_MINIMUM = BLOCKS_PER_HOUR * 2; - inline constexpr int64_t DECOMMISSION_INITIAL_CREDIT_V18 = BLOCKS_PER_HOUR * 3; + inline constexpr int64_t DECOMMISSION_CREDIT_PER_DAY = cryptonote::BLOCKS_PER_DAY / 30; + inline constexpr int64_t DECOMMISSION_INITIAL_CREDIT = cryptonote::BLOCKS_PER_HOUR * 2; + inline constexpr int64_t DECOMMISSION_MAX_CREDIT = cryptonote::BLOCKS_PER_DAY * 2; + inline constexpr int64_t DECOMMISSION_MINIMUM = cryptonote::BLOCKS_PER_HOUR * 2; + inline constexpr int64_t DECOMMISSION_INITIAL_CREDIT_V18 = cryptonote::BLOCKS_PER_HOUR * 3; static_assert(DECOMMISSION_INITIAL_CREDIT <= DECOMMISSION_MAX_CREDIT, "Initial registration decommission credit cannot be larger than the maximum decommission credit"); static_assert(DECOMMISSION_INITIAL_CREDIT_V18 <= DECOMMISSION_MAX_CREDIT, "Initial registration decommission credit cannot be larger than the maximum decommission credit"); @@ -133,51 +133,51 @@ namespace master_nodes { "Recommission credit should not be negative"); - constexpr uint64_t CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY = 2; // Number of consecutive checkpoints before, blocks preceeding the N checkpoints are locked in - constexpr uint64_t CHECKPOINT_INTERVAL = 4; // Checkpoint every 4 blocks and prune when too old except if (height % CHECKPOINT_STORE_PERSISTENTLY_INTERVAL == 0) - constexpr uint64_t CHECKPOINT_STORE_PERSISTENTLY_INTERVAL = 60; // Persistently store the checkpoints at these intervals - constexpr uint64_t CHECKPOINT_VOTE_LIFETIME = CHECKPOINT_STORE_PERSISTENTLY_INTERVAL; // Keep the last 60 blocks worth of votes + inline constexpr uint64_t CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY = 2; // Number of consecutive checkpoints before, blocks preceeding the N checkpoints are locked in + inline constexpr uint64_t CHECKPOINT_INTERVAL = 4; // Checkpoint every 4 blocks and prune when too old except if (height % CHECKPOINT_STORE_PERSISTENTLY_INTERVAL == 0) + inline constexpr uint64_t CHECKPOINT_STORE_PERSISTENTLY_INTERVAL = 60; // Persistently store the checkpoints at these intervals + inline constexpr uint64_t CHECKPOINT_VOTE_LIFETIME = CHECKPOINT_STORE_PERSISTENTLY_INTERVAL; // Keep the last 60 blocks worth of votes - constexpr int16_t QUORUM_VOTE_CHECK_COUNT = 8; - constexpr int16_t POS_MAX_MISSABLE_VOTES = 4; - constexpr int16_t CHECKPOINT_MAX_MISSABLE_VOTES = 4; - constexpr int16_t TIMESTAMP_MAX_MISSABLE_VOTES = 4; - constexpr int16_t TIMESYNC_MAX_UNSYNCED_VOTES = 4; + inline constexpr int16_t QUORUM_VOTE_CHECK_COUNT = 8; + inline constexpr int16_t POS_MAX_MISSABLE_VOTES = 4; + inline constexpr int16_t CHECKPOINT_MAX_MISSABLE_VOTES = 4; + inline constexpr int16_t TIMESTAMP_MAX_MISSABLE_VOTES = 4; + inline constexpr int16_t TIMESYNC_MAX_UNSYNCED_VOTES = 4; static_assert(CHECKPOINT_MAX_MISSABLE_VOTES < QUORUM_VOTE_CHECK_COUNT, "The maximum number of votes a master node can miss cannot be greater than the amount of checkpoint " "quorums they must participate in before we check if they should be deregistered or not."); - constexpr int FLASH_QUORUM_INTERVAL = 5; // We generate a new sub-quorum every N blocks (two consecutive quorums are needed for a flash signature) - constexpr int FLASH_QUORUM_LAG = 7 * FLASH_QUORUM_INTERVAL; // The lag (which must be a multiple of FLASH_QUORUM_INTERVAL) in determining the base flash quorum height - constexpr int FLASH_EXPIRY_BUFFER = FLASH_QUORUM_LAG + 10; // We don't select any MNs that have a scheduled unlock within this many blocks (measured from the lagged height) + inline constexpr int FLASH_QUORUM_INTERVAL = 5; // We generate a new sub-quorum every N blocks (two consecutive quorums are needed for a flash signature) + inline constexpr int FLASH_QUORUM_LAG = 7 * FLASH_QUORUM_INTERVAL; // The lag (which must be a multiple of FLASH_QUORUM_INTERVAL) in determining the base flash quorum height + inline constexpr int FLASH_EXPIRY_BUFFER = FLASH_QUORUM_LAG + 10; // We don't select any MNs that have a scheduled unlock within this many blocks (measured from the lagged height) static_assert(FLASH_QUORUM_LAG % FLASH_QUORUM_INTERVAL == 0, "FLASH_QUORUM_LAG must be an integral multiple of FLASH_QUORUM_INTERVAL"); static_assert(FLASH_EXPIRY_BUFFER > FLASH_QUORUM_LAG + FLASH_QUORUM_INTERVAL, "FLASH_EXPIRY_BUFFER is too short to cover a flash quorum height range"); // State change quorums are in charge of policing the network by changing the state of a master // node on the network: temporary decommissioning, recommissioning, and permanent deregistration. - constexpr size_t STATE_CHANGE_NTH_OF_THE_NETWORK_TO_TEST = 100; - constexpr size_t STATE_CHANGE_MIN_NODES_TO_TEST = 50; - constexpr uint64_t VOTE_LIFETIME = BLOCKS_PER_HOUR * 2; + inline constexpr size_t STATE_CHANGE_NTH_OF_THE_NETWORK_TO_TEST = 100; + inline constexpr size_t STATE_CHANGE_MIN_NODES_TO_TEST = 50; + inline constexpr uint64_t VOTE_LIFETIME = cryptonote::BLOCKS_PER_HOUR * 2; // Small Stake prevented from unlocking stake until a certain number of blocks have passed - constexpr uint64_t SMALL_CONTRIBUTOR_UNLOCK_TIMER = 2880 * 30; - constexpr uint64_t SMALL_CONTRIBUTOR_THRESHOLD = 2500; + inline constexpr uint64_t SMALL_CONTRIBUTOR_UNLOCK_TIMER = 2880 * 30; + inline constexpr uint64_t SMALL_CONTRIBUTOR_THRESHOLD = 2500; #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) - constexpr size_t STATE_CHANGE_QUORUM_SIZE = 5; - constexpr size_t STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE = 1; - constexpr int MIN_TIME_IN_S_BEFORE_VOTING = 0; - constexpr size_t CHECKPOINT_QUORUM_SIZE = 5; - constexpr size_t CHECKPOINT_MIN_VOTES = 1; - constexpr int FLASH_SUBQUORUM_SIZE = 5; - constexpr int FLASH_MIN_VOTES = 1; + inline constexpr size_t STATE_CHANGE_QUORUM_SIZE = 5; + inline constexpr size_t STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE = 1; + inline constexpr int MIN_TIME_IN_S_BEFORE_VOTING = 0; + inline constexpr size_t CHECKPOINT_QUORUM_SIZE = 5; + inline constexpr size_t CHECKPOINT_MIN_VOTES = 1; + inline constexpr int FLASH_SUBQUORUM_SIZE = 5; + inline constexpr int FLASH_MIN_VOTES = 1; #else - constexpr size_t STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE = 7; - constexpr size_t STATE_CHANGE_QUORUM_SIZE = 10; - constexpr size_t CHECKPOINT_QUORUM_SIZE = 20; - constexpr size_t CHECKPOINT_MIN_VOTES = 13; - constexpr int FLASH_SUBQUORUM_SIZE = 10; - constexpr int FLASH_MIN_VOTES = 7; + inline constexpr size_t STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE = 7; + inline constexpr size_t STATE_CHANGE_QUORUM_SIZE = 10; + inline constexpr size_t CHECKPOINT_QUORUM_SIZE = 20; + inline constexpr size_t CHECKPOINT_MIN_VOTES = 13; + inline constexpr int FLASH_SUBQUORUM_SIZE = 10; + inline constexpr int FLASH_MIN_VOTES = 7; #endif static_assert(STATE_CHANGE_MIN_VOTES_TO_CHANGE_STATE <= STATE_CHANGE_QUORUM_SIZE, "The number of votes required to kick can't exceed the actual quorum size, otherwise we never kick."); @@ -188,65 +188,65 @@ namespace master_nodes { #endif // NOTE: We can reorg up to last 2 checkpoints + the number of extra blocks before the next checkpoint is set - constexpr uint64_t REORG_SAFETY_BUFFER_BLOCKS_POST_HF12 = (CHECKPOINT_INTERVAL * CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY) + (CHECKPOINT_INTERVAL - 1); - constexpr uint64_t REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12 = 20; + inline constexpr uint64_t REORG_SAFETY_BUFFER_BLOCKS_POST_HF12 = (CHECKPOINT_INTERVAL * CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY) + (CHECKPOINT_INTERVAL - 1); + inline constexpr uint64_t REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12 = 20; // A single public IP address is restricted from hosting more than 3 masternodes. - constexpr int16_t MAX_ALLOWED_MASTERNODES_PER_IP = 3; + inline constexpr int16_t MAX_ALLOWED_MASTERNODES_PER_IP = 3; - constexpr auto IP_CHANGE_WINDOW = 24h; // How far back an obligations quorum looks for multiple IPs (unless the following buffer is more recent) - constexpr auto IP_CHANGE_BUFFER = 2h; // After we bump a MN for an IP change we don't bump again for changes within this time period + inline constexpr auto IP_CHANGE_WINDOW = 24h; // How far back an obligations quorum looks for multiple IPs (unless the following buffer is more recent) + inline constexpr auto IP_CHANGE_BUFFER = 2h; // After we bump a MN for an IP change we don't bump again for changes within this time period - constexpr size_t MAX_SWARM_SIZE = 10; + inline constexpr size_t MAX_SWARM_SIZE = 10; // We never create a new swarm unless there are SWARM_BUFFER extra nodes // available in the queue. - constexpr size_t SWARM_BUFFER = 5; + inline constexpr size_t SWARM_BUFFER = 5; // if a swarm has strictly less nodes than this, it is considered unhealthy // and nearby swarms will mirror it's data. It will disappear, and is already considered gone. - constexpr size_t MIN_SWARM_SIZE = 5; - constexpr size_t IDEAL_SWARM_MARGIN = 2; - constexpr size_t IDEAL_SWARM_SIZE = MIN_SWARM_SIZE + IDEAL_SWARM_MARGIN; - // constexpr size_t EXCESS_BASE = MIN_SWARM_SIZE; - constexpr size_t NEW_SWARM_SIZE = IDEAL_SWARM_SIZE; + inline constexpr size_t MIN_SWARM_SIZE = 5; + inline constexpr size_t IDEAL_SWARM_MARGIN = 2; + inline constexpr size_t IDEAL_SWARM_SIZE = MIN_SWARM_SIZE + IDEAL_SWARM_MARGIN; + // inline constexpr size_t EXCESS_BASE = MIN_SWARM_SIZE; + inline constexpr size_t NEW_SWARM_SIZE = IDEAL_SWARM_SIZE; // The lower swarm percentile that will be randomly filled with new master nodes - constexpr size_t FILL_SWARM_LOWER_PERCENTILE = 25; + inline constexpr size_t FILL_SWARM_LOWER_PERCENTILE = 25; // Redistribute mnodes from decommissioned swarms to the smallest swarms - constexpr size_t DECOMMISSIONED_REDISTRIBUTION_LOWER_PERCENTILE = 0; + inline constexpr size_t DECOMMISSIONED_REDISTRIBUTION_LOWER_PERCENTILE = 0; // The upper swarm percentile that will be randomly selected during stealing - constexpr size_t STEALING_SWARM_UPPER_PERCENTILE = 75; - constexpr uint64_t KEY_IMAGE_AWAITING_UNLOCK_HEIGHT = 0; + inline constexpr size_t STEALING_SWARM_UPPER_PERCENTILE = 75; + inline constexpr uint64_t KEY_IMAGE_AWAITING_UNLOCK_HEIGHT = 0; - constexpr uint64_t STATE_CHANGE_TX_LIFETIME_IN_BLOCKS = VOTE_LIFETIME; + inline constexpr uint64_t STATE_CHANGE_TX_LIFETIME_IN_BLOCKS = VOTE_LIFETIME; // If we get an incoming vote of state change tx that is outside the acceptable range by this many // blocks then ignore it but don't trigger a connection drop; the sending side could be a couple // blocks out of sync and sending something that it thinks is legit. - constexpr uint64_t VOTE_OR_TX_VERIFY_HEIGHT_BUFFER = 5; + inline constexpr uint64_t VOTE_OR_TX_VERIFY_HEIGHT_BUFFER = 5; - constexpr std::array MIN_STORAGE_SERVER_VERSION{{2, 3, 0}}; - constexpr std::array MIN_BELNET_VERSION{{0, 9, 7}}; + inline constexpr std::array MIN_STORAGE_SERVER_VERSION{{2, 4, 0}}; + inline constexpr std::array MIN_BELNET_VERSION{{0, 9, 8}}; // The minimum accepted version number, broadcasted by Master Nodes via uptime proofs for each hardfork struct proof_version { - std::pair hardfork_revision; + std::pair hardfork_revision; std::array beldexd; std::array belnet; std::array storage_server; }; - constexpr proof_version MIN_UPTIME_PROOF_VERSIONS[] = { - proof_version{{cryptonote::network_version_19, 0}, {6,0,0}, {0,9,7}, {2,3,0}}, - proof_version{{cryptonote::network_version_18_bns, 0}, {5,0,0}, {0,9,7}, {2,3,0}}, - proof_version{{cryptonote::network_version_17_POS, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, - proof_version{{cryptonote::network_version_16, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, - proof_version{{cryptonote::network_version_15_flash, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, - proof_version{{cryptonote::network_version_14_enforce_checkpoints, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, - proof_version{{cryptonote::network_version_13_checkpointing, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, + inline constexpr proof_version MIN_UPTIME_PROOF_VERSIONS[] = { + proof_version{{cryptonote::hf::hf20_bulletproof_plus, 0}, {7,0,0}, {0,9,8}, {2,4,0}}, + proof_version{{cryptonote::hf::hf19_enhance_bns, 0}, {6,0,0}, {0,9,7}, {2,3,0}}, + proof_version{{cryptonote::hf::hf18_bns, 0}, {5,0,0}, {0,9,7}, {2,3,0}}, + proof_version{{cryptonote::hf::hf17_POS, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, + proof_version{{cryptonote::hf::hf16, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, + proof_version{{cryptonote::hf::hf15_flash, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, + proof_version{{cryptonote::hf::hf13_checkpointing, 0}, {4,0,0}, {0,9,5}, {2,2,0}}, }; - using swarm_id_t = uint64_t; - constexpr swarm_id_t UNASSIGNED_SWARM_ID = UINT64_MAX; + using swarm_id_t = uint64_t; + inline constexpr swarm_id_t UNASSIGNED_SWARM_ID = UINT64_MAX; constexpr size_t min_votes_for_quorum_type(quorum_type q) { return @@ -256,36 +256,36 @@ namespace master_nodes { std::numeric_limits::max(); }; - constexpr quorum_type max_quorum_type_for_hf(uint8_t hf_version) + constexpr quorum_type max_quorum_type_for_hf(cryptonote::hf hf_version) { return - hf_version <= cryptonote::network_version_13_checkpointing ? quorum_type::obligations : - hf_version < cryptonote::network_version_15_flash ? quorum_type::checkpointing : - hf_version < cryptonote::network_version_17_POS ? quorum_type::flash : + hf_version <= cryptonote::hf::hf13_checkpointing ? quorum_type::obligations : + hf_version < cryptonote::hf::hf15_flash ? quorum_type::checkpointing : + hf_version < cryptonote::hf::hf17_POS ? quorum_type::flash : quorum_type::POS; } - constexpr uint64_t staking_num_lock_blocks(cryptonote::network_type nettype,uint8_t hf_version) + constexpr uint64_t staking_num_lock_blocks(cryptonote::network_type nettype, cryptonote::hf hf_version) { - auto blocks_per_day = (hf_version>=cryptonote::network_version_17_POS) ? BLOCKS_PER_DAY : BLOCKS_PER_DAY_OLD ; + auto blocks_per_day = (hf_version >= cryptonote::hf::hf17_POS) ? cryptonote::BLOCKS_PER_DAY : cryptonote::old::BLOCKS_PER_DAY_12 ; switch (nettype) { - case cryptonote::FAKECHAIN: return 30; - case cryptonote::TESTNET: return 2 * blocks_per_day; + case cryptonote::network_type::FAKECHAIN: return 30; + case cryptonote::network_type::TESTNET: return 2 * blocks_per_day; default: return 30 * blocks_per_day; } } //If a nodes timestamp varies by this amount of seconds they will be considered out of sync - constexpr uint8_t THRESHOLD_SECONDS_OUT_OF_SYNC = 30; +inline constexpr uint8_t THRESHOLD_SECONDS_OUT_OF_SYNC = 30; - //If the below percentage of service nodes are out of sync we will consider our clock out of sync - constexpr uint8_t MAXIMUM_EXTERNAL_OUT_OF_SYNC = 80; + //If the below percentage of master nodes are out of sync we will consider our clock out of sync +inline constexpr uint8_t MAXIMUM_EXTERNAL_OUT_OF_SYNC = 80; -static_assert(STAKING_PORTIONS != UINT64_MAX, "UINT64_MAX is used as the invalid value for failing to calculate the min_node_contribution"); +static_assert(cryptonote::old::STAKING_PORTIONS != UINT64_MAX, "UINT64_MAX is used as the invalid value for failing to calculate the min_node_contribution"); // return: UINT64_MAX if (num_contributions > the max number of contributions), otherwise the amount in beldex atomic units -uint64_t get_min_node_contribution (uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions); -uint64_t get_min_node_contribution_in_portions(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions); +uint64_t get_min_node_contribution (cryptonote::hf version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions); +uint64_t get_min_node_contribution_in_portions(cryptonote::hf version, uint64_t staking_requirement, uint64_t total_reserved, size_t num_contributions); // Gets the maximum allowed stake amount. This is used to prevent significant overstaking. The // wallet tries to avoid this when submitting a stake, but it can still happen when competing stakes @@ -294,7 +294,7 @@ uint64_t get_min_node_contribution_in_portions(uint8_t version, uint64_t staking // of stake despite locking 8k. // Starting in HF16, we disallow a stake if it is more than MAXIMUM_ACCEPTABLE_STAKE ratio of the // available contribution room, which allows slight overstaking but disallows larger overstakes. -uint64_t get_max_node_contribution(uint8_t version, uint64_t staking_requirement, uint64_t total_reserved); +uint64_t get_max_node_contribution(cryptonote::hf version, uint64_t staking_requirement, uint64_t total_reserved); uint64_t get_staking_requirement(uint64_t height); @@ -302,13 +302,13 @@ uint64_t portions_to_amount(uint64_t portions, uint64_t staking_requirement); /// Check if portions are sufficiently large (provided the contributions /// are made in the specified order) and don't exceed the required amount -bool check_master_node_portions(uint8_t version, const std::vector& portions); +bool check_master_node_portions(cryptonote::hf version, const std::vector& portions); crypto::hash generate_request_stake_unlock_hash(uint32_t nonce); -uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, uint64_t curr_height,uint8_t version); +uint64_t get_locked_key_image_unlock_height(cryptonote::network_type nettype, uint64_t curr_height, cryptonote::hf version); // Returns lowest x such that (staking_requirement * x/STAKING_PORTIONS) >= amount -uint64_t get_portions_to_make_amount(uint64_t staking_requirement, uint64_t amount, uint64_t max_portions = STAKING_PORTIONS); +uint64_t get_portions_to_make_amount(uint64_t staking_requirement, uint64_t amount, uint64_t max_portions = cryptonote::old::STAKING_PORTIONS); bool get_portions_from_percent_str(std::string cut_str, uint64_t& portions); } diff --git a/src/cryptonote_core/master_node_voting.cpp b/src/cryptonote_core/master_node_voting.cpp index a104d36a109..76d9919851f 100755 --- a/src/cryptonote_core/master_node_voting.cpp +++ b/src/cryptonote_core/master_node_voting.cpp @@ -46,6 +46,7 @@ #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "master_nodes" +using cryptonote::hf; namespace master_nodes { static crypto::hash make_state_change_vote_hash(uint64_t block_height, uint32_t master_node_index, new_state state) @@ -132,10 +133,10 @@ namespace master_nodes uint64_t latest_height, cryptonote::tx_verification_context &tvc, const master_nodes::quorum &quorum, - const uint8_t hf_version) + const hf hf_version) { auto &vvc = tvc.m_vote_ctx; - if (state_change.state != new_state::deregister && hf_version < cryptonote::network_version_13_checkpointing) + if (state_change.state != new_state::deregister && hf_version < hf::hf13_checkpointing) { LOG_PRINT_L1("Received state change TX with Non-deregister state changes are invalid before v12"); return bad_tx(tvc); @@ -198,7 +199,7 @@ namespace master_nodes int validator_index_tracker = -1; for (const auto &vote : state_change.votes) { - if (hf_version >= cryptonote::network_version_14_enforce_checkpoints) // NOTE: After HF13, votes must be stored in ascending order + if (hf_version >= hf::hf15_flash) // NOTE: After HF13, votes must be stored in ascending order { if (validator_index_tracker >= static_cast(vote.validator_index)) { @@ -238,7 +239,7 @@ namespace master_nodes return true; } - bool verify_quorum_signatures(master_nodes::quorum const &quorum, master_nodes::quorum_type type, uint8_t hf_version, uint64_t height, crypto::hash const &hash, std::vector const &signatures, const cryptonote::block* block) + bool verify_quorum_signatures(master_nodes::quorum const &quorum, master_nodes::quorum_type type, hf hf_version, uint64_t height, crypto::hash const &hash, std::vector const &signatures, const cryptonote::block* block) { bool enforce_vote_ordering = true; constexpr size_t MAX_QUORUM_SIZE = std::max(CHECKPOINT_QUORUM_SIZE, POS_QUORUM_NUM_VALIDATORS); @@ -266,7 +267,7 @@ namespace master_nodes return false; } - enforce_vote_ordering = hf_version >= cryptonote::network_version_14_enforce_checkpoints; + enforce_vote_ordering = hf_version >= hf::hf15_flash; } break; @@ -362,7 +363,7 @@ namespace master_nodes return result; } - bool verify_checkpoint(uint8_t hf_version, cryptonote::checkpoint_t const &checkpoint, master_nodes::quorum const &quorum) + bool verify_checkpoint(hf hf_version, cryptonote::checkpoint_t const &checkpoint, master_nodes::quorum const &quorum) { if (checkpoint.type == cryptonote::checkpoint_type::master_node) { @@ -404,7 +405,7 @@ namespace master_nodes return result; } - quorum_vote_t make_checkpointing_vote(uint8_t hf_version, crypto::hash const &block_hash, uint64_t block_height, uint16_t index_in_quorum, const master_node_keys &keys) + quorum_vote_t make_checkpointing_vote(hf hf_version, crypto::hash const &block_hash, uint64_t block_height, uint16_t index_in_quorum, const master_node_keys &keys) { quorum_vote_t result = {}; result.type = quorum_type::checkpointing; @@ -425,7 +426,7 @@ namespace master_nodes return result; } - bool verify_vote_age(const quorum_vote_t& vote, uint64_t latest_height, cryptonote::vote_verification_context &vvc,uint8_t hf_version) + bool verify_vote_age(const quorum_vote_t& vote, uint64_t latest_height, cryptonote::vote_verification_context &vvc, hf hf_version) { bool result = true; bool height_in_buffer = false; @@ -454,7 +455,7 @@ namespace master_nodes return result; } - bool verify_vote_signature(uint8_t hf_version, const quorum_vote_t &vote, cryptonote::vote_verification_context &vvc, const master_nodes::quorum &quorum) { + bool verify_vote_signature(hf hf_version, const quorum_vote_t &vote, cryptonote::vote_verification_context &vvc, const master_nodes::quorum &quorum) { bool result = true; if (vote.type > tools::enum_top) { vvc.m_invalid_vote_type = true; @@ -597,7 +598,7 @@ namespace master_nodes result.push_back(vote_entry.vote); } - std::vector voting_pool::get_relayable_votes(uint64_t height, uint8_t hf_version, bool quorum_relay) const + std::vector voting_pool::get_relayable_votes(uint64_t height, hf hf_version, bool quorum_relay) const { std::unique_lock lock{m_lock}; @@ -612,13 +613,13 @@ namespace master_nodes std::vector result; - if (quorum_relay && hf_version < cryptonote::network_version_15_flash) + if (quorum_relay && hf_version < hf::hf15_flash) return result; // no quorum relaying before HF14 - if (hf_version < cryptonote::network_version_15_flash || quorum_relay) + if (hf_version < hf::hf15_flash || quorum_relay) append_relayable_votes(result, m_obligations_pool, max_last_sent, min_height); - if (hf_version < cryptonote::network_version_15_flash || !quorum_relay) + if (hf_version < hf::hf15_flash || !quorum_relay) append_relayable_votes(result, m_checkpoint_pool, max_last_sent, min_height); return result; @@ -653,7 +654,7 @@ namespace master_nodes return *votes; } - void voting_pool::remove_used_votes(std::vector const &txs, uint8_t hard_fork_version) + void voting_pool::remove_used_votes(std::vector const &txs, hf version) { // TODO(doyle): Cull checkpoint votes std::unique_lock lock{m_lock}; @@ -666,7 +667,7 @@ namespace master_nodes continue; cryptonote::tx_extra_master_node_state_change state_change; - if (!get_master_node_state_change_from_tx_extra(tx.extra, state_change, hard_fork_version)) + if (!get_master_node_state_change_from_tx_extra(tx.extra, state_change, version)) { LOG_ERROR("Could not get state change from tx, possibly corrupt tx"); continue; @@ -692,7 +693,7 @@ namespace master_nodes } } - void voting_pool::remove_expired_votes(uint64_t height,uint8_t hf_version) + void voting_pool::remove_expired_votes(uint64_t height) { std::unique_lock lock{m_lock}; uint64_t min_height = (height < VOTE_LIFETIME) ? 0 : height - VOTE_LIFETIME; diff --git a/src/cryptonote_core/master_node_voting.h b/src/cryptonote_core/master_node_voting.h index 9a56a04f400..e7b48e3a151 100755 --- a/src/cryptonote_core/master_node_voting.h +++ b/src/cryptonote_core/master_node_voting.h @@ -102,14 +102,14 @@ namespace master_nodes struct master_node_keys; quorum_vote_t make_state_change_vote(uint64_t block_height, uint16_t index_in_group, uint16_t worker_index, new_state state, uint16_t reason, const master_node_keys &keys); - quorum_vote_t make_checkpointing_vote(uint8_t hf_version, crypto::hash const &block_hash, uint64_t block_height, uint16_t index_in_quorum, const master_node_keys &keys); + quorum_vote_t make_checkpointing_vote(cryptonote::hf hf_version, crypto::hash const &block_hash, uint64_t block_height, uint16_t index_in_quorum, const master_node_keys &keys); cryptonote::checkpoint_t make_empty_master_node_checkpoint(crypto::hash const &block_hash, uint64_t height); - bool verify_checkpoint (uint8_t hf_version, cryptonote::checkpoint_t const &checkpoint, master_nodes::quorum const &quorum); - bool verify_tx_state_change (const cryptonote::tx_extra_master_node_state_change& state_change, uint64_t latest_height, cryptonote::tx_verification_context& vvc, const master_nodes::quorum &quorum, const uint8_t hf_version); - bool verify_vote_age (const quorum_vote_t& vote, uint64_t latest_height, cryptonote::vote_verification_context &vvc,uint8_t hf_version); - bool verify_vote_signature (uint8_t hf_version, const quorum_vote_t& vote, cryptonote::vote_verification_context &vvc, const master_nodes::quorum &quorum); - bool verify_quorum_signatures (master_nodes::quorum const &quorum, master_nodes::quorum_type type, uint8_t hf_version, uint64_t height, crypto::hash const &hash, std::vector const &signatures, const cryptonote::block* block = nullptr); + bool verify_checkpoint (cryptonote::hf hf_version, cryptonote::checkpoint_t const &checkpoint, master_nodes::quorum const &quorum); + bool verify_tx_state_change (const cryptonote::tx_extra_master_node_state_change& state_change, uint64_t latest_height, cryptonote::tx_verification_context& vvc, const master_nodes::quorum &quorum, const cryptonote::hf hf_version); + bool verify_vote_age (const quorum_vote_t& vote, uint64_t latest_height, cryptonote::vote_verification_context &vvc, cryptonote::hf hf_version); + bool verify_vote_signature (cryptonote::hf hf_version, const quorum_vote_t& vote, cryptonote::vote_verification_context &vvc, const master_nodes::quorum &quorum); + bool verify_quorum_signatures (master_nodes::quorum const &quorum, master_nodes::quorum_type type, cryptonote::hf hf_version, uint64_t height, crypto::hash const &hash, std::vector const &signatures, const cryptonote::block* block = nullptr); bool verify_POS_quorum_sizes (master_nodes::quorum const &quorum); crypto::signature make_signature_from_vote (quorum_vote_t const &vote, const master_node_keys &keys); crypto::signature make_signature_from_tx_state_change(cryptonote::tx_extra_master_node_state_change const &state_change, const master_node_keys &keys); @@ -128,13 +128,13 @@ namespace master_nodes // TODO(beldex): Review relay behaviour and all the cases when it should be triggered void set_relayed (const std::vector& votes); - void remove_expired_votes(uint64_t height,uint8_t hf_version); - void remove_used_votes (std::vector const &txs, uint8_t hard_fork_version); + void remove_expired_votes(uint64_t height); + void remove_used_votes (std::vector const &txs, cryptonote::hf version); /// Returns relayable votes for either p2p (quorum_relay=false) or quorumnet /// (quorum_relay=true). Before HF14 everything goes via p2p; starting in HF14 obligation votes /// go via quorumnet, checkpoints go via p2p. - std::vector get_relayable_votes (uint64_t height, uint8_t hf_version, bool quorum_relay) const; + std::vector get_relayable_votes (uint64_t height, cryptonote::hf hf_version, bool quorum_relay) const; bool received_checkpoint_vote(uint64_t height, size_t index_in_quorum) const; private: diff --git a/src/cryptonote_core/pos.cpp b/src/cryptonote_core/pos.cpp index 70be8fb2c99..8c8e8133fe4 100755 --- a/src/cryptonote_core/pos.cpp +++ b/src/cryptonote_core/pos.cpp @@ -723,9 +723,9 @@ void POS::handle_message(void *quorumnet_state, POS::message const &msg) } // TODO(doyle): Update POS::prepare_for_round with this function after the hard fork and sanity check it on testnet. -bool POS::convert_time_to_round(POS::time_point const &time, POS::time_point const &r0_timestamp, uint8_t *round) +bool POS::convert_time_to_round(POS::time_point const& time, POS::time_point const& r0_timestamp, uint8_t* round) { - auto const time_since_round_started = time <= r0_timestamp ? std::chrono::seconds(0) : (time - r0_timestamp); + const auto time_since_round_started = time <= r0_timestamp ? 0s : (time - r0_timestamp); size_t result_usize = time_since_round_started / master_nodes::POS_ROUND_TIME; if (round) *round = static_cast(result_usize); return result_usize <= master_nodes::POS_MAX_ROUNDS_BEFORE_NETWORK_STALLED; @@ -734,7 +734,7 @@ bool POS::convert_time_to_round(POS::time_point const &time, POS::time_point con bool POS::get_round_timings(cryptonote::Blockchain const &blockchain, uint64_t block_height, uint64_t prev_timestamp, POS::timings ×) { times = {}; - auto hf17 = hard_fork_begins(blockchain.nettype(), cryptonote::network_version_17_POS); + auto hf17 = hard_fork_begins(blockchain.nettype(), cryptonote::hf::hf17_POS); if (!hf17 || blockchain.get_current_blockchain_height() < *hf17) return false; @@ -745,7 +745,7 @@ bool POS::get_round_timings(cryptonote::Blockchain const &blockchain, uint64_t b uint64_t const delta_height = block_height - cryptonote::get_block_height(POS_genesis_block); times.genesis_timestamp = POS::time_point(std::chrono::seconds(POS_genesis_block.timestamp)); times.prev_timestamp = POS::time_point(std::chrono::seconds(prev_timestamp)); - times.ideal_timestamp = POS::time_point(times.genesis_timestamp + (TARGET_BLOCK_TIME * delta_height)); //only for POS + times.ideal_timestamp = POS::time_point(times.genesis_timestamp + (cryptonote::TARGET_BLOCK_TIME * delta_height)); //only for POS #if 1 @@ -762,13 +762,13 @@ bool POS::get_round_timings(cryptonote::Blockchain const &blockchain, uint64_t b /* POS progresses via a state-machine that is iterated through job submissions - to 1 dedicated POS thread, started by LMQ. + to 1 dedicated POS thread, started by OMQ. Iterating the state-machine is done by a periodic invocation of POS::main(...) and messages received via Quorumnet for POS, which are queued in the thread's job queue. - Using 1 dedicated thread via LMQ avoids any synchronization required in the + Using 1 dedicated thread via OMQ avoids any synchronization required in the user code when implementing POS. Skip control flow graph for textual description of stages. @@ -1144,7 +1144,7 @@ round_state prepare_for_round(round_context &context, master_nodes::master_node_ return goto_wait_for_next_block_and_clear_round_data(context); } - uint8_t curr_round = static_cast(round_usize); + auto curr_round = static_cast(round_usize); if (curr_round > context.prepare_for_round.round) context.prepare_for_round.round = curr_round; } @@ -1162,7 +1162,7 @@ round_state prepare_for_round(round_context &context, master_nodes::master_node_ std::vector const entropy = master_nodes::get_POS_entropy_for_next_block(blockchain.get_db(), context.wait_for_next_block.top_hash, context.prepare_for_round.round); auto const active_node_list = blockchain.get_master_node_list().active_master_nodes_infos(); - uint8_t const hf_version = blockchain.get_network_version(); + auto hf_version = blockchain.get_network_version(); crypto::public_key const &block_leader = blockchain.get_master_node_list().get_block_leader().key; context.prepare_for_round.quorum = @@ -1682,7 +1682,7 @@ void POS::main(void *quorumnet_state, cryptonote::core &core) // // NOTE: Early exit if too early // - auto hf17 = hard_fork_begins(core.get_nettype(), cryptonote::network_version_17_POS); + auto hf17 = hard_fork_begins(core.get_nettype(), cryptonote::hf::hf17_POS); if (!hf17) { for (static bool once = true; once; once = !once) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 910de9a1e09..28d4170a12f 100755 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -33,6 +33,7 @@ #include #include +#include "common/util.h" #include "tx_pool.h" #include "cryptonote_tx_utils.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" @@ -79,29 +80,29 @@ namespace cryptonote return d; } - uint64_t get_transaction_weight_limit(uint8_t version) + uint64_t get_transaction_weight_limit(hf version) { // from v10, bulletproofs, limit a tx to 50% of the minimum block weight - if (version >= network_version_10_bulletproofs) - return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + if (version >= hf::hf10_bulletproofs) + return get_min_block_weight(version) / 2 - COINBASE_BLOB_RESERVED_SIZE; else - return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + return get_min_block_weight(version) - COINBASE_BLOB_RESERVED_SIZE; } } //--------------------------------------------------------------------------------- // warning: bchs is passed here uninitialized, so don't do anything but store it - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_MEMPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_duplicated_non_standard_tx(transaction const &tx, uint8_t hard_fork_version) const + bool tx_memory_pool::have_duplicated_non_standard_tx(transaction const &tx, hf version) const { auto &master_node_list = m_blockchain.get_master_node_list(); if (tx.type == txtype::state_change) { tx_extra_master_node_state_change state_change; - if (!get_master_node_state_change_from_tx_extra(tx.extra, state_change, hard_fork_version)) + if (!get_master_node_state_change_from_tx_extra(tx.extra, state_change, version)) { MERROR("Could not get master node state change from tx: " << get_transaction_hash(tx) << ", possibly corrupt tx in your blockchain, rejecting malformed state change"); return false; @@ -126,13 +127,13 @@ namespace cryptonote continue; tx_extra_master_node_state_change pool_tx_state_change; - if (!get_master_node_state_change_from_tx_extra(pool_tx.extra, pool_tx_state_change, hard_fork_version)) + if (!get_master_node_state_change_from_tx_extra(pool_tx.extra, pool_tx_state_change, version)) { LOG_PRINT_L1("Could not get master node state change from tx: " << get_transaction_hash(pool_tx) << ", possibly corrupt tx in the pool"); continue; } - if (hard_fork_version >= cryptonote::network_version_13_checkpointing) + if (version >= hf::hf13_checkpointing) { crypto::public_key master_node_to_change_in_the_pool; bool same_master_node = false; @@ -235,7 +236,7 @@ namespace cryptonote // to set `do_not_relay` to false and starts relaying it (other quorum members do the same). //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, const tx_pool_options &opts, uint8_t hf_version, + bool tx_memory_pool::add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, const tx_pool_options &opts, hf hf_version, uint64_t *flash_rollback_height) { // this should already be called with that lock, but let's make it explicit for clarity @@ -262,7 +263,7 @@ namespace cryptonote if(tx.type == txtype::key_image_unlock) { - if(hf_version >= cryptonote::network_version_18_bns) + if(hf_version >= hf::hf18_bns) { crypto::public_key mnode_key; if (!cryptonote::get_master_node_pubkey_from_tx_extra(tx.extra, mnode_key)) @@ -284,7 +285,7 @@ namespace cryptonote }); if (cit != contributor.locked_contributions.end()) { - if (cit->amount < (master_nodes::SMALL_CONTRIBUTOR_THRESHOLD*COIN) && (block_height - node_info.registration_height) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) + if (cit->amount < (master_nodes::SMALL_CONTRIBUTOR_THRESHOLD * beldex::COIN) && (block_height - node_info.registration_height) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) { MWARNING("Unlock TX: small contributor trying to unlock node before " << std::to_string(master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER) <<" blocks from the registration height" @@ -307,7 +308,7 @@ namespace cryptonote uint64_t fee, burned; - if (!get_tx_miner_fee(tx, fee, hf_version >= HF_VERSION_FEE_BURNING, &burned)) + if (!get_tx_miner_fee(tx, fee, hf_version >= feature::FEE_BURNING, &burned)) { // This code is a bit convoluted: the above sets `fee`, and returns false for a pre-ringct tx // with a too-low fee, but for ringct (v2+) txes it just sets `fee` but doesn't check it and @@ -325,7 +326,7 @@ namespace cryptonote } size_t tx_weight_limit = get_transaction_weight_limit(hf_version); - if ((!opts.kept_by_block || hf_version >= HF_VERSION_PER_BYTE_FEE) && tx_weight > tx_weight_limit) + if ((!opts.kept_by_block || hf_version >= feature::PER_BYTE_FEE) && tx_weight > tx_weight_limit) { LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit); tvc.m_verifivation_failed = true; @@ -508,7 +509,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, const tx_pool_options &opts, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, const tx_pool_options &opts, hf version) { crypto::hash h = null_hash; size_t blob_size = 0; @@ -833,7 +834,7 @@ namespace cryptonote } }; - const auto unexpired = std::time(nullptr) - MEMPOOL_PRUNE_NON_STANDARD_TX_LIFETIME; + const auto unexpired = std::time(nullptr) - static_cast(tools::to_seconds(MEMPOOL_PRUNE_NON_STANDARD_TX_LIFETIME)); for (auto it = m_txs_by_fee_and_receive_time.begin(); it != m_txs_by_fee_and_receive_time.end(); ) { const bool is_standard_tx = !std::get<0>(it->first); @@ -910,6 +911,14 @@ namespace cryptonote ++m_cookie; return true; } + tx_memory_pool::key_images_container tx_memory_pool::get_spent_key_images(bool already_locked) { + std::unique_lock tx_lock{*this, std::defer_lock}; + std::unique_lock bc_lock{m_blockchain, std::defer_lock}; + if (!already_locked) + std::lock(tx_lock, bc_lock); + + return m_spent_key_images; + } //--------------------------------------------------------------------------------- bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) { @@ -995,8 +1004,8 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { uint64_t tx_age = time(nullptr) - meta.receive_time; - if((tx_age > CRYPTONOTE_MEMPOOL_TX_LIVETIME && !meta.kept_by_block) || - (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && meta.kept_by_block) ) + if((tx_age > tools::to_seconds(MEMPOOL_TX_LIVETIME) && !meta.kept_by_block) || + (tx_age > tools::to_seconds(MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME) && meta.kept_by_block) ) { LOG_PRINT_L1("Tx " << txid << " removed from tx pool due to outdated, age: " << tx_age ); auto sorted_it = find_tx_in_sorted_container(txid); @@ -1062,7 +1071,7 @@ namespace cryptonote // if the tx is older than half the max lifetime, we don't re-relay it, to avoid a problem // mentioned by smooth where nodes would flush txes at slightly different times, causing // flushed txes to be re-added when received from a node which was just about to flush it - uint64_t max_age = meta.kept_by_block ? CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : CRYPTONOTE_MEMPOOL_TX_LIVETIME; + uint64_t max_age = tools::to_seconds(meta.kept_by_block ? MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : MEMPOOL_TX_LIVETIME); if (now - meta.receive_time <= max_age / 2) { try @@ -1195,24 +1204,13 @@ namespace cryptonote LOG_PRINT_L2("get_transaction_hashes end"); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_backlog(std::vector& backlog, bool include_unrelayed_txes) const + tx_memory_pool::tx_stats tx_memory_pool::get_transaction_stats(bool include_unrelayed_txes) const { auto locks = tools::unique_locks(m_transactions_lock, m_blockchain); + tx_stats stats{}; const uint64_t now = time(NULL); - backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); - m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ - backlog.push_back({meta.weight, meta.fee, meta.receive_time - now}); - return true; - }, false, include_unrelayed_txes); - } - //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_stats(struct rpc::txpool_stats& stats, bool include_unrelayed_txes) const - { - auto locks = tools::unique_locks(m_transactions_lock, m_blockchain); - - const uint64_t now = time(NULL); - std::map agebytes; + std::map> agebytes; stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); std::vector weights; weights.reserve(stats.txs_total); @@ -1232,9 +1230,10 @@ namespace cryptonote stats.num_10m++; if (meta.last_failed_height) stats.num_failing++; - uint64_t age = now - meta.receive_time + (now == meta.receive_time); - agebytes[age].txs++; - agebytes[age].bytes += meta.weight; + uint64_t age = now < meta.receive_time ? 0 : now - meta.receive_time; + auto& a = agebytes[age]; + a.first++; + a.second += meta.weight; if (meta.double_spend_seen) ++stats.num_double_spends; return true; @@ -1242,10 +1241,11 @@ namespace cryptonote stats.bytes_med = tools::median(std::move(weights)); if (stats.txs_total > 1) { + stats.histo.resize(10); /* looking for 98th percentile */ size_t end = stats.txs_total * 0.02; uint64_t delta, factor; - std::map::iterator it, i2; + decltype(agebytes.begin()) it; if (end) { /* If enough txs, spread the first 98% of results across @@ -1258,7 +1258,7 @@ namespace cryptonote */ do { --it; - cumulative_num += it->second.txs; + cumulative_num += it->second.first; } while (it != agebytes.begin() && cumulative_num < end); stats.histo_98pc = it->first; factor = 9; @@ -1271,106 +1271,27 @@ namespace cryptonote */ stats.histo_98pc = 0; it = agebytes.end(); - factor = stats.txs_total > 9 ? 10 : stats.txs_total; + factor = 10; delta = now - stats.oldest; - stats.histo.resize(factor); } if (!delta) delta = 1; - for (i2 = agebytes.begin(); i2 != it; i2++) + auto i2 = agebytes.begin(); + for (; i2 != it; i2++) { size_t i = (i2->first * factor - 1) / delta; - stats.histo[i].txs += i2->second.txs; - stats.histo[i].bytes += i2->second.bytes; + stats.histo[i].first += i2->second.first; + stats.histo[i].second += i2->second.second; } for (; i2 != agebytes.end(); i2++) { - stats.histo[factor].txs += i2->second.txs; - stats.histo[factor].bytes += i2->second.bytes; - } - } - } - //------------------------------------------------------------------ - //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, std::function post_process, bool include_sensitive_data) const - { - std::unique_lock tx_lock{m_transactions_lock, std::defer_lock}; - std::unique_lock bc_lock{m_blockchain, std::defer_lock}; - auto flash_lock = flash_shared_lock(std::defer_lock); - std::lock(tx_lock, bc_lock, flash_lock); - - tx_infos.reserve(m_blockchain.get_txpool_tx_count()); - key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); - - m_blockchain.for_all_txpool_txes([&tx_infos, this, include_sensitive_data, post_process=std::move(post_process)](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ - transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) - { - MERROR("Failed to parse tx from txpool"); - // continue - return true; + auto& h = stats.histo[factor]; + h.first += i2->second.first; + h.second += i2->second.second; } - tx_infos.emplace_back(); - auto& txi = tx_infos.back(); - txi.id_hash = tools::type_to_hex(txid); - txi.tx_blob = *bd; - tx.set_hash(txid); - txi.tx_json = obj_to_json_str(tx); - txi.blob_size = bd->size(); - txi.weight = meta.weight; - txi.fee = meta.fee; - txi.kept_by_block = meta.kept_by_block; - txi.max_used_block_height = meta.max_used_block_height; - txi.max_used_block_id_hash = tools::type_to_hex(meta.max_used_block_id); - txi.last_failed_height = meta.last_failed_height; - txi.last_failed_id_hash = tools::type_to_hex(meta.last_failed_id); - // In restricted mode we do not include this data: - txi.receive_time = include_sensitive_data ? meta.receive_time : 0; - txi.relayed = meta.relayed; - // In restricted mode we do not include this data: - txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0; - txi.do_not_relay = meta.do_not_relay; - txi.double_spend_seen = meta.double_spend_seen; - txi.flash = has_flash(txid); - if (post_process) - post_process(tx, txi); - return true; - }, true, include_sensitive_data); - - txpool_tx_meta_t meta; - for (const key_images_container::value_type& kee : m_spent_key_images) { - const crypto::key_image& k_image = kee.first; - const std::unordered_set& kei_image_set = kee.second; - rpc::spent_key_image_info ki{}; - ki.id_hash = tools::type_to_hex(k_image); - for (const crypto::hash& tx_id_hash : kei_image_set) - { - if (!include_sensitive_data) - { - try - { - if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta)) - { - MERROR("Failed to get tx meta from txpool"); - return false; - } - if (!meta.relayed) - // Do not include that transaction if in restricted mode and it's not relayed - continue; - } - catch (const std::exception &e) - { - MERROR("Failed to get tx meta from txpool: " << e.what()); - return false; - } - } - ki.txs_hashes.push_back(tools::type_to_hex(tx_id_hash)); - } - // Only return key images for which we have at least one tx that we can show for them - if (!ki.txs_hashes.empty()) - key_image_infos.push_back(ki); } - return true; + + return stats; } //--------------------------------------------------------------------------------- bool tx_memory_pool::check_for_key_images(const std::vector& key_images, std::vector& spent) const @@ -1387,7 +1308,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - int tx_memory_pool::find_transactions(const std::vector &tx_hashes, std::vector &txblobs) const + int tx_memory_pool::find_transactions(const std::unordered_set& tx_hashes, std::vector& txblobs) const { if (tx_hashes.empty()) return 0; @@ -1599,16 +1520,13 @@ namespace cryptonote if (m_blockchain.get_blocks_only(immutable + 1, height, blocks)) { std::vector txs; - std::vector missed_txs; uint64_t earliest = height; for (auto it = blocks.rbegin(); it != blocks.rend(); it++) { const auto& block = *it; auto block_height = cryptonote::get_block_height(block); txs.clear(); - missed_txs.clear(); - - if (!m_blockchain.get_transactions(block.tx_hashes, txs, missed_txs)) + if (!m_blockchain.get_transactions(block.tx_hashes, txs)) { MERROR("Unable to get transactions for block " << block.hash); can_fix_with_a_rollback = false; @@ -1800,7 +1718,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &raw_fee, uint64_t &expected_reward, uint8_t version, uint64_t height) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &raw_fee, uint64_t &expected_reward, hf version, uint64_t height) { auto locks = tools::unique_locks(m_transactions_lock, m_blockchain); @@ -1819,12 +1737,17 @@ namespace cryptonote return false; } - best_reward = version >= cryptonote::network_version_17_POS ? 0 /*Empty block, starts with 0 fee*/ : reward_parts.base_miner; + best_reward = version >= hf::hf17_POS ? 0 /*Empty block, starts with 0 fee*/ : reward_parts.base_miner; } - size_t const max_total_weight = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t const max_total_weight = 2 * median_weight - COINBASE_BLOB_RESERVED_SIZE; std::unordered_set k_images; + // Track BNS buys because we can't put more than one for the same BNS name into the same block + // (otherwise the *block* will fail but validation won't, because validation here won't see the + // earlier tx has having taken effect, but the block addition will). + std::unordered_set bns_buys; + LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); LockedTXN lock(m_blockchain); @@ -1864,7 +1787,7 @@ namespace cryptonote // NOTE: Use the net fee for comparison (after penalty is applied). // After HF16, penalty is applied on the miner fee. Before, penalty is // applied on the base reward. - if (version >= cryptonote::network_version_17_POS) + if (version >= hf::hf17_POS) { next_reward = next_reward_parts.miner_fee; } @@ -1920,6 +1843,24 @@ namespace cryptonote LOG_PRINT_L2(" key images already seen"); continue; } + if (tx.type == txtype::beldex_name_system) { + // TX validation above has checked that this isn't an BNS buy for a name that is already + // registered, but it can't check that we don't create such a conflict from trying to + // put two conflicting registrations in the same block: when actually processing such a + // block the second one *would* be invalid because processing the first one created it. + // + // We only filter buys based on name_hash here which means technically we might + // over-filter (e.g. if there is both a bchat + wallet BNS) but that's not a big deal + // (one of the two will just get delayed for a block), and perfectly figuring out + // whether two might conflict is complicated enough that it's not worth doing here. + cryptonote::tx_extra_beldex_name_system bns; + if (cryptonote::get_field_from_tx_extra(tx.extra, bns) && bns.is_buying() && + !bns_buys.emplace(bns.name_hash).second) { + + LOG_PRINT_L2(" conflicting BNS buy in mempool"); + continue; + } + } bl.tx_hashes.push_back(sorted_it.second); total_weight += meta.weight; @@ -1938,7 +1879,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::validate(uint8_t version) + size_t tx_memory_pool::validate(hf version) { auto locks = tools::unique_locks(m_transactions_lock, m_blockchain); @@ -2006,7 +1947,7 @@ namespace cryptonote { auto locks = tools::unique_locks(m_transactions_lock, m_blockchain); - m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_TXPOOL_MAX_WEIGHT; + m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_MEMPOOL_MAX_WEIGHT; m_txs_by_fee_and_receive_time.clear(); m_spent_key_images.clear(); m_txpool_weight = 0; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 66ef8bc702e..7bf9e3812f6 100755 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -43,7 +43,6 @@ #include "cryptonote_basic/verification_context.h" #include "blockchain_db/blockchain_db.h" #include "crypto/hash.h" -#include "rpc/core_rpc_server_commands_defs.h" #include "tx_flash.h" #include "beldex_economy.h" @@ -84,13 +83,13 @@ namespace cryptonote static tx_pool_options from_block() { tx_pool_options o; o.kept_by_block = true; o.relayed = true; return o; } static tx_pool_options from_peer() { tx_pool_options o; o.relayed = true; return o; } static tx_pool_options new_tx(bool do_not_relay = false) { tx_pool_options o; o.do_not_relay = do_not_relay; return o; } - static tx_pool_options new_flash(bool approved, uint8_t hf_version) { + static tx_pool_options new_flash(bool approved, hf hf_version) { tx_pool_options o; o.do_not_relay = !approved; o.approved_flash = approved; - o.fee_percent = FLASH_MINER_TX_FEE_PERCENT; - o.burn_percent = FLASH_BURN_TX_FEE_PERCENT_OLD; - o.burn_fixed = FLASH_BURN_FIXED; + o.fee_percent = beldex::FLASH_MINER_TX_FEE_PERCENT; + o.burn_percent = beldex::FLASH_BURN_TX_FEE_PERCENT_OLD; + o.burn_fixed = beldex::FLASH_BURN_FIXED; return o; } }; @@ -124,7 +123,7 @@ namespace cryptonote tx_memory_pool &operator=(const tx_memory_pool &) = delete; /** - * @copydoc add_tx(transaction&, tx_verification_context&, const tx_pool_options &, uint8_t) + * @copydoc add_tx(transaction&, tx_verification_context&, const tx_pool_options &, hf) * * @param id the transaction's hash * @param tx_weight the transaction's weight @@ -132,7 +131,7 @@ namespace cryptonote * block tx then set this pointer to the required new height: that is, all blocks with height * `block_rollback_height` and above must be removed. */ - bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, const tx_pool_options &opts, uint8_t hf_version, uint64_t *flash_rollback_height = nullptr); + bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, const tx_pool_options &opts, hf hf_version, uint64_t *flash_rollback_height = nullptr); /** * @brief add a transaction to the transaction pool @@ -149,7 +148,7 @@ namespace cryptonote * * @return true if the transaction passes validations, otherwise false */ - bool add_tx(transaction &tx, tx_verification_context& tvc, const tx_pool_options &opts, uint8_t hf_version); + bool add_tx(transaction &tx, tx_verification_context& tvc, const tx_pool_options &opts, hf hf_version); /** * @brief attempts to add a flash transaction to the transaction pool. @@ -389,7 +388,7 @@ namespace cryptonote * * @return true */ - bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &raw_fee, uint64_t &expected_reward, uint8_t version, uint64_t height); + bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &raw_fee, uint64_t &expected_reward, hf version, uint64_t height); /** * @brief get a list of all transactions in the pool @@ -409,37 +408,32 @@ namespace cryptonote */ void get_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true, bool include_only_flashed = false) const; - /** - * @brief get (weight, fee, receive time) for all transaction in the pool - * - * @param txs return-by-reference that data - * @param include_unrelayed_txes include unrelayed txes in the result - * - */ - void get_transaction_backlog(std::vector& backlog, bool include_unrelayed_txes = true) const; + /// Return type of get_transaction_stats() + struct tx_stats + { + uint64_t bytes_total; ///< Total size of all transactions in pool. + uint32_t bytes_min; ///< Min transaction size in pool. + uint32_t bytes_max; ///< Max transaction size in pool. + uint32_t bytes_med; ///< Median transaction size in pool. + uint64_t fee_total; ///< Total fee's in pool in atomic units. + uint64_t oldest; ///< Unix time of the oldest transaction in the pool. + uint32_t txs_total; ///< Total number of transactions. + uint32_t num_failing; ///< Bumber of failing transactions. + uint32_t num_10m; ///< Number of transactions in pool for more than 10 minutes. + uint32_t num_not_relayed; ///< Number of non-relayed transactions. + uint64_t histo_98pc; ///< the time 98% of txes are "younger" than. + std::vector> histo; ///< List of txpool histo [number of txes, size in bytes] pairs. + uint32_t num_double_spends; ///< Number of double spend transactions. + }; /** * @brief get a summary statistics of all transaction hashes in the pool * - * @param stats return-by-reference the pool statistics * @param include_unrelayed_txes include unrelayed txes in the result * + * @return txpool_stats struct of pool statistics */ - void get_transaction_stats(struct rpc::txpool_stats& stats, bool include_unrelayed_txes = true) const; - - /** - * @brief get information about all transactions and key images in the pool - * - * see documentation on tx_info and spent_key_image_info for more details - * - * @param tx_infos return-by-reference the transactions' information - * @param key_image_infos return-by-reference the spent key images' information - * @param post_process optional function to call to do any extra tx_info processing from the transaction - * @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node confidentiality - * - * @return true - */ - bool get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, std::function post_process = nullptr, bool include_sensitive_data = true) const; + tx_stats get_transaction_stats(bool include_unrelayed_txes = true) const; /** * @brief check for presence of key images in the pool @@ -470,7 +464,7 @@ namespace cryptonote * * @return number of transactions added to txblobs */ - int find_transactions(const std::vector &tx_hashes, std::vector &txblobs) const; + int find_transactions(const std::unordered_set& tx_hashes, std::vector& txblobs) const; /** * @brief get a list of all relayable transactions and their hashes @@ -522,7 +516,7 @@ namespace cryptonote * * @return the number of transactions removed */ - size_t validate(uint8_t version); + size_t validate(hf version); /** * @brief return the cookie @@ -544,6 +538,23 @@ namespace cryptonote * @param bytes the max cumulative txpool weight in bytes */ void set_txpool_max_weight(size_t bytes); + + //TODO: confirm the below comments and investigate whether or not this + // is the desired behavior + //! map key images to transactions which spent them + /*! this seems odd, but it seems that multiple transactions can exist + * in the pool which both have the same spent key. This would happen + * in the event of a reorg where someone creates a new/different + * transaction on the assumption that the original will not be in a + * block again. + */ + using key_images_container = std::unordered_map>; + + /// Returns a copy of the map of key images -> set of transactions which spent them. + /// + /// \param already_locked can be passed as true if the caller already has a lock on the + /// blockchain and mempool objects; otherwise a new lock will be obtained by the call. + key_images_container get_spent_key_images(bool already_locked = false); private: @@ -580,7 +591,7 @@ namespace cryptonote * @return true if it already exists * */ - bool have_duplicated_non_standard_tx(transaction const &tx, uint8_t hard_fork_version) const; + bool have_duplicated_non_standard_tx(transaction const &tx, hf version) const; /** * @brief check if any spent key image in a transaction is in the pool @@ -681,17 +692,6 @@ namespace cryptonote */ bool remove_flash_conflicts(const crypto::hash &id, const std::vector &conflict_txs, uint64_t *flash_rollback_height); - //TODO: confirm the below comments and investigate whether or not this - // is the desired behavior - //! map key images to transactions which spent them - /*! this seems odd, but it seems that multiple transactions can exist - * in the pool which both have the same spent key. This would happen - * in the event of a reorg where someone creates a new/different - * transaction on the assumption that the original will not be in a - * block again. - */ - typedef std::unordered_map > key_images_container; - mutable std::recursive_mutex m_transactions_lock; //!< mutex for the pool //! container for spent key images from the transactions in the pool diff --git a/src/cryptonote_core/uptime_proof.cpp b/src/cryptonote_core/uptime_proof.cpp index c75a613476c..08c57c4481c 100644 --- a/src/cryptonote_core/uptime_proof.cpp +++ b/src/cryptonote_core/uptime_proof.cpp @@ -1,5 +1,6 @@ #include "uptime_proof.h" #include "common/string_util.h" +#include "epee/string_tools.h" #include "version.h" extern "C" diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index b4ba0be8931..ee0383568c7 100755 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -31,8 +31,7 @@ #include #include -#include -#include + #include "epee/string_tools.h" #include "cryptonote_protocol_defs.h" #include "common/pruning.h" @@ -41,21 +40,10 @@ #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "cn.block_queue" -namespace std { -template<> -struct hash -{ - size_t operator()(const boost::uuids::uuid& uid) const - { - return boost::uuids::hash_value(uid); - } -}; -} - namespace cryptonote { -void block_queue::add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) +void block_queue::add_blocks(uint64_t height, std::vector bcel, const connection_id_t& connection_id, float rate, size_t size) { std::unique_lock lock{mutex}; std::vector hashes; @@ -72,14 +60,14 @@ void block_queue::add_blocks(uint64_t height, std::vector 0, "Empty span"); std::unique_lock lock{mutex}; blocks.insert(span(height, nblocks, connection_id, time)); } -void block_queue::flush_spans(const boost::uuids::uuid &connection_id, bool all) +void block_queue::flush_spans(const connection_id_t& connection_id, bool all) { std::unique_lock lock{mutex}; block_map::iterator i = blocks.begin(); @@ -104,7 +92,7 @@ void block_queue::erase_block(block_map::iterator j) blocks.erase(j); } -void block_queue::flush_stale_spans(const std::set &live_connections) +void block_queue::flush_stale_spans(const std::set& live_connections) { std::unique_lock lock{mutex}; block_map::iterator i = blocks.begin(); @@ -134,7 +122,7 @@ bool block_queue::remove_span(uint64_t start_block_height, std::vector block_queue::reserve_span( uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, - const boost::uuids::uuid &connection_id, + const connection_id_t& connection_id, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector &block_hashes) @@ -268,8 +256,8 @@ std::pair block_queue::reserve_span( // if the peer's pruned for the starting block and its unpruned stripe comes next, start downloading from there const uint32_t next_unpruned_height = tools::get_next_unpruned_block_height(span_start_height, blockchain_height, pruning_seed); MDEBUG("reserve_span: next_unpruned_height " << next_unpruned_height << " from " << span_start_height << " and seed " - << epee::string_tools::to_string_hex(pruning_seed) << ", limit " << span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE); - if (next_unpruned_height > span_start_height && next_unpruned_height < span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE) + << epee::string_tools::to_string_hex(pruning_seed) << ", limit " << span_start_height + PRUNING_STRIPE_SIZE); + if (next_unpruned_height > span_start_height && next_unpruned_height < span_start_height + PRUNING_STRIPE_SIZE) { MDEBUG("We can download from next span: ideal height " << span_start_height << ", next unpruned height " << next_unpruned_height << "(+" << next_unpruned_height - span_start_height << "), current seed " << pruning_seed); @@ -309,7 +297,7 @@ std::pair block_queue::reserve_span( return std::make_pair(span_start_height, span_length); } -std::pair block_queue::get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id) const +std::pair block_queue::get_next_span_if_scheduled(std::vector &hashes, connection_id_t& connection_id) const { std::unique_lock lock{mutex}; if (blocks.empty()) @@ -336,7 +324,7 @@ void block_queue::reset_next_span_time() } -void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes) +void block_queue::set_span_hashes(uint64_t start_height, const connection_id_t& connection_id, std::vector hashes) { std::unique_lock lock{mutex}; for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i) @@ -354,7 +342,7 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui } } -bool block_queue::get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled) const +bool block_queue::get_next_span(uint64_t &height, std::vector &bcel, connection_id_t& connection_id, bool filled) const { std::unique_lock lock{mutex}; if (blocks.empty()) @@ -373,7 +361,7 @@ bool block_queue::get_next_span(uint64_t &height, std::vector speeds; + std::unordered_map speeds; for (const auto &span: blocks) { if (span.blocks.empty()) @@ -448,7 +436,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const // note that the average below does not average over the whole set, but over the // previous pseudo average and the latest rate: this gives much more importance // to the latest measurements, which is fine here - std::unordered_map::iterator i = speeds.find(span.connection_id); + std::unordered_map::iterator i = speeds.find(span.connection_id); if (i == speeds.end()) speeds.insert(std::make_pair(span.connection_id, span.rate)); else @@ -473,7 +461,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const return speed; } -float block_queue::get_download_rate(const boost::uuids::uuid &connection_id) const +float block_queue::get_download_rate(const connection_id_t& connection_id) const { std::unique_lock lock{mutex}; float conn_rate = -1.f; diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index ba23b56cbdc..d3a7b105544 100755 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -36,8 +36,8 @@ #include #include #include -#include #include "crypto/hash.h" +#include "epee/net/net_utils_base.h" #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "cn.block_queue" @@ -46,6 +46,8 @@ namespace cryptonote { struct block_complete_entry; + using epee::connection_id_t; + class block_queue { public: @@ -54,16 +56,16 @@ namespace cryptonote uint64_t start_block_height; std::vector hashes; std::vector blocks; - boost::uuids::uuid connection_id; + connection_id_t connection_id; uint64_t nblocks; float rate; size_t size; std::chrono::steady_clock::time_point time; - span(uint64_t start_block_height, std::vector blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): + span(uint64_t start_block_height, std::vector blocks, const connection_id_t& connection_id, float rate, size_t size): start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time{std::chrono::steady_clock::now()} {} - span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, std::chrono::steady_clock::time_point time): + span(uint64_t start_block_height, uint64_t nblocks, const connection_id_t& connection_id, std::chrono::steady_clock::time_point time): start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time) {} bool operator<(const span &s) const { return start_block_height < s.start_block_height; } @@ -71,29 +73,29 @@ namespace cryptonote typedef std::set block_map; public: - void add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); - void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, std::chrono::steady_clock::time_point time); - void flush_spans(const boost::uuids::uuid &connection_id, bool all = false); - void flush_stale_spans(const std::set &live_connections); + void add_blocks(uint64_t height, std::vector bcel, const connection_id_t& connection_id, float rate, size_t size); + void add_blocks(uint64_t height, uint64_t nblocks, const connection_id_t& connection_id, std::chrono::steady_clock::time_point time); + void flush_spans(const connection_id_t& connection_id, bool all = false); + void flush_stale_spans(const std::set& live_connections); bool remove_span(uint64_t start_block_height, std::vector *hashes = nullptr); - void remove_spans(const boost::uuids::uuid &connection_id, uint64_t start_block_height); + void remove_spans(const connection_id_t& connection_id, uint64_t start_block_height); uint64_t get_max_block_height() const; void print() const; std::string get_overview(uint64_t blockchain_height) const; bool has_unpruned_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) const; - std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector &block_hashes); + std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const connection_id_t& connection_id, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector &block_hashes); uint64_t get_next_needed_height(uint64_t blockchain_height) const; - std::pair get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id) const; + std::pair get_next_span_if_scheduled(std::vector &hashes, connection_id_t& connection_id) const; void reset_next_span_time(); - void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes); - bool get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; - bool has_next_span(uint64_t height, bool &filled, std::chrono::steady_clock::time_point& time, boost::uuids::uuid &connection_id) const; + void set_span_hashes(uint64_t start_height, const connection_id_t& connection_id, std::vector hashes); + bool get_next_span(uint64_t &height, std::vector &bcel, connection_id_t& connection_id, bool filled = true) const; + bool has_next_span(uint64_t height, bool &filled, std::chrono::steady_clock::time_point& time, connection_id_t& connection_id) const; size_t get_data_size() const; size_t get_num_filled_spans() const; - crypto::hash get_last_known_hash(const boost::uuids::uuid &connection_id) const; - bool has_spans(const boost::uuids::uuid &connection_id) const; - float get_speed(const boost::uuids::uuid &connection_id) const; - float get_download_rate(const boost::uuids::uuid &connection_id) const; + crypto::hash get_last_known_hash(const connection_id_t &connection_id) const; + bool has_spans(const connection_id_t &connection_id) const; + float get_speed(const connection_id_t &connection_id) const; + float get_download_rate(const connection_id_t &connection_id) const; bool foreach(std::function f) const; bool requested(const crypto::hash &hash) const; bool have(const crypto::hash &hash) const; diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.cpp b/src/cryptonote_protocol/cryptonote_protocol_defs.cpp index 73739338424..5bf65468efd 100755 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.cpp @@ -2,43 +2,43 @@ namespace cryptonote { -KV_SERIALIZE_MAP_CODE_BEGIN(connection_info) - KV_SERIALIZE(incoming) - KV_SERIALIZE(localhost) - KV_SERIALIZE(local_ip) - KV_SERIALIZE(address) - KV_SERIALIZE(host) - KV_SERIALIZE(ip) - KV_SERIALIZE(port) - KV_SERIALIZE(rpc_port) - KV_SERIALIZE(peer_id) - KV_SERIALIZE(recv_count) - uint64_t recv_idle_time, send_idle_time, live_time; - if (is_store) { - recv_idle_time = std::chrono::duration_cast(this_ref.recv_idle_time).count(); - send_idle_time = std::chrono::duration_cast(this_ref.send_idle_time).count(); - live_time = std::chrono::duration_cast(this_ref.live_time).count(); - } - KV_SERIALIZE_VALUE(recv_idle_time) - KV_SERIALIZE(send_count) - KV_SERIALIZE_VALUE(send_idle_time) - KV_SERIALIZE(state) - KV_SERIALIZE_VALUE(live_time) - if constexpr (!is_store) { - this_ref.recv_idle_time = std::chrono::seconds{recv_idle_time}; - this_ref.send_idle_time = std::chrono::seconds{send_idle_time}; - this_ref.live_time = std::chrono::seconds{live_time}; - } - KV_SERIALIZE(avg_download) - KV_SERIALIZE(current_download) - KV_SERIALIZE(avg_upload) - KV_SERIALIZE(current_upload) - KV_SERIALIZE(support_flags) - KV_SERIALIZE(connection_id) - KV_SERIALIZE(height) - KV_SERIALIZE(pruning_seed) - KV_SERIALIZE(address_type) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(connection_info) +// KV_SERIALIZE(incoming) +// KV_SERIALIZE(localhost) +// KV_SERIALIZE(local_ip) +// KV_SERIALIZE(address) +// KV_SERIALIZE(host) +// KV_SERIALIZE(ip) +// KV_SERIALIZE(port) +// KV_SERIALIZE(rpc_port) +// KV_SERIALIZE(peer_id) +// KV_SERIALIZE(recv_count) +// uint64_t recv_idle_time, send_idle_time, live_time; +// if (is_store) { +// recv_idle_time = std::chrono::duration_cast(this_ref.recv_idle_time).count(); +// send_idle_time = std::chrono::duration_cast(this_ref.send_idle_time).count(); +// live_time = std::chrono::duration_cast(this_ref.live_time).count(); +// } +// KV_SERIALIZE_VALUE(recv_idle_time) +// KV_SERIALIZE(send_count) +// KV_SERIALIZE_VALUE(send_idle_time) +// KV_SERIALIZE(state) +// KV_SERIALIZE_VALUE(live_time) +// if constexpr (!is_store) { +// this_ref.recv_idle_time = std::chrono::seconds{recv_idle_time}; +// this_ref.send_idle_time = std::chrono::seconds{send_idle_time}; +// this_ref.live_time = std::chrono::seconds{live_time}; +// } +// KV_SERIALIZE(avg_download) +// KV_SERIALIZE(current_download) +// KV_SERIALIZE(avg_upload) +// KV_SERIALIZE(current_upload) +// KV_SERIALIZE(support_flags) +// KV_SERIALIZE(connection_id) +// KV_SERIALIZE(height) +// KV_SERIALIZE(pruning_seed) +// KV_SERIALIZE(address_type) +// KV_SERIALIZE_MAP_CODE_END() KV_SERIALIZE_MAP_CODE_BEGIN(serializable_flash_metadata) KV_SERIALIZE_VAL_POD_AS_BLOB_N(tx_hash, "#") @@ -76,7 +76,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(CORE_SYNC_DATA) KV_SERIALIZE(current_height) KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) - KV_SERIALIZE_OPT(top_version, (uint8_t)0) + KV_SERIALIZE_ENUM(top_version) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) KV_SERIALIZE(flash_blocks) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(flash_hash) diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 7765777484d..1e733cd40f4 100755 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -50,7 +50,7 @@ namespace cryptonote #define BC_COMMANDS_POOL_BASE 2000 /************************************************************************/ - /* P2P connection info, serializable to json */ + /* P2P connection info */ /************************************************************************/ struct connection_info { @@ -63,7 +63,6 @@ namespace cryptonote std::string host; std::string ip; std::string port; - uint16_t rpc_port; std::string peer_id; @@ -83,8 +82,6 @@ namespace cryptonote uint64_t avg_upload; uint64_t current_upload; - uint32_t support_flags; - std::string connection_id; uint64_t height; @@ -92,8 +89,6 @@ namespace cryptonote uint32_t pruning_seed; uint8_t address_type; - - KV_MAP_SERIALIZABLE }; /************************************************************************/ @@ -174,7 +169,7 @@ namespace cryptonote uint64_t current_height; uint64_t cumulative_difficulty; crypto::hash top_id; - uint8_t top_version; + hf top_version; uint32_t pruning_seed; std::vector flash_blocks; std::vector flash_hash; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 5e473089bf4..e9c846bf414 100755 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -62,9 +62,8 @@ namespace cryptonote class t_cryptonote_protocol_handler: public i_cryptonote_protocol { public: - typedef cryptonote_connection_context connection_context; - typedef t_cryptonote_protocol_handler cryptonote_protocol_handler; - typedef CORE_SYNC_DATA payload_type; + using connection_context = cryptonote_connection_context; + using cryptonote_protocol_handler = t_cryptonote_protocol_handler; t_cryptonote_protocol_handler(t_core& rcore, bool offline = false); @@ -129,8 +128,8 @@ namespace cryptonote bool relay_to_synchronized_peers(typename T::request& arg, cryptonote_connection_context& exclude_context) { LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << tools::type_name() << " -->"); - std::vector> connections; - m_p2p->for_each_connection([&exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) + std::vector> connections; + m_p2p->for_each_connection([&exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id) { if (context.m_state > cryptonote_connection_context::state_synchronizing) { @@ -186,7 +185,7 @@ namespace cryptonote tools::periodic_task m_sync_search_checker{101s}; std::atomic m_max_out_peers; tools::PerformanceTimer m_sync_timer, m_add_timer; - uint64_t m_last_add_end_time; + std::optional m_last_add_end_time; uint64_t m_sync_spans_downloaded, m_sync_old_spans_downloaded, m_sync_bad_spans_downloaded; uint64_t m_sync_download_chain_size, m_sync_download_objects_size; size_t m_block_download_max_size; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b599768f3ac..06306fd6e0d 100755 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -36,18 +36,18 @@ // (may contain code and/or modifications by other developers) // developer rfree: this code is caller of our new network code, and is modded; e.g. for rate limiting -#include -#include #include #include #include +#include +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "common/string_util.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" #include "cryptonote_basic/verification_context.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/tx_pool.h" -#include "epee/profile_tools.h" #include "epee/net/network_throttle-detail.hpp" #include "common/pruning.h" #include "common/random.h" @@ -106,7 +106,7 @@ namespace cryptonote m_sync_timer.reset(); m_add_timer.pause(); m_add_timer.reset(); - m_last_add_end_time = 0; + m_last_add_end_time = std::nullopt; m_sync_spans_downloaded = 0; m_sync_old_spans_downloaded = 0; m_sync_bad_spans_downloaded = 0; @@ -211,7 +211,6 @@ namespace cryptonote ss << std::setw(30) << std::left << "Remote Host" << std::setw(20) << "Peer id" - << std::setw(20) << "Support Flags" << std::setw(30) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" << std::setw(20) << "Livetime(sec)" @@ -221,15 +220,14 @@ namespace cryptonote << std::setw(13) << "Up(now)" << "\n"; - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) { bool local_ip = cntxt.m_remote_address.is_local(); const auto now = std::chrono::steady_clock::now(); seconds_f connection_time{now - cntxt.m_started}; ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + cntxt.m_remote_address.str() - << std::setw(20) << nodetool::peerid_to_string(peer_id) - << std::setw(20) << std::hex << support_flags + << std::setw(20) << fmt::format("{:016x}", peer_id) << std::setw(30) << std::to_string(cntxt.m_recv_cnt) + "(" + std::to_string(tools::to_seconds(now - cntxt.m_last_recv)) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(tools::to_seconds(now - cntxt.m_last_send)) + ")" << std::setw(25) << get_protocol_state_string(cntxt.m_state) @@ -270,7 +268,7 @@ namespace cryptonote { std::list connections; - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) { connection_info cnx; auto now = std::chrono::steady_clock::now(); @@ -286,11 +284,7 @@ namespace cryptonote cnx.ip = cnx.host; cnx.port = std::to_string(cntxt.m_remote_address.as().port()); } - cnx.rpc_port = cntxt.m_rpc_port; - - cnx.peer_id = nodetool::peerid_to_string(peer_id); - - cnx.support_flags = support_flags; + cnx.peer_id = fmt::format("{:016x}", peer_id); cnx.live_time = std::chrono::duration_cast(now - cntxt.m_started); cnx.recv_idle_time = std::chrono::duration_cast(now - std::max(cntxt.m_started, cntxt.m_last_recv)); @@ -313,12 +307,12 @@ namespace cryptonote else { - cnx.avg_download = cntxt.m_recv_cnt / connection_time.count() / 1024; - cnx.avg_upload = cntxt.m_send_cnt / connection_time.count() / 1024; + cnx.avg_download = cntxt.m_recv_cnt / connection_time.count(); + cnx.avg_upload = cntxt.m_send_cnt / connection_time.count(); } - cnx.current_download = cntxt.m_current_speed_down / 1024; - cnx.current_upload = cntxt.m_current_speed_up / 1024; + cnx.current_download = cntxt.m_current_speed_down; + cnx.current_upload = cntxt.m_current_speed_up; cnx.connection_id = tools::type_to_hex(cntxt.m_connection_id); @@ -347,7 +341,7 @@ namespace cryptonote if (hshd.current_height > 0) { auto nettype = m_core.get_nettype(); - const uint8_t version = get_network_version(nettype, hshd.current_height - 1); + const hf version = get_network_version(nettype, hshd.current_height - 1); if (version != hshd.top_version) { if (version < hshd.top_version && version == get_network_version(nettype, m_core.get_current_blockchain_height())) @@ -362,7 +356,7 @@ namespace cryptonote if (hshd.pruning_seed) { const uint32_t log_stripes = tools::get_pruning_log_stripes(hshd.pruning_seed); - if (log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES || tools::get_pruning_stripe(hshd.pruning_seed) > (1u << log_stripes)) + if (log_stripes != PRUNING_LOG_STRIPES || tools::get_pruning_stripe(hshd.pruning_seed) > (1u << log_stripes)) { MINFO("ERROR " << context << " peer claim unexpected pruning seed " << epee::string_tools::to_string_hex(hshd.pruning_seed) << ", disconnecting"); return false; @@ -371,11 +365,10 @@ namespace cryptonote context.m_remote_blockchain_height = hshd.current_height; context.m_pruning_seed = hshd.pruning_seed; -#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED - context.m_pruning_seed = tools::make_pruning_seed(1 + (context.m_remote_address.as().ip()) % (1 << CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES); - LOG_INFO_CC(context, "New connection posing as pruning seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << ", seed address " << &context.m_pruning_seed); -#endif - + if constexpr (PRUNING_DEBUG_SPOOF_SEED) { + context.m_pruning_seed = tools::make_pruning_seed(1 + (context.m_remote_address.as().ip()) % (1 << PRUNING_LOG_STRIPES), PRUNING_LOG_STRIPES); + LOG_INFO_CC(context, "New connection posing as pruning seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << ", seed address " << &context.m_pruning_seed); + } // No chain synchronization over hidden networks (tor, i2p, etc.) if(context.m_remote_address.get_zone() != epee::net_utils::zone::public_) { @@ -387,7 +380,7 @@ namespace cryptonote context.m_need_flash_sync = false; // Check for any flash txes being advertised that we don't know about - if (is_hard_fork_at_least(m_core.get_nettype(), HF_VERSION_FLASH, curr_height)) + if (is_hard_fork_at_least(m_core.get_nettype(), feature::FLASH, curr_height)) { if (hshd.flash_blocks.size() != hshd.flash_hash.size()) { @@ -468,19 +461,19 @@ namespace cryptonote auto nettype = m_core.get_nettype(); LOG_PRINT_CCONTEXT_L0("process_payload_sync_data hard_fork_begins"); - auto hf17 = hard_fork_begins(nettype, cryptonote::network_version_17_POS); + auto hf17 = hard_fork_begins(nettype, hf::hf17_POS); LOG_PRINT_CCONTEXT_L0("process_payload_sync_data hf17?"); if (hf17) { LOG_PRINT_CCONTEXT_L0("process_payload_sync_data hf17"); - std::chrono::seconds behindtime = 0 * TARGET_BLOCK_TIME_OLD; + std::chrono::seconds behindtime = 0 * old::TARGET_BLOCK_TIME_12; int64_t diff = static_cast(hshd.current_height) - static_cast(curr_height); uint64_t abs_diff = std::abs(diff); if (curr_height<*hf17){ LOG_PRINT_CCONTEXT_L0("process_payload_sync_data curr_height(*hf17) - static_cast(curr_height); - behindtime = old_diff * TARGET_BLOCK_TIME_OLD; + behindtime = old_diff * old::TARGET_BLOCK_TIME_12; uint64_t max_block_height = std::max(hshd.current_height, curr_height); behindtime = behindtime + ((max_block_height - *hf17) * TARGET_BLOCK_TIME); } else{ @@ -512,7 +505,7 @@ namespace cryptonote m_sync_timer.reset(); m_add_timer.pause(); m_add_timer.reset(); - m_last_add_end_time = 0; + m_last_add_end_time = std::nullopt; m_sync_spans_downloaded = 0; m_sync_old_spans_downloaded = 0; m_sync_bad_spans_downloaded = 0; @@ -555,7 +548,7 @@ namespace cryptonote template bool t_cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { - m_core.get_blockchain_top(hshd.current_height, hshd.top_id); + std::tie(hshd.current_height, hshd.top_id) = m_core.get_blockchain_top(); hshd.top_version = get_network_version(m_core.get_nettype(), hshd.current_height); hshd.cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height); hshd.current_height +=1; @@ -760,9 +753,9 @@ namespace cryptonote { std::vector tx_ids; std::vector txes; - std::vector missing; + std::unordered_set missing; tx_ids.push_back(tx_hash); - if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty()) + if (m_core.get_transactions(tx_ids, txes, &missing) && missing.empty()) { if (txes.size() == 1) { @@ -1052,8 +1045,8 @@ namespace cryptonote } std::vector txs; - std::vector missed; - if (!m_core.get_transactions(txids, txs, missed)) + std::unordered_set missed; + if (!m_core.get_transactions(txids, txs, &missed)) { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " << "failed to get requested transactions"); @@ -1425,7 +1418,7 @@ namespace cryptonote { m_add_timer.pause(); m_core.resume_mine(); - if (!starting) m_last_add_end_time = epee::misc_utils::get_ns_count(); + if (!starting) m_last_add_end_time = std::chrono::steady_clock::now(); }; while (1) @@ -1433,7 +1426,7 @@ namespace cryptonote const uint64_t previous_height = m_core.get_current_blockchain_height(); uint64_t start_height; std::vector blocks; - boost::uuids::uuid span_connection_id; + connection_id_t span_connection_id; if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) { MDEBUG(context << " no next span found, going back to download"); @@ -1523,8 +1516,8 @@ namespace cryptonote starting = false; if (m_last_add_end_time) { - const uint64_t ns = epee::misc_utils::get_ns_count() - m_last_add_end_time; - MINFO("Restarting adding block after idle for " << ns/1e9 << " seconds"); + auto elapsed = std::chrono::steady_clock::now() - *m_last_add_end_time; + MINFO("Restarting adding block after idle for " << tools::friendly_duration(elapsed)); } } @@ -1553,7 +1546,8 @@ namespace cryptonote return 1; } - uint64_t block_process_time_full = 0, transactions_process_time_full = 0; + auto block_process_time_full = 0ns; + auto transactions_process_time_full = 0ns; size_t num_txs = 0, blockidx = 0; for(const block_complete_entry& block_entry: blocks) { @@ -1561,7 +1555,7 @@ namespace cryptonote return 1; // process transactions - TIME_MEASURE_START(transactions_process_time); + auto transactions_process_start = std::chrono::steady_clock::now(); num_txs += block_entry.txs.size(); auto parsed_txs = m_core.handle_incoming_txs(block_entry.txs, tx_pool_options::from_block()); @@ -1569,7 +1563,7 @@ namespace cryptonote { if (parsed_txs[i].tvc.m_verifivation_failed) { - if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id) -> bool{ cryptonote::transaction tx; parse_and_validate_tx_from_blob(block_entry.txs[i], tx); // must succeed if we got here LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_BLOCKS, tx_id = " @@ -1583,8 +1577,7 @@ namespace cryptonote return 1; } } - TIME_MEASURE_FINISH(transactions_process_time); - transactions_process_time_full += transactions_process_time; + transactions_process_time_full += std::chrono::steady_clock::now() - transactions_process_start; // // NOTE: Checkpoint parsing @@ -1608,14 +1601,14 @@ namespace cryptonote // process block - TIME_MEASURE_START(block_process_time); + auto block_process_start = std::chrono::steady_clock::now(); block_verification_context bvc{}; m_core.handle_incoming_block(block_entry.block, pblocks.empty() ? NULL : &pblocks[blockidx], bvc, checkpoint, false); // <--- process block if (bvc.m_verifivation_failed || bvc.m_marked_as_orphaned) { - if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool{ char const *ERR_MSG = bvc.m_verifivation_failed ? "Block verification failed, dropping connection" @@ -1631,14 +1624,16 @@ namespace cryptonote return 1; } - TIME_MEASURE_FINISH(block_process_time); - block_process_time_full += block_process_time; + block_process_time_full += std::chrono::steady_clock::now() - block_process_start; ++blockidx; } // each download block remove_spans = true; - MDEBUG(context << "Block process time (" << blocks.size() << " blocks, " << num_txs << " txs): " << block_process_time_full + transactions_process_time_full << " (" << transactions_process_time_full << "/" << block_process_time_full << ") ms"); + MDEBUG(context << "Block process time (" << blocks.size() << " blocks, " << num_txs << " txs): " << + tools::friendly_duration(block_process_time_full + transactions_process_time_full) << " (" << + tools::friendly_duration(transactions_process_time_full) << "+" << + tools::friendly_duration(block_process_time_full) << ")"); } const uint64_t current_blockchain_height = m_core.get_current_blockchain_height(); @@ -1664,8 +1659,8 @@ namespace cryptonote } progress_message += ")"; } - const uint32_t previous_stripe = tools::get_pruning_stripe(previous_height, target_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t current_stripe = tools::get_pruning_stripe(current_blockchain_height, target_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); + const uint32_t previous_stripe = tools::get_pruning_stripe(previous_height, target_blockchain_height, PRUNING_LOG_STRIPES); + const uint32_t current_stripe = tools::get_pruning_stripe(current_blockchain_height, target_blockchain_height, PRUNING_LOG_STRIPES); std::string timing_message = ""; if (ELPP->vRegistry()->allowed(el::Level::Info, "sync-info")) timing_message = std::string(" (") + std::to_string(dt.count()) + " sec, " @@ -1713,7 +1708,7 @@ skip: template void t_cryptonote_protocol_handler::notify_new_stripe(cryptonote_connection_context& cntxt, uint32_t stripe) { - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool { if (cntxt.m_connection_id == context.m_connection_id) return true; @@ -1773,7 +1768,7 @@ skip: bool t_cryptonote_protocol_handler::kick_idle_peers() { MTRACE("Checking for idle peers..."); - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool { if (context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time) { @@ -1803,8 +1798,8 @@ skip: MTRACE("Checking for outgoing syncing peers..."); unsigned n_syncing = 0, n_synced = 0; - boost::uuids::uuid last_synced_peer_id(boost::uuids::nil_uuid()); - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + connection_id_t last_synced_peer_id{}; + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool { if (!peer_id || context.m_is_income) // only consider connected outgoing peers return true; @@ -1821,9 +1816,9 @@ skip: MTRACE(n_syncing << " syncing, " << n_synced << " synced"); // if we're at max out peers, and not enough are syncing - if (n_synced + n_syncing >= m_max_out_peers && n_syncing < P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT && last_synced_peer_id != boost::uuids::nil_uuid()) + if (n_synced + n_syncing >= m_max_out_peers && n_syncing < p2p::DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT && !last_synced_peer_id.is_nil()) { - if (!m_p2p->for_connection(last_synced_peer_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{ + if (!m_p2p->for_connection(last_synced_peer_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id)->bool{ MINFO(ctx << "dropping synced peer, " << n_syncing << " syncing, " << n_synced << " synced"); drop_connection(ctx, false, false); return true; @@ -1837,7 +1832,7 @@ skip: template bool t_cryptonote_protocol_handler::check_standby_peers() { - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool { if (context.m_state == cryptonote_connection_context::state_standby) { @@ -1870,7 +1865,7 @@ skip: bool t_cryptonote_protocol_handler::should_download_next_span(cryptonote_connection_context& context, bool standby) { std::chrono::steady_clock::time_point request_time; - boost::uuids::uuid connection_id; + connection_id_t connection_id{}; std::pair span; bool filled; @@ -1902,7 +1897,7 @@ skip: if (standby && dt >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY && dl_speed > 0) { bool download = false; - if (m_p2p->for_connection(connection_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{ + if (m_p2p->for_connection(connection_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id)->bool{ const auto last_activity = std::min(now - ctx.m_last_recv, dt); const bool stalled = last_activity > LAST_ACTIVITY_STALL_THRESHOLD; if (stalled) @@ -1992,14 +1987,14 @@ skip: if (next_stripe > 0) { unsigned int n_out_peers = 0, n_peers_on_next_stripe = 0; - m_p2p->for_each_connection([&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + m_p2p->for_each_connection([&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id)->bool{ if (!ctx.m_is_income) ++n_out_peers; if (ctx.m_state >= cryptonote_connection_context::state_synchronizing && tools::get_pruning_stripe(ctx.m_pruning_seed) == next_stripe) ++n_peers_on_next_stripe; return true; }); - const uint32_t distance = (peer_stripe + (1<= m_max_out_peers && n_peers_on_next_stripe == 0) || (distance > 1 && n_peers_on_next_stripe <= 2) || distance > 2) { MDEBUG(context << "we want seed " << next_stripe << ", and either " << n_out_peers << " is at max out peers (" @@ -2036,8 +2031,8 @@ skip: bool t_cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks, bool force_next_span) { // flush stale spans - std::set live_connections; - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + std::set live_connections; + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool{ live_connections.insert(context.m_connection_id); return true; }); @@ -2053,7 +2048,7 @@ skip: size_t size = m_block_queue.get_data_size(); const uint64_t bc_height = m_core.get_current_blockchain_height(); const auto next_needed_pruning_stripe = get_next_needed_pruning_stripe(); - const uint32_t add_stripe = tools::get_pruning_stripe(bc_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); + const uint32_t add_stripe = tools::get_pruning_stripe(bc_height, context.m_remote_blockchain_height, PRUNING_LOG_STRIPES); const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD; bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold; @@ -2068,7 +2063,7 @@ skip: bool stripe_proceed_main = (add_stripe == 0 || peer_stripe == 0 || add_stripe == peer_stripe) && (next_block_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS || next_needed_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS); bool stripe_proceed_secondary = tools::has_unpruned_block(next_block_height, context.m_remote_blockchain_height, context.m_pruning_seed); bool proceed = stripe_proceed_main || (queue_proceed && stripe_proceed_secondary); - if (!stripe_proceed_main && !stripe_proceed_secondary && should_drop_connection(context, tools::get_pruning_stripe(next_block_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES))) + if (!stripe_proceed_main && !stripe_proceed_secondary && should_drop_connection(context, tools::get_pruning_stripe(next_block_height, context.m_remote_blockchain_height, PRUNING_LOG_STRIPES))) { if (!context.m_is_income) m_p2p->add_used_stripe_peer(context); @@ -2109,7 +2104,7 @@ skip: { bool filled = false; std::chrono::steady_clock::time_point time; - boost::uuids::uuid connection_id; + connection_id_t connection_id; if (m_block_queue.has_next_span(m_core.get_current_blockchain_height(), filled, time, connection_id) && filled) { LOG_DEBUG_CC(context, "No other thread is adding blocks, and next span needed is ready, resuming"); @@ -2126,7 +2121,7 @@ skip: // if this has gone on for too long, drop incoming connection to guard against some wedge state if (!context.m_is_income) { - std::chrono::nanoseconds ns{epee::misc_utils::get_ns_count() - m_last_add_end_time}; + auto ns = std::chrono::steady_clock::now() - m_last_add_end_time.value_or(std::chrono::steady_clock::time_point::min()); if (ns >= DROP_ON_SYNC_WEDGE_THRESHOLD) { MDEBUG(context << "Block addition seems to have wedged, dropping connection"); @@ -2167,7 +2162,7 @@ skip: if (span.second == 0) { std::vector hashes; - boost::uuids::uuid span_connection_id; + connection_id_t span_connection_id; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id); if (span.second > 0) { @@ -2198,7 +2193,7 @@ skip: MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); if (span.second > 0) { - const uint32_t stripe = tools::get_pruning_stripe(span.first, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); + const uint32_t stripe = tools::get_pruning_stripe(span.first, context.m_remote_blockchain_height, PRUNING_LOG_STRIPES); if (context.m_pruning_seed && stripe != tools::get_pruning_stripe(context.m_pruning_seed)) { MDEBUG(context << " starting early on next seed (" << span.first << " with stripe " << stripe << @@ -2210,7 +2205,7 @@ skip: { MDEBUG(context << " still no span reserved, we may be in the corner case of next span scheduled and everything else scheduled/filled"); std::vector hashes; - boost::uuids::uuid span_connection_id; + connection_id_t span_connection_id; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id); if (span.second > 0 && !tools::has_unpruned_block(span.first, context.m_remote_blockchain_height, context.m_pruning_seed)) span = std::make_pair(0, 0); @@ -2305,7 +2300,7 @@ skip: { uint64_t start_height; std::vector blocks; - boost::uuids::uuid span_connection_id; + connection_id_t span_connection_id; bool filled = false; if (m_block_queue.get_next_span(start_height, blocks, span_connection_id, filled) && filled) { @@ -2392,18 +2387,20 @@ skip: << "Use the \"help\" command to see the list of available commands.\n" << "**********************************************************************"); m_sync_timer.pause(); - if (ELPP->vRegistry()->allowed(el::Level::Info, "sync-info")) + if (CLOG_ENABLED(Info, "sync-info")) { - const uint64_t sync_time = m_sync_timer.value(); - const uint64_t add_time = m_add_timer.value(); - if (sync_time && add_time) + const auto sync_time = m_sync_timer.value(); + const auto add_time = m_add_timer.value(); + if (sync_time > 0ns && add_time > 0ns) { - MCLOG_YELLOW(el::Level::Info, "sync-info", "Sync time: " << sync_time/1e9/60 << " min, idle time " << - (100.f * (1.0f - add_time / (float)sync_time)) << "%" << ", " << - (10 * m_sync_download_objects_size / 1024 / 1024) / 10.f << " + " << - (10 * m_sync_download_chain_size / 1024 / 1024) / 10.f << " MB downloaded, " << - 100.0f * m_sync_old_spans_downloaded / m_sync_spans_downloaded << "% old spans, " << - 100.0f * m_sync_bad_spans_downloaded / m_sync_spans_downloaded << "% bad spans"); + MCLOG_YELLOW(el::Level::Info, "sync-info", + fmt::format("Sync time: {}, idle time {:.2f}%, {:.1f} + {:.1f} MB downloaded, {:.2f}% old spans, {:2f}% bad spans", + tools::friendly_duration(sync_time), + ((sync_time - add_time) / sync_time) * 100.0, + m_sync_download_objects_size / 1'000'000.0, + m_sync_download_chain_size / 1'000'000.0, + 100.0 * m_sync_old_spans_downloaded / m_sync_spans_downloaded, + 100.0 * m_sync_bad_spans_downloaded / m_sync_spans_downloaded)); } } m_core.on_synchronized(); @@ -2417,7 +2414,7 @@ skip: size_t t_cryptonote_protocol_handler::get_synchronizing_connections_count() { size_t count = 0; - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool{ if(context.m_state == cryptonote_connection_context::state_synchronizing) ++count; return true; @@ -2450,7 +2447,7 @@ skip: } MDEBUG(context << "first block hash " << arg.m_block_ids.front() << ", last " << arg.m_block_ids.back()); - if (arg.total_height >= CRYPTONOTE_MAX_BLOCK_NUMBER || arg.m_block_ids.size() >= CRYPTONOTE_MAX_BLOCK_NUMBER) + if (arg.total_height >= MAX_BLOCK_NUMBER || arg.m_block_ids.size() >= MAX_BLOCK_NUMBER) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with total_height=" << arg.total_height << " and block_ids=" << arg.m_block_ids.size()); drop_connection(context, false, false); @@ -2546,8 +2543,8 @@ skip: bool t_cryptonote_protocol_handler::relay_block(NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& exclude_context) { // sort peers between fluffy ones and others - std::vector> fluffyConnections; - m_p2p->for_each_connection([&exclude_context, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) + std::vector> fluffyConnections; + m_p2p->for_each_connection([&exclude_context, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id) { if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_) { @@ -2623,7 +2620,7 @@ skip: // flash data that got sent to us (we may have additional flash info, or may have rejected some // of the incoming flash data). arg.flashes.clear(); - if (is_hard_fork_at_least(m_core.get_nettype(), HF_VERSION_FLASH, m_core.get_current_blockchain_height())) + if (is_hard_fork_at_least(m_core.get_nettype(), feature::FLASH, m_core.get_current_blockchain_height())) { auto &pool = m_core.get_pool(); auto lock = pool.flash_shared_lock(); @@ -2648,7 +2645,7 @@ skip: { std::ostringstream ss; const auto now = std::chrono::steady_clock::now(); - m_p2p->for_each_connection([&](const connection_context &ctx, nodetool::peerid_type peer_id, uint32_t support_flags) { + m_p2p->for_each_connection([&](const connection_context &ctx, nodetool::peerid_type peer_id) { const uint32_t stripe = tools::get_pruning_stripe(ctx.m_pruning_seed); char state_char = cryptonote::get_protocol_state_char(ctx.m_state); ss << stripe + state_char; @@ -2669,14 +2666,14 @@ skip: uint64_t blockchain_height = m_core.get_target_blockchain_height(); // if we don't know the remote chain size yet, assume infinitely large so we get the right stripe if we're not near the tip if (blockchain_height == 0) - blockchain_height = CRYPTONOTE_MAX_BLOCK_NUMBER; - const uint32_t next_pruning_stripe = tools::get_pruning_stripe(want_height, blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES); + blockchain_height = MAX_BLOCK_NUMBER; + const uint32_t next_pruning_stripe = tools::get_pruning_stripe(want_height, blockchain_height, PRUNING_LOG_STRIPES); if (next_pruning_stripe == 0) return std::make_pair(0, 0); // if we already have a few peers on this stripe, but none on next one, try next one unsigned int n_next = 0, n_subsequent = 0, n_others = 0; - const uint32_t subsequent_pruning_stripe = 1 + next_pruning_stripe % (1<for_each_connection([&](const connection_context &context, nodetool::peerid_type peer_id, uint32_t support_flags) { + const uint32_t subsequent_pruning_stripe = 1 + next_pruning_stripe % (1<for_each_connection([&](const connection_context &context, nodetool::peerid_type peer_id) { if (context.m_state >= cryptonote_connection_context::state_synchronizing) { if (context.m_pruning_seed == 0 || tools::get_pruning_stripe(context.m_pruning_seed) == next_pruning_stripe) @@ -2694,7 +2691,7 @@ skip: want_height_from_blockchain << " from blockchain, " << want_height_from_block_queue << " from block queue), stripe " << next_pruning_stripe << " (" << n_next << "/" << m_max_out_peers << " on it and " << n_subsequent << " on " << subsequent_pruning_stripe << ", " << n_others << " others) -> " << ret_stripe << " (+" << - (ret_stripe - next_pruning_stripe + (1 << CRYPTONOTE_PRUNING_LOG_STRIPES)) % (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) << + (ret_stripe - next_pruning_stripe + (1 << PRUNING_LOG_STRIPES)) % (1 << PRUNING_LOG_STRIPES) << "), current peers " << po); return std::make_pair(next_pruning_stripe, ret_stripe); } @@ -2707,7 +2704,7 @@ skip: if (target && target <= height) return false; size_t n_out_peers = 0; - m_p2p->for_each_connection([&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + m_p2p->for_each_connection([&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id)->bool{ if (!ctx.m_is_income) ++n_out_peers; return true; @@ -2746,7 +2743,7 @@ skip: void t_cryptonote_protocol_handler::on_connection_close(cryptonote_connection_context &context) { uint64_t target = 0; - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) { MINFO("DEBUGconnection state:" << cntxt.m_state << " cntxtId:" << cntxt.m_connection_id << " context:"<= cryptonote_connection_context::state_synchronizing && cntxt.m_connection_id != context.m_connection_id){ target = std::max(target, cntxt.m_remote_blockchain_height); diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index b0f455d8b45..0b813a8c12d 100755 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -46,20 +46,13 @@ #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "net.p2p.tx" -namespace cryptonote -{ -namespace levin +namespace cryptonote::levin { + using epee::connection_id_t; namespace { constexpr std::size_t connection_id_reserve_size = 100; - constexpr const std::chrono::minutes noise_min_epoch{CRYPTONOTE_NOISE_MIN_EPOCH}; - constexpr const std::chrono::seconds noise_epoch_range{CRYPTONOTE_NOISE_EPOCH_RANGE}; - - constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY}; - constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE}; - /*! Select a randomized duration from 0 to `range`. The precision will be to the systems `steady_clock`. As an example, supplying 3 seconds to this function will select a duration from [0, 3] seconds, and the increments @@ -74,20 +67,20 @@ namespace levin } //! \return All outgoing connections supporting fragments in `connections`. - std::vector get_out_connections(connections& p2p) + + std::vector get_out_connections(connections &p2p) { - std::vector outs; - outs.reserve(connection_id_reserve_size); + std::vector outs; /* The foreach call is serialized with a lock, but should be quick due to the reserve call so a strand is not used. Investigate if there is lots of waiting in here. */ - p2p.foreach_connection([&outs] (detail::p2p_context& context) { + p2p.foreach_connection([&outs](detail::p2p_context &context) + { if (!context.m_is_income) outs.emplace_back(context.m_connection_id); - return true; - }); + return true; }); return outs; } @@ -168,7 +161,7 @@ namespace levin : queue(), strand(io_service), next_noise(io_service), - connection(boost::uuids::nil_uuid()) + connection{} {} // `asio::io_service::strand` cannot be copied or moved @@ -181,7 +174,7 @@ namespace levin std::deque queue; boost::asio::io_service::strand strand; boost::asio::steady_timer next_noise; - boost::uuids::uuid connection; + connection_id_t connection; }; } // anonymous @@ -199,7 +192,7 @@ namespace levin connection_count(0), is_public(is_public) { - for (std::size_t count = 0; !noise.view.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count) + for (std::size_t count = 0; !noise.view.empty() && count < NOISE_CHANNELS; ++count) channels.emplace_back(io_service); } @@ -254,10 +247,10 @@ namespace levin { std::shared_ptr zone_; epee::shared_sv message_; // Requires manual copy - boost::uuids::uuid source_; + connection_id_t source_; public: - explicit flood_notify(std::shared_ptr zone, epee::shared_sv message, const boost::uuids::uuid& source) + explicit flood_notify(std::shared_ptr zone, epee::shared_sv message, const connection_id_t& source) : zone_(std::move(zone)), message_(message), source_(source) {} @@ -279,7 +272,7 @@ namespace levin algorithm changes or the locking strategy within the levin config class changes. */ - std::vector connections; + std::vector connections; connections.reserve(connection_id_reserve_size); zone_->p2p->foreach_connection([this, &connections] (detail::p2p_context& context) { /* Only send to outgoing connections when "flooding" over i2p/tor. @@ -290,7 +283,7 @@ namespace levin return true; }); - for (const boost::uuids::uuid& connection : connections) + for (const connection_id_t& connection : connections) zone_->p2p->send(message_, connection); } }; @@ -300,7 +293,7 @@ namespace levin { std::shared_ptr zone_; const std::size_t channel_; - const boost::uuids::uuid connection_; + const connection_id_t connection_; //! \pre Called within `stem_.strand`. void operator()() const @@ -311,7 +304,7 @@ namespace levin noise_channel& channel = zone_->channels.at(channel_); assert(channel.strand.running_in_this_thread()); static_assert( - CRYPTONOTE_MAX_FRAGMENTS <= (noise_min_epoch / (noise_min_delay + noise_delay_range)), + MAX_FRAGMENTS <= static_cast(NOISE_MIN_EPOCH / (NOISE_MIN_DELAY + NOISE_DELAY_RANGE)), "Max fragments more than the max that can be sent in an epoch" ); @@ -332,7 +325,7 @@ namespace levin struct update_channels { std::shared_ptr zone_; - std::vector out_connections_; + std::vector out_connections_; //! \pre Called within `zone->strand`. static void post(std::shared_ptr zone) @@ -346,7 +339,7 @@ namespace levin for (auto id = zone->map.begin(); id != zone->map.end(); ++id) { const std::size_t i = id - zone->map.begin(); - zone->channels[i].strand.post(update_channel{zone, i, *id}); + zone->channels[i].strand.post(update_channel{zone, i, *id}, std::allocator{}); } } @@ -403,7 +396,7 @@ namespace levin return; noise_channel& channel = zone->channels.at(index); - channel.next_noise.expires_at(start + noise_min_delay + random_duration(noise_delay_range)); + channel.next_noise.expires_at(start + NOISE_MIN_DELAY + random_duration(NOISE_DELAY_RANGE)); channel.next_noise.async_wait( channel.strand.wrap(send_noise{std::move(zone), index}) ); @@ -444,13 +437,14 @@ namespace levin else { channel.active = {}; - channel.connection = boost::uuids::nil_uuid(); + channel.connection = {}; auto connections = get_out_connections(*zone_->p2p); if (connections.empty()) MWARNING("Lost all outbound connections to anonymity network - currently unable to send transaction(s)"); - zone_->strand.post(update_channels{zone_, std::move(connections)}); + zone_->strand.post( + update_channels{zone_, std::move(connections)}, std::allocator{}); } } @@ -478,8 +472,11 @@ namespace levin const auto start = std::chrono::steady_clock::now(); zone_->strand.dispatch( - change_channels{zone_, net::dandelionpp::connection_map{get_out_connections(*(zone_->p2p)), count_}} - ); + change_channels{ + zone_, + net::dandelionpp::connection_map{ + get_out_connections(*(zone_->p2p)), count_}}, + std::allocator{}); detail::zone& alias = *zone_; alias.next_epoch.expires_at(start + min_epoch_ + random_duration(epoch_range_)); @@ -497,7 +494,7 @@ namespace levin if (!zone_->noise.view.empty()) { const auto now = std::chrono::steady_clock::now(); - start_epoch{zone_, noise_min_epoch, noise_epoch_range, CRYPTONOTE_NOISE_CHANNELS}(); + start_epoch{zone_, NOISE_MIN_EPOCH, NOISE_EPOCH_RANGE, NOISE_CHANNELS}(); for (std::size_t channel = 0; channel < zone_->channels.size(); ++channel) send_noise::wait(now, zone_, channel); } @@ -511,17 +508,16 @@ namespace levin if (!zone_) return {false, false}; - return {!zone_->noise.view.empty(), CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count}; + return {!zone_->noise.view.empty(), NOISE_CHANNELS <= zone_->connection_count}; } void notify::new_out_connection() { - if (!zone_ || zone_->noise.view.empty() || CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count) + if (!zone_ || zone_->noise.view.empty() || NOISE_CHANNELS <= zone_->connection_count) return; zone_->strand.dispatch( - update_channels{zone_, get_out_connections(*(zone_->p2p))} - ); + update_channels{zone_, get_out_connections(*(zone_->p2p))}, std::allocator{}); } void notify::run_epoch() @@ -540,7 +536,7 @@ namespace levin channel.next_noise.cancel(); } - bool notify::send_txs(std::vector txs, const boost::uuids::uuid& source, const bool pad_txs) + bool notify::send_txs(std::vector txs, const connection_id_t& source, const bool pad_txs) { @@ -557,7 +553,7 @@ namespace levin MINFO("send_txs covert send in \"noise\" channel"); // covert send in "noise" channel static_assert( - CRYPTONOTE_MAX_FRAGMENTS * CRYPTONOTE_NOISE_BYTES <= LEVIN_DEFAULT_MAX_PACKET_SIZE, "most nodes will reject this fragment setting" + MAX_FRAGMENTS * NOISE_BYTES <= LEVIN_DEFAULT_MAX_PACKET_SIZE, "most nodes will reject this fragment setting" ); // padding is not useful when using noise mode @@ -565,7 +561,7 @@ namespace levin epee::shared_sv message{epee::levin::make_fragmented_notify( zone_->noise.view, NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan(payload) )}; - if (CRYPTONOTE_MAX_FRAGMENTS * zone_->noise.size() < message.size()) + if (MAX_FRAGMENTS * zone_->noise.size() < message.size()) { MERROR("notify::send_txs provided message exceeding covert fragment size"); return false; @@ -574,8 +570,7 @@ namespace levin for (std::size_t channel = 0; channel < zone_->channels.size(); ++channel) { zone_->channels[channel].strand.dispatch( - queue_covert_notify{zone_, message, channel} - ); + queue_covert_notify{zone_, message, channel}, std::allocator{}); } } else @@ -586,10 +581,10 @@ namespace levin epee::levin::make_notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan(payload))}; // traditional monero send technique - zone_->strand.dispatch(flood_notify{zone_, std::move(message), source}); + zone_->strand.dispatch( + flood_notify{zone_, std::move(message), source}, std::allocator{}); } return true; } -} // levin -} // net +} // cryptonote::levin diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 67a115b4bd1..3fec6b72d96 100755 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -28,8 +28,7 @@ #pragma once -#include -#include +#include #include #include @@ -37,6 +36,11 @@ #include "cryptonote_basic/blobdatatype.h" #include "epee/net/enums.h" #include "epee/span.h" +#include "epee/net/net_utils_base.h" + +namespace boost::asio { +using io_service = io_context; +} namespace epee { @@ -126,7 +130,7 @@ namespace levin construction. \return True iff the notification is queued for sending. */ - bool send_txs(std::vector txs, const boost::uuids::uuid& source, bool pad_txs); + bool send_txs(std::vector txs, const epee::connection_id_t& source, bool pad_txs); }; } // levin } // net diff --git a/src/cryptonote_protocol/quorumnet.cpp b/src/cryptonote_protocol/quorumnet.cpp index 7bcc4e8d113..da3956ac8ad 100755 --- a/src/cryptonote_protocol/quorumnet.cpp +++ b/src/cryptonote_protocol/quorumnet.cpp @@ -859,7 +859,7 @@ void handle_flash(Message& m, QnetState& qnet) { auto local_height = qnet.core.get_current_blockchain_height(); auto hf_version = get_network_version(qnet.core.get_nettype(), local_height); - if (hf_version < HF_VERSION_FLASH) { + if (hf_version < cryptonote::feature::FLASH) { MWARNING("Rejecting flash message: flash is not available for hardfork " << (int) hf_version); if (tag) m.send_back("bl.nostart", bt_serialize(bt_dict{{"!", tag}, {"e", "Invalid flash authorization height"sv}})); diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 7666a5a5521..ec76fe39870 100755 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -40,12 +40,12 @@ namespace daemon_args const command_line::arg_descriptor arg_config_file = { "config-file", "Specify configuration file", - "/" CRYPTONOTE_NAME ".conf"}; + "/" + std::string{cryptonote::CONF_FILENAME}}; const command_line::arg_descriptor arg_log_file = { "log-file", "Specify log file", - "/" CRYPTONOTE_NAME ".log"}; + "/" + std::string{cryptonote::LOG_FILENAME}}; const command_line::arg_descriptor arg_max_log_file_size = { "max-log-file-size" , "Specify maximum log file size [B]" diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 6492e3a3fb9..49077c1f21f 100755 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -31,6 +31,8 @@ #include "common/command_line.h" #include "common/hex.h" +#include "common/scoped_message_writer.h" +#include "common/string_util.h" #include "version.h" #include "daemon/command_parser_executor.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -40,21 +42,23 @@ namespace daemonize { -command_parser_executor::command_parser_executor(std::string daemon_url, const std::optional& login) - : m_executor{std::move(daemon_url), login} -{} - -command_parser_executor::command_parser_executor(cryptonote::rpc::core_rpc_server& rpc_server) - : m_executor{rpc_server} -{} +template +constexpr bool is_std_optional = false; +template +inline constexpr bool is_std_optional> = true; // Consumes an argument from the given list, if present, parsing it into `var`. // Returns false upon parse failure, true otherwise. template -static bool parse_if_present(std::forward_list &list, T &var, const char *name) +static bool parse_if_present(std::forward_list& list, T& var, const char* name) { if (list.empty()) return true; - if (epee::string_tools::get_xtype_from_string(var, list.front())) + bool good = false; + if constexpr (is_std_optional) + good = epee::string_tools::get_xtype_from_string(var.emplace(), list.front()); + else + good = epee::string_tools::get_xtype_from_string(var, list.front()); + if (good) { list.pop_front(); return true; @@ -64,10 +68,9 @@ static bool parse_if_present(std::forward_list &list, T &var, const return false; } -bool command_parser_executor::print_checkpoints(const std::vector &args) +bool command_parser_executor::print_checkpoints(const std::vector& args) { - uint64_t start_height = cryptonote::rpc::GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE; - uint64_t end_height = cryptonote::rpc::GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE; + std::optional start_height, end_height; std::forward_list args_list(args.begin(), args.end()); bool print_json = !args_list.empty() && args_list.front() == "+json"; @@ -95,7 +98,7 @@ bool command_parser_executor::print_checkpoints(const std::vector & bool command_parser_executor::print_mn_state_changes(const std::vector &args) { uint64_t start_height; - uint64_t end_height = cryptonote::rpc::GET_MN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE; + std::optional end_height; if (args.empty()) { std::cout << "Missing first argument start_height" << std::endl; @@ -108,7 +111,6 @@ bool command_parser_executor::print_mn_state_changes(const std::vector& args) { - if (args.size() > 3) - { - std::cout << "use: print_pl [white] [gray] [] [pruned] [publicrpc]" << std::endl; - return true; - } - bool white = false; bool gray = false; bool pruned = false; - bool publicrpc = false; size_t limit = 0; - for (size_t i = 0; i < args.size(); ++i) + for (const auto& arg : args) { - if (args[i] == "white") - { + if (arg == "white") white = true; - } - else if (args[i] == "gray") - { + else if (arg == "gray") gray = true; - } - else if (args[i] == "pruned") - { + else if (arg == "pruned") pruned = true; - } - else if (args[i] == "publicrpc") - { - publicrpc = true; - } - else if (!epee::string_tools::get_xtype_from_string(limit, args[i])) - { - std::cout << "unexpected argument: " << args[i] << std::endl; + else if (tools::parse_int(arg, limit)) + /*limit already set*/; + else { + std::cout << "Unexpected argument: " << arg << "\n"; return true; } } - const bool print_both = !white && !gray; - return m_executor.print_peer_list(white | print_both, gray | print_both, limit, pruned, publicrpc); + if (!white && !gray) + white = gray = true; + return m_executor.print_peer_list(white, gray, limit, pruned); } bool command_parser_executor::print_peer_list_stats(const std::vector& args) @@ -181,20 +168,6 @@ bool command_parser_executor::save_blockchain(const std::vector& ar return m_executor.save_blockchain(); } -bool command_parser_executor::show_hash_rate(const std::vector& args) -{ - if (!args.empty()) return false; - - return m_executor.show_hash_rate(); -} - -bool command_parser_executor::hide_hash_rate(const std::vector& args) -{ - if (!args.empty()) return false; - - return m_executor.hide_hash_rate(); -} - bool command_parser_executor::show_difficulty(const std::vector& args) { if (!args.empty()) return false; @@ -258,8 +231,8 @@ bool command_parser_executor::print_blockchain_info(const std::vector& args) { - uint64_t start_height = cryptonote::rpc::GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE; - uint64_t end_height = cryptonote::rpc::GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE; + std::optional start_height; + std::optional end_height; std::forward_list args_list(args.begin(), args.end()); if (!parse_if_present(args_list, start_height, "start height")) @@ -353,7 +326,23 @@ bool command_parser_executor::set_log_level(const std::vector& args } else { - return m_executor.set_log_categories(args.front()); + static std::unordered_map level_map = { + {"WARNING", "*:WARNING"}, {"INFO", "*:INFO"}, {"DEBUG", "*:DEBUG"}, {"TRACE", "*:TRACE"}, + {"*WARNING", "*:WARNING"}, {"*INFO", "*:INFO"}, {"*DEBUG", "*:DEBUG"}, {"*TRACE", "*:TRACE"}, + {":WARNING", "*:WARNING"}, {":INFO", "*:INFO"}, {":DEBUG", "*:DEBUG"}, {":TRACE", "*:TRACE"} + }; + + std::string input = args[0]; + std::transform(input.begin(), input.end(), input.begin(), ::toupper); + + auto it = level_map.find(input); + if (it != level_map.end()) + return m_executor.set_log_categories(it->second); + + if (args[0].find(':') != std::string::npos) return m_executor.set_log_categories(args.front()); + + std::cout << "Invalid log level. Use: 0-4, WARNING/INFO/DEBUG/TRACE, or category:level format" << std::endl; + return true; } } @@ -441,20 +430,18 @@ bool command_parser_executor::is_key_image_spent(const std::vector& { if (args.empty()) { - std::cout << "expected: is_key_image_spent " << std::endl; + tools::fail_msg_writer() << "Invalid arguments. Expected: is_key_image_spent [ ...]\n"; return true; } - const std::string& str = args.front(); - crypto::key_image ki; - crypto::hash hash; - if (tools::hex_to_type(str, hash)) - { - memcpy(&ki, &hash, sizeof(ki)); - m_executor.is_key_image_spent(ki); + std::vector kis; + for (const auto& hex : args) { + if (!tools::hex_to_type(hex, kis.emplace_back())) { + tools::fail_msg_writer() << "Invalid key image: '" << hex << "'"; + return true; + } } - else - MERROR("invalid key image hash: " << str); + m_executor.is_key_image_spent(kis); return true; } @@ -463,14 +450,14 @@ bool command_parser_executor::print_transaction_pool_long(const std::vector& args) { if (!args.empty()) return false; - return m_executor.print_transaction_pool_short(); + return m_executor.print_transaction_pool(false); } bool command_parser_executor::print_transaction_pool_stats(const std::vector& args) @@ -490,12 +477,12 @@ bool command_parser_executor::start_mining(const std::vector& args) cryptonote::address_parse_info info; cryptonote::network_type nettype; - if (cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, args.front())) - nettype = cryptonote::MAINNET; - else if (cryptonote::get_account_address_from_str(info, cryptonote::TESTNET, args.front())) - nettype = cryptonote::TESTNET; - else if (cryptonote::get_account_address_from_str(info, cryptonote::DEVNET, args.front())) - nettype = cryptonote::DEVNET; + if (cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, args.front())) + nettype = cryptonote::network_type::MAINNET; + else if (cryptonote::get_account_address_from_str(info, cryptonote::network_type::TESTNET, args.front())) + nettype = cryptonote::network_type::TESTNET; + else if (cryptonote::get_account_address_from_str(info, cryptonote::network_type::DEVNET, args.front())) + nettype = cryptonote::network_type::DEVNET; else { std::cout << "target account address has wrong format" << std::endl; @@ -506,30 +493,15 @@ bool command_parser_executor::start_mining(const std::vector& args) tools::fail_msg_writer() << "subaddress for mining reward is not yet supported!"; return true; } - if(nettype != cryptonote::MAINNET) - std::cout << "Mining to a " << (nettype == cryptonote::TESTNET ? "testnet" : "devnet") << " address, make sure this is intentional!"; std::string_view threads_val = tools::find_prefixed_value(args.begin() + 1, args.end(), "threads="sv); std::string_view num_blocks_val = tools::find_prefixed_value(args.begin() + 1, args.end(), "num_blocks="sv); - int threads_count = 1; - uint32_t num_blocks = 0; - if (threads_val.size()) + unsigned int threads_count = 1, num_blocks = 0; + if (threads_val.size() && !tools::parse_int(threads_val, threads_count)) { - if (threads_val == "auto"sv || threads_val == "autodetect"sv) - { - threads_count = 0; - } - else - { - if (!tools::parse_int(threads_val, threads_count)) - { - tools::fail_msg_writer() << "Failed to parse threads value" << threads_val; - return false; - } - - threads_count = 0 < threads_count ? threads_count : 1; - } + tools::fail_msg_writer() << "Failed to parse threads value" << threads_val; + return false; } if (num_blocks_val.size()) tools::parse_int(num_blocks_val, num_blocks); @@ -556,65 +528,34 @@ bool command_parser_executor::stop_daemon(const std::vector& args) return m_executor.stop_daemon(); } -bool command_parser_executor::print_status(const std::vector& args) -{ - if (!args.empty()) return false; - - return m_executor.print_status(); -} - bool command_parser_executor::set_limit(const std::vector& args) { - if(args.size()>1) return false; - if(args.size()==0) { + if (args.size() == 0) return m_executor.get_limit(); - } - int64_t limit; - try { - limit = std::stoll(args[0]); - } - catch(const std::exception& ex) { - std::cout << "failed to parse argument" << std::endl; - return false; - } - - return m_executor.set_limit(limit, limit); -} -bool command_parser_executor::set_limit_up(const std::vector& args) -{ - if(args.size()>1) return false; - if(args.size()==0) { - return m_executor.get_limit(true, false); - } - int64_t limit; - try { - limit = std::stoll(args[0]); + if (args.size() > 2) { + tools::fail_msg_writer() << "Too many arguments: expected 0-2 values"; + return false; } - catch(const std::exception& ex) { - std::cout << "failed to parse argument" << std::endl; - return false; + int64_t limit_down; + if (args[0] == "default") // Accept "default" as a string because getting -1 through the cli arg parsing is a nuissance + limit_down = -1; + else if (!tools::parse_int(args[0], limit_down)) { + tools::fail_msg_writer() << "Failed to parse '" << args[0] << "' as a limit"; + return false; } - return m_executor.set_limit(0, limit); -} - -bool command_parser_executor::set_limit_down(const std::vector& args) -{ - if(args.size()>1) return false; - if(args.size()==0) { - return m_executor.get_limit(false, true); - } - int64_t limit; - try { - limit = std::stoll(args[0]); - } - catch(const std::exception& ex) { - std::cout << "failed to parse argument" << std::endl; - return false; + int64_t limit_up; + if (args.size() == 1) + limit_up = limit_down; + else if (args[1] == "default") + limit_up = -1; + else if (!tools::parse_int(args[1], limit_up)) { + tools::fail_msg_writer() << "Failed to parse '" << args[1] << "' as a limit"; + return false; } - return m_executor.set_limit(limit, 0); + return m_executor.set_limit(limit_down, limit_up); } bool command_parser_executor::out_peers(const std::vector& args) @@ -667,7 +608,7 @@ bool command_parser_executor::ban(const std::vector& args) { if (args.size() != 1 && args.size() != 2) return false; std::string ip = args[0]; - time_t seconds = P2P_IP_BLOCKTIME; + time_t seconds = tools::to_seconds(cryptonote::p2p::IP_BLOCK_TIME); if (args.size() > 1) { try diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index a7762b4c986..daa780be24c 100755 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -42,11 +42,12 @@ class command_parser_executor final private: rpc_command_executor m_executor; public: - /// Invokes via remote RPC - command_parser_executor(std::string daemon_url, const std::optional& login); - - /// Invokes via local daemon - command_parser_executor(cryptonote::rpc::core_rpc_server& rpc_server); + + /// Creators a command parser; arguments are forwarded to the rpc_command_executor constructor + template + command_parser_executor(T&&... args) + : m_executor{std::forward(args)...} + {} bool print_checkpoints(const std::vector& args); @@ -56,10 +57,6 @@ class command_parser_executor final bool save_blockchain(const std::vector& args); - bool show_hash_rate(const std::vector& args); - - bool hide_hash_rate(const std::vector& args); - bool show_difficulty(const std::vector& args); bool show_status(const std::vector& args); @@ -106,14 +103,8 @@ class command_parser_executor final bool stop_daemon(const std::vector& args); - bool print_status(const std::vector& args); - bool set_limit(const std::vector& args); - bool set_limit_up(const std::vector& args); - - bool set_limit_down(const std::vector& args); - bool out_peers(const std::vector& args); bool in_peers(const std::vector& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8e9d4369193..ae3b3a5bd3b 100755 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -44,18 +44,6 @@ namespace daemonize { -command_server::command_server(std::string daemon_url, const std::optional& login) - : m_parser{std::move(daemon_url), login} -{ - init_commands(); -} - -command_server::command_server(cryptonote::rpc::core_rpc_server& rpc) - : m_is_rpc{false}, m_parser{rpc} -{ - init_commands(&rpc); -} - void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server) { m_command_lookup.set_handler( @@ -72,7 +60,7 @@ void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server) m_command_lookup.set_handler( "print_pl" , [this](const auto &x) { return m_parser.print_peer_list(x); } - , "print_pl [white] [gray] [pruned] [publicrpc] []" + , "print_pl [white] [gray] [pruned] []" , "Print the current peer list." ); m_command_lookup.set_handler( @@ -147,19 +135,14 @@ void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server) m_command_lookup.set_handler( "is_key_image_spent" , [this](const auto &x) { return m_parser.is_key_image_spent(x); } - , "is_key_image_spent " - , "Print whether a given key image is in the spent key images set." + , "is_key_image_spent [ ...]" + , "Queries whether one or more key images have been spent on the blockchain or in the memory pool." ); m_command_lookup.set_handler( "start_mining" , [this](const auto &x) { return m_parser.start_mining(x); } -#if defined NDEBUG - , "start_mining [threads=(|auto)" - , "Start mining for specified address. Defaults to 1 thread; use \"auto\" to autodetect optimal number of threads." -#else - , "start_mining [threads=(|auto) [num_blocks=]" - , "Start mining for specified address. Defaults to 1 thread; use \"auto\" to autodetect optimal number of threads. When num_blocks is set, continue mining until the (current_height + num_blocks) is met, irrespective of if this Daemon found those block(s) or not." -#endif + , "start_mining [threads=N] [num_blocks=B]" + , "Start mining for specified address, primarily for debug and testing purposes as Oxen is proof-of-stake. Defaults to 1 thread. When num_blocks is set, continue mining until the blockchain reaches height (current + B)." ); m_command_lookup.set_handler( "stop_mining" @@ -186,16 +169,6 @@ void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server) , [this](const auto &x) { return m_parser.print_transaction_pool_stats(x); } , "Print the transaction pool's statistics." ); - m_command_lookup.set_handler( - "show_hr" - , [this](const auto &x) { return m_parser.show_hash_rate(x); } - , "Start showing the current hash rate." - ); - m_command_lookup.set_handler( - "hide_hr" - , [this](const auto &x) { return m_parser.hide_hash_rate(x); } - , "Stop showing the hash rate." - ); m_command_lookup.set_handler( "save" , [this](const auto &x) { return m_parser.save_blockchain(x); } @@ -227,28 +200,15 @@ void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server) , [this](const auto &x) { return m_parser.stop_daemon(x); } , "Stop the daemon." ); - m_command_lookup.set_handler( - "print_status" - , [this](const auto &x) { return m_parser.print_status(x); } - , "Print the current daemon status." - ); m_command_lookup.set_handler( "limit" , [this](const auto &x) { return m_parser.set_limit(x); } - , "limit []" - , "Get or set the download and upload limit." - ); - m_command_lookup.set_handler( - "limit_up" - , [this](const auto &x) { return m_parser.set_limit_up(x); } - , "limit_up []" - , "Get or set the upload limit." - ); - m_command_lookup.set_handler( - "limit_down" - , [this](const auto &x) { return m_parser.set_limit_down(x); } - , "limit_down []" - , "Get or set the download limit." + , "limit [ [ exit_handler) { - if (m_is_rpc) return false; #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) auto handle_pipe = [&]() @@ -490,8 +448,6 @@ bool command_server::start_handling(std::function exit_handler) void command_server::stop_handling() { - if (m_is_rpc) return; - m_command_lookup.stop_handling(); } diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index 2a9fa18efa7..67862630309 100755 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -38,21 +38,23 @@ namespace daemonize { class command_server { private: - bool m_is_rpc{true}; command_parser_executor m_parser; epee::console_handlers_binder m_command_lookup; + std::optional m_omq; public: - /// Remote HTTP RPC constructor - command_server(std::string daemon_url, const std::optional& login); - - /// Non-remote constructor - command_server(cryptonote::rpc::core_rpc_server& rpc_server); + /// command_server constructor; forwards to command_parser_executor + template + command_server(T&&... args) + : m_parser{std::forward(args)...} + { + init_commands(); + } template bool process_command_and_log(T&&... args) { return m_command_lookup.process_command_and_log(std::forward(args)...); } - bool start_handling(std::function exit_handler = {}); + bool start_handling(std::function exit_handler = {}); void stop_handling(); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index d359a22bb47..b80ba643187 100755 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -41,19 +41,19 @@ #if defined(PER_BLOCK_CHECKPOINT) #include "blocks/blocks.h" #endif -#include "rpc/rpc_args.h" +#include "rpc/common/rpc_args.h" #include "rpc/http_server.h" -#include "rpc/lmq_server.h" +#include "rpc/omq_server.h" +#include "rpc/bootstrap_daemon.h" #include "cryptonote_protocol/quorumnet.h" #include "cryptonote_core/uptime_proof.h" #include "common/password.h" #include "common/signal_handler.h" -#include "daemon/command_server.h" -#include "daemon/command_line_args.h" #include "net/parse.h" #include "version.h" +#include "command_line_args.h" #include "command_server.h" #include "daemon.h" @@ -121,6 +121,10 @@ daemon::daemon(boost::program_options::variables_map vm_) : if (!p2p->init(vm)) throw std::runtime_error("Failed to initialize p2p server."); + MGINFO("- rpc"); + if (!rpc->init(vm)) + throw std::runtime_error("Failed to initialize rpc server."); + // Handle circular dependencies protocol->set_p2p_endpoint(p2p.get()); core->set_cryptonote_protocol(protocol.get()); @@ -160,11 +164,11 @@ daemon::daemon(boost::program_options::variables_map vm_) : if (restricted && restricted_rpc_port != 0) std::swap(main_rpc_port, restricted_rpc_port); else if (command_line::get_arg(vm, cryptonote::arg_testnet_on)) - main_rpc_port = config::testnet::RPC_DEFAULT_PORT; + main_rpc_port = cryptonote::config::testnet::RPC_DEFAULT_PORT; else if (command_line::get_arg(vm, cryptonote::arg_devnet_on)) - main_rpc_port = config::devnet::RPC_DEFAULT_PORT; + main_rpc_port = cryptonote::config::devnet::RPC_DEFAULT_PORT; else - main_rpc_port = config::RPC_DEFAULT_PORT; + main_rpc_port = cryptonote::config::RPC_DEFAULT_PORT; } if (main_rpc_port && main_rpc_port == restricted_rpc_port) restricted = true; @@ -235,6 +239,13 @@ daemon::~daemon() http_rpc_admin.reset(); } + MGINFO("- rpc"); + try { + rpc->deinit(); + } catch (const std::exception& e) { + MERROR("Failed to deinitialize rpc: " << e.what()); + } + MGINFO("- p2p"); try { p2p->deinit(); @@ -320,11 +331,25 @@ bool daemon::run(bool interactive) http_rpc_public->start(); } - std::unique_ptr rpc_commands; + std::optional rpc_commands; if (interactive) { MGINFO("Starting command-line processor"); - rpc_commands = std::make_unique(*rpc); + auto& omq = core->get_omq(); + + std::promise p; + auto conn = omq.connect_inproc( + [&p] (oxenmq::ConnectionID) { p.set_value(); }, + [&p] (oxenmq::ConnectionID, std::string_view err) { + try { + throw std::runtime_error{"Internal beldexd RPC connection failed: " + std::string{err}}; + } catch (...) { + p.set_exception(std::current_exception()); + } + }); + p.get_future().get(); + + rpc_commands.emplace(omq, std::move(conn)); rpc_commands->start_handling([this] { stop(); }); } @@ -341,6 +366,7 @@ bool daemon::run(bool interactive) { MGINFO("Stopping RPC command processor"); rpc_commands->stop_handling(); + rpc_commands.reset(); } if (http_rpc_public) { diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index ed3d8cf8a52..6838c6c4eaa 100755 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -34,7 +34,7 @@ #include "p2p/net_node.h" #include "rpc/core_rpc_server.h" #include "rpc/http_server.h" -#include "rpc/lmq_server.h" +#include "rpc/omq_server.h" #include "blocks/blocks.h" #include "rpc/core_rpc_server.h" diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index a450740c2c0..67ab747a330 100755 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -39,7 +39,7 @@ #include "daemonizer/daemonizer.h" #include "epee/misc_log_ex.h" #include "p2p/net_node.h" -#include "rpc/rpc_args.h" +#include "rpc/common/rpc_args.h" #include "rpc/core_rpc_server.h" #include "daemon/command_line_args.h" #include "version.h" @@ -171,8 +171,8 @@ int main(int argc, char const * argv[]) // want to rename if we find one (the data-dir migration happens later): std::list> potential; if (std::error_code ec; fs::exists(data_dir, ec)) { - potential.emplace_back(data_dir / CRYPTONOTE_NAME ".conf", false); - potential.emplace_back(data_dir / "beldex.conf", true); + potential.emplace_back(data_dir / cryptonote::CONF_FILENAME, false); + potential.emplace_back(data_dir / cryptonote::CONF_FILENAME, true); } else if (command_line::is_arg_defaulted(vm, cryptonote::arg_data_dir)) { // If we weren't given an explict command-line data-dir then we also need to check the // legacy data directory. (We will rename it, later, but we have to check it *first* @@ -185,14 +185,14 @@ int main(int argc, char const * argv[]) else if (command_line::get_arg(vm, cryptonote::arg_devnet_on)) old_data_dir /= "devnet"; else if (command_line::get_arg(vm, cryptonote::arg_regtest_on)) old_data_dir /= "regtest"; - potential.emplace_back(old_data_dir / CRYPTONOTE_NAME ".conf", false); - potential.emplace_back(old_data_dir / "beldex.conf", true); + potential.emplace_back(old_data_dir / cryptonote::CONF_FILENAME, false); + potential.emplace_back(old_data_dir / cryptonote::CONF_FILENAME, true); } for (auto& [conf, rename] : potential) { if (std::error_code ec; fs::exists(conf, ec)) { if (rename) { fs::path renamed = conf; - renamed.replace_filename(CRYPTONOTE_NAME ".conf"); + renamed.replace_filename(cryptonote::CONF_FILENAME); assert(renamed != conf); if (fs::rename(conf, renamed, ec); ec) { std::cerr << RED << "Failed to migrate " << conf << " -> " << renamed << @@ -299,11 +299,11 @@ int main(int argc, char const * argv[]) po::notify(vm); // log_file_path - // default: /.log + // default: /beldex.log // if log-file argument given: // absolute path // relative path: relative to data_dir - auto log_file_path = data_dir / CRYPTONOTE_NAME ".log"; + auto log_file_path = data_dir / cryptonote::LOG_FILENAME; if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_file)) log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file); if (log_file_path.is_relative()) @@ -338,9 +338,9 @@ int main(int argc, char const * argv[]) auto rpc_port = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_bind_port); if (rpc_port == 0) rpc_port = - command_line::get_arg(vm, cryptonote::arg_testnet_on) ? config::testnet::RPC_DEFAULT_PORT : - command_line::get_arg(vm, cryptonote::arg_devnet_on) ? config::devnet::RPC_DEFAULT_PORT : - config::RPC_DEFAULT_PORT; + command_line::get_arg(vm, cryptonote::arg_testnet_on) ? cryptonote::config::testnet::RPC_DEFAULT_PORT : + command_line::get_arg(vm, cryptonote::arg_devnet_on) ? cryptonote::config::devnet::RPC_DEFAULT_PORT : + cryptonote::config::RPC_DEFAULT_PORT; rpc_addr = rpc_config.bind_ip.value_or("127.0.0.1") + ":" + std::to_string(rpc_port); } else { rpc_addr = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_admin)[0]; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 39fe1f7dbbb..519446ca9e6 100755 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -22,13 +22,14 @@ // 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 +// PROCUREMENT OF SUBSTITUTE GOODS OR MASTERS; 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 "common/string_util.h" #include "epee/string_tools.h" #include "common/password.h" #include "common/scoped_message_writer.h" @@ -37,26 +38,33 @@ #include "daemon/rpc_command_executor.h" #include "common/median.h" #include "epee/int-util.h" +#include "beldex_economy.h" #include "rpc/core_rpc_server_commands_defs.h" #include "cryptonote_core/master_node_rules.h" #include "cryptonote_basic/hardfork.h" #include "checkpoints/checkpoints.h" -#include +#include #include +#include #include #include "common/beldex_integration_test_hooks.h" #include #include +#include #include #include #include +#include #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "daemon" using namespace cryptonote::rpc; +using nlohmann::json; + +using cryptonote::hf; namespace daemonize { @@ -123,37 +131,6 @@ namespace { return input_line_result::yes; } - const char *get_address_type_name(epee::net_utils::address_type address_type) - { - switch (address_type) - { - default: - case epee::net_utils::address_type::invalid: return "invalid"; - case epee::net_utils::address_type::ipv4: return "IPv4"; - case epee::net_utils::address_type::ipv6: return "IPv6"; - case epee::net_utils::address_type::i2p: return "I2P"; - case epee::net_utils::address_type::tor: return "Tor"; - } - } - - void print_peer(std::string const & prefix, GET_PEER_LIST::peer const & peer, bool pruned_only, bool publicrpc_only) - { - if (pruned_only && peer.pruning_seed == 0) - return; - if (publicrpc_only && peer.rpc_port == 0) - return; - - time_t now = std::time(nullptr); - time_t last_seen = static_cast(peer.last_seen); - - std::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen); - std::string id_str = epee::string_tools::pad_string(epee::string_tools::to_string_hex(peer.id), 16, '0', true); - std::string addr_str = peer.host + ":" + std::to_string(peer.port); - std::string rpc_port = peer.rpc_port ? std::to_string(peer.rpc_port) : "-"; - std::string pruning_seed = epee::string_tools::to_string_hex(peer.pruning_seed); - tools::msg_writer() << fmt::format("{:<10} {:<25} {:<25} {:<5} {:<4} {}", prefix, id_str, addr_str, rpc_port, pruning_seed, elapsed); - } - void print_block_header(block_header_response const & header) { tools::success_msg_writer() @@ -172,14 +149,16 @@ namespace { << "long term weight: " << header.long_term_weight << "\n" << "num txes: " << header.num_txes << "\n" << "reward: " << cryptonote::print_money(header.reward) << "\n" - << "miner reward: " << cryptonote::print_money(header.miner_reward) << "\n" + << "coinbase_payouts: " << cryptonote::print_money(header.coinbase_payouts) << "\n" << "master node winner: " << header.master_node_winner << "\n" << "miner tx hash: " << header.miner_tx_hash; } - std::string get_human_time_ago(std::chrono::seconds ago, bool abbreviate = false) + template + std::string get_human_time_ago(std::chrono::duration ago_dur, bool abbreviate = false) { - if (ago == 0s) + auto ago = std::chrono::duration_cast(ago_dur); + if (ago == 0s) return "now"; auto dt = ago > 0s ? ago : -ago; std::string s; @@ -193,105 +172,149 @@ namespace { s = fmt::format("{:.1f} days", static_cast(dt.count()) / 86400); if (abbreviate) { if (ago < 0s) - return s + " (in fut.)"; - return s; + s += " (in fut.)"; + } else { + s += ' '; + s += (ago < 0s ? "in the future" : "ago"); } - return s + " " + (ago < 0s ? "in the future" : "ago"); + return s; } std::string get_human_time_ago(std::time_t t, std::time_t now, bool abbreviate = false) { return get_human_time_ago(std::chrono::seconds{now - t}, abbreviate); } - char const *get_date_time(time_t t) + bool print_peer(std::string_view prefix, const json& peer, bool pruned_only) { - static char buf[128]; - buf[0] = 0; + auto pruning_seed = peer.value("pruning_seed", 0); + if (pruned_only && pruning_seed == 0) + return false; - struct tm tm; - epee::misc_utils::get_gmt_time(t, tm); - strftime(buf, sizeof(buf), "%Y-%m-%d %I:%M:%S %p UTC", &tm); - return buf; + time_t now = std::time(nullptr); + time_t last_seen = peer.value("last_seen", 0); + + tools::msg_writer() << fmt::format("{:<10} {:016x} {:<30} {}", + prefix, + peer["id"].get(), + fmt::format("{}:{}", peer["host"].get(), peer["port"].get()), + last_seen == 0 ? "never" : get_human_time_ago(last_seen, now)); + return true; } - std::string get_time_hms(time_t t) - { - unsigned int hours, minutes, seconds; - char buffer[24]; - hours = t / 3600; - t %= 3600; - minutes = t / 60; - t %= 60; - seconds = t; - snprintf(buffer, sizeof(buffer), "%02u:%02u:%02u", hours, minutes, seconds); - return std::string(buffer); + template + void print_peers(std::string_view prefix, const json& peers, size_t& limit, Args&&... args) { + if (limit > 0) + tools::msg_writer() << fmt::format("{:<10} {:<16} {:<30} {}", "Type", "Peer id", "Remote address", "Last seen"); + + for (auto it = peers.begin(); it != peers.end() && limit > 0; it++) + if (print_peer(prefix, *it, std::forward(args)...)) + limit--; } } rpc_command_executor::rpc_command_executor( - std::string remote_url, - const std::optional& login - ) + std::string http_url, + const std::optional& login): m_rpc{std::in_place_type, http_url} { - m_rpc_client.emplace(remote_url); if (login) - m_rpc_client->set_auth(login->username, std::string{login->password.password().view()}); + std::get(m_rpc).set_auth(login->username, std::string{login->password.password().view()}); } -bool rpc_command_executor::print_checkpoints(uint64_t start_height, uint64_t end_height, bool print_json) -{ - GET_CHECKPOINTS::request req{start_height, end_height}; - if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE && - req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE) - { - req.count = GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT; - } - else if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE || - req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE) - { - req.count = 1; +rpc_command_executor::rpc_command_executor(oxenmq::OxenMQ& omq, oxenmq::ConnectionID conn) + : m_rpc{std::move(conn)}, m_omq{&omq} +{} + +template +static auto try_running(Callback code, std::string_view error_prefix) -> std::optional { + try { + return code(); + } catch (const std::exception& e) { + tools::fail_msg_writer() << error_prefix << ": " << e.what(); + return std::nullopt; } - // Otherwise, neither heights are set to HEIGHT_SENTINEL_VALUE, so get all the checkpoints between start and end +} - GET_CHECKPOINTS::response res{}; - if (!invoke(std::move(req), res, "Failed to query blockchain checkpoints")) - return false; +json rpc_command_executor::invoke( + std::string_view method, + bool public_method, + std::optional params, + bool check_status_ok) { - std::string entry; - if (print_json) entry.append("{\n\"checkpoints\": ["); - for (size_t i = 0; i < res.checkpoints.size(); i++) - { - GET_CHECKPOINTS::checkpoint_serialized &checkpoint = res.checkpoints[i]; - if (print_json) - { - entry.append("\n"); - entry.append(epee::serialization::store_t_to_json(checkpoint)); - entry.append(",\n"); - } - else - { - entry.append("["); - entry.append(std::to_string(i)); - entry.append("]"); + json result; - entry.append(" Type: "); - entry.append(checkpoint.type); + if (auto* rpc_client = std::get_if(&m_rpc)) { + result = rpc_client->json_rpc(method, std::move(params)); + } else { + assert(m_omq); + auto conn = std::get(m_rpc); + auto endpoint = (public_method ? "rpc." : "admin.") + std::string{method}; + std::promise result_p; + m_omq->request(conn, endpoint, [&result_p](bool success, auto data) { + try { + if (!success) + throw std::runtime_error{"Request timed out"}; + if (data.size() >= 2 && data[0] == "200") + result_p.set_value(json::parse(data[1])); + else + throw std::runtime_error{"RPC method failed: " + ( + data.empty() ? "empty response" : + tools::join(" ", data))}; + } catch (...) { + result_p.set_exception(std::current_exception()); + } + }, + params ? params->dump() : "{}"); - entry.append(" Height: "); - entry.append(std::to_string(checkpoint.height)); + result = result_p.get_future().get(); + } - entry.append(" Hash: "); - entry.append(checkpoint.block_hash); - entry.append("\n"); - } + if (check_status_ok) { + if (auto it = result.find("status"); + it == result.end() || it->get() != cryptonote::rpc::STATUS_OK) + throw std::runtime_error{"Received status " + (it == result.end() ? "(empty)" : it->get_ref()) + " != OK"}; } + return result; +} + +bool rpc_command_executor::print_checkpoints(std::optional start_height, std::optional end_height, bool print_json) +{ + uint32_t count; + if (!start_height && !end_height) + count = GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT; + else if (!start_height || !end_height) + count = 1; + // Otherwise, both start/end are set so get all the checkpoints between start and end + + auto maybe_checkpoints = try_running([&] { + json params{ + {"count", count} + }; + if (start_height) + params["start_height"] = *start_height; + if (end_height) + params["end_height"] = *end_height; + return invoke(std::move(params)); }, "Failed to query blockchain checkpoints"); + if (!maybe_checkpoints) + return false; + + auto& checkpoints = maybe_checkpoints->at("checkpoints"); + + std::string entry; if (print_json) - { - entry.append("]\n}"); - } - else - { + entry = checkpoints.dump(); + else { + for (size_t i = 0; i < checkpoints.size(); i++) { + auto& cp = checkpoints[i]; + int type = cp["type"].get(); + fmt::format_to( + std::back_inserter(entry), + "[{}] Type: {}, Height: {}, Hash: {}\n", + i, + type == 0 ? "hard-coded" : "Master Node", + cp["height"].get(), + cp["block_hash"].get()); + } if (entry.empty()) entry.append("No Checkpoints"); } @@ -300,119 +323,87 @@ bool rpc_command_executor::print_checkpoints(uint64_t start_height, uint64_t end return true; } -bool rpc_command_executor::print_mn_state_changes(uint64_t start_height, uint64_t end_height) +bool rpc_command_executor::print_mn_state_changes(uint64_t start_height, std::optional end_height) { - GET_MN_STATE_CHANGES::request req{}; - GET_MN_STATE_CHANGES::response res{}; - - req.start_height = start_height; - req.end_height = end_height; - - if (!invoke(std::move(req), res, "Failed to query master nodes state changes")) + auto maybe_mn_state = try_running([&] { + json params{{"start_height", start_height}}; + if (end_height) + params["end_height"] = *end_height; + return invoke(std::move(params)); }, "Failed to query master node state changes"); + if (!maybe_mn_state) return false; + auto mn_state_changes = *maybe_mn_state; + std::stringstream output; - output << "Master Node State Changes (blocks " << res.start_height << "-" << res.end_height << ")" << std::endl; - output << " Recommissions:\t\t" << res.total_recommission << std::endl; - output << " Unlocks:\t\t" << res.total_unlock << std::endl; - output << " Decommissions:\t\t" << res.total_decommission << std::endl; - output << " Deregistrations:\t" << res.total_deregister << std::endl; - output << " IP change penalties:\t" << res.total_ip_change_penalty << std::endl; + output << "Master Node State Changes (blocks " << mn_state_changes["start_height"].get() << "-" << mn_state_changes["end_height"].get() << ")" << std::endl; + output << " Recommissions:\t\t" << mn_state_changes["total_recommission"].get() << std::endl; + output << " Unlocks:\t\t" << mn_state_changes["total_unlock"].get() << std::endl; + output << " Decommissions:\t\t" << mn_state_changes["total_decommission"].get() << std::endl; + output << " Deregistrations:\t" << mn_state_changes["total_deregister"].get() << std::endl; + output << " IP change penalties:\t" << mn_state_changes["total_ip_change_penalty"].get() << std::endl; tools::success_msg_writer() << output.str(); return true; } -bool rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit, bool pruned_only, bool publicrpc_only) { - GET_PEER_LIST::response res{}; - - if (!invoke({}, res, "Couldn't retrieve peer list")) +bool rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit, bool pruned_only) { + auto maybe_pl = try_running([this] { return invoke(); }, "Failed to retrieve peer list"); + if (!maybe_pl) return false; + auto& pl = *maybe_pl; - if (white) - { - auto peer = res.white_list.cbegin(); - const auto end = limit ? peer + std::min(limit, res.white_list.size()) : res.white_list.cend(); - for (; peer != end; ++peer) - { - print_peer("white", *peer, pruned_only, publicrpc_only); - } + if (!limit) limit = std::numeric_limits::max(); + if (white) { + tools::success_msg_writer() << pl["white_list"].size() << " whitelist peers:"; + print_peers("white", pl["white_list"], limit, pruned_only); } - - if (gray) - { - auto peer = res.gray_list.cbegin(); - const auto end = limit ? peer + std::min(limit, res.gray_list.size()) : res.gray_list.cend(); - for (; peer != end; ++peer) - { - print_peer("gray", *peer, pruned_only, publicrpc_only); - } + if (gray) { + tools::success_msg_writer() << pl["gray_list"].size() << " graylist peers:"; + print_peers("gray", pl["gray_list"], limit, pruned_only); } return true; } bool rpc_command_executor::print_peer_list_stats() { - GET_PEER_LIST::response res{}; + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) + return false; + auto& info = *maybe_info; - if (!invoke({}, res, "Couldn't retrieve peer list")) + auto wls = info.find("white_peerlist_size"); + auto gls = info.find("grey_peerlist_size"); + if (wls == info.end() || gls == info.end()) { + tools::fail_msg_writer() << "Failed to retrieve whitelist info"; return false; + } tools::msg_writer() - << "White list size: " << res.white_list.size() << "/" << P2P_LOCAL_WHITE_PEERLIST_LIMIT << " (" << res.white_list.size() * 100.0 / P2P_LOCAL_WHITE_PEERLIST_LIMIT << "%)" << std::endl - << "Gray list size: " << res.gray_list.size() << "/" << P2P_LOCAL_GRAY_PEERLIST_LIMIT << " (" << res.gray_list.size() * 100.0 / P2P_LOCAL_GRAY_PEERLIST_LIMIT << "%)"; - + << "White list size: " << wls->get() << "/" << cryptonote::p2p::LOCAL_WHITE_PEERLIST_LIMIT << " (" << wls->get() * 100.0 / cryptonote::p2p::LOCAL_WHITE_PEERLIST_LIMIT << "%)\n" + << "Gray list size: " << gls->get() << "/" << cryptonote::p2p::LOCAL_GRAY_PEERLIST_LIMIT << " (" << gls->get() * 100.0 / cryptonote::p2p::LOCAL_GRAY_PEERLIST_LIMIT << "%)"; return true; } bool rpc_command_executor::save_blockchain() { - SAVE_BC::response res{}; - - if (!invoke({}, res, "Couldn't save blockchain")) - return false; - - tools::success_msg_writer() << "Blockchain saved"; - - return true; -} - -bool rpc_command_executor::show_hash_rate() { - SET_LOG_HASH_RATE::request req{}; - SET_LOG_HASH_RATE::response res{}; - req.visible = true; - - if (!invoke(std::move(req), res, "Couldn't enable hash rate logging")) - return false; - - tools::success_msg_writer() << "Hash rate logging is on"; - - return true; -} - -bool rpc_command_executor::hide_hash_rate() { - SET_LOG_HASH_RATE::request req{}; - SET_LOG_HASH_RATE::response res{}; - req.visible = false; - - if (!invoke(std::move(req), res, "Couldn't disable hash rate logging")) - return false; - - tools::success_msg_writer() << "Hash rate logging is off"; - - return true; + return invoke_simple("Couldn't save blockchain", "Blockchain saved"); } bool rpc_command_executor::show_difficulty() { - GET_INFO::response res{}; - if (!invoke({}, res, "Failed to get node info")) + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) return false; - - tools::success_msg_writer() << "BH: " << res.height - << ", TH: " << res.top_block_hash - << ", DIFF: " << res.difficulty - << ", CUM_DIFF: " << res.cumulative_difficulty - << ", HR: " << res.difficulty / res.target << " H/s"; + auto& info = *maybe_info; + auto msg = tools::success_msg_writer(); + msg << "HEIGHT: " << info["height"].get() + << ", HASH: " << info["top_block_hash"].get(); + if (info.value("POS", false)) + msg << ", POS"; + else + msg << ", DIFF: " << info["difficulty"].get() + << ", CUM_DIFF: " << info["cumulative_difficulty"].get() + << ", HR: " << info["difficulty"].get() / info["target"].get() << " H/s"; return true; } @@ -425,9 +416,9 @@ static std::string get_mining_speed(uint64_t hr) return fmt::format("{:d} H/s",hr); } -static std::ostream& print_fork_extra_info(std::ostream& o, uint64_t t, uint64_t now, uint64_t block_time) +static std::ostream& print_fork_extra_info(std::ostream& o, uint64_t t, uint64_t now, std::chrono::seconds block_time) { - uint64_t blocks_per_day = 86400 / block_time; + double blocks_per_day = 24h / block_time; if (t == now) return o << " (forking now)"; @@ -452,33 +443,40 @@ static float get_sync_percentage(uint64_t height, uint64_t target_height) return 99.9f; // to avoid 100% when not fully synced return pc; } -static float get_sync_percentage(const GET_INFO::response &ires) -{ - return get_sync_percentage(ires.height, ires.target_height); -} bool rpc_command_executor::show_status() { - GET_INFO::response ires{}; - HARD_FORK_INFO::request hfreq{}; - HARD_FORK_INFO::response hfres{}; - MINING_STATUS::response mres{}; - bool has_mining_info = false; + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) + return false; + auto& info = *maybe_info; - hfreq.version = 0; - bool mining_busy = false; - if (!invoke({}, ires, "Failed to get node info") || - !invoke(std::move(hfreq), hfres, "Failed to retrieve hard fork info")) + auto maybe_hf = try_running([this] { return invoke(); }, + "Failed to retrieve hard fork info"); + if (!maybe_hf) return false; - if (ires.start_time) // This will only be non-null if we were recognized as admin (which we need for mining info) + auto& hfinfo = *maybe_hf; + bool has_mining_info = false, mining_active = false; + long mining_hashrate = 0; + bool mining_busy = false; + bool restricted_response = false; + if (auto it = info.find("start_time"); it != info.end() && it->get() > 0) // This will only be non-null if we were recognized as admin (which we need for mining info) { - has_mining_info = invoke({}, mres, "Failed to retrieve mining info", false); - if (has_mining_info) { - if (mres.status == STATUS_BUSY) + restricted_response = true; + if (auto maybe_mining_info = try_running([this] { return invoke(false); }, "Failed to retrieve mining info")) { + has_mining_info = true; + auto& mres = *maybe_mining_info; + if (mres["status"] == STATUS_BUSY) mining_busy = true; - else if (mres.status != STATUS_OK) { + else if (mres["status"] != STATUS_OK) { tools::fail_msg_writer() << "Failed to retrieve mining info"; return false; + } else { + mining_active = mres["active"].get(); + if (mining_active) + mining_hashrate = mres["speed"].get(); } + } else { + return false; } } @@ -487,78 +485,82 @@ bool rpc_command_executor::show_status() { uint64_t my_mn_last_uptime = 0; bool my_mn_registered = false, my_mn_staked = false, my_mn_active = false; uint16_t my_reason_all = 0, my_reason_any = 0; - if (ires.master_node && *ires.master_node) { - GET_MASTER_KEYS::response res{}; - - if (!invoke({}, res, "Failed to retrieve master node keys")) + if (info["master_node"].get()) { + auto maybe_master_keys = try_running([this] { return invoke(json{}); }, "Failed to retrieve master node keys"); + if (!maybe_master_keys) return false; - my_mn_key = std::move(res.master_node_pubkey); - GET_MASTER_NODES::request mn_req{}; - GET_MASTER_NODES::response mn_res{}; - - mn_req.master_node_pubkeys.push_back(my_mn_key); - if (invoke(std::move(mn_req), mn_res, "") && mn_res.master_node_states.size() == 1) - { - auto &entry = mn_res.master_node_states.front(); - my_mn_registered = true; - my_mn_staked = entry.total_contributed >= entry.staking_requirement; - my_mn_active = entry.active; - my_decomm_remaining = entry.earned_downtime_blocks; - my_mn_last_uptime = entry.last_uptime_proof; - my_reason_all = entry.last_decommission_reason_consensus_all; - my_reason_any = entry.last_decommission_reason_consensus_any; + if (maybe_master_keys->contains("master_node_pubkey")) { + my_mn_key = (*maybe_master_keys)["master_node_pubkey"].get(); + + auto maybe_mns = try_running([&] { return invoke(json{{"master_node_pubkeys", json::array({my_mn_key})}}); }, "Failed to retrieve master node info"); + + if (maybe_mns) { + if (auto it = maybe_mns->find("master_node_states"); it != maybe_mns->end() && it->is_array() && it->size() > 0) { + auto& state = it->front(); + my_mn_registered = true; + my_mn_staked = state["total_contributed"].get() >= state["staking_requirement"].get(); + my_mn_active = state["active"].get(); + my_decomm_remaining = state["earned_downtime_blocks"].get(); + my_mn_last_uptime = state["last_uptime_proof"].get(); + my_reason_all = state.value("last_decommission_reason_consensus_all", 0); + my_reason_any = state.value("last_decommission_reason_consensus_any", 0); + } + } } } - uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height; + uint64_t height = info["height"].get(); + uint64_t net_height = std::max(info["target_height"].get(), height); std::string bootstrap_msg; std::ostringstream str; - str << "Height: " << ires.height; - if (ires.height != net_height) - str << "/" << net_height << " (" << fmt::format("{:.1f}%)", get_sync_percentage(ires)); + str << "Height: " << height; + if (height != net_height) + str << fmt::format("/{} ({:.1f}%)", net_height, get_sync_percentage(height, net_height)); - if (ires.testnet) str << " ON TESTNET"; - else if (ires.devnet) str << " ON DEVNET"; + auto net = info["nettype"].get(); + if (net == "testnet") str << " ON TESTNET"; + else if (net == "devnet") str << " ON DEVNET"; - if (ires.height < ires.target_height) + if (height < net_height) str << ", syncing"; - if (ires.was_bootstrap_ever_used && *ires.was_bootstrap_ever_used && ires.bootstrap_daemon_address) + if (info.value("was_bootstrap_ever_used", false)) { - str << ", bootstrap " << *ires.bootstrap_daemon_address; - if (ires.untrusted) - str << fmt::format(", local height: {} ({:.1f}%)", *ires.height_without_bootstrap, get_sync_percentage(*ires.height_without_bootstrap, net_height)); + str << ", bootstrap " << info["bootstrap_daemon_address"].get(); + if (info.value("untrusted", false)){ + auto hwb = info["height_without_bootstrap"].get(); + str << fmt::format(", local height: {} ({:.1f}%)", hwb, get_sync_percentage(hwb, net_height)); + } else str << " was used"; } - if (hfres.version < HF_VERSION_POS && !has_mining_info) + auto hf_version = hfinfo["version"].get(); + if (hf_version < cryptonote::feature::POS && !has_mining_info) str << ", mining info unavailable"; - if (has_mining_info && !mining_busy && mres.active) - str << ", mining at " << get_mining_speed(mres.speed); + if (has_mining_info && !mining_busy && mining_active) + str << ", mining at " << get_mining_speed(mining_hashrate); - if (hfres.version < HF_VERSION_POS) - str << ", net hash " << get_mining_speed(ires.difficulty / ires.target); + if (hf_version < cryptonote::feature::POS) + str << ", net hash " << get_mining_speed(info["difficulty"].get() / info["target"].get()); - str << ", v" << (ires.version.empty() ? "?.?.?" : ires.version); - str << "(net v" << +hfres.version << ')'; - if (hfres.earliest_height) - print_fork_extra_info(str, *hfres.earliest_height, net_height, ires.target); + str << ", v" << info["version"].get(); + str << "(net v" << static_cast(hf_version) << ')'; + auto earliest = hfinfo.value("earliest_height", uint64_t{0}); + if (earliest) + print_fork_extra_info(str, earliest, net_height, 1s * info["target"].get()); std::time_t now = std::time(nullptr); // restricted RPC does not disclose these: - if (ires.outgoing_connections_count && ires.incoming_connections_count && ires.start_time) + if (restricted_response) { - std::time_t uptime = now - *ires.start_time; - str << ", " << *ires.outgoing_connections_count << "(out)+" << *ires.incoming_connections_count << "(in) connections" + std::chrono::seconds uptime{now - info["start_time"].get()}; + str << ", " << info["outgoing_connections_count"].get() << "(out)+" << info["incoming_connections_count"].get() << "(in) connections" << ", uptime " - << (uptime / (24*60*60)) << 'd' - << (uptime / (60*60)) % 24 << 'h' - << (uptime / 60) % 60 << 'm' - << uptime % 60 << 's'; + << tools::friendly_duration(uptime); } tools::success_msg_writer() << str.str(); @@ -572,14 +574,14 @@ bool rpc_command_executor::show_status() { str << (!my_mn_staked ? "awaiting" : my_mn_active ? "active" : "DECOMMISSIONED (" + std::to_string(my_decomm_remaining) + " blocks credit)") << ", proof: " << (my_mn_last_uptime ? get_human_time_ago(my_mn_last_uptime, now) : "(never)"); str << ", last pings: "; - if (*ires.last_storage_server_ping > 0) - str << get_human_time_ago(*ires.last_storage_server_ping, now, true /*abbreviate*/); + if (auto last_ss_ping = info["last_storage_server_ping"].get(); last_ss_ping > 0) + str << get_human_time_ago(last_ss_ping, now, true /*abbreviate*/); else str << "NOT RECEIVED"; str << " (storage), "; - if (*ires.last_belnet_ping > 0) - str << get_human_time_ago(*ires.last_belnet_ping, now, true /*abbreviate*/); + if (auto last_belnet_ping = info["last_belnet_ping"].get(); last_belnet_ping > 0) + str << get_human_time_ago(last_belnet_ping, now, true /*abbreviate*/); else str << "NOT RECEIVED"; str << " (belnet)"; @@ -603,88 +605,76 @@ bool rpc_command_executor::show_status() { } bool rpc_command_executor::mining_status() { - MINING_STATUS::response mres{}; - - if (!invoke({}, mres, "Failed to retrieve mining info", false)) + auto maybe_mining_info = try_running([this] { return invoke(false); }, "Failed to retrieve mining info"); + if (!maybe_mining_info) return false; bool mining_busy = false; - if (mres.status == STATUS_BUSY) - { + auto& mres = *maybe_mining_info; + if (mres["status"].get() == STATUS_BUSY) mining_busy = true; - } - else if (mres.status != STATUS_OK) - { + else if (mres["status"].get() != STATUS_OK) { tools::fail_msg_writer() << "Failed to retrieve mining info"; return false; } - - if (mining_busy || !mres.active) - { + bool active = mres["active"].get(); + if (mining_busy || !active) tools::msg_writer() << "Not currently mining"; + else { + tools::msg_writer() << "Mining at " << get_mining_speed(mres["speed"].get()) << " with " << mres["threads_count"].get() << " threads"; + tools::msg_writer() << "Mining address: " << mres["address"].get(); } - else - { - tools::msg_writer() << "Mining at " << get_mining_speed(mres.speed) << " with " << mres.threads_count << " threads"; - } + tools::msg_writer() << "PoW algorithm: " << mres["pow_algorithm"].get(); - tools::msg_writer() << "PoW algorithm: " << mres.pow_algorithm; - if (mres.active) - { - tools::msg_writer() << "Mining address: " << mres.address; - } + return true; +} - if (!mining_busy && mres.active && mres.speed > 0 && mres.block_target > 0 && mres.difficulty > 0) +static const char *get_address_type_name(epee::net_utils::address_type address_type) +{ + switch (address_type) { - uint64_t daily = 86400 / (double)mres.difficulty * mres.speed * mres.block_reward; - tools::msg_writer() << "Expected: " << cryptonote::print_money(daily) << " BELDEX daily, " << cryptonote::print_money(7*daily) << " weekly"; + default: + case epee::net_utils::address_type::invalid: return "invalid"; + case epee::net_utils::address_type::ipv4: return "IPv4"; + case epee::net_utils::address_type::ipv6: return "IPv6"; + case epee::net_utils::address_type::i2p: return "I2P"; + case epee::net_utils::address_type::tor: return "Tor"; } - - return true; } bool rpc_command_executor::print_connections() { - GET_CONNECTIONS::response res{}; - - if (!invoke({}, res, "Failed to retrieve connection info")) + auto maybe_conns = try_running([this] { return invoke(); }, "Failed to retrieve connection info"); + if (!maybe_conns) return false; + auto& conns = *maybe_conns; - tools::msg_writer() << std::setw(30) << std::left << "Remote Host" - << std::setw(8) << "Type" - << std::setw(20) << "Peer id" - << std::setw(20) << "Support Flags" - << std::setw(30) << "Recv/Sent (inactive,sec)" - << std::setw(25) << "State" - << std::setw(20) << "Livetime(sec)" - << std::setw(12) << "Down (kB/s)" - << std::setw(14) << "Down(now)" - << std::setw(10) << "Up (kB/s)" - << std::setw(13) << "Up(now)" - << std::endl; - - for (auto & info : res.connections) - { - std::string address = info.incoming ? "INC " : "OUT "; - address += info.ip + ":" + info.port; - //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(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type)) - << std::setw(20) << info.peer_id - << std::setw(20) << info.support_flags - << std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(tools::to_seconds(info.recv_idle_time)) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(tools::to_seconds(info.send_idle_time)) + ")" - << std::setw(25) << info.state - << std::setw(20) << std::to_string(tools::to_seconds(info.live_time)) - << std::setw(12) << info.avg_download - << std::setw(14) << info.current_download - << std::setw(10) << info.avg_upload - << std::setw(13) << info.current_upload - - << std::left << (info.localhost ? "[LOCALHOST]" : "") - << std::left << (info.local_ip ? "[LAN]" : ""); - //tools::msg_writer() << boost::format("%-25s peer_id: %-25s %s") % address % info.peer_id % in_out; + constexpr auto hdr_fmt = "{:<30}{:<8}{:<20}{:<30}{:<25}{:<20}{:<12s}{:<14s}{:<10s}{:<13s}"sv; + constexpr auto row_fmt = "{:<30}{:<8}{:<20}{:<30}{:<25}{:<20}{:<12.1f}{:<14.1f}{:<10.1f}{:<13.1f}{}{}"sv; + tools::msg_writer() << fmt::format(hdr_fmt, + "Remote Host", "Type", "Peer id", "Recv/Sent (inactive,sec)", "State", "Livetime(sec)", + "Down (kB/sec)", "Down(now)", "Up (kB/s)", "Up(now)"); + for (auto& info : conns["connections"]) + { + std::string address = info["incoming"].get() ? "INC " : "OUT "; + address += info["ip"].get(); + address += ':'; + address += tools::int_to_string(info["port"].get()); + tools::msg_writer() << fmt::format(row_fmt, + address, + get_address_type_name(info["address_type"].get()), + info["peer_id"].get(), + fmt::format("{}({}/{})", info["recv_count"].get(), + tools::friendly_duration(1ms * info["recv_idle_ms"].get()), + tools::friendly_duration(1ms * info["send_idle_ms"].get())), + info["state"].get(), + tools::friendly_duration(1ms * info["live_ms"].get()), + info["avg_download"].get() / 1000., + info["current_download"].get() / 1000., + info["avg_upload"].get() / 1000., + info["current_upload"].get() / 1000., + info.value("localhost", false) ? "[LOCALHOST]" : "", + info.value("local_ip", false) ? "[LAN]" : ""); } return true; @@ -692,69 +682,56 @@ bool rpc_command_executor::print_connections() { bool rpc_command_executor::print_net_stats() { - GET_NET_STATS::response net_stats_res{}; - GET_LIMIT::response limit_res{}; - - if (!invoke({}, net_stats_res, "Unable to retrieve net statistics") || - !invoke({}, limit_res, "Unable to retrieve bandwidth limits")) + auto maybe_stats = try_running([this] { return invoke(); }, "Failed to retrieve net statistics"); + auto maybe_limit = try_running([this] { return invoke(); }, "Failed to retrieve bandwidth limits"); + if (!maybe_stats || !maybe_limit) return false; - - uint64_t seconds = (uint64_t)time(NULL) - net_stats_res.start_time; - uint64_t average = seconds > 0 ? net_stats_res.total_bytes_in / seconds : 0; - uint64_t limit = limit_res.limit_down * 1024; // convert to bytes, as limits are always kB/s - double percent = (double)average / (double)limit * 100.0; - tools::success_msg_writer() << fmt::format("Received {} bytes ({}) in {} packets, average {}/s = {:.2f}% of the limit of {}/s" - , net_stats_res.total_bytes_in - , tools::get_human_readable_bytes(net_stats_res.total_bytes_in) - , net_stats_res.total_packets_in - , tools::get_human_readable_bytes(average) - , percent - , tools::get_human_readable_bytes(limit)); - - average = seconds > 0 ? net_stats_res.total_bytes_out / seconds : 0; - limit = limit_res.limit_up * 1024; - percent = (double)average / (double)limit * 100.0; - tools::success_msg_writer() << fmt::format("Sent {} bytes ({}) in {} packets, average {}/s = {:.2f}% of the limit of {}/s" - , net_stats_res.total_bytes_out - , tools::get_human_readable_bytes(net_stats_res.total_bytes_out) - , net_stats_res.total_packets_out - , tools::get_human_readable_bytes(average) - , percent - , tools::get_human_readable_bytes(limit)); + auto& stats = *maybe_stats; + auto& limit = *maybe_limit; + auto uptime = time(nullptr) - stats["start_time"].get(); + + for (bool in : {true, false}) { + auto bytes = stats[in ? "total_bytes_in" : "total_bytes_out"].get(); + double average = uptime > 0 ? bytes / (double) uptime : 0.0; + uint64_t lim = limit[in ? "limit_down" : "limit_up"].get() * 1024; // convert to bytes, as limits are always kB/s + tools::success_msg_writer() << fmt::format("{} {} in {} packets, average {}/s = {:.2f}% of the limit of {}/s", + in ? "Received" : "Sent", + tools::get_human_readable_bytes(bytes), + stats[in ? "total_packets_in" : "total_packets_out"].get(), + tools::get_human_readable_bytes(average), + average / lim * 100.0, + tools::get_human_readable_bytes(lim)); + } return true; } bool rpc_command_executor::print_blockchain_info(int64_t start_block_index, uint64_t end_block_index) { - GET_BLOCK_HEADERS_RANGE::request req{}; - GET_BLOCK_HEADERS_RANGE::response res{}; - // negative: relative to the end if (start_block_index < 0) { - GET_INFO::response ires; - if (!invoke(GET_INFO::request{}, ires, "Failed to query daemon info")) - return false; + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) + return false; + auto& info = *maybe_info; - if (start_block_index < 0 && (uint64_t)-start_block_index >= ires.height) + if (start_block_index < 0 && -start_block_index >= info["height"].get()) { tools::fail_msg_writer() << "start offset is larger than blockchain height"; return false; } - start_block_index = ires.height + start_block_index; - end_block_index = start_block_index + end_block_index - 1; + start_block_index += info["height"].get(); + end_block_index += start_block_index - 1; } - req.start_height = start_block_index; - req.end_height = end_block_index; - req.fill_pow_hash = false; - - if (!invoke(std::move(req), res, "Failed to retrieve block headers")) + auto maybe_block_headers = try_running([this, start_block_index, end_block_index] { return invoke(json{{"start_height", start_block_index},{"end_height", end_block_index}, {"fill_pow_hash", false}}); }, "Failed to retrieve block headers"); + if (!maybe_block_headers) return false; + auto& block_headers = *maybe_block_headers; bool first = true; - for (auto & header : res.headers) + for (auto & header : block_headers["headers"]) { if (first) first = false; @@ -762,481 +739,459 @@ bool rpc_command_executor::print_blockchain_info(int64_t start_block_index, uint tools::msg_writer() << "\n"; tools::msg_writer() - << "height: " << header.height << ", timestamp: " << header.timestamp << " (" << tools::get_human_readable_timestamp(header.timestamp) << ")" - << ", size: " << header.block_size << ", weight: " << header.block_weight << " (long term " << header.long_term_weight << "), transactions: " << header.num_txes - << "\nmajor version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version - << "\nblock id: " << header.hash << ", previous block id: " << header.prev_hash - << "\ndifficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << "\n"; + << "height: " << header["height"] << ", timestamp: " << header["timestamp"] << " (" << tools::get_human_readable_timestamp(header["timestamp"].get()) << ")" + << ", size: " << header["block_size"] << ", weight: " << header["block_weight"] << " (long term " << header["long_term_weight"] << "), transactions: " << header["num_txes"] + << "\nmajor version: " << header["major_version"] << ", minor version: " << header["minor_version"] + << "\nblock id: " << header["hash"] << ", previous block id: " << header["prev_hash"] + << "\ndifficulty: " << header["difficulty"] << ", nonce " << header["nonce"] << ", reward " << cryptonote::print_money(header["reward"].get()) << "\n"; } return true; } -bool rpc_command_executor::print_quorum_state(uint64_t start_height, uint64_t end_height) +bool rpc_command_executor::print_quorum_state(std::optional start_height, std::optional end_height) { - GET_QUORUM_STATE::request req{}; - GET_QUORUM_STATE::response res{}; - - req.start_height = start_height; - req.end_height = end_height; - req.quorum_type = GET_QUORUM_STATE::ALL_QUORUMS_SENTINEL_VALUE; - - if (!invoke(std::move(req), res, "Failed to retrieve quorum state")) + auto maybe_quorums = try_running([this, start_height, end_height] { + json params; + if (start_height) + params["start_height"] = *start_height; + if (end_height) + params["end_height"] = *end_height; + return invoke(std::move(params)); + }, "Failed to retrieve quorum state"); + + if (!maybe_quorums) return false; + auto& quorums = *maybe_quorums; - std::string output; - output.append("{\n\"quorums\": ["); - for (GET_QUORUM_STATE::quorum_for_height const &quorum : res.quorums) - { - output.append("\n"); - output.append(epee::serialization::store_t_to_json(quorum)); - output.append(",\n"); - } - output.append("]\n}"); - tools::success_msg_writer() << output; + tools::success_msg_writer() << "{{\n quorums : \n}}"<({level}, res, "Failed to set log level")) + auto maybe_level = try_running([this, &level] { return invoke(json{{"level", level}}); }, "Failed to set log categories"); + if (!maybe_level) return false; tools::success_msg_writer() << "Log level is now " << std::to_string(level); - return true; } bool rpc_command_executor::set_log_categories(std::string categories) { - SET_LOG_CATEGORIES::response res{}; - - if (!invoke({std::move(categories)}, res, "Failed to set log categories")) + auto maybe_categories = try_running([this, &categories] { return invoke(json{{"categories", std::move(categories)}}); }, "Failed to set log categories"); + if (!maybe_categories) return false; - - tools::success_msg_writer() << "Log categories are now " << res.categories; + auto& categories_response = *maybe_categories; + tools::success_msg_writer() << "Log categories are now " << categories_response["categories"].get(); return true; } bool rpc_command_executor::print_height() { - GET_HEIGHT::response res{}; + if (auto height = try_running([this] { + return invoke().at("height").get(); + }, "Failed to retrieve height")) { + tools::success_msg_writer() << *height; + return true; + } + return false; +} - if (!invoke({}, res, "Failed to retrieve height")) - return false; +bool rpc_command_executor::print_block_by_hash(const crypto::hash& block_hash, bool include_hex) { + auto maybe_block = try_running([this, &block_hash] { + return invoke(json{ + {"hash", tools::type_to_hex(block_hash)}, + {"fill_pow_hash", true}}); + }, "Block retrieval failed"); + if (!maybe_block) + return false; + auto& block = *maybe_block; - tools::success_msg_writer() << res.height; + if (include_hex) + tools::success_msg_writer() << block["blob"] << std::endl; + print_block_header(block["block_header"]); + tools::success_msg_writer() << block["json"] << "\n"; return true; } -bool rpc_command_executor::print_block(GET_BLOCK::request&& req, bool include_hex) { - req.fill_pow_hash = true; - GET_BLOCK::response res{}; - - if (!invoke(std::move(req), res, "Block retrieval failed")) +bool rpc_command_executor::print_block_by_height(uint64_t height, bool include_hex) { + auto maybe_block = try_running([this, height] { + return invoke(json{ + {"height", height}, + {"fill_pow_hash", true}}); + }, "Block retrieval failed"); + if (!maybe_block) return false; + auto& block = *maybe_block; if (include_hex) - tools::success_msg_writer() << res.blob << std::endl; - print_block_header(res.block_header); - tools::success_msg_writer() << res.json << "\n"; + tools::success_msg_writer() << block["blob"] << std::endl; + print_block_header(block["block_header"]); + tools::success_msg_writer() << block["json"] << "\n"; return true; } -bool rpc_command_executor::print_block_by_hash(const crypto::hash& block_hash, bool include_hex) { - GET_BLOCK::request req{}; - req.hash = tools::type_to_hex(block_hash); - return print_block(std::move(req), include_hex); -} - -bool rpc_command_executor::print_block_by_height(uint64_t height, bool include_hex) { - GET_BLOCK::request req{}; - req.height = height; - return print_block(std::move(req), include_hex); -} - bool rpc_command_executor::print_transaction(const crypto::hash& transaction_hash, bool include_metadata, bool include_hex, bool include_json) { - GET_TRANSACTIONS::request req{}; - GET_TRANSACTIONS::response res{}; - req.txs_hashes.push_back(tools::type_to_hex(transaction_hash)); - req.split = true; - if (!invoke(std::move(req), res, "Transaction retrieval failed")) + auto maybe_tx = try_running([this, &transaction_hash] { + return invoke(json{ + {"tx_hashes", json::array({tools::type_to_hex(transaction_hash)})}, + {"split", true}}); + }, "Transaction retrieval failed"); + if (!maybe_tx) return false; - if (1 == res.txs.size()) - { - auto& tx = res.txs.front(); - bool pruned = tx.prunable_hash && !tx.prunable_as_hex; + auto& txi = *maybe_tx; + auto txs = txi["txs"]; + if (txs.size() != 1) { + tools::fail_msg_writer() << "Transaction wasn't found: " << transaction_hash << "\n"; + return true; + } - if (tx.in_pool) - tools::success_msg_writer() << "Found in pool"; - else - tools::success_msg_writer() << "Found in blockchain at height " << tx.block_height << (pruned ? " (pruned)" : ""); + auto tx = txs.front(); + auto prunable_hash = tx.value("prunable_hash", ""sv); + auto prunable_hex = tx.value("prunable", ""sv); + bool pruned = !prunable_hash.empty() && prunable_hex.empty(); + + bool in_pool = tx.value("in_pool", false); + if (in_pool) + tools::success_msg_writer() << "Found in pool"; + else + tools::success_msg_writer() << "Found in blockchain at height " << tx["block_height"].get() << (pruned ? " (pruned)" : ""); - const std::string &pruned_as_hex = *tx.pruned_as_hex; // Always included with req.split=true + auto pruned_hex = tx["pruned"].get(); // Always included with req.split=true - std::optional t; - if (include_metadata || include_json) + std::optional t; + if (include_metadata || include_json) + { + if (oxenc::is_hex(pruned_hex) && oxenc::is_hex(prunable_hex)) { - if (oxenc::is_hex(pruned_as_hex) && (!tx.prunable_as_hex || oxenc::is_hex(*tx.prunable_as_hex))) + std::string blob = oxenc::from_hex(pruned_hex); + if (!prunable_hex.empty()) + blob += oxenc::from_hex(prunable_hex); + + bool parsed = pruned + ? cryptonote::parse_and_validate_tx_base_from_blob(blob, t.emplace()) + : cryptonote::parse_and_validate_tx_from_blob(blob, t.emplace()); + if (!parsed) { - std::string blob = oxenc::from_hex(pruned_as_hex); - if (tx.prunable_as_hex) - blob += oxenc::from_hex(*tx.prunable_as_hex); - - bool parsed = pruned - ? cryptonote::parse_and_validate_tx_base_from_blob(blob, t.emplace()) - : cryptonote::parse_and_validate_tx_from_blob(blob, t.emplace()); - if (!parsed) - { - tools::fail_msg_writer() << "Failed to parse transaction data"; - t.reset(); - } + tools::fail_msg_writer() << "Failed to parse transaction data"; + t.reset(); } } + } - // Print metadata if requested - if (include_metadata) - { - if (!tx.in_pool) - tools::msg_writer() << "Block timestamp: " << tx.block_timestamp << " (" << tools::get_human_readable_timestamp(tx.block_timestamp) << ")"; - tools::msg_writer() << "Size: " << tx.size; - if (t) - tools::msg_writer() << "Weight: " << cryptonote::get_transaction_weight(*t); + // Print metadata if requested + if (include_metadata) + { + if (!in_pool) { + auto ts = tx["block_timestamp"].get(); + tools::msg_writer() << "Block timestamp: " << ts << " (" << tools::get_human_readable_timestamp(ts) << ")"; } + tools::msg_writer() << "Size: " << tx["size"].get(); + if (t) + tools::msg_writer() << "Weight: " << cryptonote::get_transaction_weight(*t); + } - // Print raw hex if requested - if (include_hex) - tools::success_msg_writer() << pruned_as_hex << (tx.prunable_as_hex ? *tx.prunable_as_hex : "") << '\n'; + // Print raw hex if requested + if (include_hex) + tools::success_msg_writer() << pruned_hex << prunable_hex << '\n'; - // Print json if requested - if (include_json && t) + // Print json if requested + if (include_json && t) tools::success_msg_writer() << cryptonote::obj_to_json_str(*t) << '\n'; - } - else - tools::fail_msg_writer() << "Transaction wasn't found: " << transaction_hash << std::endl; return true; } -bool rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) { - IS_KEY_IMAGE_SPENT::response res{}; - if (!invoke({{tools::type_to_hex(ki)}}, res, "Failed to retrieve key image status")) +bool rpc_command_executor::is_key_image_spent(const std::vector& ki) +{ + auto maybe_spent = try_running([this, &ki] { + auto kis = json::array(); + for (auto& k : ki) kis.push_back(tools::type_to_hex(k)); + return invoke(json{{"key_images", std::move(kis)}}); }, + "Failed to retrieve key image status"); + if (!maybe_spent) return false; + auto& spent_status = (*maybe_spent)["spent_status"]; - if (1 == res.spent_status.size()) - { - // first as hex - tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent") << (res.spent_status.front() == IS_KEY_IMAGE_SPENT::SPENT_IN_POOL ? " (in pool)" : ""); - return true; + if (spent_status.size() != ki.size()) { + tools::fail_msg_writer() << "key image status could not be determined\n"; + return false; } - tools::fail_msg_writer() << "key image status could not be determined" << std::endl; - return false; + for (size_t i = 0; i < ki.size(); i++) { + int status = spent_status[i].get(); + tools::success_msg_writer() << ki[i] << ": " + << (status == 0 ? "unspent" : status == 1 ? "spent" : status == 2 ? "spent (in pool)" : "unknown"); + } + return true; } -static void print_pool(const std::vector &transactions, bool include_json) { - if (transactions.empty()) +static void print_pool(const json& txs) { + if (txs.empty()) { - tools::msg_writer() << "Pool is empty" << std::endl; + tools::msg_writer() << "Pool is empty\n"; return; } - const time_t now = time(NULL); - tools::msg_writer() << "Transactions:"; - for (auto &tx_info : transactions) + const time_t now = time(nullptr); + tools::msg_writer() << txs.size() << " Transactions:\n"; + std::vector lines; + for (auto &tx : txs) { - auto w = tools::msg_writer(); - w << "id: " << tx_info.id_hash << "\n"; - if (include_json) w << tx_info.tx_json << "\n"; - w << "blob_size: " << tx_info.blob_size << "\n" - << "weight: " << tx_info.weight << "\n" - << "fee: " << cryptonote::print_money(tx_info.fee) << "\n" - /// NB(beldex): in v13 we have min_fee = per_out*outs + per_byte*bytes, only the total fee/byte matters for - /// the purpose of building a block template from the pool, so we still print the overall fee / byte here. - /// (we can't back out the individual per_out and per_byte that got used anyway). - << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << "\n" - << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")\n" - << "relayed: " << (tx_info.relayed ? std::to_string(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")" : "no") << "\n" - << std::boolalpha - << "do_not_relay: " << tx_info.do_not_relay << "\n" - << "flash: " << tx_info.flash << "\n" - << "kept_by_block: " << tx_info.kept_by_block << "\n" - << "double_spend_seen: " << tx_info.double_spend_seen << "\n" - << std::noboolalpha - << "max_used_block_height: " << tx_info.max_used_block_height << "\n" - << "max_used_block_id: " << tx_info.max_used_block_id_hash << "\n" - << "last_failed_height: " << tx_info.last_failed_height << "\n" - << "last_failed_id: " << tx_info.last_failed_id_hash << "\n"; + std::vector status; + if (tx.value("blink", false)) status.push_back("blink"sv); + status.push_back(tx["relayed"].get() ? "relayed"sv : "not relayed"sv); + if (tx.value("do_not_relay", false)) status.push_back("do not relay"sv); + if (tx.value("double_spend_seen", false)) status.push_back("double spend"sv); + if (tx.value("kept_by_block", false)) status.push_back("from popped block"sv); + + lines.clear(); + lines.push_back(tx["tx_hash"].get_ref() + ":"s); + lines.push_back(fmt::format("size/weight: {}/{}", tx["size"].get(), tx["weight"].get())); + lines.push_back(fmt::format("fee: {} ({}/byte)", + cryptonote::print_money(tx["fee"].get()), cryptonote::print_money(tx["fee"].get() / tx["weight"].get()))); + lines.push_back(fmt::format("received: {} ({})", tx["received_timestamp"].get(), get_human_time_ago(tx["received_timestamp"].get(), now))); + lines.push_back("status: " + tools::join(", ", status)); + lines.push_back(fmt::format("top required block: {} ({})", tx["max_used_height"].get(), tx["max_used_block"])); + if (tx.count("last_failed_height")) + lines.push_back(fmt::format("last failed block: {} ({})", tx["last_failed_height"].get(), tx["last_failed_block"].get())); + if (auto extra = tx.find("extra"); extra != tx.end()) { + lines.push_back("transaction extra: "); + for (auto c : extra->dump(2)) { + if (c == '\n') + lines.back() += "\n "sv; + else + lines.back() += c; + } + } + tools::msg_writer() << tools::join("\n ", lines) << "\n"; } } -bool rpc_command_executor::print_transaction_pool_long() { - GET_TRANSACTION_POOL::response res{}; - - if (!invoke({}, res, "Failed to retrieve transaction pool details")) +bool rpc_command_executor::print_transaction_pool(bool long_format) { + json args{{"memory_pool", true}}; + if (long_format) args["tx_extra"] = true; + auto maybe_pool = try_running([this, &args] { return invoke(args); }, + "Failed to retrieve transaction pool details"); + if (!maybe_pool) return false; + auto& pool = *maybe_pool; + + print_pool(pool["txs"]); - print_pool(res.transactions, true); + if (long_format) { + // We used to have a warning here when we had transactions but no key_images; but that can + // happen on Oxen with 0-output tx state change transactions. - if (res.spent_key_images.empty()) - { - if (! res.transactions.empty()) - tools::msg_writer() << "WARNING: Inconsistent pool state - no spent key images"; - } - else - { - tools::msg_writer() << ""; // one newline - tools::msg_writer() << "Spent key images: "; - for (const auto& kinfo : res.spent_key_images) + if (!pool["mempool_key_images"].empty()) { - tools::msg_writer() << "key image: " << kinfo.id_hash; - if (kinfo.txs_hashes.size() == 1) - { - tools::msg_writer() << " tx: " << kinfo.txs_hashes[0]; - } - else if (kinfo.txs_hashes.size() == 0) - { - tools::msg_writer() << " WARNING: spent key image has no txs associated"; - } - else + tools::msg_writer() << "\nSpent key images: "; + for (const auto& [key, tx_hashes] : pool["mempool_key_images"].items()) { - tools::msg_writer() << " NOTE: key image for multiple txs: " << kinfo.txs_hashes.size(); - for (const std::string& tx_id : kinfo.txs_hashes) + tools::msg_writer() << "key image: " << key; + if (tx_hashes.size() == 1) + tools::msg_writer() << " tx: " << tx_hashes.front().get(); + else if (tx_hashes.empty()) + tools::msg_writer() << " WARNING: spent key image has no txs associated!"; + else { - tools::msg_writer() << " tx: " << tx_id; + tools::msg_writer() << fmt::format(" NOTE: key image for multiple transactions ({}):", tx_hashes.size()); + for (const auto& txid : tx_hashes) + tools::msg_writer() << " - " << txid.get(); } } - } - if (res.transactions.empty()) - { - tools::msg_writer() << "WARNING: Inconsistent pool state - no transactions"; + if (pool["txs"].empty()) + tools::msg_writer() << "WARNING: Inconsistent pool state - key images but no no transactions"; } } return true; } -bool rpc_command_executor::print_transaction_pool_short() { - GET_TRANSACTION_POOL::request req{}; - GET_TRANSACTION_POOL::response res{}; - - if (!invoke({}, res, "Failed to retrieve transaction pool details")) - return false; - - print_pool(res.transactions, false); - - return true; -} - bool rpc_command_executor::print_transaction_pool_stats() { - GET_TRANSACTION_POOL_STATS::response res{}; - GET_INFO::response ires{}; + auto full_reward_zone = try_running([this] { + return invoke().at("block_size_limit").get() / 2; + }, "Failed to retrieve node info"); + if (!full_reward_zone) + return false; - if (!invoke({}, res, "Failed to retreive transaction pool statistics") || - !invoke({}, ires, "Failed to retrieve node info")) + auto maybe_stats = try_running([this] { return invoke(json{{"include_unrelayed", true}}); }, + "Failed to retrieve transaction pool statistics"); + if (!maybe_stats) return false; + auto& pstats = maybe_stats->at("pool_stats"); - size_t n_transactions = res.pool_stats.txs_total; + size_t n_transactions = pstats["txs_total"].get(); const uint64_t now = time(NULL); - size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0; + auto bytes_total = pstats["bytes_total"].get(); + size_t avg_bytes = n_transactions ? bytes_total / n_transactions : 0; - std::string backlog_message; - const uint64_t full_reward_zone = ires.block_weight_limit / 2; - if (res.pool_stats.bytes_total <= full_reward_zone) - { - backlog_message = "no backlog"; - } - else + std::string backlog_message = "no backlog"; + if (bytes_total > *full_reward_zone) { - uint64_t backlog = (res.pool_stats.bytes_total + full_reward_zone - 1) / full_reward_zone; - backlog_message = fmt::format("estimated {} block ({} minutes ) backlog", backlog, backlog * TARGET_BLOCK_TIME / 1min); + uint64_t backlog = (bytes_total + *full_reward_zone - 1) / *full_reward_zone; + backlog_message = fmt::format("estimated {} block ({} minutes ) backlog", backlog, backlog * cryptonote::TARGET_BLOCK_TIME / 1min); } - tools::msg_writer() << n_transactions << " tx(es), " << res.pool_stats.bytes_total << " bytes total (min " << res.pool_stats.bytes_min << ", max " << res.pool_stats.bytes_max << ", avg " << avg_bytes << ", median " << res.pool_stats.bytes_med << ")" << std::endl - << "fees " << cryptonote::print_money(res.pool_stats.fee_total) << " (avg " << cryptonote::print_money(n_transactions ? res.pool_stats.fee_total / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(res.pool_stats.bytes_total ? res.pool_stats.fee_total / res.pool_stats.bytes_total : 0) << " per byte)" << std::endl - << res.pool_stats.num_double_spends << " double spends, " << res.pool_stats.num_not_relayed << " not relayed, " << res.pool_stats.num_failing << " failing, " << res.pool_stats.num_10m << " older than 10 minutes (oldest " << (res.pool_stats.oldest == 0 ? "-" : get_human_time_ago(res.pool_stats.oldest, now)) << "), " << backlog_message; - - if (n_transactions > 1 && res.pool_stats.histo.size()) + uint64_t fee_total = pstats["fee_total"].get(); + std::time_t oldest = pstats["oldest"].get(); + tools::msg_writer() << n_transactions << " tx(es), " + << bytes_total << " bytes total (min " << pstats["bytes_min"].get() << ", max " << pstats["bytes_max"].get() + << ", avg " << avg_bytes << ", median " << pstats["bytes_med"].get() << ')' + << '\n' + << "fees " << cryptonote::print_money(fee_total) << " (avg " << cryptonote::print_money(n_transactions ? fee_total / n_transactions : 0) << " per tx, " + << cryptonote::print_money(bytes_total ? fee_total / bytes_total : 0) << " per byte)" + << '\n' + << pstats["num_double_spends"].get() << " double spends, " + << pstats["num_not_relayed"].get() << " not relayed, " + << pstats["num_failing"].get() << " failing, " + << pstats["num_10m"].get() << " older than 10 minutes (oldest " + << (oldest == 0 ? "-" : get_human_time_ago(oldest, now)) << "), " + << backlog_message; + + auto histo = pstats["histo"].get>>(); + if (n_transactions > 1 && !histo.empty()) { - std::vector times; - uint64_t numer; - size_t i, n = res.pool_stats.histo.size(), denom; - times.resize(n); - if (res.pool_stats.histo_98pc) - { - numer = res.pool_stats.histo_98pc; - denom = n-1; - for (i=0; i times; + bool last_is_gt = false; + if (auto it = pstats.find("histo_98pc"); it != pstats.end()) { - numer = now - res.pool_stats.oldest; - denom = n; - for (i=0; iget(); + for (size_t i = 0; i < 11; i++) + times[i] = i * histo98 / 9; + last_is_gt = true; } - tools::msg_writer() << " Age Txes Bytes"; - for (i=0; i(); + for (size_t i = 0; i < 11; i++) + times[i] = i * histo_max / 10; } + + constexpr auto hist_fmt = "{:>10} - {:<14} {:>7} {:>11}"sv; + tools::msg_writer() << fmt::format("{:^23} {:>7} {:>11}", "Age", "Txes", "Bytes"); + for (size_t i = 0; i < 10; i++) + tools::msg_writer() + << fmt::format(hist_fmt, + get_human_time_ago(times[i] * 1s, true), + (last_is_gt && i == 10 ? "" : get_human_time_ago(times[i+1] * 1s, true) + " ago"), + histo[i].first, + histo[i].second); } tools::msg_writer(); return true; } -bool rpc_command_executor::start_mining(const cryptonote::account_public_address& address, uint64_t num_threads, uint32_t num_blocks, cryptonote::network_type nettype) { - START_MINING::request req{}; - START_MINING::response res{}; - req.num_blocks = num_blocks; - req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address); - req.threads_count = num_threads; - - if (!invoke(std::move(req), res, "Unable to start mining")) +bool rpc_command_executor::start_mining(const cryptonote::account_public_address& address, int num_threads, int num_blocks, cryptonote::network_type nettype) { + json args{ + {"num_blocks", num_blocks}, + {"threads_count", num_threads}, + {"miner_address", cryptonote::get_account_address_as_str(nettype, false, address)}}; + if (!try_running([this, &args] { return invoke(args); }, "Unable to start mining")) return false; - std::stringstream stream; - stream << "Mining started"; - if (num_threads) stream << " with " << num_threads << " thread(s)."; - else stream << ", auto detecting the number of threads to use."; - - if (num_blocks) stream << " Mining for " << num_blocks << " blocks before stopping or until manually stopped."; - tools::success_msg_writer() << stream.str(); + tools::success_msg_writer() + << fmt::format("Mining started with {} thread(s).", std::max(num_threads, 1)) + << (num_blocks ? fmt::format(" Will stop after {} blocks", num_blocks) : ""); return true; } bool rpc_command_executor::stop_mining() { - STOP_MINING::response res{}; - - if (!invoke({}, res, "Unable to stop mining")) - return false; - - tools::success_msg_writer() << "Mining stopped"; - return true; + return invoke_simple("Couldn't stop mining", "Mining stopped"); } bool rpc_command_executor::stop_daemon() { - STOP_DAEMON::response res{}; - - if (!invoke({}, res, "Failed to stop daemon")) - return false; - - tools::success_msg_writer() << "Stop signal sent"; - - return true; -} - -bool rpc_command_executor::print_status() -{ - if (!m_rpc_client) - { - tools::fail_msg_writer() << "print_status makes no sense in interactive mode"; - return false; - } - - // Make a request to get_height because it is public and relatively simple - GET_HEIGHT::response res; - if (invoke({}, res, "beldexd is NOT running")) { - tools::success_msg_writer() << "beldexd is running (height: " << res.height << ")"; - return true; - } - return false; + return invoke_simple("Couldn't stop daemon", "Stop signal sent"); } -bool rpc_command_executor::get_limit(bool up, bool down) +bool rpc_command_executor::get_limit() { - GET_LIMIT::response res{}; - - if (!invoke({}, res, "Failed to retrieve current bandwidth limits")) + auto maybe_limit = try_running([this] { return invoke(); }, "Failed to retrieve current traffic limits"); + if (!maybe_limit) return false; + auto& limit = *maybe_limit; - if (down) - tools::msg_writer() << "limit-down is " << res.limit_down << " kB/s"; - if (up) - tools::msg_writer() << "limit-up is " << res.limit_up << " kB/s"; + tools::msg_writer() << fmt::format("Current limits are {} kiB/s down, {} kiB/s up", + limit["limit_down"].get(), limit["limit_up"].get()); return true; } bool rpc_command_executor::set_limit(int64_t limit_down, int64_t limit_up) { - SET_LIMIT::response res{}; - if (!invoke({limit_down, limit_up}, res, "Failed to set bandwidth limits")) + json args{ + {"limit_down", limit_down}, + {"limit_up", limit_up}}; + auto maybe_limit = try_running([this, &args] { return invoke(args); }, "Failed to set traffic limits"); + if (!maybe_limit) return false; + auto& limit = *maybe_limit; - tools::msg_writer() << "Set limit-down to " << res.limit_down << " kB/s"; - tools::msg_writer() << "Set limit-up to " << res.limit_up << " kB/s"; + tools::success_msg_writer() << fmt::format("New limits are {} kiB/s down, {} kiB/s up", + limit["limit_down"].get(), limit["limit_up"].get()); return true; } bool rpc_command_executor::out_peers(bool set, uint32_t limit) { - OUT_PEERS::request req{set, limit}; - OUT_PEERS::response res{}; - if (!invoke(std::move(req), res, "Failed to set max out peers")) - return false; - - const std::string s = res.out_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.out_peers); - tools::msg_writer() << "Max number of out peers set to " << s << std::endl; - + auto maybe_out_peers = try_running([this, set, limit] { return invoke(json{{"set", set}, {"out_peers", limit}}); }, "Failed to set max out peers"); + if (!maybe_out_peers) + return false; + auto& out_peers = *maybe_out_peers; + auto peers = out_peers["out_peers"].get(); + tools::msg_writer() << "Max number of out peers set to " + << (peers == std::numeric_limits::max() ? "unlimited" : std::to_string(peers)); return true; } bool rpc_command_executor::in_peers(bool set, uint32_t limit) { - IN_PEERS::request req{set, limit}; - IN_PEERS::response res{}; - if (!invoke(std::move(req), res, "Failed to set max in peers")) - return false; - - const std::string s = res.in_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.in_peers); - tools::msg_writer() << "Max number of in peers set to " << s << std::endl; + auto maybe_in_peers = try_running([this, set, limit] { return invoke(json{{"set", set}, {"in_peers", limit}}); }, "Failed to set max in peers"); + if (!maybe_in_peers) + return false; + auto& in_peers = *maybe_in_peers; + auto peers = in_peers["in_peers"].get(); + tools::msg_writer() << "Max number of incoming peers set to " + << (peers == std::numeric_limits::max() ? "unlimited" : std::to_string(peers)); return true; } bool rpc_command_executor::print_bans() { - GETBANS::response res{}; - - if (!invoke({}, res, "Failed to retrieve ban list")) - return false; - - if (!res.bans.empty()) - { - for (auto i = res.bans.begin(); i != res.bans.end(); ++i) - { - tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds"; - } - } - else - tools::msg_writer() << "No IPs are banned"; + auto maybe_bans = + try_running([this] { return invoke(); }, "Failed to retrieve ban list"); + if (!maybe_bans) + return false; + auto bans = *maybe_bans; + auto ban_array = bans.at("bans"); + assert(ban_array.is_array() && "Internal error, RPC API has changed"); + + if (ban_array.empty()) { + tools::msg_writer()<<"No IPs are banned"; return true; + } + + for(const auto& ban : ban_array){ + tools::msg_writer() << ban["host"].get() << " banned for " << ban["seconds"].get() << " seconds"; + } + return true; } bool rpc_command_executor::ban(const std::string &address, time_t seconds, bool clear_ban) { - SETBANS::request req{}; - SETBANS::response res{}; - - req.bans.emplace_back(); - auto& ban = req.bans.back(); - ban.host = address; - ban.ip = 0; - ban.ban = !clear_ban; - ban.seconds = seconds; - - if (!invoke(std::move(req), res, clear_ban ? "Failed to clear ban" : "Failed to set ban")) + auto maybe_banned = try_running([this, &address, seconds, clear_ban] { return invoke(json{{"host", std::move(address)}, {"ip", 0}, {"seconds", seconds}, {"ban", !clear_ban}}); }, clear_ban ? "Failed to clear ban" : "Failed to set ban"); + if (!maybe_banned) return false; // TODO(doyle): Work around because integration tests break when using @@ -1256,16 +1211,14 @@ bool rpc_command_executor::unban(const std::string &address) bool rpc_command_executor::banned(const std::string &address) { - BANNED::request req{}; - BANNED::response res{}; + auto maybe_banned = try_running([this, &address] { return invoke(json{{"address", std::move(address)}}); }, "Failed to retrieve ban information"); - req.address = address; - - if (!invoke({address}, res, "Failed to retrieve ban information")) + if (!maybe_banned) return false; + auto& banned_response = *maybe_banned; - if (res.banned) - tools::msg_writer() << address << " is banned for " << res.seconds << " seconds"; + if (banned_response["banned"].get()) + tools::msg_writer() << address << " is banned for " << banned_response["seconds"].get() << " seconds"; else tools::msg_writer() << address << " is not banned"; @@ -1274,14 +1227,16 @@ bool rpc_command_executor::banned(const std::string &address) bool rpc_command_executor::flush_txpool(std::string txid) { - FLUSH_TRANSACTION_POOL::request req{}; - FLUSH_TRANSACTION_POOL::response res{}; - + std::vector txids{}; if (!txid.empty()) - req.txids.push_back(std::move(txid)); + txids.push_back(std::move(txid)); - if (!invoke(std::move(req), res, "Failed to flush tx pool")) - return false; + try { + invoke(json{{"txids", std::move(txids)}}); + } catch (const std::exception& e) { + tools::fail_msg_writer() << "Failed to flush tx pool:"<< e.what(); + return false; + } tools::success_msg_writer() << "Pool successfully flushed"; return true; @@ -1289,55 +1244,57 @@ bool rpc_command_executor::flush_txpool(std::string txid) bool rpc_command_executor::output_histogram(const std::vector &amounts, uint64_t min_count, uint64_t max_count) { - GET_OUTPUT_HISTOGRAM::request req{}; - GET_OUTPUT_HISTOGRAM::response res{}; - - req.amounts = amounts; - req.min_count = min_count; - req.max_count = max_count; - req.unlocked = false; - req.recent_cutoff = 0; - - if (!invoke(std::move(req), res, "Failed to retrieve output histogram")) + auto maybe_histogram= try_running([this, &amounts, min_count, max_count] + { return invoke( + json{{"amounts", amounts}, + {"min_count", min_count}, + {"max_count", max_count}, + {"unlocked", false}, + {"recent_cutoff", 0}}); + }, "Failed to retrieve output histogram"); + if (!maybe_histogram) return false; - - std::sort(res.histogram.begin(), res.histogram.end(), - [](const auto& e1, const auto& e2)->bool { return e1.total_instances < e2.total_instances; }); - for (const auto &e: res.histogram) - { - tools::msg_writer() << e.total_instances << " " << cryptonote::print_money(e.amount); - } - - return true; + std::vector histogram = (*maybe_histogram)["histogram"]; + std::sort(histogram.begin(), histogram.end(), + [](const auto& e1, const auto& e2)->bool { return e1.total_instances < e2.total_instances; }); + for (const auto &e: histogram) + tools::msg_writer() << e.total_instances << " " << cryptonote::print_money(e.amount); + return true; } bool rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t count) { - GET_COINBASE_TX_SUM::response res{}; - if (!invoke({height, count}, res, "Failed to retrieve coinbase info")) - return false; + auto maybe_coinbase = try_running([this, &height, &count] { return invoke(json{{"height", height}, {"count", count}}); }, "Failed to retrieve coinbase info"); + if (!maybe_coinbase) + return false; + auto& coinbase = *maybe_coinbase; tools::msg_writer() << "Sum of coinbase transactions between block heights [" - << height << ", " << (height + count) << ") is " - << cryptonote::print_money(res.emission_amount + res.fee_amount) << " " - << "consisting of " << cryptonote::print_money(res.emission_amount) - << " in emissions, " << cryptonote::print_money(res.fee_amount) << " in fees, and " << cryptonote::print_money(res.burn_amount) << " in burn amount"; + << height << ", " << (height + count) << ") is " + << cryptonote::print_money(coinbase["emission_amount"].get() + coinbase["fee_amount"].get()) << " " + << "consisting of " << cryptonote::print_money(coinbase["emission_amount"]) + << " in emissions, and " << cryptonote::print_money(coinbase["fee_amount"]) << " in fees"; return true; } bool rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks) { - GET_INFO::response ires{}; - GET_ALTERNATE_CHAINS::response res{}; + auto height = try_running([this] { + return invoke().at("height").get(); + }, "Failed to retrieve node info"); + if (!height) + return false; - if (!invoke({}, ires, "Failed to retrieve node info") || - !invoke({}, res, "Failed to retrieve alt chain data")) + auto maybe_chains = try_running([this] { + return invoke(); + }, "Failed to retrieve node info"); + if (!maybe_chains) return false; + std::vector chains = (*maybe_chains)["chains"]; if (tip.empty()) { - auto chains = res.chains; - std::sort(chains.begin(), chains.end(), [](const GET_ALTERNATE_CHAINS::chain_info &info0, GET_ALTERNATE_CHAINS::chain_info &info1){ return info0.height < info1.height; }); + std::sort(chains.begin(), chains.end(), [](const auto& info0, auto& info1){ return info0.height < info1.height; }); std::vector display; for (size_t i = 0; i < chains.size(); ++i) { @@ -1345,7 +1302,7 @@ bool rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, if (chain.length <= above) continue; const uint64_t start_height = (chain.height - chain.length + 1); - if (last_blocks > 0 && ires.height - 1 - start_height >= last_blocks) + if (last_blocks > 0 && *height - 1 - start_height >= last_blocks) continue; display.push_back(i); } @@ -1354,42 +1311,43 @@ bool rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, { const auto &chain = chains[idx]; const uint64_t start_height = (chain.height - chain.length + 1); - tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (*height - start_height - 1) << " deep), diff " << chain.difficulty << ": " << chain.block_hash; } } else { const uint64_t now = time(NULL); - const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; }); - if (i != res.chains.end()) + const auto i = std::find_if(chains.begin(), chains.end(), [&tip](GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; }); + if (i != chains.end()) { const auto &chain = *i; tools::success_msg_writer() << "Found alternate chain with tip " << tip; uint64_t start_height = (chain.height - chain.length + 1); - tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (*height - start_height - 1) << " deep), diff " << chain.difficulty << ":"; for (const std::string &block_id: chain.block_hashes) tools::msg_writer() << " " << block_id; tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block; - GET_BLOCK_HEADER_BY_HASH::request bhreq{}; - GET_BLOCK_HEADER_BY_HASH::response bhres{}; - bhreq.hashes = chain.block_hashes; - bhreq.hashes.push_back(chain.main_chain_parent_block); - bhreq.fill_pow_hash = false; - if (!invoke(std::move(bhreq), bhres, "Failed to query block header by hash")) + std::vector hashes{chain.block_hashes}; + hashes.push_back(chain.main_chain_parent_block); + auto maybe_headers = try_running([&] { return invoke(json{{"hashes", hashes}, {"fill_pow_hash", false}}); }, "Failed to query block header by hash"); + if (!maybe_headers) return false; - if (bhres.block_headers.size() != chain.length + 1) + auto headers = *maybe_headers; + if (headers["block_headers"].size() != chain.length + 1) { tools::fail_msg_writer() << "Failed to get block header info for alt chain"; return true; } - uint64_t t0 = bhres.block_headers.front().timestamp, t1 = t0; - for (const block_header_response &block_header: bhres.block_headers) + uint64_t t0 = std::numeric_limits::max(), + t1 = std::numeric_limits::min(); + for (const auto& block_header: headers["block_headers"]) { - t0 = std::min(t0, block_header.timestamp); - t1 = std::max(t1, block_header.timestamp); + const uint64_t ts = block_header.get(); + t0 = std::min(t0, ts); + t1 = std::max(t1, ts); } const uint64_t dt = t1 - t0; const uint64_t age = std::max(dt, t0 < now ? now - t0 : 0); @@ -1397,9 +1355,9 @@ bool rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, if (chain.length > 1) { tools::msg_writer() << "Time span: " << tools::get_human_readable_timespan(std::chrono::seconds(dt)); - cryptonote::difficulty_type start_difficulty = bhres.block_headers.back().difficulty; + cryptonote::difficulty_type start_difficulty = headers["block_headers"].back()["difficulty"]; if (start_difficulty > 0) - tools::msg_writer() << "Approximated " << 100.f * tools::to_seconds(TARGET_BLOCK_TIME_OLD) * chain.length / dt << "% of network hash rate"; //old block time + tools::msg_writer() << "Approximated " << 100.f * tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12) * chain.length / dt << "% of network hash rate"; //old block time else tools::fail_msg_writer() << "Bad cumulative difficulty reported by dameon"; } @@ -1412,32 +1370,47 @@ bool rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, bool rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { - GET_INFO::response ires{}; - GET_BASE_FEE_ESTIMATE::response feres{}; - HARD_FORK_INFO::response hfres{}; + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) + return false; + auto& info = *maybe_info; - if (!invoke({}, ires, "Failed to retrieve node info") || - !invoke({}, feres, "Failed to retrieve current fee info") || - !invoke({HF_VERSION_PER_BYTE_FEE}, hfres, "Failed to retrieve hard fork info")) + auto maybe_hf = try_running([this] { return invoke(); }, + "Failed to retrieve hard fork info"); + if (!maybe_hf) return false; + auto& hfinfo = *maybe_hf; + + auto maybe_fees = try_running([this] { return invoke(json{}); }, "Failed to retrieve current fee info"); + if (!maybe_fees) + return false; + auto& feres = *maybe_fees; + + // Safe access with fallback + auto safe_uint64 = [](const json& j, const std::string& key) { + return j.contains(key) && !j[key].is_null() ? j[key].get() : 0; + }; + auto safe_int = [](const json& j, const std::string& key) { + return j.contains(key) && !j[key].is_null() ? j[key].get() : 0; + }; - tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty - << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee_per_byte) << "/" << (hfres.enabled ? "byte" : "kB") - << " + " << cryptonote::print_money(feres.fee_per_output) << "/out"; + uint64_t height = safe_uint64(info, "height"); + uint64_t difficulty = safe_uint64(info, "difficulty"); + uint64_t cumulative_difficulty = safe_uint64(info, "cumulative_difficulty"); + int target = safe_int(info, "target"); + tools::msg_writer() << "Height: " << height << ", diff " << difficulty << ", cum. diff " << cumulative_difficulty + << ", target " << target << " sec" << ", dyn fee " << cryptonote::print_money(safe_uint64(feres, "fee_per_byte")) << "/" << (hfinfo["enabled"].get() ? "byte" : "kB") + << " + " << cryptonote::print_money(safe_uint64(feres, "fee_per_output")) << "/out"; if (nblocks > 0) { - if (nblocks > ires.height) - nblocks = ires.height; + if (nblocks > height) + nblocks = height; - GET_BLOCK_HEADERS_RANGE::request bhreq{}; - GET_BLOCK_HEADERS_RANGE::response bhres{}; - - bhreq.start_height = ires.height - nblocks; - bhreq.end_height = ires.height - 1; - bhreq.fill_pow_hash = false; - if (!invoke(std::move(bhreq), bhres, "Failed to retrieve block headers")) + auto maybe_block_headers = try_running([this, height, nblocks] { return invoke(json{{"start_height", height - nblocks},{"end_height", height - 1}, {"fill_pow_hash", false}}); }, "Failed to retrieve block headers"); + if (!maybe_block_headers) return false; + auto& block_headers = *maybe_block_headers; double avgdiff = 0; double avgnumtxes = 0; @@ -1446,16 +1419,26 @@ bool rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) weights.reserve(nblocks); uint64_t earliest = std::numeric_limits::max(), latest = 0; std::map> versions; // version -> {majorcount, minorcount} - for (const auto &bhr: bhres.headers) + for (const auto &bhr: block_headers["headers"]) { - avgdiff += bhr.difficulty; - avgnumtxes += bhr.num_txes; - avgreward += bhr.reward; - weights.push_back(bhr.block_weight); - versions[bhr.major_version].first++; - versions[bhr.minor_version].second++; - earliest = std::min(earliest, bhr.timestamp); - latest = std::max(latest, bhr.timestamp); + auto get_or = [](const json& j, const std::string& key, uint64_t def) { + return j.contains(key) && !j[key].is_null() ? j[key].get() : def; + }; + auto get_or_unsigned = [](const json& j, const std::string& key) { + return j.contains(key) && !j[key].is_null() ? j[key].get() : 0; + }; + + avgdiff += get_or(bhr, "difficulty", 0); + avgnumtxes += get_or(bhr, "num_txes", 0); + avgreward += get_or(bhr, "reward", 0); + weights.push_back(get_or(bhr, "block_weight", 0)); + + versions[get_or_unsigned(bhr, "major_version")].first++; + versions[get_or_unsigned(bhr, "minor_version")].second++; + + uint64_t timestamp = get_or(bhr, "timestamp", 0); + earliest = std::min(earliest, timestamp); + latest = std::max(latest, timestamp); } avgdiff /= nblocks; avgnumtxes /= nblocks; @@ -1479,64 +1462,81 @@ bool rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) bool rpc_command_executor::relay_tx(const std::string &txid) { - RELAY_TX::response res{}; - if (!invoke({{txid}}, res, "Failed to relay tx")) - return false; + auto maybe_relay = try_running([&] { return invoke(json{{"txid", txid}}); }, "Failed to relay tx"); + if (!maybe_relay) + return false; - tools::success_msg_writer() << "Transaction successfully relayed"; - return true; + tools::success_msg_writer() << "Transaction successfully relayed"; + return true; } bool rpc_command_executor::sync_info() { - SYNC_INFO::response res{}; - - if (!invoke({}, res, "Failed to retrieve synchronization info")) - return false; - - uint64_t target = res.target_height < res.height ? res.height : res.target_height; - tools::success_msg_writer() << "Height: " << res.height << ", target: " << target << " (" << (100.0 * res.height / target) << "%)"; - uint64_t current_download = 0; - for (const auto &p: res.peers) - current_download += p.info.current_download; - tools::success_msg_writer() << "Downloading at " << current_download << " kB/s"; - if (res.next_needed_pruning_seed) - tools::success_msg_writer() << "Next needed pruning seed: " << res.next_needed_pruning_seed; - - tools::success_msg_writer() << std::to_string(res.peers.size()) << " peers"; - for (const auto &p: res.peers) - { - std::string address = epee::string_tools::pad_string(p.info.address, 24); - uint64_t nblocks = 0, size = 0; - for (const auto &s: res.spans) - if (s.connection_id == p.info.connection_id) - nblocks += s.nblocks, size += s.size; - tools::success_msg_writer() << address << " " << p.info.peer_id << " " << - epee::string_tools::pad_string(p.info.state, 16) << " " << - epee::string_tools::pad_string(epee::string_tools::to_string_hex(p.info.pruning_seed), 8) << " " << p.info.height << " " << - p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; + auto maybe_sync = try_running([this] { return invoke(); }, "Failed to retrieve sync info"); + if (!maybe_sync) + return false; + auto& sync = *maybe_sync; + + uint64_t height = sync["height"].get(); + uint64_t target = std::max(sync.value("target_height", height), height); + tools::success_msg_writer() << "Height: " << height << ", target: " << target << " (" << (100.0 * height / target) << "%)"; + auto& spans = sync["spans"]; + auto& peers = sync["peers"]; + uint64_t current_download = 0; + for (const auto& p: peers) + current_download += p["current_download"].get(); + tools::success_msg_writer() << "Downloading at " << current_download/1000.0 << " kB/s"; + if (auto nnps = sync.value("next_needed_pruning_seed", 0)) + tools::success_msg_writer() << "Next needed pruning seed: " << nnps; + + tools::success_msg_writer() << std::to_string(peers.size()) << " peers"; + for (const auto& [cid, p]: peers.items()) + { + std::string address = epee::string_tools::pad_string(p["ip"].get() + ":" + std::to_string(p["port"].get()), 24); + uint64_t nblocks = 0, size = 0; + for (const auto& s: spans) { + if (s["connection_id"] == cid) { + nblocks += s["nblocks"].get(); + size += s["size"].get(); + } } + tools::success_msg_writer() << address << " " << p["peer_id"].get() << " " << + epee::string_tools::pad_string(p["state"].get(), 16) << " " << + //epee::string_tools::pad_string(epee::string_tools::to_string_hex(p.info.pruning_seed), 8) << " " << + p["height"].get() << " " << + p["current_download"].get() / 1000. << " kB/s, " << + nblocks << " blocks / " << size/1'000'000. << " MB queued"; + } + - uint64_t total_size = 0; - for (const auto &s: res.spans) - total_size += s.size; - tools::success_msg_writer() << std::to_string(res.spans.size()) << " spans, " << total_size/1e6 << " MB"; - tools::success_msg_writer() << res.overview; - for (const auto &s: res.spans) + uint64_t total_size = 0; + for (const auto& s: spans) + total_size += s["size"].get(); + tools::success_msg_writer() << std::to_string(spans.size()) << " spans, " << total_size/1e6 << " MB"; + if (auto overview = sync["overview"].get(); overview != "[]"sv) + tools::success_msg_writer() << overview; + for (const auto& s: spans) + { + auto& c = peers[s["connection_id"].get_ref()]; + std::string address = "(unknown)"; + if (c.is_object()) + address = c["ip"].get() + ":" + std::to_string(c["port"].get()); + address = epee::string_tools::pad_string(std::move(address), 24); + //std::string pruning_seed = epee::string_tools::to_string_hex(tools::get_pruning_seed(s.start_block_height, std::numeric_limits::max(), CRYPTONOTE_PRUNING_LOG_STRIPES)); + auto size = s["size"].get(); + auto start = s["start_block_height"].get(); + auto nblocks = s["nblocks"].get(); { - std::string address = epee::string_tools::pad_string(s.remote_address, 24); - std::string pruning_seed = epee::string_tools::to_string_hex(tools::get_pruning_seed(s.start_block_height, std::numeric_limits::max(), CRYPTONOTE_PRUNING_LOG_STRIPES)); - if (s.size == 0) - { - tools::success_msg_writer() << address << " " << s.nblocks << "/" << pruning_seed << " (" << s.start_block_height << " - " << (s.start_block_height + s.nblocks - 1) << ") -"; - } + auto writer = tools::success_msg_writer(); + writer << address << " " << nblocks << /*"/" << pruning_seed <<*/ " (" << start << " - " << (start + nblocks - 1); + if (size == 0) + writer << ") -"; else - { - tools::success_msg_writer() << address << " " << s.nblocks << "/" << pruning_seed << " (" << s.start_block_height << " - " << (s.start_block_height + s.nblocks - 1) << ", " << (uint64_t)(s.size/1e3) << " kB) " << (unsigned)(s.rate/1e3) << " kB/s (" << s.speed/100.0f << ")"; - } + writer << ", " << size/1000. << " kB) " << s["rate"].get() / 1000. << " kB/s (" << s["speed"].get() / 100. << ")"; } + } - return true; + return true; } static std::string to_string_rounded(double d, int precision) { @@ -1545,143 +1545,155 @@ static std::string to_string_rounded(double d, int precision) { return ss.str(); } -static void print_vote_history(std::ostringstream &stream, std::vector const &votes) -{ - if (votes.empty()) - stream << "(Awaiting votes from master node)"; - - // NOTE: Votes were stored in a ring buffer and copied naiively into the vote - // array so they may be out of order. Find the smallest entry (by height) and - // print starting from that entry. - auto it = std::min_element(votes.begin(), votes.end(), [](const auto &a, const auto &b) { return a.height < b.height; }); - size_t offset = std::distance(votes.begin(), it); - - for (size_t i = 0; i < votes.size(); i++) - { - if (i > 0) stream << ", "; - const auto& entry = votes[(offset + i) % votes.size()]; - stream << "[" << entry.height; - if (entry.is_POS and entry.POS.round > 0) - // For a typical POS round just [1234,yes]. For a backup round: [1234+3,yes] - stream << "+" << +entry.POS.round; - - stream << "," << (entry.voted ? "yes" : "NO") << "]"; +// static void print_vote_history(std::ostringstream &stream, std::vector const &votes) +// { +// if (votes.empty()) +// stream << "(Awaiting votes from master node)"; + +// // NOTE: Votes were stored in a ring buffer and copied naiively into the vote +// // array so they may be out of order. Find the smallest entry (by height) and +// // print starting from that entry. +// auto it = std::min_element(votes.begin(), votes.end(), [](const auto &a, const auto &b) { return a.height < b.height; }); +// size_t offset = std::distance(votes.begin(), it); + +// for (size_t i = 0; i < votes.size(); i++) +// { +// if (i > 0) stream << ", "; +// const auto& entry = votes[(offset + i) % votes.size()]; +// stream << "[" << entry.height; +// if (entry.is_POS and entry.POS.round > 0) +// // For a typical POS round just [1234,yes]. For a backup round: [1234+3,yes] +// stream << "+" << +entry.POS.round; + +// stream << "," << (entry.voted ? "yes" : "NO") << "]"; +// } +// } + +// template +// static void print_participation_history(std::ostringstream &stream, std::vector const &votes) +// { +// if (votes.empty()) +// stream << "(Awaiting timesync data from master node)"; + +// for (size_t i = 0; i < votes.size(); i++) +// { +// if (i > 0) stream << ", "; +// stream << "["<< (votes[i].pass() ? "yes" : "NO") << "]"; +// } +// } + +template +void print_votes(std::ostream& o, const json& elem, const std::string& key, EPrinter eprint) { + std::vector voted, missed; + if (auto it = elem.find(key); it != elem.end()) { + (*it)["voted"].get_to(voted); + (*it)["missed"].get_to(missed); } -} - -template -static void print_participation_history(std::ostringstream &stream, std::vector const &votes) -{ - if (votes.empty()) - stream << "(Awaiting timesync data from master node)"; - - for (size_t i = 0; i < votes.size(); i++) - { - if (i > 0) stream << ", "; - stream << "["<< (votes[i].pass() ? "yes" : "NO") << "]"; + if (voted.empty() && missed.empty()) + o << "(Awaiting votes from master node)"; + else { + o << voted.size() << " voted"; + if (!voted.empty()) + o << " [" << tools::join_transform(" ", voted, eprint) << "]"; + if (missed.empty()) + o << ", none missed."; + else + o << ", " << missed.size() << " MISSED VOTES [" << tools::join_transform(" ", missed, eprint) << "]"; } } -static void append_printable_master_node_list_entry(cryptonote::network_type nettype, bool detailed_view, uint64_t blockchain_height, uint64_t entry_index, GET_MASTER_NODES::response::entry const &entry, std::string &buffer,uint8_t hf_version) +static void append_printable_master_node_list_entry(cryptonote::network_type nettype, bool detailed_view, uint64_t blockchain_height, uint64_t entry_index, const json& entry, std::string& buffer) { const char indent1[] = " "; const char indent2[] = " "; const char indent3[] = " "; - bool is_registered = entry.total_contributed >= entry.staking_requirement; + bool is_funded = entry["funded"].get(); std::ostringstream stream; // Print Funding Status { - stream << indent1 << "[" << entry_index << "] " << "Master Node: " << entry.master_node_pubkey << " "; - stream << "v" << tools::join(".", entry.master_node_version) << "\n"; + stream << indent1 << "[" << entry_index << "] " << "Master Node: " << entry["master_node_pubkey"].get() << " "; + if (auto e = entry.find("master_node_version"); e != entry.end()) + stream << "v" << tools::join(".", entry["master_node_version"].get>()) << "\n"; + else + stream << "v(unknown)\n"; if (detailed_view) { - stream << indent2 << "Total Contributed/Staking Requirement: " << cryptonote::print_money(entry.total_contributed) << "/" << cryptonote::print_money(entry.staking_requirement) << "\n"; - stream << indent2 << "Total Reserved: " << cryptonote::print_money(entry.total_reserved) << "\n"; + stream << indent2 << "Total Contributed/Staking Requirement: " << cryptonote::print_money(entry["total_contributed"].get()) + << "/" << cryptonote::print_money(entry["staking_requirement"].get()) << "\n"; + if (auto it = entry.find("total_reserved"); it != entry.end()) + stream << indent2 << "Total Reserved: " << cryptonote::print_money(it->get()) << "\n"; } } // Print expiry information uint64_t const now = time(nullptr); { - uint64_t expiry_height = 0; - if (entry.registration_hf_version >= cryptonote::network_version_11_infinite_staking) - { - expiry_height = entry.requested_unlock_height; - } - else if (entry.registration_hf_version >= cryptonote::network_version_10_bulletproofs) - { - expiry_height = entry.registration_height + master_nodes::staking_num_lock_blocks(nettype,entry.registration_hf_version); - expiry_height += STAKING_REQUIREMENT_LOCK_BLOCKS_EXCESS; - } - else - { - expiry_height = entry.registration_height + master_nodes::staking_num_lock_blocks(nettype,entry.registration_hf_version); - } + auto expiry_height = entry["requested_unlock_height"].get(); - stream << indent2 << "Registration: Hardfork Version: " << entry.registration_hf_version << "; Height: " << entry.registration_height << "; Expiry: "; + stream << indent2 << "Registration: Hardfork Version: " << entry["registration_hf_version"].get() + << "; Height: " << entry["registration_height"].get() + << "; Expiry: "; if (expiry_height == master_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT) - { stream << "Staking Infinitely (stake unlock not requested)\n"; - } else { uint64_t delta_height = (blockchain_height >= expiry_height) ? 0 : expiry_height - blockchain_height; - uint64_t expiry_epoch_time = now + (delta_height * tools::to_seconds(TARGET_BLOCK_TIME)); + auto expiry_epoch_time = now + (delta_height * tools::to_seconds(cryptonote::TARGET_BLOCK_TIME)); stream << expiry_height << " (in " << delta_height << ") blocks\n"; - stream << indent2 << "Expiry Date (estimated): " << get_date_time(expiry_epoch_time) << " (" << get_human_time_ago(expiry_epoch_time, now) << ")\n"; + stream << indent2 << "Expiry Date (estimated): " << + date::format("%Y-%m-%d %I:%M:%S %p UTC", std::chrono::system_clock::from_time_t(expiry_epoch_time)) << + " (" << get_human_time_ago(expiry_epoch_time, now) << ")\n"; } } - if (detailed_view && is_registered) // Print reward status + if (detailed_view && is_funded) // Print reward status { - stream << indent2 << "Last Reward (Or Penalty) At (Height/TX Index): " << entry.last_reward_block_height << "/" << entry.last_reward_transaction_index << "\n"; + stream << indent2 << "Last Reward (Or Penalty) At (Height/TX Index): " << entry["last_reward_block_height"].get() << "/" << entry["last_reward_transaction_index"].get() << "\n"; } if (detailed_view) // Print operator information { - stream << indent2 << "Operator Cut (\% Of Reward): " << to_string_rounded((entry.portions_for_operator / (double)STAKING_PORTIONS) * 100.0, 2) << "%\n"; - stream << indent2 << "Operator Address: " << entry.operator_address << "\n"; + stream << indent2 << "Operator Fee: " << to_string_rounded(entry["operator_fee"].get() / 1000., 3) << "%\n"; + stream << indent2 << "Operator Address: " << entry["operator_address"].get() << "\n"; } - if (is_registered) // Print master node tests + if (is_funded) // Print master node tests { - epee::console_colors uptime_proof_color = (entry.last_uptime_proof == 0) ? epee::console_color_red : epee::console_color_green; + auto proof_time = entry.value("last_uptime_proof", uint64_t{0}); + epee::console_colors uptime_proof_color = proof_time ? epee::console_color_red : epee::console_color_green; - stream << indent2; - if (entry.last_uptime_proof == 0) - { - stream << "Last Uptime Proof Received: (Awaiting confirmation from network)"; - } - else - { - stream << "Last Uptime Proof Received: " << get_human_time_ago(entry.last_uptime_proof, time(nullptr)); - } + stream << indent2 << "Last Uptime Proof Received: " << (proof_time == 0 ? "(Awaiting confirmation from network)" : get_human_time_ago(proof_time, time(nullptr))); // // NOTE: Node Identification // stream << "\n"; stream << indent2 << "IP Address & Ports: "; - if (entry.public_ip == "0.0.0.0") + if (entry.value("public_ip", "0.0.0.0"s) == "0.0.0.0") stream << "(Awaiting confirmation from network)"; else - stream << entry.public_ip << " :" << entry.storage_port << " (storage https), :" << entry.storage_lmq_port - << " (storage omq), :" << entry.quorumnet_port << " (quorumnet)"; + stream << entry["public_ip"].get() << " :" << entry["storage_port"].get() << " (storage https), :" + << entry["storage_lmq_port"].get() << " (storage omq), :" << entry["quorumnet_port"].get() << " (quorumnet)"; stream << "\n"; - if (detailed_view) + if (detailed_view){ + auto ed_pk = entry.value("pubkey_ed25519", ""sv); stream << indent2 << "Auxiliary Public Keys:\n" - << indent3 << (entry.pubkey_ed25519.empty() ? "(not yet received)" : entry.pubkey_ed25519) << " (Ed25519)\n" - << indent3 << (entry.pubkey_ed25519.empty() ? "(not yet received)" : oxenc::to_base32z(oxenc::from_hex(entry.pubkey_ed25519)) + ".mnode") << " (Belnet)\n" - << indent3 << (entry.pubkey_x25519.empty() ? "(not yet received)" : entry.pubkey_x25519) << " (X25519)\n"; - + << indent3 << (ed_pk.empty() ? "(not yet received)"sv : ed_pk) << " (Ed25519)\n" + << indent3 << (ed_pk.empty() ? "(not yet received)"s : oxenc::to_base32z(oxenc::from_hex(ed_pk)) + ".mnode") << " (Belnet)\n" + << indent3 << entry.value("pubkey_x25519", "(not yet received)"sv) << " (X25519)\n"; + } // // NOTE: Storage Server Test // - auto print_reachable = [&stream, &now] (bool reachable, auto first_unreachable, auto last_unreachable, auto last_reachable) { + auto print_reachable = [&stream, &now] (const json& j, const std::string& prefix) { + auto first_unreachable = j.value(prefix + "_first_unreachable", 0), + last_unreachable = j.value(prefix + "_last_unreachable", 0), + last_reachable = j.value(prefix + "_last_reachable", 0); + if (first_unreachable == 0) { if (last_reachable == 0) stream << "Not yet tested"; @@ -1693,7 +1705,7 @@ static void append_printable_master_node_list_entry(cryptonote::network_type net } } else { stream << "NO"; - if (!reachable) + if (!j.value(prefix+"_reachable", false)) stream << " - FAILING!"; stream << " (last tested " << get_human_time_ago(last_unreachable, now) << "; failing since " << get_human_time_ago(first_unreachable, now); @@ -1704,71 +1716,105 @@ static void append_printable_master_node_list_entry(cryptonote::network_type net stream << '\n'; }; stream << indent2 << "Storage Server Reachable: "; - print_reachable(entry.storage_server_reachable, entry.storage_server_first_unreachable, entry.storage_server_last_unreachable, entry.storage_server_last_reachable); + print_reachable(entry, "storage_server"); stream << indent2 << "Belnet Reachable: "; - print_reachable(entry.belnet_reachable, entry.belnet_first_unreachable, entry.belnet_last_unreachable, entry.belnet_last_reachable); + print_reachable(entry, "belnet"); // // NOTE: Component Versions // + auto show_component_version = [] (const json& j, std::string_view name) { + if (!j.is_array() || j.empty()) + return "("s + std::string{name} + " ping not yet received)"s; + return tools::join(".", j.get>()); + }; stream << indent2 << "Storage Server / Belnet Router versions: " - << ((entry.storage_server_version[0] == 0 && entry.storage_server_version[1] == 0 && entry.storage_server_version[2] == 0) ? "(Storage server ping not yet received) " : tools::join(".", entry.storage_server_version)) << " / " << ((entry.belnet_version[0] == 0 && entry.belnet_version[1] == 0 && entry.belnet_version[2] == 0) ? "(Belnet ping not yet received)" : tools::join(".", entry.belnet_version)) << "\n"; - - - + << show_component_version(entry["storage_server_version"], "Storage Server") + << " / " + << show_component_version(entry["belnet_version"], "Belnet") + << "\n"; // // NOTE: Print Voting History // - stream << indent2 << "Checkpoints [Height,Voted]: "; - print_vote_history(stream, entry.checkpoint_participation); - - stream << "\n" << indent2 << "POS [Height,Voted]: "; - print_vote_history(stream, entry.POS_participation); + stream << indent2 << "Checkpoints votes: "; + print_votes(stream, entry, "checkpoint_votes", [](uint64_t height) { return height; }); + + stream << '\n' << indent2 << "POS blocks: "; + print_votes>(stream, entry, "POS_votes", + [](const auto& val) { return tools::int_to_string(val.first) + (val.second ? " " + tools::int_to_string(val.second) : ""); }); + + auto print_pass_fail = [&stream, &entry](const std::string& key) { + std::pair val; + auto& [success, fail] = val; + if (auto it = entry.find(key); it != entry.end()) + it->get_to(val); + + if (!success && !fail) + stream << "(Awaiting test data)"; + else { + stream << success << " passes, "; + if (fail) + stream << fail << " FAILURES"; + else + stream << "no failures"; + } + }; - stream << "\n" << indent2 << "Timestamps [in_sync]: "; - print_participation_history(stream, entry.timestamp_participation); + stream << '\n' << indent2 << "Quorumnet tests: "; + print_pass_fail("quorumnet_tests"); - stream << "\n" << indent2 << "Timesync [responded]: "; - print_participation_history(stream, entry.timesync_status); + stream << '\n' << indent2 << "Timesync tests: "; + print_pass_fail("timesync_tests"); + stream << '\n'; } - stream << "\n"; if (detailed_view) // Print contributors { - for (size_t j = 0; j < entry.contributors.size(); ++j) + auto n_contributors = entry["contributors"].size(); + stream << indent2 << "Contributors (" << n_contributors << "):\n"; + for (auto& contributor : entry["contributors"]) { - const auto& contributor = entry.contributors[j]; - stream << indent2 << "[" << j << "] Contributor: " << contributor.address << "\n"; - stream << indent3 << "Amount / Reserved: " << cryptonote::print_money(contributor.amount) << "/" << cryptonote::print_money(contributor.reserved) << "\n"; + stream << indent3 << contributor["address"].get(); + auto amount = contributor["amount"].get(); + auto reserved = contributor.value("reserved", amount); + stream << " (" << cryptonote::print_money(amount, true); + if (reserved != amount) + stream << " / " << cryptonote::print_money(reserved, true); + if (!is_funded || n_contributors > 1) { + auto required = entry["staking_requirement"].get(); + stream << " = " << std::round(reserved / (double) required * 10000.) / 100. << "%"; + } + stream << ")\n"; } } // // NOTE: Overall status // - if (entry.active) { + if (entry["active"].get()) { stream << indent2 << "Current Status: ACTIVE\n"; - stream << indent2 << "Downtime Credits: " << entry.earned_downtime_blocks << " blocks" - << " (about " << to_string_rounded(entry.earned_downtime_blocks / (double) BLOCKS_PER_HOUR, 2) << " hours)"; - - if (entry.earned_downtime_blocks < master_nodes::DECOMMISSION_MINIMUM) + auto downtime = entry["earned_downtime_blocks"].get(); + stream << indent2 << "Downtime Credits: " << downtime << " blocks" + << " (about " << to_string_rounded(downtime / (double)cryptonote::BLOCKS_PER_HOUR, 2) << " hours)"; + if (downtime < master_nodes::DECOMMISSION_MINIMUM) stream << " (Note: " << master_nodes::DECOMMISSION_MINIMUM << " blocks required to enable deregistration delay)"; - } else if (is_registered) { + } else if (is_funded) { stream << indent2 << "Current Status: DECOMMISSIONED" ; - if (entry.last_decommission_reason_consensus_all || entry.last_decommission_reason_consensus_any) + auto reason_all = entry["last_decommission_reason_consensus_all"].get(); + auto reason_any = entry["last_decommission_reason_consensus_any"].get(); + if (reason_any) stream << " - "; - if (auto reasons = cryptonote::readable_reasons(entry.last_decommission_reason_consensus_all); !reasons.empty()) + if (auto reasons = cryptonote::readable_reasons(reason_all); !reasons.empty()) stream << tools::join(", ", reasons); // Add any "any" reasons that aren't in all with a (some) qualifier - if (auto reasons = cryptonote::readable_reasons(entry.last_decommission_reason_consensus_any & ~entry.last_decommission_reason_consensus_all); !reasons.empty()) { + if (auto reasons = cryptonote::readable_reasons(reason_any & ~reason_all); !reasons.empty()) { for (auto& r : reasons) r += "(some)"; - stream << (entry.last_decommission_reason_consensus_all ? ", " : "") << tools::join(", ", reasons); + stream << (reason_all ? ", " : "") << tools::join(", ", reasons); } stream << "\n"; - stream << indent2 << "Remaining Decommission Time Until DEREGISTRATION: " << entry.earned_downtime_blocks << " blocks"; - } else { + stream << indent2 << "Remaining Decommission Time Until DEREGISTRATION: " << entry["earned_downtime_blocks"].get() << " blocks"; } else { stream << indent2 << "Current Status: awaiting contributions\n"; } stream << "\n"; @@ -1776,176 +1822,174 @@ static void append_printable_master_node_list_entry(cryptonote::network_type net buffer.append(stream.str()); } -bool rpc_command_executor::print_mn(const std::vector &args) +bool rpc_command_executor::print_mn(const std::vector &args, bool self) { - GET_MASTER_NODES::request req{}; - GET_MASTER_NODES::response res{}; + std::vector pubkeys; bool detailed_view = false; for (auto& arg : args) { if (arg == "+json") - req.include_json = true; + tools::fail_msg_writer() << "+json is no longer supported"; else if (arg == "+detail") detailed_view = true; - else - req.master_node_pubkeys.push_back(arg); + else if (self) { + tools::fail_msg_writer() << "print_mn_status takes no pubkey arguments"; + return false; + } else + pubkeys.push_back(arg); } - GET_INFO::response get_info_res{}; - - if (!invoke({}, get_info_res, "Failed to retrieve node info") || - !invoke(std::move(req), res, "Failed to retrieve master node data")) + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) return false; + auto& info = *maybe_info; cryptonote::network_type nettype = - get_info_res.mainnet ? cryptonote::MAINNET : - get_info_res.devnet ? cryptonote::DEVNET : - get_info_res.testnet ? cryptonote::TESTNET : - cryptonote::UNDEFINED; - uint64_t curr_height = get_info_res.height; + info.value("mainnet", false) ? cryptonote::network_type::MAINNET : + info.value("devnet", false) ? cryptonote::network_type::DEVNET : + info.value("testnet", false) ? cryptonote::network_type::TESTNET : + cryptonote::network_type::UNDEFINED; + uint64_t curr_height = info["height"].get(); + + std::vector awaiting; + std::vector registered; + + std::string my_mn_pk; + if (!self) { + auto maybe_mns = try_running([&] { return invoke(json{{"master_node_pubkeys", pubkeys}}); }, + "Failed to retrieve master node data"); + if (!maybe_mns) + return false; - std::vector unregistered; - std::vector registered; - registered.reserve(res.master_node_states.size()); + for (auto &entry : (*maybe_mns)["master_node_states"]) + { + if (entry["total_contributed"].get() == entry["staking_requirement"].get()) + registered.push_back(std::move(entry)); + else + awaiting.push_back(std::move(entry)); + } + } else { + auto maybe_mn = try_running([&] { return invoke(); }, + "Failed to retrieve master node status"); + if (!maybe_mn) + return false; + auto& mn = (*maybe_mn)["master_node_state"]; + my_mn_pk = mn["master_node_pubkey"]; + if (mn.find("registration_height") != mn.end()) { + if (mn["total_contributed"].get() == mn["staking_requirement"].get()) + registered.push_back(std::move(mn)); + else + awaiting.push_back(std::move(mn)); + } + } - for (auto &entry : res.master_node_states) + if (awaiting.size() == 0 && registered.size() == 0) { - if (entry.total_contributed == entry.staking_requirement) - registered.push_back(&entry); + if (pubkeys.size() > 0) + tools::msg_writer() << "No master node is currently known on the network: " << tools::join(", ", pubkeys); + else if (self) + tools::msg_writer() << "master node " << my_mn_pk << " is not currently registered on the network"; else - unregistered.push_back(&entry); + tools::msg_writer() << "No master nodes are currently known on the network"; + + return true; } - std::sort(unregistered.begin(), unregistered.end(), [](auto *a, auto *b) { - uint64_t a_remaining = a->staking_requirement - a->total_reserved; - uint64_t b_remaining = b->staking_requirement - b->total_reserved; + + std::sort(awaiting.begin(), awaiting.end(), [](const json& a, const json& b) { + auto a_res = a.find("total_reserved"); + auto b_res = b.find("total_reserved"); + uint64_t total_a = (a_res == a.end() ? a["total_contributed"] : *a_res).get(); + uint64_t total_b = (b_res == b.end() ? b["total_contributed"] : *b_res).get(); + uint64_t a_remaining = a["staking_requirement"].get() - total_a; + uint64_t b_remaining = b["staking_requirement"].get() - total_b; if (b_remaining == a_remaining) - return b->portions_for_operator < a->portions_for_operator; + return b["portions_for_operator"].get() < a["portions_for_operator"].get(); return b_remaining < a_remaining; }); - std::sort(registered.begin(), registered.end(), [](auto *a, auto *b) { - return std::make_tuple(a->last_reward_block_height, a->last_reward_transaction_index, a->master_node_pubkey) - < std::make_tuple(b->last_reward_block_height, b->last_reward_transaction_index, b->master_node_pubkey); + std::sort(registered.begin(), registered.end(), [](const json& a, const json& b) { + return std::make_tuple(a["last_reward_block_height"].get(), a["last_reward_transaction_index"].get(), a["master_node_pubkey"].get()) + < std::make_tuple(b["last_reward_block_height"].get(), b["last_reward_transaction_index"].get(), b["master_node_pubkey"].get()); }); - if (req.include_json) - { - std::cout << res.as_json << std::endl; - return true; - } - - if (unregistered.size() == 0 && registered.size() == 0) - { - if (req.master_node_pubkeys.size() > 0) - { - int str_size = 0; - for (const std::string &arg : req.master_node_pubkeys) str_size += (arg.size() + 2); - - std::string buffer; - buffer.reserve(str_size); - for (size_t i = 0; i < req.master_node_pubkeys.size(); ++i) - { - buffer.append(req.master_node_pubkeys[i]); - if (i < req.master_node_pubkeys.size() - 1) buffer.append(", "); - } - - tools::msg_writer() << "No master node is currently known on the network: " << buffer; - } - else - { - tools::msg_writer() << "No master node is currently known on the network"; - } - - return true; - } - - std::string unregistered_print_data; + std::string awaiting_print_data; std::string registered_print_data; - const uint8_t hf_version = cryptonote::get_network_version(nettype, curr_height); - for (size_t i = 0; i < unregistered.size(); i++) + for (size_t i = 0; i < awaiting.size(); i++) { - if (i) unregistered_print_data.append("\n"); - append_printable_master_node_list_entry(nettype, detailed_view, curr_height, i, *unregistered[i], unregistered_print_data, hf_version); + if (i > 0) awaiting_print_data += '\n'; + append_printable_master_node_list_entry(nettype, detailed_view, curr_height, i, awaiting[i], awaiting_print_data); } for (size_t i = 0; i < registered.size(); i++) { - if (i) registered_print_data.append("\n"); - append_printable_master_node_list_entry(nettype, detailed_view, curr_height, i, *registered[i], registered_print_data,hf_version); + if (i > 0) registered_print_data += '\n'; + append_printable_master_node_list_entry(nettype, detailed_view, curr_height, i, registered[i], registered_print_data); } - if (unregistered.size() > 0) - tools::msg_writer() << "Master Node Unregistered State [" << unregistered.size() << "]\n" << unregistered_print_data; + if (awaiting.size() > 0) + tools::msg_writer() << "master Node Awaiting State [" << awaiting.size() << "]\n" << awaiting_print_data; if (registered.size() > 0) - tools::msg_writer() << "Master Node Registration State [" << registered.size() << "]\n" << registered_print_data; + tools::msg_writer() << "master Node Registration State [" << registered.size() << "]\n" << registered_print_data; return true; } bool rpc_command_executor::flush_cache(bool bad_txs, bool bad_blocks) { - FLUSH_CACHE::response res{}; - FLUSH_CACHE::request req{}; - req.bad_txs = bad_txs; - req.bad_blocks = bad_blocks; - if (!invoke(std::move(req), res, "Failed to flush TX cache")) - return false; + try { + invoke(json{{"bad_txs", bad_txs}, {"bad_blocks", bad_blocks}}); + } catch (const std::exception& e) { + tools::fail_msg_writer()<<"Failed to flush cache: "<< e.what(); + return false; + } + tools::success_msg_writer() << "Cache flushed successfully"; return true; } bool rpc_command_executor::print_mn_status(std::vector args) { - if (args.size() > 1) - { - tools::fail_msg_writer() << "Unexpected arguments"; - return false; - } - - GET_MASTER_KEYS::response res{}; - if (!invoke({}, res, "Failed to retrieve master node keys")) - return false; - - args.push_back(std::move(res.master_node_pubkey)); - - return print_mn(args); + return print_mn(std::move(args), true); } bool rpc_command_executor::print_sr(uint64_t height) { - GET_STAKING_REQUIREMENT::response res{}; - if (!invoke({height}, res, "Failed to retrieve staking requirements")) + auto maybe_staking_requirement = try_running([this, height] { return invoke(json{{"height", height}}); }, "Failed to retrieve staking requirements"); + if (!maybe_staking_requirement) return false; - - tools::success_msg_writer() << "Staking Requirement: " << cryptonote::print_money(res.staking_requirement); + + auto& staking_requirement = *maybe_staking_requirement; + tools::success_msg_writer() << "Staking Requirement: " << cryptonote::print_money(staking_requirement["staking_requirement"]); return true; } bool rpc_command_executor::pop_blocks(uint64_t num_blocks) { - POP_BLOCKS::response res{}; - if (!invoke({num_blocks}, res, "Popping blocks failed")) + auto maybe_pop_blocks = try_running([this, num_blocks] { return invoke(json{{"nblocks", num_blocks}}); }, "Failed to pop blocks"); + if (!maybe_pop_blocks) return false; + auto& pop_blocks = *maybe_pop_blocks; - tools::success_msg_writer() << "new height: " << res.height; + tools::success_msg_writer() << "new height: " << pop_blocks["height"]; return true; } bool rpc_command_executor::print_mn_key() { - GET_MASTER_KEYS::response res{}; - - if (!invoke({}, res, "Failed to retrieve master node keys")) + auto maybe_master_keys = try_running([this] { return invoke(json{}); }, "Failed to retrieve master node keys"); + if (!maybe_master_keys) return false; + auto my_mn_keys = *maybe_master_keys; + tools::success_msg_writer() - << "Master Node Public Key: " << res.master_node_pubkey - << "\n Ed25519 Public Key: " << res.master_node_ed25519_pubkey - << "\n X25519 Public Key: " << res.master_node_x25519_pubkey; + << "Master Node Public Key: " << my_mn_keys["master_node_pubkey"] + << "\n Ed25519 Public Key: " << my_mn_keys["master_node_ed25519_pubkey"] + << "\n X25519 Public Key: " << my_mn_keys["master_node_x25519_pubkey"]; return true; } @@ -1954,10 +1998,10 @@ static uint64_t get_amount_to_make_portions(uint64_t amount, uint64_t portions) { uint64_t lo, hi, resulthi, resultlo; lo = mul128(amount, portions, &hi); - if (lo > UINT64_MAX - (STAKING_PORTIONS - 1)) + if (lo > UINT64_MAX - (cryptonote::old::STAKING_PORTIONS - 1)) hi++; - lo += STAKING_PORTIONS-1; - div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo); + lo += cryptonote::old::STAKING_PORTIONS-1; + div128_64(hi, lo, cryptonote::old::STAKING_PORTIONS, &resulthi, &resultlo); return resultlo; } @@ -1965,7 +2009,7 @@ static uint64_t get_actual_amount(uint64_t amount, uint64_t portions) { uint64_t lo, hi, resulthi, resultlo; lo = mul128(amount, portions, &hi); - div128_64(hi, lo, STAKING_PORTIONS, &resulthi, &resultlo); + div128_64(hi, lo, cryptonote::old::STAKING_PORTIONS, &resulthi, &resultlo); return resultlo; } @@ -1980,69 +2024,68 @@ bool rpc_command_executor::prepare_registration(bool force_registration) auto scoped_log_cats = std::unique_ptr(new clear_log_categories()); // Check if the daemon was started in master Node or not - GET_INFO::response res{}; - GET_MASTER_KEYS::response kres{}; - HARD_FORK_INFO::response hf_res{}; - if (!invoke({}, res, "Failed to get node info") || - !invoke({}, hf_res, "Failed to retrieve hard fork info") || - !invoke({}, kres, "Failed to retrieve master node keys")) + auto maybe_info = try_running([this] { return invoke(); }, "Failed to retrieve node info"); + if (!maybe_info) return false; + auto& info = *maybe_info; - if (!res.master_node) + auto maybe_hf = try_running([this] { return invoke(); }, + "Failed to retrieve hard fork info"); + if (!maybe_hf) + return false; + auto& hfinfo = *maybe_hf; + + if (!info.value("master_node", false)) { tools::fail_msg_writer() << "Unable to prepare registration: this daemon is not running in --master-node mode"; return false; } - else if (auto last_belnet_ping = static_cast(res.last_belnet_ping.value_or(0)); + else if (auto last_belnet_ping = info.value("last_belnet_ping", 0); last_belnet_ping < (time(nullptr) - 60) && !force_registration) { tools::fail_msg_writer() << "Unable to prepare registration: this daemon has not received a ping from belnet " - << (res.last_belnet_ping == 0 ? "yet" : "since " + get_human_time_ago(last_belnet_ping, std::time(nullptr))); + << (last_belnet_ping == 0 ? "yet" : "since " + get_human_time_ago(last_belnet_ping, std::time(nullptr))); return false; } - else if (auto last_storage_server_ping = static_cast(res.last_storage_server_ping.value_or(0)); + else if (auto last_storage_server_ping = info.value("last_storage_server_ping", 0); last_storage_server_ping < (time(nullptr) - 60) && !force_registration) { tools::fail_msg_writer() << "Unable to prepare registration: this daemon has not received a ping from the storage server " - << (res.last_storage_server_ping == 0 ? "yet" : "since " + get_human_time_ago(last_storage_server_ping, std::time(nullptr))); + << (last_storage_server_ping == 0 ? "yet" : "since " + get_human_time_ago(last_storage_server_ping, std::time(nullptr))); return false; } - uint64_t block_height = std::max(res.height, res.target_height); - uint8_t hf_version = hf_res.version; + uint64_t block_height = std::max(info["height"].get(), info["target_height"].get()); + auto hf_version = hfinfo["version"].get(); #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) cryptonote::network_type const nettype = cryptonote::FAKECHAIN; #else - cryptonote::network_type const nettype = - res.mainnet ? cryptonote::MAINNET : - res.devnet ? cryptonote::DEVNET : - res.testnet ? cryptonote::TESTNET : - res.nettype == "fakechain" ? cryptonote::FAKECHAIN : - cryptonote::UNDEFINED; + cryptonote::network_type nettype = + info.value("mainnet", false) ? cryptonote::network_type::MAINNET : + info.value("devnet", false) ? cryptonote::network_type::DEVNET : + info.value("testnet", false) ? cryptonote::network_type::TESTNET : + cryptonote::UNDEFINED; #endif // Query the latest block we've synced and check that the timestamp is sensible, issue a warning if not { - GET_LAST_BLOCK_HEADER::response res{}; - - if (!invoke({}, res, "Get latest block failed, unable to check sync status")) + auto const& maybe_header = try_running([this] { return invoke().at("block_header").get();}, "Get latest block failed, unable to check sync status"); + if (!maybe_header) return false; - auto const& header = res.block_header; - uint64_t const now = time(nullptr); + auto const& header = *maybe_header; + + const auto now = std::chrono::system_clock::now(); + const auto block_ts = std::chrono::system_clock::from_time_t(header.timestamp); - if (now >= header.timestamp) + if (now - block_ts >= 10min) { - uint64_t delta = now - header.timestamp; - if (delta > (60 * 60)) - { - tools::fail_msg_writer() << "The last block this Master Node knows about was at least " << get_human_time_ago(header.timestamp, now) - << "\nYour node is possibly desynced from the network or still syncing to the network." - << "\n\nRegistering this node may result in a deregistration due to being out of date with the network\n"; - } + tools::fail_msg_writer() << "The last block this Master Node knows about was at least " << get_human_time_ago(now - block_ts) + << "\nYour node is possibly desynced from the network or still syncing to the network." + << "\n\nRegistering this node may result in a deregistration due to being out of date with the network\n"; } - if (block_height >= header.height) + if (auto synced_height = header.height; block_height >= synced_height) { uint64_t delta = block_height - header.height; if (delta > 15) @@ -2058,8 +2101,8 @@ bool rpc_command_executor::prepare_registration(bool force_registration) master_nodes::get_staking_requirement(block_height + 30 * 24)); // allow 1 day // anything less than DUST will be added to operator stake - const uint64_t DUST = MAX_NUMBER_OF_CONTRIBUTORS; - std::cout << "\n BELDEX MASTER NODE REGISTRATION "<< std::endl; + const uint64_t DUST = beldex::MAX_NUMBER_OF_CONTRIBUTORS; + std::cout << "\n BELDEX MASTER NODE REGISTRATION "<< std::endl; //check std::cout << "Current staking requirement: " << cryptonote::print_money(staking_requirement) << " " << cryptonote::get_unit() << std::endl; enum struct register_step @@ -2082,8 +2125,8 @@ bool rpc_command_executor::prepare_registration(bool force_registration) register_step prev_step = register_step::is_open_stake__operator_address_to_reserve; bool is_solo_stake; size_t num_participants = 1; - uint64_t operator_fee_portions = STAKING_PORTIONS; - uint64_t portions_remaining = STAKING_PORTIONS; + uint64_t operator_fee_portions = cryptonote::old::STAKING_PORTIONS; + uint64_t portions_remaining = cryptonote::old::STAKING_PORTIONS; uint64_t total_reserved_contributions = 0; std::vector addresses; std::vector contributions; @@ -2120,7 +2163,7 @@ bool rpc_command_executor::prepare_registration(bool force_registration) { std::string address_str; state.addresses.push_back(address_str); - state.contributions.push_back(STAKING_PORTIONS); + state.contributions.push_back(cryptonote::old::STAKING_PORTIONS); state.portions_remaining = 0; state.total_reserved_contributions += staking_requirement; state.prev_step = step; @@ -2189,7 +2232,7 @@ bool rpc_command_executor::prepare_registration(bool force_registration) case register_step::is_open_stake__how_many_more_contributors: { - std::string prompt = "Number of additional contributors [1-" + std::to_string(MAX_NUMBER_OF_CONTRIBUTORS - 1) + "]"; + std::string prompt = "Number of additional contributors [1-" + std::to_string(beldex::MAX_NUMBER_OF_CONTRIBUTORS - 1) + "]"; std::string input; last_input_result = input_line_back_cancel_get_input(prompt.c_str(), input); @@ -2203,9 +2246,9 @@ bool rpc_command_executor::prepare_registration(bool force_registration) } long additional_contributors = strtol(input.c_str(), NULL, 10 /*base 10*/); - if (additional_contributors < 1 || additional_contributors > (MAX_NUMBER_OF_CONTRIBUTORS - 1)) + if (additional_contributors < 1 || additional_contributors > static_cast(beldex::MAX_NUMBER_OF_CONTRIBUTORS - 1)) { - std::cout << "Invalid value. Should be between [1-" << (MAX_NUMBER_OF_CONTRIBUTORS - 1) << "]" << std::endl; + std::cout << "Invalid value. Should be between [1-" << (beldex::MAX_NUMBER_OF_CONTRIBUTORS - 1) << "]" << std::endl; continue; } @@ -2412,8 +2455,8 @@ bool rpc_command_executor::prepare_registration(bool force_registration) assert(state.addresses.size() == state.contributions.size()); const uint64_t amount_left = staking_requirement - state.total_reserved_contributions; - std::cout << "Summary:" << std::endl; - std::cout << "Operating costs as % of reward: " << (state.operator_fee_portions * 100.0 / static_cast(STAKING_PORTIONS)) << "%" << std::endl; + std::cout << "nRegistration Summary:" << std::endl; + std::cout << "Operating costs as % of reward: " << (state.operator_fee_portions * 100.0 / static_cast(cryptonote::old::STAKING_PORTIONS)) << "%" << std::endl; printf("%-16s%-9s%-19s%-s\n", "Contributor", "Address", "Contribution", "Contribution(%)"); printf("%-16s%-9s%-19s%-s\n", "___________", "_______", "____________", "_______________"); @@ -2423,7 +2466,7 @@ bool rpc_command_executor::prepare_registration(bool force_registration) uint64_t amount = get_actual_amount(staking_requirement, state.contributions[i]); if (amount_left <= DUST && i == 0) amount += amount_left; // add dust to the operator. - printf("%-16s%-9s%-19s%-.9f\n", participant_name.c_str(), state.addresses[i].substr(0, 6).c_str(), cryptonote::print_money(amount).c_str(), (double)state.contributions[i] * 100 / (double)STAKING_PORTIONS); + printf("%-16s%-9s%-19s%-.9f\n", participant_name.c_str(), state.addresses[i].substr(0, 6).c_str(), cryptonote::print_money(amount).c_str(), (double)state.contributions[i] * 100 / (double)cryptonote::old::STAKING_PORTIONS); } if (amount_left > DUST) @@ -2489,18 +2532,13 @@ bool rpc_command_executor::prepare_registration(bool force_registration) scoped_log_cats.reset(); { - GET_MASTER_NODE_REGISTRATION_CMD_RAW::request req{}; - GET_MASTER_NODE_REGISTRATION_CMD_RAW::response res{}; - - req.args = args; - req.make_friendly = true; - req.staking_requirement = staking_requirement; - - if (!invoke(std::move(req), res, "Failed to validate registration arguments; " - "check the addresses and registration parameters and that the Daemon is running with the '--master-node' flag")) + auto maybe_registration = try_running([this, staking_requirement, &args] { return invoke(json{{"staking_requirement", staking_requirement}, {"args", args}, {"make_friendly", true}}); }, "Failed to validate registration arguments; check the addresses and registration parameters and that the Daemon is running with the '--master-node' flag"); + if (!maybe_registration) return false; - tools::success_msg_writer() << res.registration_cmd; + auto& registration = *maybe_registration; + + tools::success_msg_writer() << registration["registration_cmd"].get(); } return true; @@ -2509,10 +2547,8 @@ bool rpc_command_executor::prepare_registration(bool force_registration) bool rpc_command_executor::prune_blockchain() { #if 0 - PRUNE_BLOCKCHAIN::response res{}; - if (!invoke({false}, res, "Failed to prune blockchain")) + if (!invoke(json{{"check", false}}, "Failed to prune blockchain")) return false; - tools::success_msg_writer() << "Blockchain pruned"; #else tools::fail_msg_writer() << "Blockchain pruning is not supported in Beldex yet"; @@ -2522,11 +2558,12 @@ bool rpc_command_executor::prune_blockchain() bool rpc_command_executor::check_blockchain_pruning() { - PRUNE_BLOCKCHAIN::response res{}; - if (!invoke({true}, res, "Failed to check blockchain pruning status")) + auto maybe_pruning = try_running([this] { return invoke(json{{"check", true}}); }, "Failed to check blockchain pruning status"); + if (!maybe_pruning) return false; + auto& pruning = *maybe_pruning; - tools::success_msg_writer() << "Blockchain is" << (res.pruning_seed ? "" : " not") << " pruned"; + tools::success_msg_writer() << "Blockchain " << (pruning["pruning_seed"].get() > 0 ? "is" : "is not") << " pruned"; return true; } @@ -2535,35 +2572,41 @@ bool rpc_command_executor::set_bootstrap_daemon( const std::string &username, const std::string &password) { - SET_BOOTSTRAP_DAEMON::request req{}; - req.address = address; - req.username = username; - req.password = password; - SET_BOOTSTRAP_DAEMON::response res{}; - if (!invoke(std::move(req), res, "Failed to set bootstrap daemon to: " + address)) - return false; + auto maybe_set_bootstrap = try_running([&] { + json params{ + {"address", address}, + {"username", username}, + {"password", password} + }; + return invoke(std::move(params)); }, "Failed to query master node state changes"); + + if (!maybe_set_bootstrap) + return false; + + auto set_bootstrap_daemon = *maybe_set_bootstrap; tools::success_msg_writer() << "Successfully set bootstrap daemon address to " - << (!req.address.empty() ? req.address : "none"); + << (!address.empty() ? address : "none"); return true; } bool rpc_command_executor::version() { - GET_INFO::response response{}; - if (!invoke(GET_INFO::request{}, response, "Failed to query daemon info")) - return false; - tools::success_msg_writer() << response.version; - return true; + auto version = try_running([this] { + return invoke().at("version").get(); + }, "Failed to retrieve node info"); + if (!version) + return false; + tools::success_msg_writer() << *version; + return true; } bool rpc_command_executor::test_trigger_uptime_proof() { - TEST_TRIGGER_UPTIME_PROOF::request req{}; - TEST_TRIGGER_UPTIME_PROOF::response res{}; - return invoke(std::move(req), res, "Failed to trigger uptime proof"); + tools::success_msg_writer() << invoke(json{{}}, "Failed to trigger uptime proof"); + return true; } }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 648ca2a316d..7048ae11b6c 100755 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -39,10 +39,12 @@ #pragma once +#include #include #include "common/common_fwd.h" #include "common/scoped_message_writer.h" +#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/http_client.h" #include "cryptonote_basic/cryptonote_basic.h" #include "rpc/core_rpc_server.h" @@ -54,36 +56,35 @@ namespace daemonize { class rpc_command_executor final { private: - std::optional m_rpc_client; - cryptonote::rpc::core_rpc_server* m_rpc_server = nullptr; - const cryptonote::rpc::rpc_context m_server_context{true}; + std::variant m_rpc; + oxenmq::OxenMQ* m_omq = nullptr; public: - /// Executor for remote connection RPC + /// Executor for HTTP remote connection RPC rpc_command_executor( - std::string remote_url, + std::string http_url, const std::optional& user ); - /// Executor for local daemon RPC - rpc_command_executor(cryptonote::rpc::core_rpc_server& rpc_server) - : m_rpc_server{&rpc_server} {} + /// Executor for OMQ RPC, either local (inproc) or remote. + rpc_command_executor(oxenmq::OxenMQ& omq, oxenmq::ConnectionID conn); + /// FIXME: remove this! + /// /// Runs some RPC command either via json_rpc or a direct core rpc call. /// /// @param req the request object (rvalue reference) - /// @param res the response object (lvalue reference) /// @param error print this (and, on exception, the exception message) on failure. If empty then /// nothing is printed on failure. /// @param check_status_ok whether we require res.status == STATUS_OK to consider the request /// successful - template + template && cryptonote::rpc::FIXME_has_nested_response_v, int> = 0> bool invoke(typename RPC::request&& req, typename RPC::response& res, const std::string& error, bool check_status_ok = true) { try { - if (m_rpc_client) { - res = m_rpc_client->json_rpc(RPC::names()[0], req); + if (auto* rpc_client = std::get_if(&m_rpc)) { + res = rpc_client->json_rpc(RPC::names()[0], req); } else { - res = m_rpc_server->invoke(std::move(req), m_server_context); + throw std::runtime_error{"fixme"}; } if (!check_status_ok || res.status == cryptonote::rpc::STATUS_OK) return true; @@ -97,19 +98,66 @@ class rpc_command_executor final { return false; } - bool print_checkpoints(uint64_t start_height, uint64_t end_height, bool print_json); + /// Runs some RPC command either via json_rpc or an internal rpc call. Returns nlohmann::json + /// results on success, throws on failure. + /// + /// Note that for a json_rpc request this is the "result" value inside the json_rpc wrapper, not + /// the wrapper itself. + /// + /// This is the low-level implementing method for `invoke(...)`. + /// + /// @param method the method name, typically `SOMERPC::names()[0]` + /// @param public_method true if this is a public rpc request; this is used, in particular, to + /// decide whether "rpc." or "admin." should be prefixed if this goes through OMQ RPC. + /// @param params the "params" field for the request. Can be nullopt to pass no "params". + /// @param check_status_ok whether we require the result to have a "status" key set to STATUS_OK + /// to consider the request successful. Note that this defaults to *false* if this is called + /// directly, unlike the RPC-type-templated version, below. + nlohmann::json invoke( + std::string_view method, + bool public_method, + std::optional params, + bool check_status_ok = false); + + /// Runs some RPC command either via json_rpc or an internal rpc call. Returns nlohmann::json + /// results on success, throws on failure. + /// + /// @tparam RPC the rpc type class + /// @param params the "params" value to pass to json_rpc, or std::nullopt to omit it + /// @param check_status_ok whether we require the result to have a "status" key set to STATUS_OK + /// to consider the request successful + template && !cryptonote::rpc::FIXME_has_nested_response_v, int> = 0> + nlohmann::json invoke(std::optional params = std::nullopt, bool check_status_ok = true) + { + return invoke(RPC::names()[0], std::is_base_of_v, std::move(params), check_status_ok); + } - bool print_mn_state_changes(uint64_t start_height, uint64_t end_height); + // Invokes a simple RPC method that doesn't take any arguments and for which we don't care about + // the return value beyond the "status": "OK" field. Returns true (and prints a success message) + // on success, false (with a failure message printed) on failure. + template && !cryptonote::rpc::FIXME_has_nested_response_v, int> = 0> + bool invoke_simple(std::string_view error_prefix, std::string_view success_msg) { + if (!try_running([this] { return invoke(); }, error_prefix)) + return false; - bool print_peer_list(bool white = true, bool gray = true, size_t limit = 0, bool pruned_only = false, bool publicrpc_only = false); + tools::success_msg_writer() << success_msg; + return true; + } - bool print_peer_list_stats(); + template + nlohmann::json make_request(nlohmann::json params) { + return invoke(params)["response"]; + } - bool save_blockchain(); + bool print_checkpoints(std::optional start_height, std::optional end_height, bool print_json); + + bool print_mn_state_changes(uint64_t start_height, std::optional end_height); - bool show_hash_rate(); + bool print_peer_list(bool white = true, bool gray = true, size_t limit = 0, bool pruned_only = false); + + bool print_peer_list_stats(); - bool hide_hash_rate(); + bool save_blockchain(); bool show_difficulty(); @@ -119,7 +167,7 @@ class rpc_command_executor final { bool print_blockchain_info(int64_t start_block_index, uint64_t end_block_index); - bool print_quorum_state(uint64_t start_height, uint64_t end_height); + bool print_quorum_state(std::optional start_height, std::optional end_height); bool set_log_level(int8_t level); @@ -127,9 +175,6 @@ class rpc_command_executor final { bool print_height(); -private: - bool print_block(cryptonote::rpc::GET_BLOCK::request&& req, bool include_hdex); - public: bool print_block_by_hash(const crypto::hash& block_hash, bool include_hex); @@ -137,15 +182,13 @@ class rpc_command_executor final { bool print_transaction(const crypto::hash& transaction_hash, bool include_metadata, bool include_hex, bool include_json); - bool is_key_image_spent(const crypto::key_image &ki); + bool is_key_image_spent(const std::vector& ki); - bool print_transaction_pool_long(); - - bool print_transaction_pool_short(); + bool print_transaction_pool(bool long_format); bool print_transaction_pool_stats(); - bool start_mining(const cryptonote::account_public_address& address, uint64_t num_threads, uint32_t num_blocks, cryptonote::network_type nettype); + bool start_mining(const cryptonote::account_public_address& address, int num_threads, int num_blocks, cryptonote::network_type nettype); bool stop_mining(); @@ -153,9 +196,7 @@ class rpc_command_executor final { bool stop_daemon(); - bool print_status(); - - bool get_limit(bool up = true, bool down = true); + bool get_limit(); bool set_limit(int64_t limit_down, int64_t limit_up); @@ -195,7 +236,7 @@ class rpc_command_executor final { bool prepare_registration(bool force_registration=false); - bool print_mn(const std::vector &args); + bool print_mn(const std::vector &args, bool self = false); bool prune_blockchain(); diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index a40dda30aba..18de6e81443 100755 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -100,13 +100,13 @@ namespace daemonizer if (admin) { return fs::absolute( - tools::get_special_folder_path(CSIDL_COMMON_APPDATA, true) / CRYPTONOTE_NAME + tools::get_special_folder_path(CSIDL_COMMON_APPDATA, true) / cryptonote::DATA_DIRNAME ); } else { return fs::absolute( - tools::get_special_folder_path(CSIDL_APPDATA, true) / CRYPTONOTE_NAME + tools::get_special_folder_path(CSIDL_APPDATA, true) / cryptonote::DATA_DIRNAME ); } } diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 5f4f57bd4ac..0aa97738931 100755 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -115,6 +115,19 @@ static void print_extra_fields(const std::vector &fi } } +constexpr static std::string_view network_type_str(network_type nettype) +{ + switch(nettype) + { + case network_type::MAINNET: return "Mainnet"sv; + case network_type::TESTNET: return "Testnet"sv; + case network_type::DEVNET: return "Devnet"sv; + case network_type::FAKECHAIN: return "Fakenet"sv; + case network_type::UNDEFINED: return "Undefined Net"sv; + } + return "Unhandled Net"sv; +} + int main(int argc, char* argv[]) { uint32_t log_level = 0; @@ -214,14 +227,14 @@ int main(int argc, char* argv[]) else { bool addr_decoded = false; - for (uint8_t nettype = MAINNET; nettype < DEVNET + 1; nettype++) + for (auto nettype : {network_type::MAINNET, network_type::TESTNET, network_type::DEVNET}) { cryptonote::address_parse_info addr_info = {}; if (cryptonote::get_account_address_from_str(addr_info, static_cast(nettype), input)) { addr_decoded = true; cryptonote::account_public_address const &address = addr_info.address; - std::cout << "Network Type: " << cryptonote::network_type_str(static_cast(nettype)) << "\n"; + std::cout << "Network Type: " << network_type_str(static_cast(nettype)) << "\n"; std::cout << "Address: " << input << "\n"; std::cout << "Subaddress: " << (addr_info.is_subaddress ? "Yes" : "No") << "\n"; std::cout << "Payment ID: " << (addr_info.has_payment_id ? tools::type_to_hex(addr_info.payment_id) : "(none)") << "\n"; diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index c2d9652d5fc..d41cb4e535f 100755 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -95,8 +95,6 @@ int main(int argc, char* argv[]) SL(nodetool::anchor_peerlist_entry); SL(nodetool::node_server>); SL(nodetool::p2p_connection_context_t::connection_context>); - SL(nodetool::network_address_old); - SL(nodetool::peerlist_entry_base); SL(nodetool::network_config); SL(nodetool::basic_node_data); diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index ee157bbc612..9e262a24fe8 100755 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -100,7 +100,7 @@ namespace hw::core { epee::mlocked> data; memcpy(data.data(), &view_key, sizeof(view_key)); memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); - data[sizeof(data) - 1] = config::HASH_KEY_WALLET; + data[sizeof(data) - 1] = cryptonote::hashkey::WALLET; crypto::generate_chacha_key(data.data(), sizeof(data), key, kdf_rounds); return true; } @@ -190,13 +190,13 @@ namespace hw::core { } crypto::secret_key device_default::get_subaddress_secret_key(const crypto::secret_key &a, const cryptonote::subaddress_index &index) { - char data[config::HASH_KEY_SUBADDRESS.size() + sizeof(crypto::secret_key) + 2 * sizeof(uint32_t)]; - memcpy(data, config::HASH_KEY_SUBADDRESS.data(), config::HASH_KEY_SUBADDRESS.size()); - memcpy(data + config::HASH_KEY_SUBADDRESS.size(), &a, sizeof(crypto::secret_key)); + char data[cryptonote::hashkey::SUBADDRESS.size() + sizeof(crypto::secret_key) + 2 * sizeof(uint32_t)]; + memcpy(data, cryptonote::hashkey::SUBADDRESS.data(), cryptonote::hashkey::SUBADDRESS.size()); + memcpy(data + cryptonote::hashkey::SUBADDRESS.size(), &a, sizeof(crypto::secret_key)); uint32_t idx = SWAP32LE(index.major); - memcpy(data + config::HASH_KEY_SUBADDRESS.size() + sizeof(crypto::secret_key), &idx, sizeof(uint32_t)); + memcpy(data + cryptonote::hashkey::SUBADDRESS.size() + sizeof(crypto::secret_key), &idx, sizeof(uint32_t)); idx = SWAP32LE(index.minor); - memcpy(data + config::HASH_KEY_SUBADDRESS.size() + sizeof(crypto::secret_key) + sizeof(uint32_t), &idx, sizeof(uint32_t)); + memcpy(data + cryptonote::hashkey::SUBADDRESS.size() + sizeof(crypto::secret_key) + sizeof(uint32_t), &idx, sizeof(uint32_t)); crypto::secret_key m; crypto::hash_to_scalar(data, sizeof(data), m); return m; @@ -375,7 +375,7 @@ namespace hw::core { return false; memcpy(data, &derivation, 32); - data[32] = config::HASH_KEY_ENCRYPTED_PAYMENT_ID; + data[32] = cryptonote::hashkey::ENCRYPTED_PAYMENT_ID; cn_fast_hash(data, 33, hash); for (size_t b = 0; b < 8; ++b) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 65f6e59f9ed..7b8cdbda2d6 100755 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -39,7 +39,7 @@ #include "common/lock.h" #include "common/varint.h" #include -#include +#include #ifdef DEBUG_HWDEVICE #include @@ -433,19 +433,19 @@ } void device_ledger::send_u32(uint32_t x, int& offset) { - boost::endian::native_to_big_inplace(x); + oxenc::host_to_big_inplace(x); send_bytes(&x, 4, offset); } void device_ledger::send_u16(uint16_t x, int& offset) { - boost::endian::native_to_big_inplace(x); + oxenc::host_to_big_inplace(x); send_bytes(&x, 2, offset); } uint32_t device_ledger::receive_u32(int& offset) { uint32_t x; receive_bytes(&x, 4, offset); - boost::endian::big_to_native_inplace(x); + oxenc::big_to_host_inplace(x); return x; } uint32_t device_ledger::receive_u32() { diff --git a/src/device/io_ledger_tcp.cpp b/src/device/io_ledger_tcp.cpp index e768908a527..a521a63b874 100644 --- a/src/device/io_ledger_tcp.cpp +++ b/src/device/io_ledger_tcp.cpp @@ -1,7 +1,7 @@ #include "io_ledger_tcp.hpp" #include "common/beldex.h" #include -#include +#include #include #include #include "epee/misc_log_ex.h" @@ -168,7 +168,7 @@ int ledger_tcp::exchange(const unsigned char* command, unsigned int cmd_len, uns throw std::runtime_error{"Unable to exchange data with hardware wallet: not connected"}; // Sending: [SIZE][DATA], where SIZE is a uint32_t in network order - uint32_t size = boost::endian::native_to_big(cmd_len); + uint32_t size = oxenc::host_to_big(cmd_len); const unsigned char* size_bytes = reinterpret_cast(&size); full_write(*sockfd, size_bytes, 4); full_write(*sockfd, command, cmd_len); @@ -178,7 +178,7 @@ int ledger_tcp::exchange(const unsigned char* command, unsigned int cmd_len, uns // bytes of DATA are a 2-byte, u16 status code and... therefore not... included. Good job, Ledger // devs. full_read(*sockfd, reinterpret_cast(&size), 4); - auto data_size = boost::endian::big_to_native(size) + 2; + auto data_size = oxenc::big_to_host(size) + 2; if (data_size > max_resp_len) throw std::runtime_error{"Hardware wallet returned unexpectedly large response: got " + diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index 4fea83eab27..f6cf006ecc9 100755 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -63,6 +63,4 @@ if(DEVICE_TREZOR_READY) else() message(STATUS "Trezor support disabled") - add_library(device_trezor device_trezor.cpp) - target_link_libraries(device_trezor PRIVATE cncrypto common) endif() diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 3f1ca3e2572..b33f1cc8c0e 100755 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -434,7 +433,7 @@ namespace tx { static rct::RangeProofType get_rsig_type(const rct::RCTConfig &rct_config, size_t num_outputs){ if (rct_config.range_proof_type == rct::RangeProofType::Borromean){ return rct::RangeProofType::Borromean; - } else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){ + } else if (num_outputs > TX_BULLETPROOF_MAX_OUTPUTS){ return rct::RangeProofType::MultiOutputBulletproof; } else { return rct::RangeProofType::PaddedBulletproof; @@ -450,15 +449,15 @@ namespace tx { amount_batched += 1; } else if (rsig_type == rct::RangeProofType::PaddedBulletproof){ - if (num_outputs > BULLETPROOF_MAX_OUTPUTS){ - throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements"); + if (num_outputs > TX_BULLETPROOF_MAX_OUTPUTS){ + throw std::invalid_argument("BP padded can support only TX_BULLETPROOF_MAX_OUTPUTS statements"); } batches.push_back(num_outputs); amount_batched += num_outputs; } else if (rsig_type == rct::RangeProofType::MultiOutputBulletproof){ size_t batch_size = 1; - while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){ + while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= TX_BULLETPROOF_MAX_OUTPUTS){ batch_size *= 2; } batch_size = std::min(batch_size, num_outputs - amount_batched); diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index aae6419dcc9..81bdc4190f5 100755 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -170,8 +170,8 @@ namespace trezor{ } static void serialize_message_header(void * buff, uint16_t tag, uint32_t len){ - uint16_t wire_tag = boost::endian::native_to_big(static_cast(tag)); - uint32_t wire_len = boost::endian::native_to_big(static_cast(len)); + uint16_t wire_tag = oxenc::host_to_big(static_cast(tag)); + uint32_t wire_len = oxenc::host_to_big(static_cast(len)); memcpy(buff, (void *) &wire_tag, 2); memcpy((uint8_t*)buff + 2, (void *) &wire_len, 4); } @@ -182,8 +182,8 @@ namespace trezor{ memcpy(&wire_tag, buff, 2); memcpy(&wire_len, (uint8_t*)buff + 2, 4); - tag = boost::endian::big_to_native(wire_tag); - len = boost::endian::big_to_native(wire_len); + tag = oxenc::big_to_host(wire_tag); + len = oxenc::big_to_host(wire_len); } static void serialize_message(const google::protobuf::Message &req, size_t msg_size, uint8_t * buff, size_t buff_size) { diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 4d20c7c94d3..10327a0bb4d 100755 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -242,7 +242,7 @@ int main(int argc, char* argv[]) } bool create_address_file = command_line::get_arg(*vm, arg_create_address_file); - if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : devnet ? DEVNET : MAINNET, create_address_file)) + if (!generate_multisig(threshold, total, basename, testnet ? network_type::TESTNET : devnet ? network_type::DEVNET : network_type::MAINNET, create_address_file)) return 1; return 0; diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index c432d43e5ef..98a837e3cac 100755 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -46,8 +46,8 @@ namespace cryptonote crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key) { rct::key multisig_salt; - static_assert(sizeof(rct::key) == config::HASH_KEY_MULTISIG.size(), "Hash domain separator is an unexpected size"); - memcpy(multisig_salt.bytes, config::HASH_KEY_MULTISIG.data(), sizeof(rct::key)); + static_assert(sizeof(rct::key) == cryptonote::hashkey::MULTISIG.size(), "Hash domain separator is an unexpected size"); + memcpy(multisig_salt.bytes, cryptonote::hashkey::MULTISIG.data(), sizeof(rct::key)); rct::keyV data; data.reserve(2); diff --git a/src/net/dandelionpp.cpp b/src/net/dandelionpp.cpp index 4d2f7542893..78b9bba2431 100755 --- a/src/net/dandelionpp.cpp +++ b/src/net/dandelionpp.cpp @@ -29,7 +29,6 @@ #include "dandelionpp.h" #include -#include #include #include "common/expect.h" @@ -42,7 +41,7 @@ namespace dandelionpp { namespace { - constexpr const std::size_t expected_max_channels = CRYPTONOTE_NOISE_CHANNELS; + constexpr const std::size_t expected_max_channels = cryptonote::NOISE_CHANNELS; // could be in util somewhere struct key_less @@ -60,7 +59,8 @@ namespace dandelionpp } }; - std::size_t select_stem(epee::span usage, epee::span out_map) + std::size_t select_stem( + epee::span usage, epee::span out_map) { assert(usage.size() < std::numeric_limits::max()); // prevented in constructor if (usage.size() < out_map.size()) @@ -71,7 +71,7 @@ namespace dandelionpp boost::container::small_vector choices; static_assert(sizeof(choices) < 256, "choices is too large based on current configuration"); - for (const boost::uuids::uuid& out : out_map) + for (const connection_id_t& out : out_map) { if (!out.is_nil()) { @@ -100,7 +100,7 @@ namespace dandelionpp } } // anonymous - connection_map::connection_map(std::vector out_connections, const std::size_t stems) + connection_map::connection_map(std::vector out_connections, const std::size_t stems) : out_mapping_(std::move(out_connections)), in_mapping_(), usage_count_() @@ -131,7 +131,7 @@ namespace dandelionpp return {*this}; } - bool connection_map::update(std::vector current) + bool connection_map::update(std::vector current) { std::sort(current.begin(), current.end()); @@ -141,7 +141,7 @@ namespace dandelionpp const auto elem = std::lower_bound(current.begin(), current.end(), existing_out); if (elem == current.end() || *elem != existing_out) { - existing_out = boost::uuids::nil_uuid(); + existing_out = {}; replace = true; } else // already using connection, remove it from candidate list @@ -172,7 +172,7 @@ namespace dandelionpp std::size_t connection_map::size() const noexcept { std::size_t count = 0; - for (const boost::uuids::uuid& connection : out_mapping_) + for (const connection_id_t& connection : out_mapping_) { if (!connection.is_nil()) ++count; @@ -180,14 +180,14 @@ namespace dandelionpp return count; } - boost::uuids::uuid connection_map::get_stem(const boost::uuids::uuid& source) + connection_id_t connection_map::get_stem(const connection_id_t& source) { auto elem = std::lower_bound(in_mapping_.begin(), in_mapping_.end(), source, key_less{}); if (elem == in_mapping_.end() || elem->first != source) { const std::size_t index = select_stem(epee::to_span(usage_count_), epee::to_span(out_mapping_)); if (out_mapping_.size() < index) - return boost::uuids::nil_uuid(); + return {}; elem = in_mapping_.emplace(elem, source, index); usage_count_[index]++; @@ -199,7 +199,7 @@ namespace dandelionpp if (out_mapping_.size() < index) { in_mapping_.erase(elem); - return boost::uuids::nil_uuid(); + return {}; } elem->second = index; diff --git a/src/net/dandelionpp.h b/src/net/dandelionpp.h index e156b5857c1..5b43698d2b8 100755 --- a/src/net/dandelionpp.h +++ b/src/net/dandelionpp.h @@ -28,45 +28,44 @@ #pragma once -#include #include #include #include #include +#include "epee/net/net_utils_base.h" #include "epee/span.h" namespace net { namespace dandelionpp { + using epee::connection_id_t; //! Assists with mapping source -> stem and tracking connections for stem. class connection_map { // Make sure to update clone method if changing members - std::vector out_mapping_; //> in_mapping_; // out_mapping_; //> in_mapping_; // usage_count_; // Use clone method to prevent "hidden" copies. connection_map(const connection_map&) = default; public: - using value_type = boost::uuids::uuid; - using size_type = std::vector::size_type; - using difference_type = std::vector::difference_type; - using reference = const boost::uuids::uuid&; + using value_type = connection_id_t; + using size_type = std::vector::size_type; + using difference_type = std::vector::difference_type; + using reference = const connection_id_t &; using const_reference = reference; - using iterator = std::vector::const_iterator; + using iterator = std::vector::const_iterator; using const_iterator = iterator; //! Initialized with zero stem connections. - explicit connection_map() - : connection_map(std::vector{}, 0) - {} + explicit connection_map() : connection_map(std::vector{}, 0) {} //! Initialized with `out_connections` and `stem_count`. - explicit connection_map(std::vector out_connections, std::size_t stems); + explicit connection_map(std::vector out_connections, std::size_t stems); connection_map(connection_map&&) = default; ~connection_map() noexcept; @@ -94,13 +93,13 @@ namespace dandelionpp \param connections Current outbound connection ids. \return True if any updates to `get_connections()` was made. */ - bool update(std::vector current); + bool update(std::vector current); //! \return Number of outgoing connections in use. std::size_t size() const noexcept; //! \return Current stem mapping for `source` or `nil_uuid()` if none is possible. - boost::uuids::uuid get_stem(const boost::uuids::uuid& source); + connection_id_t get_stem(const connection_id_t& source); }; } // dandelionpp } // net diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 5dac3385bf3..72735b79915 100755 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -32,8 +32,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -58,8 +57,8 @@ namespace socks { std::uint8_t version; std::uint8_t command_code; - boost::endian::big_uint16_t port; - boost::endian::big_uint32_t ip; + std::uint16_t port; + std::uint32_t ip; }; std::size_t write_domain_header(epee::span out, const std::uint8_t command, const std::uint16_t port, std::string_view domain) @@ -72,7 +71,7 @@ namespace socks return 0; // version 4, 1 indicates invalid ip for domain extension - const v4_header temp{4, command, port, std::uint32_t(1)}; + const v4_header temp{4, command, oxenc::host_to_little(port), oxenc::host_to_little(std::uint32_t{1})}; std::memcpy(out.data(), std::addressof(temp), sizeof(temp)); out.remove_prefix(sizeof(temp)); @@ -178,8 +177,8 @@ namespace socks { std::shared_ptr self_; - static boost::asio::mutable_buffers_1 get_buffer(client& self) noexcept - { + static boost::asio::mutable_buffer get_buffer(client& self) noexcept { + static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response"); return boost::asio::buffer(self.buffer_, sizeof(v4_header)); } @@ -203,8 +202,7 @@ namespace socks { std::shared_ptr self_; - static boost::asio::const_buffers_1 get_buffer(client const& self) noexcept - { + static boost::asio::const_buffer get_buffer(client const& self) noexcept { return boost::asio::buffer(self.buffer_, self.buffer_size_); } @@ -243,7 +241,7 @@ namespace socks static_assert(0 < sizeof(buffer_), "buffer size too small for null termination"); // version 4 - const v4_header temp{4, v4_connect_command, address.port(), boost::endian::big_to_native(address.ip())}; + const v4_header temp{4, v4_connect_command, oxenc::host_to_big(address.port()), oxenc::host_to_big(address.ip())}; std::memcpy(std::addressof(buffer_), std::addressof(temp), sizeof(temp)); buffer_[sizeof(temp)] = 0; buffer_size_ = sizeof(temp) + 1; @@ -319,14 +317,14 @@ namespace socks if (self_ && error != boost::system::errc::operation_canceled) { const std::shared_ptr self = std::move(self_); - self->strand_.dispatch([self] () - { - if (self && self->proxy_.is_open()) - { - self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both); - self->proxy_.close(); - } - }); + self->strand_.dispatch( + [self]() { + if (self && self->proxy_.is_open()) { + self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + self->proxy_.close(); + } + }, + std::allocator{}); } } } // socks diff --git a/src/net/socks.h b/src/net/socks.h index d9ffe26c088..dc590a82f44 100755 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include @@ -41,6 +41,11 @@ #include "net/fwd.h" #include "epee/span.h" +namespace boost::asio { +using io_service = io_context; +} + + namespace epee { namespace net_utils diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 3be1386f086..53c39a30d26 100755 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -28,6 +28,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. add_library(p2p + p2p_protocol_defs.cpp net_node.cpp net_node.inl net_peerlist.cpp diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index eb517452a26..9ee975526ed 100755 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -28,7 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include +#include #include #include #include @@ -51,7 +51,7 @@ namespace { constexpr const std::chrono::milliseconds future_poll_interval = 500ms; - constexpr const std::chrono::seconds socks_connect_timeout{P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT}; + constexpr const std::chrono::seconds socks_connect_timeout{cryptonote::p2p::DEFAULT_SOCKS_CONNECT_TIMEOUT}; std::int64_t get_max_connections(const std::string_view value) noexcept @@ -73,7 +73,7 @@ namespace if (!address) { MERROR( - "Failed to parse " << epee::net_utils::zone_to_string(T::get_zone()) << " address \"" << value << "\": " << address.error().message() + "Failed to parse " << T::get_zone() << " address \"" << value << "\": " << address.error().message() ); return {}; } @@ -112,26 +112,26 @@ namespace nodetool const command_line::arg_descriptor arg_p2p_bind_port = { "p2p-bind-port" , "Port for p2p network protocol (IPv4)" - , std::to_string(config::P2P_DEFAULT_PORT) + , std::to_string(cryptonote::config::P2P_DEFAULT_PORT) , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_devnet_on }} , [](std::array testnet_devnet, bool defaulted, std::string val)->std::string { if (testnet_devnet[0] && defaulted) - return std::to_string(config::testnet::P2P_DEFAULT_PORT); + return std::to_string(cryptonote::config::testnet::P2P_DEFAULT_PORT); else if (testnet_devnet[1] && defaulted) - return std::to_string(config::devnet::P2P_DEFAULT_PORT); + return std::to_string(cryptonote::config::devnet::P2P_DEFAULT_PORT); return val; } }; const command_line::arg_descriptor arg_p2p_bind_port_ipv6 = { "p2p-bind-port-ipv6" , "Port for p2p network protocol (IPv6)" - , std::to_string(config::P2P_DEFAULT_PORT) + , std::to_string(cryptonote::config::P2P_DEFAULT_PORT) , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_devnet_on }} , [](std::array testnet_devnet, bool defaulted, std::string val)->std::string { if (testnet_devnet[0] && defaulted) - return std::to_string(config::testnet::P2P_DEFAULT_PORT); + return std::to_string(cryptonote::config::testnet::P2P_DEFAULT_PORT); else if (testnet_devnet[1] && defaulted) - return std::to_string(config::devnet::P2P_DEFAULT_PORT); + return std::to_string(cryptonote::config::devnet::P2P_DEFAULT_PORT); return val; } }; @@ -156,8 +156,8 @@ namespace nodetool const command_line::arg_descriptor arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor arg_tos_flag = {"tos-flag", "set TOS flag", -1}; - const command_line::arg_descriptor arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", P2P_DEFAULT_LIMIT_RATE_UP}; - const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN}; + const command_line::arg_descriptor arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", cryptonote::p2p::DEFAULT_LIMIT_RATE_UP}; + const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", cryptonote::p2p::DEFAULT_LIMIT_RATE_DOWN}; const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; std::optional> get_proxies(boost::program_options::variables_map const& vm) @@ -220,7 +220,7 @@ namespace nodetool MERROR("Invalid ipv4:port given for --" << arg_tx_proxy.name); return std::nullopt; } - set_proxy.address = ip::tcp::endpoint{ip::address_v4{boost::endian::native_to_big(ip)}, port}; + set_proxy.address = ip::tcp::endpoint{ip::address_v4{oxenc::host_to_big(ip)}, port}; } return proxies; @@ -294,8 +294,8 @@ namespace nodetool { switch (command) { - case nodetool::COMMAND_HANDSHAKE_T::ID: - case nodetool::COMMAND_TIMED_SYNC_T::ID: + case nodetool::COMMAND_HANDSHAKE::ID: + case nodetool::COMMAND_TIMED_SYNC::ID: case cryptonote::NOTIFY_NEW_TRANSACTIONS::ID: return false; default: diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index fa29ae9b077..e4d704b71ff 100755 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -31,11 +31,10 @@ #pragma once #include #include -#include +#include #include #include #include -#include #include #include #include @@ -56,10 +55,15 @@ #include "common/command_line.h" #include "common/periodic_task.h" #include "common/fs.h" +#include "common/util.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) +namespace boost::asio { +using io_service = io_context; +} + namespace nodetool { struct proxy @@ -108,10 +112,9 @@ namespace nodetool template struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base { - p2p_connection_context_t(): peer_id(0), support_flags(0), m_in_timedsync(false) {} + p2p_connection_context_t(): peer_id(0), m_in_timedsync(false) {} peerid_type peer_id; - uint32_t support_flags; bool m_in_timedsync; std::set sent_addresses; }; @@ -125,12 +128,9 @@ namespace nodetool struct by_peer_id{}; struct by_addr{}; - typedef p2p_connection_context_t p2p_connection_context; + using p2p_connection_context = p2p_connection_context_t; - typedef COMMAND_HANDSHAKE_T COMMAND_HANDSHAKE; - typedef COMMAND_TIMED_SYNC_T COMMAND_TIMED_SYNC; - - typedef epee::net_utils::boosted_tcp_server> net_server; + using net_server = epee::net_utils::boosted_tcp_server>; struct network_zone; using connect_func = std::optional(network_zone&, epee::net_utils::network_address const&); @@ -139,7 +139,6 @@ namespace nodetool { network_config m_net_config{}; uint64_t m_peer_id{crypto::rand()}; - uint32_t m_support_flags{0}; }; struct network_zone @@ -201,13 +200,12 @@ namespace nodetool void set_config_defaults() noexcept { // at this moment we have a hardcoded config - m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; - m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; + m_config.m_net_config.handshake_interval = tools::to_seconds(cryptonote::p2p::DEFAULT_HANDSHAKE_INTERVAL); + m_config.m_net_config.packet_max_size = cryptonote::p2p::DEFAULT_PACKET_MAX_SIZE; m_config.m_net_config.config_id = 0; - m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; - m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT; - m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE; - m_config.m_support_flags = 0; // only set in public zone + m_config.m_net_config.connection_timeout = cryptonote::p2p::DEFAULT_CONNECTION_TIMEOUT; + m_config.m_net_config.ping_connection_timeout = cryptonote::p2p::DEFAULT_PING_CONNECTION_TIMEOUT; + m_config.m_net_config.send_peerlist_sz = cryptonote::p2p::DEFAULT_PEERS_IN_HANDSHAKE; } }; @@ -218,13 +216,10 @@ namespace nodetool node_server(t_payload_net_handler& payload_handler) : m_payload_handler(payload_handler), m_external_port(0), - m_rpc_port(0), m_allow_local_ip(false), m_hide_my_port(false), m_offline(false), - is_closing(false), - m_network_id() - {} + is_closing(false) {} virtual ~node_server(); static void init_options(boost::program_options::options_description& desc, boost::program_options::options_description& hidden); @@ -253,9 +248,9 @@ namespace nodetool uint32_t get_max_out_public_peers() const; void change_max_in_public_peers(size_t count); uint32_t get_max_in_public_peers() const; - virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = tools::to_seconds(cryptonote::p2p::IP_BLOCK_TIME)); virtual bool unblock_host(const epee::net_utils::network_address &address); - virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = tools::to_seconds(cryptonote::p2p::IP_BLOCK_TIME)); virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet); virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { return !is_remote_host_allowed(address, seconds); } virtual std::map get_blocked_hosts() { std::shared_lock lock{m_blocked_hosts_lock}; return m_blocked_hosts; } @@ -280,6 +275,7 @@ namespace nodetool HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, handle_handshake) HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, handle_timed_sync) HANDLE_INVOKE_T2(COMMAND_PING, handle_ping) + // TODO: remove after HF HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, handle_get_support_flags) CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() @@ -290,6 +286,7 @@ namespace nodetool int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); + // TODO: remove after HF int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context); bool init_config(); bool make_default_peer_id(); @@ -302,14 +299,14 @@ namespace nodetool virtual void on_connection_close(p2p_connection_context& context); virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- - virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections); - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs); + virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections); + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const connection_id_t& source, const bool pad_txs); virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); - virtual void for_each_connection(std::function f); - virtual bool for_connection(const boost::uuids::uuid&, std::function f); + virtual void for_each_connection(std::function f); + virtual bool for_connection(const connection_id_t&, std::function f); virtual bool add_host_fail(const epee::net_utils::network_address &address); //----------------- i_connection_filter -------------------------------------------------------- virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL); @@ -333,13 +330,12 @@ namespace nodetool bool make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist); bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list); bool try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); - size_t get_random_index_with_fixed_probability(size_t max_index); + size_t get_random_exp_index(size_t size, double rate = 0.13862943611198906); bool is_peer_used(const peerlist_entry& peer); bool is_peer_used(const anchor_peerlist_entry& peer); bool is_addr_connected(const epee::net_utils::network_address& peer); template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb); - bool try_get_support_flags(const p2p_connection_context& context, std::function f); bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections); void record_addr_failed(const epee::net_utils::network_address& addr); bool is_addr_recently_failed(const epee::net_utils::network_address& addr); @@ -386,11 +382,6 @@ namespace nodetool public: - void set_rpc_port(uint16_t rpc_port) - { - m_rpc_port = rpc_port; - } - void reset_peer_handshake_timer() { m_peer_handshake_idle_maker_interval.reset(); @@ -404,7 +395,6 @@ namespace nodetool uint32_t m_listening_port; uint32_t m_listening_port_ipv6; uint32_t m_external_port; - uint16_t m_rpc_port; bool m_allow_local_ip; bool m_hide_my_port; bool m_offline; @@ -416,7 +406,7 @@ namespace nodetool t_payload_net_handler& m_payload_handler; peerlist_storage m_peerlist_storage; - tools::periodic_task m_peer_handshake_idle_maker_interval{std::chrono::seconds{P2P_DEFAULT_HANDSHAKE_INTERVAL}}; + tools::periodic_task m_peer_handshake_idle_maker_interval{std::chrono::seconds{cryptonote::p2p::DEFAULT_HANDSHAKE_INTERVAL}}; tools::periodic_task m_connections_maker_interval{1s}; tools::periodic_task m_peerlist_store_interval{30min}; tools::periodic_task m_gray_peerlist_housekeeping_interval{1min}; @@ -456,14 +446,14 @@ namespace nodetool std::map m_host_fails_score; std::mutex m_used_stripe_peers_mutex; - std::array, 1 << CRYPTONOTE_PRUNING_LOG_STRIPES> m_used_stripe_peers; + std::array, 1 << cryptonote::PRUNING_LOG_STRIPES> m_used_stripe_peers; - boost::uuids::uuid m_network_id; + epee::connection_id_t m_network_id{}; cryptonote::network_type m_nettype; }; - const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s - const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s + const int64_t default_limit_up = cryptonote::p2p::DEFAULT_LIMIT_RATE_UP; // kB/s + const int64_t default_limit_down = cryptonote::p2p::DEFAULT_LIMIT_RATE_DOWN; // kB/s extern const command_line::arg_descriptor arg_p2p_bind_ip; extern const command_line::arg_descriptor arg_p2p_bind_ipv6_address; extern const command_line::arg_descriptor arg_p2p_bind_port; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 183c2e3b6d5..0a10fbeeb0d 100755 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -44,6 +43,7 @@ #include "cryptonote_config.h" #include "version.h" #include "epee/string_tools.h" +#include "epee/time_helper.h" #include "common/file.h" #include "common/pruning.h" #include "net/error.h" @@ -51,11 +51,13 @@ #include "epee/misc_log_ex.h" #include "p2p_protocol_defs.h" #include "epee/net/local_ip.h" +#include "epee/net/net_utils_base.h" #include "crypto/crypto.h" #include "epee/storages/levin_abstract_invoke2.h" #include "cryptonote_core/cryptonote_core.h" #include "net/parse.h" +#include #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "net.p2p" @@ -66,6 +68,9 @@ namespace nodetool { + + using epee::connection_id_t; + template node_server::~node_server() { @@ -115,11 +120,10 @@ namespace nodetool bool node_server::init_config() { TRY_ENTRY(); - auto storage = peerlist_storage::open(m_config_folder / P2P_NET_DATA_FILENAME); + auto storage = peerlist_storage::open(m_config_folder / cryptonote::P2P_NET_DATA_FILENAME); if (storage) m_peerlist_storage = std::move(*storage); - m_network_zones[epee::net_utils::zone::public_].m_config.m_support_flags = P2P_SUPPORT_FLAGS; m_first_connection_maker_call = true; CATCH_ENTRY_L0("node_server::init_config", false); @@ -127,23 +131,23 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - void node_server::for_each_connection(std::function f) + void node_server::for_each_connection(std::function f) { for(auto& zone : m_network_zones) { zone.second.m_net_server.get_config_object().foreach_connection([&](p2p_connection_context& cntx){ - return f(cntx, cntx.peer_id, cntx.support_flags); + return f(cntx, cntx.peer_id); }); } } //----------------------------------------------------------------------------------- template - bool node_server::for_connection(const boost::uuids::uuid &connection_id, std::function f) + bool node_server::for_connection(const connection_id_t& connection_id, std::function f) { for(auto& zone : m_network_zones) { const bool result = zone.second.m_net_server.get_config_object().for_connection(connection_id, [&](p2p_connection_context& cntx){ - return f(cntx, cntx.peer_id, cntx.support_flags); + return f(cntx, cntx.peer_id); }); if (result) return true; @@ -222,7 +226,7 @@ namespace nodetool // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is // swept ... - std::vector conns; + std::vector conns; for(auto& zone : m_network_zones) { zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) @@ -271,7 +275,7 @@ namespace nodetool // drop any connection to that subnet. This should only have to look into // the zone related to the connection, but really make sure everything is // swept ... - std::vector conns; + std::vector conns; for(auto& zone : m_network_zones) { zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) @@ -316,11 +320,11 @@ namespace nodetool std::lock_guard lock{m_host_fails_score_lock}; uint64_t fails = ++m_host_fails_score[address.host_str()]; MDEBUG("Host " << address.host_str() << " fail score=" << fails); - if(fails > P2P_IP_FAILS_BEFORE_BLOCK) + if(fails > cryptonote::p2p::IP_FAILS_BEFORE_BLOCK) { auto it = m_host_fails_score.find(address.host_str()); CHECK_AND_ASSERT_MES(it != m_host_fails_score.end(), false, "internal error"); - it->second = P2P_IP_FAILS_BEFORE_BLOCK/2; + it->second = cryptonote::p2p::IP_FAILS_BEFORE_BLOCK/2; block_host(address); } return true; @@ -335,10 +339,10 @@ namespace nodetool bool devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on); bool fakenet = command_line::get_arg(vm, cryptonote::arg_regtest_on); m_nettype = - testnet ? cryptonote::TESTNET : - devnet ? cryptonote::DEVNET : - fakenet ? cryptonote::FAKECHAIN : - cryptonote::MAINNET; + testnet ? cryptonote::network_type::TESTNET : + devnet ? cryptonote::network_type::DEVNET : + fakenet ? cryptonote::network_type::FAKECHAIN : + cryptonote::network_type::MAINNET; network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_]; public_zone.m_connect = &public_connect; @@ -446,7 +450,7 @@ namespace nodetool network_zone& zone = add_zone(proxy.zone); if (zone.m_connect != nullptr) { - MERROR("Listed --" << arg_tx_proxy.name << " twice with " << epee::net_utils::zone_to_string(proxy.zone)); + MERROR("Listed --" << arg_tx_proxy.name << " twice with " << proxy.zone); return false; } zone.m_connect = &socks_connect; @@ -458,9 +462,9 @@ namespace nodetool epee::shared_sv this_noise; if (proxy.noise) { - static_assert(sizeof(epee::levin::bucket_head2) < CRYPTONOTE_NOISE_BYTES, "noise bytes too small"); + static_assert(sizeof(epee::levin::bucket_head2) < cryptonote::NOISE_BYTES, "noise bytes too small"); if (noise.view.empty()) - noise = epee::shared_sv{epee::levin::make_noise_notify(CRYPTONOTE_NOISE_BYTES)}; + noise = epee::shared_sv{epee::levin::make_noise_notify(cryptonote::NOISE_BYTES)}; this_noise = noise; } @@ -474,7 +478,7 @@ namespace nodetool { if (zone.second.m_connect == nullptr) { - MERROR("Set outgoing peer for " << epee::net_utils::zone_to_string(zone.first) << " but did not set --" << arg_tx_proxy.name); + MERROR("Set outgoing peer for " << zone.first << " but did not set --" << arg_tx_proxy.name); return false; } } @@ -490,7 +494,7 @@ namespace nodetool if (!zone.m_bind_ip.empty()) { - MERROR("Listed --" << arg_anonymous_inbound.name << " twice with " << epee::net_utils::zone_to_string(inbound.our_address.get_zone()) << " network"); + MERROR("Listed --" << arg_anonymous_inbound.name << " twice with " << inbound.our_address.get_zone() << " network"); return false; } @@ -543,18 +547,18 @@ namespace nodetool io_service io_srv; ip::tcp::resolver resolver(io_srv); - ip::tcp::resolver::query query(host, port, boost::asio::ip::tcp::resolver::query::canonical_name); boost::system::error_code ec; - ip::tcp::resolver::iterator i = resolver.resolve(query, ec); + ip::tcp::resolver::results_type result = resolver.resolve(host, port, boost::asio::ip::tcp::resolver::canonical_name, ec); CHECK_AND_ASSERT_MES(!ec, false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); - ip::tcp::resolver::iterator iend; + auto i = result.begin(); + auto iend = result.end(); for (; i != iend; ++i) { ip::tcp::endpoint endpoint = *i; if (endpoint.address().is_v4()) { - epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}}; + epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_uint()), endpoint.port()}}; seed_nodes.push_back(na); MINFO("Added node: " << na.str()); } @@ -573,18 +577,18 @@ namespace nodetool std::set node_server::get_seed_nodes(cryptonote::network_type nettype) const { std::set full_addrs; - if (nettype == cryptonote::TESTNET) + if (nettype == cryptonote::network_type::TESTNET) { full_addrs.insert("test1.rpcnode.stream:29090"); full_addrs.insert("test2.rpcnode.stream:29090"); } - else if (nettype == cryptonote::DEVNET) + else if (nettype == cryptonote::network_type::DEVNET) { full_addrs.insert("35.237.218.150:39090"); full_addrs.insert("35.243.157.236:39090"); full_addrs.insert("35.237.118.17:39090"); } - else if (nettype == cryptonote::FAKECHAIN) + else if (nettype == cryptonote::network_type::FAKECHAIN) { } else @@ -605,16 +609,16 @@ namespace nodetool { return {}; } - if (m_nettype == cryptonote::TESTNET) + if (m_nettype == cryptonote::network_type::TESTNET) { - return get_seed_nodes(cryptonote::TESTNET); + return get_seed_nodes(cryptonote::network_type::TESTNET); } - if (m_nettype == cryptonote::DEVNET) + if (m_nettype == cryptonote::network_type::DEVNET) { - return get_seed_nodes(cryptonote::DEVNET); + return get_seed_nodes(cryptonote::network_type::DEVNET); } - return get_seed_nodes(cryptonote::MAINNET); + return get_seed_nodes(cryptonote::network_type::MAINNET); } //----------------------------------------------------------------------------------- template @@ -634,18 +638,7 @@ namespace nodetool bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - if (m_nettype == cryptonote::TESTNET) - { - memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); - } - else if (m_nettype == cryptonote::DEVNET) - { - memcpy(&m_network_id, &::config::devnet::NETWORK_ID, 16); - } - else - { - memcpy(&m_network_id, &::config::NETWORK_ID, 16); - } + static_cast&>(m_network_id) = get_config(m_nettype).NETWORK_ID; m_config_folder = fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir)); network_zone& public_zone = m_network_zones.at(epee::net_utils::zone::public_); @@ -665,32 +658,31 @@ namespace nodetool for(const auto& p: m_command_line_peers) m_network_zones.at(p.adr.get_zone()).m_peerlist.append_with_peer_white(p); -// all peers are now setup -#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED - for (auto& zone : m_network_zones) - { - std::list plw; - while (zone.second.m_peerlist.get_white_peers_count()) + // all peers are now setup + if constexpr (cryptonote::PRUNING_DEBUG_SPOOF_SEED) { + for (auto& zone : m_network_zones) { - plw.push_back(peerlist_entry()); - zone.second.m_peerlist.get_white_peer_by_index(plw.back(), 0); - zone.second.m_peerlist.remove_from_peer_white(plw.back()); - } - for (auto &e:plw) - zone.second.m_peerlist.append_with_peer_white(e); + std::list plw; + while (zone.second.m_peerlist.get_white_peers_count()) + { + plw.push_back(peerlist_entry()); + zone.second.m_peerlist.get_white_peer_by_index(plw.back(), 0); + zone.second.m_peerlist.remove_from_peer_white(plw.back()); + } + for (auto &e:plw) + zone.second.m_peerlist.append_with_peer_white(e); - std::list plg; - while (zone.second.m_peerlist.get_gray_peers_count()) - { - plg.push_back(peerlist_entry()); - zone.second.m_peerlist.get_gray_peer_by_index(plg.back(), 0); - zone.second.m_peerlist.remove_from_peer_gray(plg.back()); + std::list plg; + while (zone.second.m_peerlist.get_gray_peers_count()) + { + plg.push_back(peerlist_entry()); + zone.second.m_peerlist.get_gray_peer_by_index(plg.back(), 0); + zone.second.m_peerlist.remove_from_peer_gray(plg.back()); + } + for (auto &e:plg) + zone.second.m_peerlist.append_with_peer_gray(e); } - for (auto &e:plg) - zone.second.m_peerlist.append_with_peer_gray(e); } -#endif - //only in case if we really sure that we have external visible ip m_have_address = true; @@ -706,7 +698,7 @@ namespace nodetool for (auto& zone : m_network_zones) { zone.second.m_net_server.get_config_object().set_handler(this); - zone.second.m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + zone.second.m_net_server.get_config_object().m_invoke_timeout = std::chrono::milliseconds{cryptonote::p2p::DEFAULT_INVOKE_TIMEOUT}; if (!zone.second.m_bind_ip.empty()) { @@ -837,7 +829,7 @@ namespace nodetool for (auto& zone : m_network_zones) zone.second.m_peerlist.get_peerlist(active); - const auto state_file_path = m_config_folder / P2P_NET_DATA_FILENAME; + const auto state_file_path = m_config_folder / cryptonote::P2P_NET_DATA_FILENAME; if (!m_peerlist_storage.store(state_file_path, active)) { MWARNING("Failed to save config to file " << state_file_path); @@ -857,7 +849,7 @@ namespace nodetool for (auto& zone : m_network_zones) { - std::list connection_ids; + std::list connection_ids; zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { connection_ids.push_back(cntxt.m_connection_id); return true; @@ -875,8 +867,11 @@ namespace nodetool LOG_PRINT_CC_L0(context_,"do_handshake_with_peer"); network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone()); - typename COMMAND_HANDSHAKE::request arg{}; - typename COMMAND_HANDSHAKE::response rsp{}; + using request_t = typename COMMAND_HANDSHAKE::request; + using response_t = typename COMMAND_HANDSHAKE::response; + + request_t arg{}; + response_t rsp{}; get_local_node_data(arg.node_data, zone); m_payload_handler.get_payload_sync_data(arg.payload_data); @@ -884,8 +879,8 @@ namespace nodetool std::atomic hsh_result(false); bool timeout = false; LOG_PRINT_CC_L0(context_,"do_handshake_with_peer COMMAND_HANDSHAKE"); - bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(), - [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_, &timeout](int code, typename COMMAND_HANDSHAKE::response&& rsp, p2p_connection_context& context) + bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(), + [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_, &timeout](int code, response_t&& rsp, p2p_connection_context& context) { BELDEX_DEFER { ev.set_value(); }; if(code < 0) @@ -919,9 +914,8 @@ namespace nodetool } pi = context.peer_id = rsp.node_data.peer_id; - context.m_rpc_port = rsp.node_data.rpc_port; network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); - zone.m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); + zone.m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed); // move if(rsp.node_data.peer_id == zone.m_config.m_peer_id) @@ -937,7 +931,7 @@ namespace nodetool LOG_DEBUG_CC(context, " COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); } context_ = context; - }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); + }, std::chrono::milliseconds{cryptonote::p2p::DEFAULT_HANDSHAKE_INVOKE_TIMEOUT}); if(r) { @@ -950,28 +944,22 @@ namespace nodetool if (!timeout) zone.m_net_server.get_config_object().close(context_.m_connection_id); } - else if (!just_take_peerlist) - { - LOG_PRINT_L0("do_handshake_with_peer try_get_support_flags"); - try_get_support_flags(context_, [](p2p_connection_context& flags_context, const uint32_t& support_flags) - { - flags_context.support_flags = support_flags; - }); - } - return hsh_result; } //----------------------------------------------------------------------------------- template bool node_server::do_peer_timed_sync(const epee::net_utils::connection_context_base& context_, peerid_type peer_id) { - typename COMMAND_TIMED_SYNC::request arg{}; + using request_t = typename COMMAND_TIMED_SYNC::request; + using response_t = typename COMMAND_TIMED_SYNC::response; + request_t arg{}; + LOG_PRINT_CC_L0(context_,"do_peer_timed_sync"); m_payload_handler.get_payload_sync_data(arg.payload_data); network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone()); - bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, zone.m_net_server.get_config_object(), - [this](int code, typename COMMAND_TIMED_SYNC::response&& rsp, p2p_connection_context& context) + bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, zone.m_net_server.get_config_object(), + [this](int code, response_t&& rsp, p2p_connection_context& context) { context.m_in_timedsync = false; if(code < 0) @@ -987,7 +975,7 @@ namespace nodetool add_host_fail(context.m_remote_address); } if(!context.m_is_income) - m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); + m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed); if (!m_payload_handler.process_payload_sync_data(std::move(rsp.payload_data), context, false)) { m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); @@ -1003,15 +991,25 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - size_t node_server::get_random_index_with_fixed_probability(size_t max_index) + size_t node_server::get_random_exp_index( + const size_t size, const double rate) { - //divide by zero workaround - if(!max_index) + if (size <= 1) return 0; - size_t x = crypto::rand()%(max_index+1); - size_t res = (x*x*x)/(max_index*max_index); //parabola \/ - MDEBUG("Random connection index=" << res << "(x="<< x << ", max_index=" << max_index << ")"); + // (See net_node.h) + + crypto::random_device rng; + const double u = std::uniform_real_distribution{}(rng); + // For non-truncated exponential we could use: -1/rate * log(1-u), (or + // std::exponential_distribution) but then we'd have to repeat until we got a value < size, + // which is technically unbounded computational time. Instead we mutate the calculation like + // this, which gives us exponential, but truncated to [0, size), without loop + + const size_t res = + static_cast(-1.0 / rate * std::log(1.0 - u * (1.0 - std::exp(-rate * size)))); + + MDEBUG("Random connection index= "<(last_seen); pe_local.pruning_seed = con->m_pruning_seed; - pe_local.rpc_port = con->m_rpc_port; zone.m_peerlist.append_with_peer_white(pe_local); //update last seen and push it to peerlist manager @@ -1232,11 +1229,16 @@ namespace nodetool if(it == m_conn_fails_cache.end()) return false; - if(time(NULL) - it->second > P2P_FAILED_ADDR_FORGET_SECONDS) - return false; - else - return true; + auto ago = std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(it->second); + return ago <= cryptonote::p2p::FAILED_ADDR_FORGET; + } + + //----------------------------------------------------------------------------------- + static std::string peerid_to_string(peerid_type peer_id) + { + return fmt::format("{:016x}", peer_id); } + //----------------------------------------------------------------------------------- template bool node_server::make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist) @@ -1271,6 +1273,7 @@ namespace nodetool return false; } + //----------------------------------------------------------------------------------- template bool node_server::make_new_connection_from_peerlist(network_zone& zone, bool use_white_list) @@ -1308,7 +1311,7 @@ namespace nodetool const size_t limit = use_white_list ? 20 : std::numeric_limits::max(); for (int step = 0; step < 2; ++step) { - bool skip_duplicate_class_B = step == 0 && m_nettype == cryptonote::MAINNET; + bool skip_duplicate_class_B = step == 0 && m_nettype == cryptonote::network_type::MAINNET; size_t idx = 0, skipped = 0; zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ if (filtered.size() >= limit) @@ -1342,16 +1345,17 @@ namespace nodetool if (use_white_list) { // if using the white list, we first pick in the set of peers we've already been using earlier - random_index = get_random_index_with_fixed_probability(std::min(filtered.size() - 1, 20)); + random_index = get_random_exp_index(std::min(filtered.size() - 1, 20)); std::lock_guard lock{m_used_stripe_peers_mutex}; - if (next_needed_pruning_stripe > 0 && next_needed_pruning_stripe <= (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES) && !m_used_stripe_peers[next_needed_pruning_stripe-1].empty()) + if (next_needed_pruning_stripe > 0 && next_needed_pruning_stripe <= (1ul << cryptonote::PRUNING_LOG_STRIPES) && !m_used_stripe_peers[next_needed_pruning_stripe-1].empty()) { const epee::net_utils::network_address na = m_used_stripe_peers[next_needed_pruning_stripe-1].front(); m_used_stripe_peers[next_needed_pruning_stripe-1].pop_front(); for (size_t i = 0; i < filtered.size(); ++i) { peerlist_entry pe; - if (zone.m_peerlist.get_white_peer_by_index(pe, filtered[i]) && pe.adr == na) + if (zone.m_peerlist.get_white_peer_by_index(pe, filtered[i]) && + pe.adr == na) { MDEBUG("Reusing stripe " << next_needed_pruning_stripe << " peer " << pe.adr.str()); random_index = i; @@ -1506,7 +1510,7 @@ namespace nodetool for(auto& zone : m_network_zones) { - size_t base_expected_white_connections = (zone.second.m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + size_t base_expected_white_connections = (zone.second.m_config.m_net_config.max_out_connection_count * cryptonote::p2p::DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; size_t conn_count = get_outgoing_connections_count(zone.second); while(conn_count < zone.second.m_config.m_net_config.max_out_connection_count) @@ -1515,8 +1519,8 @@ namespace nodetool if(conn_count < expected_white_connections) { //start from anchor list - while (get_outgoing_connections_count(zone.second) < P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT - && make_expected_connections_count(zone.second, anchor, P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT)); + while (get_outgoing_connections_count(zone.second) < cryptonote::p2p::DEFAULT_ANCHOR_CONNECTIONS_COUNT + && make_expected_connections_count(zone.second, anchor, cryptonote::p2p::DEFAULT_ANCHOR_CONNECTIONS_COUNT)); //then do white list while (get_outgoing_connections_count(zone.second) < expected_white_connections && make_expected_connections_count(zone.second, white, expected_white_connections)); @@ -1744,7 +1748,7 @@ namespace nodetool }); } - std::for_each(cncts.begin(), cncts.end(), [&](const typename local_connects_type::value_type& vl){do_peer_timed_sync(vl.first, vl.second);}); + std::for_each(cncts.begin(), cncts.end(), [&](const auto& vl){do_peer_timed_sync(vl.first, vl.second);}); MDEBUG("FINISHED PEERLIST IDLE HANDSHAKE"); return true; @@ -1767,10 +1771,8 @@ namespace nodetool const epee::net_utils::ipv4_network_address &ipv4 = na.as(); if (ipv4.ip() == 0) ignore = true; - else if (ipv4.port() == be.rpc_port) - ignore = true; } - if (be.pruning_seed && (be.pruning_seed < tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES) || be.pruning_seed > tools::make_pruning_seed(1ul << CRYPTONOTE_PRUNING_LOG_STRIPES, CRYPTONOTE_PRUNING_LOG_STRIPES))) + if (be.pruning_seed && (be.pruning_seed < tools::make_pruning_seed(1, cryptonote::PRUNING_LOG_STRIPES) || be.pruning_seed > tools::make_pruning_seed(1ul << cryptonote::PRUNING_LOG_STRIPES, cryptonote::PRUNING_LOG_STRIPES))) ignore = true; if (ignore) { @@ -1782,9 +1784,9 @@ namespace nodetool } local_peerlist[i].last_seen = 0; -#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED - be.pruning_seed = tools::make_pruning_seed(1 + (be.adr.as().ip()) % (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES); -#endif + if constexpr (cryptonote::PRUNING_DEBUG_SPOOF_SEED) + be.pruning_seed = tools::make_pruning_seed(1 + (be.adr.as().ip()) % (1ul << cryptonote::PRUNING_LOG_STRIPES), cryptonote::PRUNING_LOG_STRIPES); + } return true; } @@ -1819,7 +1821,6 @@ namespace nodetool node_data.my_port = m_external_port ? m_external_port : m_listening_port; else node_data.my_port = 0; - node_data.rpc_port = zone.m_can_pingback ? m_rpc_port : 0; node_data.network_id = m_network_id; return true; } @@ -1828,7 +1829,7 @@ namespace nodetool int node_server::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context) { LOG_PRINT_CC_L0(context, "handle_get_support_flags"); - rsp.support_flags = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_support_flags; + rsp.support_flags = 0; return 1; } //----------------------------------------------------------------------------------- @@ -1839,7 +1840,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections) + bool node_server::relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections) { std::sort(connections.begin(), connections.end()); auto zone = m_network_zones.begin(); @@ -1849,8 +1850,8 @@ namespace nodetool { if (zone == m_network_zones.end()) { - MWARNING("Unable to relay all messages, " << epee::net_utils::zone_to_string(c_id.first) << " not available"); - return false; + MWARNING("Unable to relay all messages, " << c_id.first << " not available"); + return false; } if (c_id.first <= zone->first) break; @@ -1864,7 +1865,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const connection_id_t& source, const bool pad_txs) { namespace enet = epee::net_utils; @@ -1994,8 +1995,8 @@ namespace nodetool address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)}; } peerid_type pr = node_data.peer_id; - LOG_PRINT_CC_L0(context,"try_ping start ip:" << ip << " port:" << port << " timeout:" << zone.m_config.m_net_config.ping_connection_timeout ); - bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this]( + LOG_PRINT_CC_L0(context,"try_ping start ip:" << ip << " port:" << port << " timeout:" << zone.m_config.m_net_config.ping_connection_timeout.count()); + bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout.count(), [cb, /*context,*/ address, pr, this]( const typename net_server::t_connection_context& ping_context, const boost::system::error_code& ec)->bool { @@ -2027,7 +2028,7 @@ namespace nodetool } network_zone& zone = m_network_zones.at(address.get_zone()); - if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) + if(rsp.status != COMMAND_PING::OK_RESPONSE || pr != rsp.peer_id) { LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << peerid_to_string(rsp.peer_id)); zone.m_net_server.get_config_object().close(ping_context.m_connection_id); @@ -2055,36 +2056,6 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::try_get_support_flags(const p2p_connection_context& context, std::function f) - { - LOG_PRINT_CC_L0(context,"try_get_support_flags"); - if(context.m_remote_address.get_zone() != epee::net_utils::zone::public_) - return false; - - COMMAND_REQUEST_SUPPORT_FLAGS::request support_flags_request{}; - bool r = epee::net_utils::async_invoke_remote_command2 - ( - context.m_connection_id, - COMMAND_REQUEST_SUPPORT_FLAGS::ID, - support_flags_request, - m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object(), - [=](int code, const typename COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context_) - { - if(code < 0) - { - LOG_PRINT_CC_L0(context_, "COMMAND_REQUEST_SUPPORT_FLAGS invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); - return; - } - LOG_PRINT_CC_L0(context_,"try_get_support_flags response"); - f(context_, rsp.support_flags); - }, - P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT - ); - - return r; - } - //----------------------------------------------------------------------------------- - template int node_server::handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) { if(!m_payload_handler.process_payload_sync_data(std::move(arg.payload_data), context, false)) @@ -2099,7 +2070,7 @@ namespace nodetool network_zone& zone = m_network_zones.at(zone_type); std::vector local_peerlist_new; - zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, P2P_DEFAULT_PEERS_IN_HANDSHAKE); + zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, cryptonote::p2p::DEFAULT_PEERS_IN_HANDSHAKE); //only include out peers we did not already send rsp.local_peerlist_new.reserve(local_peerlist_new.size()); @@ -2189,7 +2160,6 @@ namespace nodetool //associate peer_id with this connection context.peer_id = arg.node_data.peer_id; context.m_in_timedsync = false; - context.m_rpc_port = arg.node_data.rpc_port; if(arg.node_data.my_port && zone.m_can_pingback) { @@ -2217,17 +2187,10 @@ namespace nodetool pe.last_seen = static_cast(last_seen); pe.id = peer_id_l; pe.pruning_seed = context.m_pruning_seed; - pe.rpc_port = context.m_rpc_port; this->m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.append_with_peer_white(pe); LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l); }); } - LOG_PRINT_CCONTEXT_L0("COMMAND_HANDSHAKE try_get_support_flags"); - try_get_support_flags(context, [](p2p_connection_context& flags_context, const uint32_t& support_flags) - { - LOG_PRINT_L0("COMMAND_HANDSHAKE support_flags"); - flags_context.support_flags = support_flags; - }); //fill response LOG_PRINT_CCONTEXT_L0("COMMAND_HANDSHAKE get_peerlist_head"); @@ -2248,7 +2211,7 @@ namespace nodetool int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) { LOG_PRINT_CC_L0(context,"COMMAND_PING received"); - rsp.status = PING_OK_RESPONSE_STATUS_TEXT; + rsp.status = COMMAND_PING::OK_RESPONSE;; rsp.peer_id = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_peer_id; return 1; } @@ -2369,7 +2332,7 @@ namespace nodetool bool node_server::set_max_out_peers(network_zone& zone, int64_t max) { if (max == -1) - max = P2P_DEFAULT_CONNECTIONS_COUNT_OUT; + max = cryptonote::p2p::DEFAULT_CONNECTIONS_COUNT_OUT; LOG_PRINT_L0("set_max_out_peers " << max); zone.m_config.m_net_config.max_out_connection_count = max; return true; @@ -2379,7 +2342,7 @@ namespace nodetool bool node_server::set_max_in_peers(network_zone& zone, int64_t max) { if (max == -1) - max = P2P_DEFAULT_CONNECTIONS_COUNT_IN; + max = cryptonote::p2p::DEFAULT_CONNECTIONS_COUNT_IN; LOG_PRINT_L0("set_max_in_peers " << max); zone.m_config.m_net_config.max_in_connection_count = max; return true; @@ -2501,7 +2464,7 @@ namespace nodetool if (address.get_zone() != epee::net_utils::zone::public_) return false; // Unable to determine how many connections from host - const size_t max_connections = m_nettype == cryptonote::MAINNET ? 1 : 20; + const size_t max_connections = m_nettype == cryptonote::network_type::MAINNET ? 1 : 20; size_t count = 0; m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) @@ -2544,7 +2507,7 @@ namespace nodetool } else { - zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port); + zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed); LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id)); } } @@ -2555,7 +2518,7 @@ namespace nodetool void node_server::add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context) { const uint32_t stripe = tools::get_pruning_stripe(context.m_pruning_seed); - if (stripe == 0 || stripe > (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES)) + if (stripe == 0 || stripe > (1ul << cryptonote::PRUNING_LOG_STRIPES)) return; const uint32_t index = stripe - 1; std::lock_guard lock{m_used_stripe_peers_mutex}; @@ -2569,7 +2532,7 @@ namespace nodetool void node_server::remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context) { const uint32_t stripe = tools::get_pruning_stripe(context.m_pruning_seed); - if (stripe == 0 || stripe > (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES)) + if (stripe == 0 || stripe > (1ul << cryptonote::PRUNING_LOG_STRIPES)) return; const uint32_t index = stripe - 1; std::lock_guard lock{m_used_stripe_peers_mutex}; @@ -2628,7 +2591,7 @@ namespace nodetool typename net_server::t_connection_context con{}; const bool res = zone.m_net_server.connect(address, port, - zone.m_config.m_net_config.connection_timeout, + zone.m_config.m_net_config.connection_timeout.count(), con, zone.m_bind_ip); if (res) diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 7d48ff98445..e7b061860cb 100755 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -30,7 +30,6 @@ #pragma once -#include #include #include #include "cryptonote_basic/blobdatatype.h" @@ -41,21 +40,20 @@ namespace nodetool { - typedef boost::uuids::uuid uuid; - typedef boost::uuids::uuid net_connection_id; + using epee::connection_id_t; template struct i_p2p_endpoint { - virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections)=0; - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)=0; + virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections) = 0; + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const connection_id_t& source, const bool pad_txs)=0; virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_public_connections_count()=0; - virtual void for_each_connection(std::function f)=0; - virtual bool for_connection(const boost::uuids::uuid&, std::function f)=0; + virtual void for_each_connection(std::function f)=0; + virtual bool for_connection(const connection_id_t&, std::function f)=0; virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0; virtual bool unblock_host(const epee::net_utils::network_address &address)=0; virtual std::map get_blocked_hosts()=0; @@ -69,11 +67,11 @@ namespace nodetool template struct p2p_endpoint_stub: public i_p2p_endpoint { - virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections) + virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections) { return false; } - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const connection_id_t& source, const bool pad_txs) { return epee::net_utils::zone::invalid; } @@ -93,11 +91,11 @@ namespace nodetool { } - virtual void for_each_connection(std::function f) + virtual void for_each_connection(std::function f) { } - virtual bool for_connection(const boost::uuids::uuid&, std::function f) + virtual bool for_connection(const connection_id_t&, std::function f) { return false; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 94e1c304d48..86e7405a36b 100755 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -104,7 +104,7 @@ namespace nodetool size_t get_white_peers_count(){std::lock_guard lock{m_peerlist_lock}; return m_peers_white.size();} size_t get_gray_peers_count(){std::lock_guard lock{m_peerlist_lock}; return m_peers_gray.size();} bool merge_peerlist(const std::vector& outer_bs, const std::function &f = NULL); - bool get_peerlist_head(std::vector& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_head(std::vector& bs_head, bool anonymize, uint32_t depth = cryptonote::p2p::DEFAULT_PEERS_IN_HANDSHAKE); void get_peerlist(std::vector& pl_gray, std::vector& pl_white); void get_peerlist(peerlist_types& peers); bool get_white_peer_by_index(peerlist_entry& p, size_t i); @@ -113,7 +113,7 @@ namespace nodetool bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); - bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port); + bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed); bool is_host_allowed(const epee::net_utils::network_address &address); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); @@ -197,7 +197,7 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- inline void peerlist_manager::trim_gray_peerlist() { - while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT) + while(m_peers_gray.size() > cryptonote::p2p::LOCAL_GRAY_PEERLIST_LIMIT) { peers_indexed::index::type& sorted_index=m_peers_gray.get(); sorted_index.erase(sorted_index.begin()); @@ -206,7 +206,7 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- inline void peerlist_manager::trim_white_peerlist() { - while(m_peers_white.size() > P2P_LOCAL_WHITE_PEERLIST_LIMIT) + while(m_peers_white.size() > cryptonote::p2p::LOCAL_WHITE_PEERLIST_LIMIT) { peers_indexed::index::type& sorted_index=m_peers_white.get(); sorted_index.erase(sorted_index.begin()); @@ -317,7 +317,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port) + bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed) { TRY_ENTRY(); std::lock_guard lock{m_peerlist_lock}; @@ -327,7 +327,6 @@ namespace nodetool ple.id = peer; ple.last_seen = time(NULL); ple.pruning_seed = pruning_seed; - ple.rpc_port = rpc_port; return append_with_peer_white(ple); CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); } @@ -353,8 +352,6 @@ namespace nodetool peerlist_entry new_ple = ple; if (by_addr_it_wt->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around new_ple.pruning_seed = by_addr_it_wt->pruning_seed; - if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around - new_ple.rpc_port = by_addr_it_wt->rpc_port; new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted m_peers_white.replace(by_addr_it_wt, new_ple); } @@ -394,8 +391,6 @@ namespace nodetool peerlist_entry new_ple = ple; if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around new_ple.pruning_seed = by_addr_it_gr->pruning_seed; - if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around - new_ple.rpc_port = by_addr_it_gr->rpc_port; new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted m_peers_gray.replace(by_addr_it_gr, new_ple); } diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index f88030dd9c3..6c4ddc55ad1 100755 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -37,12 +37,9 @@ #include "net/tor_address.h" #include "net/i2p_address.h" #include "p2p/p2p_protocol_defs.h" - -#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED #include "common/pruning.h" -#endif -BOOST_CLASS_VERSION(nodetool::peerlist_entry, 2) +BOOST_CLASS_VERSION(nodetool::peerlist_entry, 3) namespace boost { @@ -228,19 +225,21 @@ namespace boost return; } a & pl.pruning_seed; -#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED - if (!typename Archive::is_saving()) + if constexpr (cryptonote::PRUNING_DEBUG_SPOOF_SEED && !typename Archive::is_saving()) { - pl.pruning_seed = tools::make_pruning_seed(1+pl.adr.as().ip() % (1<().ip() % (1 << cryptonote::PRUNING_LOG_STRIPES), cryptonote::PRUNING_LOG_STRIPES); } -#endif if (ver < 2) { - if (!typename Archive::is_saving()) - pl.rpc_port = 0; return; } - a & pl.rpc_port; + + if (ver < 3) + { + // Unused, but don't break + uint16_t rpc_port = 0; + a & rpc_port; + } } template diff --git a/src/p2p/p2p_protocol_defs.cpp b/src/p2p/p2p_protocol_defs.cpp new file mode 100644 index 00000000000..a5e0ba6de77 --- /dev/null +++ b/src/p2p/p2p_protocol_defs.cpp @@ -0,0 +1,96 @@ +#include "p2p_protocol_defs.h" +#include "epee/string_tools.h" +#include "epee/time_helper.h" +#include "net/tor_address.h" // needed for serialization +#include "net/i2p_address.h" // needed for serialization +#include + +namespace nodetool { + + std::string print_peerlist_to_string(const std::vector& pl) + { + time_t now = time(nullptr); + std::string result; + for (const auto& pe : pl) { + result += fmt::format("{:016x}\t{}\tpruning seed {}\tlast_seen {}", + pe.id, pe.adr.str(), pe.pruning_seed, + (pe.last_seen == 0 ? std::string("never") : epee::misc_utils::get_time_interval_string(now - pe.last_seen))); + } + return result; + } + + +KV_SERIALIZE_MAP_CODE_BEGIN(peerlist_entry) + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE_OPT(last_seen, (int64_t)0) + KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) + // rpc_port is unused, but pass it along anyway to avoid breaking the protocol + uint16_t rpc_port = 0; + KV_SERIALIZE_VALUE(rpc_port); +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(anchor_peerlist_entry) + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE(first_seen) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(connection_entry) + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE(is_income) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(network_config) + KV_SERIALIZE(max_out_connection_count) + KV_SERIALIZE(max_in_connection_count) + KV_SERIALIZE(handshake_interval) + KV_SERIALIZE(packet_max_size) + KV_SERIALIZE(config_id) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(basic_node_data) + KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) + KV_SERIALIZE(peer_id) + KV_SERIALIZE(my_port) + // Unused, but pass a 0 to avoid breaking the protocol + uint16_t rpc_port = 0; + KV_SERIALIZE_VALUE(rpc_port); +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_HANDSHAKE::request) + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_HANDSHAKE::response) + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) + KV_SERIALIZE(local_peerlist_new) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_TIMED_SYNC::request) + KV_SERIALIZE(payload_data) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_TIMED_SYNC::response) + KV_SERIALIZE(payload_data) + KV_SERIALIZE(local_peerlist_new) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_PING::request) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_PING::response) + KV_SERIALIZE(status) + KV_SERIALIZE(peer_id) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_REQUEST_SUPPORT_FLAGS::request) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(COMMAND_REQUEST_SUPPORT_FLAGS::response) + KV_SERIALIZE(support_flags) +KV_SERIALIZE_MAP_CODE_END() +} \ No newline at end of file diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 10334e97273..3bfe1514529 100755 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -30,180 +30,96 @@ #pragma once -#include -#include #include "epee/serialization/keyvalue_serialization.h" #include "epee/net/net_utils_base.h" -#include "net/tor_address.h" // needed for serialization -#include "net/i2p_address.h" // needed for serialization -#include "epee/string_tools.h" -#include "epee/time_helper.h" -#include "cryptonote_config.h" -#include "crypto/crypto.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" namespace nodetool { - typedef boost::uuids::uuid uuid; - typedef uint64_t peerid_type; + using peerid_type = uint64_t; - static inline std::string peerid_to_string(peerid_type peer_id) + #pragma pack (push, 1) + struct peerlist_entry { - std::ostringstream s; - s << std::hex << peer_id; - return epee::string_tools::pad_string(s.str(), 16, '0', true); - } - -#pragma pack (push, 1) - - struct network_address_old - { - uint32_t ip; - uint32_t port; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(ip) - KV_SERIALIZE(port) - END_KV_SERIALIZE_MAP() - }; - - template - struct peerlist_entry_base - { - AddressType adr; + epee::net_utils::network_address adr; peerid_type id; int64_t last_seen; uint32_t pruning_seed; - uint16_t rpc_port; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(adr) - KV_SERIALIZE(id) - KV_SERIALIZE_OPT(last_seen, (int64_t)0) - KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) - KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) - END_KV_SERIALIZE_MAP() + + KV_MAP_SERIALIZABLE }; - typedef peerlist_entry_base peerlist_entry; - template - struct anchor_peerlist_entry_base + struct anchor_peerlist_entry { - AddressType adr; + epee::net_utils::network_address adr; peerid_type id; int64_t first_seen; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(adr) - KV_SERIALIZE(id) - KV_SERIALIZE(first_seen) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; - typedef anchor_peerlist_entry_base anchor_peerlist_entry; - template - struct connection_entry_base + struct connection_entry { - AddressType adr; + epee::net_utils::network_address adr; peerid_type id; bool is_income; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(adr) - KV_SERIALIZE(id) - KV_SERIALIZE(is_income) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; - typedef connection_entry_base connection_entry; -#pragma pack(pop) + #pragma pack(pop) - inline - std::string print_peerlist_to_string(const std::vector& pl) - { - time_t now_time = 0; - time(&now_time); - std::stringstream ss; - ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; - for(const peerlist_entry& pe: pl) - { - ss << peerid_to_string(pe.id) << "\t" << pe.adr.str() - << " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-") - << " \tpruning seed " << pe.pruning_seed - << " \tlast_seen: " << (pe.last_seen == 0 ? std::string("never") : epee::misc_utils::get_time_interval_string(now_time - pe.last_seen)) - << std::endl; - } - return ss.str(); - } + std::string print_peerlist_to_string(const std::vector& pl); struct network_config { - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(max_out_connection_count) - KV_SERIALIZE(max_in_connection_count) - KV_SERIALIZE(handshake_interval) - KV_SERIALIZE(packet_max_size) - KV_SERIALIZE(config_id) - END_KV_SERIALIZE_MAP() - uint32_t max_out_connection_count; uint32_t max_in_connection_count; - uint32_t connection_timeout; - uint32_t ping_connection_timeout; + std::chrono::milliseconds connection_timeout; + std::chrono::milliseconds ping_connection_timeout; uint32_t handshake_interval; uint32_t packet_max_size; uint32_t config_id; uint32_t send_peerlist_sz; + + KV_MAP_SERIALIZABLE }; struct basic_node_data { - uuid network_id; + epee::connection_id_t network_id; uint32_t my_port; - uint16_t rpc_port; peerid_type peer_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) - KV_SERIALIZE(peer_id) - KV_SERIALIZE(my_port) - KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0)) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; -#define P2P_COMMANDS_POOL_BASE 1000 + inline constexpr int P2P_COMMANDS_POOL_BASE = 1000; /************************************************************************/ /* */ /************************************************************************/ - template - struct COMMAND_HANDSHAKE_T - { - const static int ID = P2P_COMMANDS_POOL_BASE + 1; + struct COMMAND_HANDSHAKE + { + const static int ID = P2P_COMMANDS_POOL_BASE + 1; struct request { basic_node_data node_data; - Payload payload_data; + cryptonote::CORE_SYNC_DATA payload_data; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(node_data) - KV_SERIALIZE(payload_data) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; struct response { basic_node_data node_data; - Payload payload_data; + cryptonote::CORE_SYNC_DATA payload_data; std::vector local_peerlist_new; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(node_data) - KV_SERIALIZE(payload_data) - KV_SERIALIZE(local_peerlist_new) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; }; @@ -211,29 +127,24 @@ namespace nodetool /************************************************************************/ /* */ /************************************************************************/ - template - struct COMMAND_TIMED_SYNC_T + struct COMMAND_TIMED_SYNC { const static int ID = P2P_COMMANDS_POOL_BASE + 2; struct request { - Payload payload_data; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payload_data) - END_KV_SERIALIZE_MAP() + cryptonote::CORE_SYNC_DATA payload_data; + + KV_MAP_SERIALIZABLE }; struct response { uint64_t local_time; - Payload payload_data; + cryptonote::CORE_SYNC_DATA payload_data; std::vector local_peerlist_new; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payload_data) - KV_SERIALIZE(local_peerlist_new) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; }; @@ -250,14 +161,13 @@ namespace nodetool */ const static int ID = P2P_COMMANDS_POOL_BASE + 3; -#define PING_OK_RESPONSE_STATUS_TEXT "OK" + static constexpr auto OK_RESPONSE = "OK"sv; struct request { /*actually we don't need to send any real data*/ - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; struct response @@ -265,10 +175,7 @@ namespace nodetool std::string status; peerid_type peer_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(peer_id) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; }; @@ -276,23 +183,21 @@ namespace nodetool /************************************************************************/ /* */ /************************************************************************/ + // TODO: remove after HF struct COMMAND_REQUEST_SUPPORT_FLAGS { const static int ID = P2P_COMMANDS_POOL_BASE + 7; struct request { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; struct response { uint32_t support_flags; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(support_flags) - END_KV_SERIALIZE_MAP() + KV_MAP_SERIALIZABLE }; }; } diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index d27425e0c10..9aebfcd4f39 100755 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -32,7 +32,8 @@ add_library(ringct_basic rctTypes.cpp rctCryptoOps.c multiexp.cc - bulletproofs.cc) + bulletproofs.cc + bulletproofs_plus.cc) target_link_libraries(ringct_basic PUBLIC diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 85eb4d48b05..adac79e2be0 100755 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -68,14 +68,13 @@ static rct::keyV vector_dup(const rct::key &x, size_t n); static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); static constexpr size_t maxN = 64; -static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS; -static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; +static constexpr size_t maxM = cryptonote::TX_BULLETPROOF_MAX_OUTPUTS;; static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static std::shared_ptr straus_HiGi_cache; static std::shared_ptr pippenger_HiGi_cache; -static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; -static const rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; -static const rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } }; +static const constexpr rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; +static const constexpr rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; +static const constexpr rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } }; static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); @@ -99,8 +98,7 @@ static inline bool is_reduced(const rct::key &scalar) static rct::key get_exponent(const rct::key &base, size_t idx) { - static const std::string domain_separator(config::HASH_KEY_BULLETPROOF_EXPONENT); - std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + domain_separator + tools::get_varint_data(idx); + std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + std::string(cryptonote::hashkey::BULLETPROOF_EXPONENT) + tools::get_varint_data(idx); rct::key e; ge_p3 e_p3; rct::hash_to_p3(e_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); @@ -120,10 +118,10 @@ static void init_exponents() data.reserve(maxN*maxM*2); for (size_t i = 0; i < maxN*maxM; ++i) { - Hi[i] = get_exponent(rct::H, i * 2); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); - Gi[i] = get_exponent(rct::H, i * 2 + 1); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); + const rct::key Hi = get_exponent(rct::H, i * 2); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi.bytes) == 0, "ge_frombytes_vartime failed"); + const rct::key Gi = get_exponent(rct::H, i * 2 + 1); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi.bytes) == 0, "ge_frombytes_vartime failed"); data.push_back({rct::zero(), Gi_p3[i]}); data.push_back({rct::zero(), Hi_p3[i]}); @@ -132,11 +130,10 @@ static void init_exponents() straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); pippenger_HiGi_cache = pippenger_init_cache(data, 0, PIPPENGER_SIZE_LIMIT); - MINFO("Hi/Gi cache size: " << (sizeof(Hi)+sizeof(Gi))/1024 << " kB"); MINFO("Hi_p3/Gi_p3 cache size: " << (sizeof(Hi_p3)+sizeof(Gi_p3))/1024 << " kB"); MINFO("Straus cache size: " << straus_get_cache_size(straus_HiGi_cache)/1024 << " kB"); + size_t cache_size = straus_get_cache_size(straus_HiGi_cache) + pippenger_get_cache_size(pippenger_HiGi_cache); MINFO("Pippenger cache size: " << pippenger_get_cache_size(pippenger_HiGi_cache)/1024 << " kB"); - size_t cache_size = (sizeof(Hi)+sizeof(Hi_p3))*2 + straus_get_cache_size(straus_HiGi_cache) + pippenger_get_cache_size(pippenger_HiGi_cache); MINFO("Total cache size: " << cache_size/1024 << "kB"); init_done = true; } @@ -894,7 +891,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) multiexp_data.resize(2 * maxMN); PERF_TIMER_START_BP(VERIFY_line_24_25_invert); - const std::vector inverses = invert(to_invert); + const std::vector inverses = invert(std::move(to_invert)); + to_invert.clear(); PERF_TIMER_STOP_BP(VERIFY_line_24_25_invert); // setup weighted aggregates diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc new file mode 100644 index 00000000000..86767cbee50 --- /dev/null +++ b/src/ringct/bulletproofs_plus.cc @@ -0,0 +1,1123 @@ +// Copyright (c) 2017-2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Implements the Bulletproofs+ prover and verifier algorithms +// +// Preprint: https://eprint.iacr.org/2020/735, version 17 Jun 2020 +// +// NOTE ON NOTATION: +// In the signature constructions used in Monero, commitments to zero are treated as +// public keys against the curve group generator `G`. This means that amount +// commitments must use another generator `H` for values in order to show balance. +// The result is that the roles of `g` and `h` in the preprint are effectively swapped +// in this code, taking on the roles of `H` and `G`, respectively. Read carefully! + +#include +#include +#include +#include "epee/misc_log_ex.h" +#include "epee/span.h" +#include "cryptonote_config.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "rctOps.h" +#include "multiexp.h" +#include "bulletproofs_plus.h" +#include "common/varint.h" + +#undef BELDEX_DEFAULT_LOG_CATEGORY +#define BELDEX_DEFAULT_LOG_CATEGORY "bulletproof_plus" + +#define STRAUS_SIZE_LIMIT 232 +#define PIPPENGER_SIZE_LIMIT 0 + +namespace rct +{ + // Vector functions + static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b); + static rct::keyV vector_of_scalar_powers(const rct::key &x, size_t n); + + // Proof bounds + static constexpr size_t maxN = 64; // maximum number of bits in range + static constexpr size_t maxM = cryptonote::TX_BULLETPROOF_PLUS_MAX_OUTPUTS; // maximum number of outputs to aggregate into a single proof + + // Cached public generators + static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; + static std::shared_ptr straus_HiGi_cache; + static std::shared_ptr pippenger_HiGi_cache; + + // Useful scalar constants + static const constexpr rct::key ZERO = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; // 0 + static const constexpr rct::key ONE = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; // 1 + static const constexpr rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; // 2 + static const constexpr rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; // -1 + static const constexpr rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } }; // -(8**(-1)) + static rct::key TWO_SIXTY_FOUR_MINUS_ONE; // 2**64 - 1 + + // Initial transcript hash + static rct::key initial_transcript; + + static boost::mutex init_mutex; + + // Use the generator caches to compute a multiscalar multiplication + static inline rct::key multiexp(const std::vector &data, size_t HiGi_size) + { + if (HiGi_size > 0) + { + static_assert(232 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); + return HiGi_size <= 232 && data.size() == HiGi_size ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, HiGi_size, get_pippenger_c(data.size())); + } + else + { + return data.size() <= 95 ? straus(data, NULL, 0) : pippenger(data, NULL, 0, get_pippenger_c(data.size())); + } + } + + // Confirm that a scalar is properly reduced + static inline bool is_reduced(const rct::key &scalar) + { + return sc_check(scalar.bytes) == 0; + } + + // Use hashed values to produce indexed public generators + static ge_p3 get_exponent(const rct::key &base, size_t idx) + { + static const std::string domain_separator(cryptonote::hashkey::BULLETPROOF_PLUS_EXPONENT); + std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + domain_separator + tools::get_varint_data(idx); + rct::key generator; + ge_p3 generator_p3; + rct::hash_to_p3(generator_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); + ge_p3_tobytes(generator.bytes, &generator_p3); + CHECK_AND_ASSERT_THROW_MES(!(generator == rct::identity()), "Exponent is point at infinity"); + return generator_p3; + } + + // Construct public generators + static void init_exponents() + { + boost::lock_guard lock(init_mutex); + + // Only needs to be done once + static bool init_done = false; + if (init_done) + return; + + std::vector data; + data.reserve(maxN*maxM*2); + for (size_t i = 0; i < maxN*maxM; ++i) + { + Hi_p3[i] = get_exponent(rct::H, i * 2); + Gi_p3[i] = get_exponent(rct::H, i * 2 + 1); + + data.push_back({rct::zero(), Gi_p3[i]}); + data.push_back({rct::zero(), Hi_p3[i]}); + } + + straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); + pippenger_HiGi_cache = pippenger_init_cache(data, 0, PIPPENGER_SIZE_LIMIT); + + // Compute 2**64 - 1 for later use in simplifying verification + TWO_SIXTY_FOUR_MINUS_ONE = TWO; + for (size_t i = 0; i < 6; i++) + { + sc_mul(TWO_SIXTY_FOUR_MINUS_ONE.bytes, TWO_SIXTY_FOUR_MINUS_ONE.bytes, TWO_SIXTY_FOUR_MINUS_ONE.bytes); + } + sc_sub(TWO_SIXTY_FOUR_MINUS_ONE.bytes, TWO_SIXTY_FOUR_MINUS_ONE.bytes, ONE.bytes); + + // Generate the initial Fiat-Shamir transcript hash, which is constant across all proofs + const std::string domain_separator(cryptonote::hashkey::BULLETPROOF_PLUS_TRANSCRIPT); + ge_p3 initial_transcript_p3; + rct::hash_to_p3(initial_transcript_p3, rct::hash2rct(crypto::cn_fast_hash(domain_separator.data(), domain_separator.size()))); + ge_p3_tobytes(initial_transcript.bytes, &initial_transcript_p3); + + init_done = true; + } + + // Given two scalar arrays, construct a vector pre-commitment: + // + // a = (a_0, ..., a_{n-1}) + // b = (b_0, ..., b_{n-1}) + // + // Outputs a_0*Gi_0 + ... + a_{n-1}*Gi_{n-1} + + // b_0*Hi_0 + ... + b_{n-1}*Hi_{n-1} + static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) + { + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + multiexp_data.emplace_back(a[i], Gi_p3[i]); + multiexp_data.emplace_back(b[i], Hi_p3[i]); + } + return multiexp(multiexp_data, 2 * a.size()); + } + + // Helper function used to compute the L and R terms used in the inner-product round function + static rct::key compute_LR(size_t size, const rct::key &y, const std::vector &G, size_t G0, const std::vector &H, size_t H0, const rct::keyV &a, size_t a0, const rct::keyV &b, size_t b0, const rct::key &c, const rct::key &d) + { + CHECK_AND_ASSERT_THROW_MES(size + G0 <= G.size(), "Incompatible size for G"); + CHECK_AND_ASSERT_THROW_MES(size + H0 <= H.size(), "Incompatible size for H"); + CHECK_AND_ASSERT_THROW_MES(size + a0 <= a.size(), "Incompatible size for a"); + CHECK_AND_ASSERT_THROW_MES(size + b0 <= b.size(), "Incompatible size for b"); + CHECK_AND_ASSERT_THROW_MES(size <= maxN*maxM, "size is too large"); + + std::vector multiexp_data; + multiexp_data.resize(size*2 + 2); + rct::key temp; + for (size_t i = 0; i < size; ++i) + { + sc_mul(temp.bytes, a[a0+i].bytes, y.bytes); + sc_mul(multiexp_data[i*2].scalar.bytes, temp.bytes, INV_EIGHT.bytes); + multiexp_data[i*2].point = G[G0+i]; + + sc_mul(multiexp_data[i*2+1].scalar.bytes, b[b0+i].bytes, INV_EIGHT.bytes); + multiexp_data[i*2+1].point = H[H0+i]; + } + + sc_mul(multiexp_data[2*size].scalar.bytes, c.bytes, INV_EIGHT.bytes); + ge_p3 H_p3; + ge_frombytes_vartime(&H_p3, rct::H.bytes); + multiexp_data[2*size].point = H_p3; + + sc_mul(multiexp_data[2*size+1].scalar.bytes, d.bytes, INV_EIGHT.bytes); + ge_p3 G_p3; + ge_frombytes_vartime(&G_p3, rct::G.bytes); + multiexp_data[2*size+1].point = G_p3; + + return multiexp(multiexp_data, 0); + } + + // Given a scalar, construct a vector of its powers: + // + // Output (1,x,x**2,...,x**{n-1}) + static rct::keyV vector_of_scalar_powers(const rct::key &x, size_t n) + { + CHECK_AND_ASSERT_THROW_MES(n != 0, "Need n > 0"); + + rct::keyV res(n); + res[0] = rct::identity(); + if (n == 1) + return res; + res[1] = x; + for (size_t i = 2; i < n; ++i) + { + sc_mul(res[i].bytes, res[i-1].bytes, x.bytes); + } + return res; + } + + // Given a scalar, construct the sum of its powers from 2 to n (where n is a power of 2): + // + // Output x**2 + x**4 + x**6 + ... + x**n + static rct::key sum_of_even_powers(const rct::key &x, size_t n) + { + CHECK_AND_ASSERT_THROW_MES((n & (n - 1)) == 0, "Need n to be a power of 2"); + CHECK_AND_ASSERT_THROW_MES(n != 0, "Need n > 0"); + + rct::key x1 = copy(x); + sc_mul(x1.bytes, x1.bytes, x1.bytes); + + rct::key res = copy(x1); + while (n > 2) + { + sc_muladd(res.bytes, x1.bytes, res.bytes, res.bytes); + sc_mul(x1.bytes, x1.bytes, x1.bytes); + n /= 2; + } + + return res; + } + + // Given a scalar, return the sum of its powers from 1 to n + // + // Output x**1 + x**2 + x**3 + ... + x**n + static rct::key sum_of_scalar_powers(const rct::key &x, size_t n) + { + CHECK_AND_ASSERT_THROW_MES(n != 0, "Need n > 0"); + + rct::key res = ONE; + if (n == 1) + return x; + + n += 1; + rct::key x1 = copy(x); + + const bool is_power_of_2 = (n & (n - 1)) == 0; + if (is_power_of_2) + { + sc_add(res.bytes, res.bytes, x1.bytes); + while (n > 2) + { + sc_mul(x1.bytes, x1.bytes, x1.bytes); + sc_muladd(res.bytes, x1.bytes, res.bytes, res.bytes); + n /= 2; + } + } + else + { + rct::key prev = x1; + for (size_t i = 1; i < n; ++i) + { + if (i > 1) + sc_mul(prev.bytes, prev.bytes, x1.bytes); + sc_add(res.bytes, res.bytes, prev.bytes); + } + } + sc_sub(res.bytes, res.bytes, ONE.bytes); + + return res; + } + + // Given two scalar arrays, construct the weighted inner product against another scalar + // + // Output a_0*b_0*y**1 + a_1*b_1*y**2 + ... + a_{n-1}*b_{n-1}*y**n + static rct::key weighted_inner_product(const epee::span &a, const epee::span &b, const rct::key &y) + { + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::key res = rct::zero(); + rct::key y_power = ONE; + rct::key temp; + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(temp.bytes, a[i].bytes, b[i].bytes); + sc_mul(y_power.bytes, y_power.bytes, y.bytes); + sc_muladd(res.bytes, temp.bytes, y_power.bytes, res.bytes); + } + return res; + } + + static rct::key weighted_inner_product(const rct::keyV &a, const epee::span &b, const rct::key &y) + { + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + return weighted_inner_product(epee::to_span(a), b, y); + } + + // Fold inner-product point vectors + static void hadamard_fold(std::vector &v, const rct::key &a, const rct::key &b) + { + CHECK_AND_ASSERT_THROW_MES((v.size() & 1) == 0, "Vector size should be even"); + const size_t sz = v.size() / 2; + for (size_t n = 0; n < sz; ++n) + { + ge_dsmp c[2]; + ge_dsm_precomp(c[0], &v[n]); + ge_dsm_precomp(c[1], &v[sz + n]); + ge_double_scalarmult_precomp_vartime2_p3(&v[n], a.bytes, c[0], b.bytes, c[1]); + } + v.resize(sz); + } + + // Add vectors componentwise + static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b) + { + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; + } + + // Add a scalar to all elements of a vector + static rct::keyV vector_add(const rct::keyV &a, const rct::key &b) + { + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b.bytes); + } + return res; + } + + // Subtract a scalar from all elements of a vector + static rct::keyV vector_subtract(const rct::keyV &a, const rct::key &b) + { + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_sub(res[i].bytes, a[i].bytes, b.bytes); + } + return res; + } + + // Multiply a scalar by all elements of a vector + static rct::keyV vector_scalar(const epee::span &a, const rct::key &x) + { + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, x.bytes); + } + return res; + } + + // Inversion helper function + static rct::key sm(rct::key y, int n, const rct::key &x) + { + while (n--) + sc_mul(y.bytes, y.bytes, y.bytes); + sc_mul(y.bytes, y.bytes, x.bytes); + return y; + } + + // Compute the inverse of a nonzero + static rct::key invert(const rct::key &x) + { + CHECK_AND_ASSERT_THROW_MES(!(x == ZERO), "Cannot invert zero!"); + rct::key _1, _10, _100, _11, _101, _111, _1001, _1011, _1111; + + _1 = x; + sc_mul(_10.bytes, _1.bytes, _1.bytes); + sc_mul(_100.bytes, _10.bytes, _10.bytes); + sc_mul(_11.bytes, _10.bytes, _1.bytes); + sc_mul(_101.bytes, _10.bytes, _11.bytes); + sc_mul(_111.bytes, _10.bytes, _101.bytes); + sc_mul(_1001.bytes, _10.bytes, _111.bytes); + sc_mul(_1011.bytes, _10.bytes, _1001.bytes); + sc_mul(_1111.bytes, _100.bytes, _1011.bytes); + + rct::key inv; + sc_mul(inv.bytes, _1111.bytes, _1.bytes); + + inv = sm(inv, 123 + 3, _101); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 4, _1001); + inv = sm(inv, 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 3, _101); + inv = sm(inv, 3 + 3, _101); + inv = sm(inv, 3, _111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 2 + 3, _111); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 2 + 4, _1011); + inv = sm(inv, 6 + 4, _1001); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 1 + 4, _1001); + inv = sm(inv, 1 + 3, _111); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 3, _101); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 3, _101); + inv = sm(inv, 1 + 2, _11); + + return inv; + } + + // Invert a batch of scalars, all of which _must_ be nonzero + static rct::keyV invert(rct::keyV x) + { + rct::keyV scratch; + scratch.reserve(x.size()); + + rct::key acc = rct::identity(); + for (size_t n = 0; n < x.size(); ++n) + { + CHECK_AND_ASSERT_THROW_MES(!(x[n] == ZERO), "Cannot invert zero!"); + scratch.push_back(acc); + if (n == 0) + acc = x[0]; + else + sc_mul(acc.bytes, acc.bytes, x[n].bytes); + } + + acc = invert(acc); + + rct::key tmp; + for (int i = x.size(); i-- > 0; ) + { + sc_mul(tmp.bytes, acc.bytes, x[i].bytes); + sc_mul(x[i].bytes, acc.bytes, scratch[i].bytes); + acc = tmp; + } + + return x; + } + + // Compute the slice of a vector + static epee::span slice(const rct::keyV &a, size_t start, size_t stop) + { + CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); + CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); + CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); + return epee::span(&a[start], stop - start); + } + + // Update the transcript + static rct::key transcript_update(rct::key &transcript, const rct::key &update_0) + { + rct::key data[2]; + data[0] = transcript; + data[1] = update_0; + rct::hash_to_scalar(transcript, data, sizeof(data)); + return transcript; + } + + static rct::key transcript_update(rct::key &transcript, const rct::key &update_0, const rct::key &update_1) + { + rct::key data[3]; + data[0] = transcript; + data[1] = update_0; + data[2] = update_1; + rct::hash_to_scalar(transcript, data, sizeof(data)); + return transcript; + } + + // Given a value v [0..2**N) and a mask gamma, construct a range proof + BulletproofPlus bulletproof_plus_PROVE(const rct::key &sv, const rct::key &gamma) + { + return bulletproof_plus_PROVE(rct::keyV(1, sv), rct::keyV(1, gamma)); + } + + BulletproofPlus bulletproof_plus_PROVE(uint64_t v, const rct::key &gamma) + { + return bulletproof_plus_PROVE(std::vector(1, v), rct::keyV(1, gamma)); + } + + // Given a set of values v [0..2**N) and masks gamma, construct a range proof + BulletproofPlus bulletproof_plus_PROVE(const rct::keyV &sv, const rct::keyV &gamma) + { + // Sanity check on inputs + CHECK_AND_ASSERT_THROW_MES(sv.size() == gamma.size(), "Incompatible sizes of sv and gamma"); + CHECK_AND_ASSERT_THROW_MES(!sv.empty(), "sv is empty"); + for (const rct::key &sve: sv) + CHECK_AND_ASSERT_THROW_MES(is_reduced(sve), "Invalid sv input"); + for (const rct::key &g: gamma) + CHECK_AND_ASSERT_THROW_MES(is_reduced(g), "Invalid gamma input"); + + init_exponents(); + + // Useful proof bounds + // + // N: number of bits in each range (here, 64) + // logN: base-2 logarithm + // M: first power of 2 greater than or equal to the number of range proofs to aggregate + // logM: base-2 logarithm + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (j < sv.size() && (sv[j][i/8] & (((uint64_t)1)<<(i%8)))) + { + aL[j*N+i] = rct::identity(); + aL8[j*N+i] = INV_EIGHT; + aR[j*N+i] = aR8[j*N+i] = rct::zero(); + } + else + { + aL[j*N+i] = aL8[j*N+i] = rct::zero(); + aR[j*N+i] = MINUS_ONE; + aR8[j*N+i] = MINUS_INV_EIGHT; + } + } + } + +try_again: + // This is a Fiat-Shamir transcript + rct::key transcript = copy(initial_transcript); + transcript = transcript_update(transcript, rct::hash_to_scalar(V)); + + // A + rct::key alpha = rct::skGen(); + rct::key pre_A = vector_exponent(aL8, aR8); + rct::key A; + sc_mul(temp.bytes, alpha.bytes, INV_EIGHT.bytes); + rct::addKeys(A, pre_A, rct::scalarmultBase(temp)); + + // Challenges + rct::key y = transcript_update(transcript, A); + if (y == rct::zero()) + { + MINFO("y is 0, trying again"); + goto try_again; + } + rct::key z = transcript = rct::hash_to_scalar(y); + if (z == rct::zero()) + { + MINFO("z is 0, trying again"); + goto try_again; + } + rct::key z_squared; + sc_mul(z_squared.bytes, z.bytes, z.bytes); + + // Windowed vector + // d[j*N+i] = z**(2*(j+1)) * 2**i + // + // We compute this iteratively in order to reduce scalar operations. + rct::keyV d(MN, rct::zero()); + d[0] = z_squared; + for (size_t i = 1; i < N; i++) + { + sc_mul(d[i].bytes, d[i-1].bytes, TWO.bytes); + } + + for (size_t j = 1; j < M; j++) + { + for (size_t i = 0; i < N; i++) + { + sc_mul(d[j*N+i].bytes, d[(j-1)*N+i].bytes, z_squared.bytes); + } + } + + rct::keyV y_powers = vector_of_scalar_powers(y, MN+2); + + // Prepare inner product terms + rct::keyV aL1 = vector_subtract(aL, z); + + rct::keyV aR1 = vector_add(aR, z); + rct::keyV d_y(MN); + for (size_t i = 0; i < MN; i++) + { + sc_mul(d_y[i].bytes, d[i].bytes, y_powers[MN-i].bytes); + } + aR1 = vector_add(aR1, d_y); + + rct::key alpha1 = alpha; + temp = ONE; + for (size_t j = 0; j < sv.size(); j++) + { + sc_mul(temp.bytes, temp.bytes, z_squared.bytes); + sc_mul(temp2.bytes, y_powers[MN+1].bytes, temp.bytes); + sc_muladd(alpha1.bytes, temp2.bytes, gamma[j].bytes, alpha1.bytes); + } + + // These are used in the inner product rounds + size_t nprime = MN; + std::vector Gprime(MN); + std::vector Hprime(MN); + rct::keyV aprime(MN); + rct::keyV bprime(MN); + + const rct::key yinv = invert(y); + rct::keyV yinvpow(MN); + yinvpow[0] = ONE; + for (size_t i = 0; i < MN; ++i) + { + Gprime[i] = Gi_p3[i]; + Hprime[i] = Hi_p3[i]; + if (i > 0) + { + sc_mul(yinvpow[i].bytes, yinvpow[i-1].bytes, yinv.bytes); + } + aprime[i] = aL1[i]; + bprime[i] = aR1[i]; + } + rct::keyV L(logMN); + rct::keyV R(logMN); + int round = 0; + + // Inner-product rounds + while (nprime > 1) + { + nprime /= 2; + + rct::key cL = weighted_inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size()), y); + rct::key cR = weighted_inner_product(vector_scalar(slice(aprime, nprime, aprime.size()), y_powers[nprime]), slice(bprime, 0, nprime), y); + + rct::key dL = rct::skGen(); + rct::key dR = rct::skGen(); + + L[round] = compute_LR(nprime, yinvpow[nprime], Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, cL, dL); + R[round] = compute_LR(nprime, y_powers[nprime], Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, cR, dR); + + const rct::key challenge = transcript_update(transcript, L[round], R[round]); + if (challenge == rct::zero()) + { + MINFO("challenge is 0, trying again"); + goto try_again; + } + + const rct::key challenge_inv = invert(challenge); + + sc_mul(temp.bytes, yinvpow[nprime].bytes, challenge.bytes); + hadamard_fold(Gprime, challenge_inv, temp); + hadamard_fold(Hprime, challenge, challenge_inv); + + sc_mul(temp.bytes, challenge_inv.bytes, y_powers[nprime].bytes); + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), challenge), vector_scalar(slice(aprime, nprime, aprime.size()), temp)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), challenge_inv), vector_scalar(slice(bprime, nprime, bprime.size()), challenge)); + + rct::key challenge_squared; + sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes); + rct::key challenge_squared_inv; + sc_mul(challenge_squared_inv.bytes, challenge_inv.bytes, challenge_inv.bytes); + sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.bytes, alpha1.bytes); + sc_muladd(alpha1.bytes, dR.bytes, challenge_squared_inv.bytes, alpha1.bytes); + + ++round; + } + + // Final round computations + rct::key r = rct::skGen(); + rct::key s = rct::skGen(); + rct::key d_ = rct::skGen(); + rct::key eta = rct::skGen(); + + std::vector A1_data; + A1_data.reserve(4); + A1_data.resize(4); + + sc_mul(A1_data[0].scalar.bytes, r.bytes, INV_EIGHT.bytes); + A1_data[0].point = Gprime[0]; + + sc_mul(A1_data[1].scalar.bytes, s.bytes, INV_EIGHT.bytes); + A1_data[1].point = Hprime[0]; + + sc_mul(A1_data[2].scalar.bytes, d_.bytes, INV_EIGHT.bytes); + ge_p3 G_p3; + ge_frombytes_vartime(&G_p3, rct::G.bytes); + A1_data[2].point = G_p3; + + sc_mul(temp.bytes, r.bytes, y.bytes); + sc_mul(temp.bytes, temp.bytes, bprime[0].bytes); + sc_mul(temp2.bytes, s.bytes, y.bytes); + sc_mul(temp2.bytes, temp2.bytes, aprime[0].bytes); + sc_add(temp.bytes, temp.bytes, temp2.bytes); + sc_mul(A1_data[3].scalar.bytes, temp.bytes, INV_EIGHT.bytes); + ge_p3 H_p3; + ge_frombytes_vartime(&H_p3, rct::H.bytes); + A1_data[3].point = H_p3; + + rct::key A1 = multiexp(A1_data, 0); + + sc_mul(temp.bytes, r.bytes, y.bytes); + sc_mul(temp.bytes, temp.bytes, s.bytes); + sc_mul(temp.bytes, temp.bytes, INV_EIGHT.bytes); + sc_mul(temp2.bytes, eta.bytes, INV_EIGHT.bytes); + rct::key B; + rct::addKeys2(B, temp2, temp, rct::H); + + rct::key e = transcript_update(transcript, A1, B); + if (e == rct::zero()) + { + MINFO("e is 0, trying again"); + goto try_again; + } + rct::key e_squared; + sc_mul(e_squared.bytes, e.bytes, e.bytes); + + rct::key r1; + sc_muladd(r1.bytes, aprime[0].bytes, e.bytes, r.bytes); + + rct::key s1; + sc_muladd(s1.bytes, bprime[0].bytes, e.bytes, s.bytes); + + rct::key d1; + sc_muladd(d1.bytes, d_.bytes, e.bytes, eta.bytes); + sc_muladd(d1.bytes, alpha1.bytes, e_squared.bytes, d1.bytes); + + return BulletproofPlus(std::move(V), A, A1, B, r1, s1, d1, std::move(L), std::move(R)); + } + + BulletproofPlus bulletproof_plus_PROVE(const std::vector &v, const rct::keyV &gamma) + { + CHECK_AND_ASSERT_THROW_MES(v.size() == gamma.size(), "Incompatible sizes of v and gamma"); + + // vG + gammaH + rct::keyV sv(v.size()); + for (size_t i = 0; i < v.size(); ++i) + { + sv[i] = rct::d2h(v[i]); + } + return bulletproof_plus_PROVE(sv, gamma); + } + + struct bp_plus_proof_data_t + { + rct::key y, z, e; + std::vector challenges; + size_t logM, inv_offset; + }; + + // Given a batch of range proofs, determine if they are all valid + bool bulletproof_plus_VERIFY(const std::vector &proofs) + { + init_exponents(); + + const size_t logN = 6; + const size_t N = 1 << logN; + + // Set up + size_t max_length = 0; // size of each of the longest proof's inner-product vectors + size_t nV = 0; // number of output commitments across all proofs + size_t inv_offset = 0; + size_t max_logM = 0; + + std::vector proof_data; + proof_data.reserve(proofs.size()); + + // We'll perform only a single batch inversion across all proofs in the batch, + // since batch inversion requires only one scalar inversion operation. + std::vector to_invert; + to_invert.reserve(11 * proofs.size()); // maximal size, given the aggregation limit + + for (const BulletproofPlus *p: proofs) + { + const BulletproofPlus &proof = *p; + + // Sanity checks + CHECK_AND_ASSERT_MES(is_reduced(proof.r1), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.s1), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.d1), false, "Input scalar not in range"); + + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); + CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); + + max_length = std::max(max_length, proof.L.size()); + nV += proof.V.size(); + + proof_data.push_back({}); + bp_plus_proof_data_t &pd = proof_data.back(); + + // Reconstruct the challenges + rct::key transcript = copy(initial_transcript); + transcript = transcript_update(transcript, rct::hash_to_scalar(proof.V)); + pd.y = transcript_update(transcript, proof.A); + CHECK_AND_ASSERT_MES(!(pd.y == rct::zero()), false, "y == 0"); + pd.z = transcript = rct::hash_to_scalar(pd.y); + CHECK_AND_ASSERT_MES(!(pd.z == rct::zero()), false, "z == 0"); + + // Determine the number of inner-product rounds based on proof size + size_t M; + for (pd.logM = 0; (M = 1< 0, false, "Zero rounds"); + + // The inner-product challenges are computed per round + pd.challenges.resize(rounds); + for (size_t j = 0; j < rounds; ++j) + { + pd.challenges[j] = transcript_update(transcript, proof.L[j], proof.R[j]); + CHECK_AND_ASSERT_MES(!(pd.challenges[j] == rct::zero()), false, "challenges[j] == 0"); + } + + // Final challenge + pd.e = transcript_update(transcript,proof.A1,proof.B); + CHECK_AND_ASSERT_MES(!(pd.e == rct::zero()), false, "e == 0"); + + // Batch scalar inversions + pd.inv_offset = inv_offset; + for (size_t j = 0; j < rounds; ++j) + to_invert.push_back(pd.challenges[j]); + to_invert.push_back(pd.y); + inv_offset += rounds + 1; + } + CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); + size_t maxMN = 1u << max_length; + + rct::key temp; + rct::key temp2; + + // Final batch proof data + std::vector multiexp_data; + multiexp_data.reserve(nV + (2 * (max_logM + logN) + 3) * proofs.size() + 2 * maxMN); + multiexp_data.resize(2 * maxMN); + + const std::vector inverses = invert(std::move(to_invert)); + to_invert.clear(); + + // Weights and aggregates + // + // The idea is to take the single multiscalar multiplication used in the verification + // of each proof in the batch and weight it using a random weighting factor, resulting + // in just one multiscalar multiplication check to zero for the entire batch. + // We can further simplify the verifier complexity by including common group elements + // only once in this single multiscalar multiplication. + // Common group elements' weighted scalar sums are tracked across proofs for this reason. + // + // To build a multiscalar multiplication for each proof, we use the method described in + // Section 6.1 of the preprint. Note that the result given there does not account for + // the construction of the inner-product inputs that are produced in the range proof + // verifier algorithm; we have done so here. + rct::key G_scalar = rct::zero(); + rct::key H_scalar = rct::zero(); + rct::keyV Gi_scalars(maxMN, rct::zero()); + rct::keyV Hi_scalars(maxMN, rct::zero()); + + int proof_data_index = 0; + rct::keyV challenges_cache; + std::vector proof8_V, proof8_L, proof8_R; + + // Process each proof and add to the weighted batch + for (const BulletproofPlus *p: proofs) + { + const BulletproofPlus &proof = *p; + const bp_plus_proof_data_t &pd = proof_data[proof_data_index++]; + + CHECK_AND_ASSERT_MES(proof.L.size() == 6+pd.logM, false, "Proof is not the expected size"); + const size_t M = 1 << pd.logM; + const size_t MN = M*N; + + // Random weighting factor must be nonzero, which is exceptionally unlikely! + rct::key weight = ZERO; + while (weight == ZERO) + { + weight = rct::skGen(); + } + + // Rescale previously offset proof elements + // + // This ensures that all such group elements are in the prime-order subgroup. + proof8_V.resize(proof.V.size()); for (size_t i = 0; i < proof.V.size(); ++i) rct::scalarmult8(proof8_V[i], proof.V[i]); + proof8_L.resize(proof.L.size()); for (size_t i = 0; i < proof.L.size(); ++i) rct::scalarmult8(proof8_L[i], proof.L[i]); + proof8_R.resize(proof.R.size()); for (size_t i = 0; i < proof.R.size(); ++i) rct::scalarmult8(proof8_R[i], proof.R[i]); + ge_p3 proof8_A1; + ge_p3 proof8_B; + ge_p3 proof8_A; + rct::scalarmult8(proof8_A1, proof.A1); + rct::scalarmult8(proof8_B, proof.B); + rct::scalarmult8(proof8_A, proof.A); + + // Compute necessary powers of the y-challenge + rct::key y_MN = copy(pd.y); + rct::key y_MN_1; + size_t temp_MN = MN; + while (temp_MN > 1) + { + sc_mul(y_MN.bytes, y_MN.bytes, y_MN.bytes); + temp_MN /= 2; + } + sc_mul(y_MN_1.bytes, y_MN.bytes, pd.y.bytes); + + // V_j: -e**2 * z**(2*j+1) * y**(MN+1) * weight + rct::key e_squared; + sc_mul(e_squared.bytes, pd.e.bytes, pd.e.bytes); + + rct::key z_squared; + sc_mul(z_squared.bytes, pd.z.bytes, pd.z.bytes); + + sc_sub(temp.bytes, ZERO.bytes, e_squared.bytes); + sc_mul(temp.bytes, temp.bytes, y_MN_1.bytes); + sc_mul(temp.bytes, temp.bytes, weight.bytes); + for (size_t j = 0; j < proof8_V.size(); j++) + { + sc_mul(temp.bytes, temp.bytes, z_squared.bytes); + multiexp_data.emplace_back(temp, proof8_V[j]); + } + + // B: -weight + sc_mul(temp.bytes, MINUS_ONE.bytes, weight.bytes); + multiexp_data.emplace_back(temp, proof8_B); + + // A1: -weight*e + sc_mul(temp.bytes, temp.bytes, pd.e.bytes); + multiexp_data.emplace_back(temp, proof8_A1); + + // A: -weight*e*e + rct::key minus_weight_e_squared; + sc_mul(minus_weight_e_squared.bytes, temp.bytes, pd.e.bytes); + multiexp_data.emplace_back(minus_weight_e_squared, proof8_A); + + // G: weight*d1 + sc_muladd(G_scalar.bytes, weight.bytes, proof.d1.bytes, G_scalar.bytes); + + // Windowed vector + // d[j*N+i] = z**(2*(j+1)) * 2**i + rct::keyV d(MN, rct::zero()); + d[0] = z_squared; + for (size_t i = 1; i < N; i++) + { + sc_add(d[i].bytes, d[i-1].bytes, d[i-1].bytes); + } + + for (size_t j = 1; j < M; j++) + { + for (size_t i = 0; i < N; i++) + { + sc_mul(d[j*N+i].bytes, d[(j-1)*N+i].bytes, z_squared.bytes); + } + } + + // More efficient computation of sum(d) + rct::key sum_d; + sc_mul(sum_d.bytes, TWO_SIXTY_FOUR_MINUS_ONE.bytes, sum_of_even_powers(pd.z, 2*M).bytes); + + // H: weight*( r1*y*s1 + e**2*( y**(MN+1)*z*sum(d) + (z**2-z)*sum(y) ) ) + rct::key sum_y = sum_of_scalar_powers(pd.y, MN); + sc_sub(temp.bytes, z_squared.bytes, pd.z.bytes); + sc_mul(temp.bytes, temp.bytes, sum_y.bytes); + + sc_mul(temp2.bytes, y_MN_1.bytes, pd.z.bytes); + sc_mul(temp2.bytes, temp2.bytes, sum_d.bytes); + sc_add(temp.bytes, temp.bytes, temp2.bytes); + sc_mul(temp.bytes, temp.bytes, e_squared.bytes); + sc_mul(temp2.bytes, proof.r1.bytes, pd.y.bytes); + sc_mul(temp2.bytes, temp2.bytes, proof.s1.bytes); + sc_add(temp.bytes, temp.bytes, temp2.bytes); + sc_muladd(H_scalar.bytes, temp.bytes, weight.bytes, H_scalar.bytes); + + // Compute the number of rounds for the inner-product argument + const size_t rounds = pd.logM+logN; + CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); + + const rct::key *challenges_inv = &inverses[pd.inv_offset]; + const rct::key yinv = inverses[pd.inv_offset + rounds]; + + // Compute challenge products + challenges_cache.resize(1< 0; --s) + { + sc_mul(challenges_cache[s].bytes, challenges_cache[s/2].bytes, pd.challenges[j].bytes); + sc_mul(challenges_cache[s-1].bytes, challenges_cache[s/2].bytes, challenges_inv[j].bytes); + } + } + + // Gi and Hi + rct::key e_r1_w_y; + sc_mul(e_r1_w_y.bytes, pd.e.bytes, proof.r1.bytes); + sc_mul(e_r1_w_y.bytes, e_r1_w_y.bytes, weight.bytes); + rct::key e_s1_w; + sc_mul(e_s1_w.bytes, pd.e.bytes, proof.s1.bytes); + sc_mul(e_s1_w.bytes, e_s1_w.bytes, weight.bytes); + rct::key e_squared_z_w; + sc_mul(e_squared_z_w.bytes, e_squared.bytes, pd.z.bytes); + sc_mul(e_squared_z_w.bytes, e_squared_z_w.bytes, weight.bytes); + rct::key minus_e_squared_z_w; + sc_sub(minus_e_squared_z_w.bytes, ZERO.bytes, e_squared_z_w.bytes); + rct::key minus_e_squared_w_y; + sc_sub(minus_e_squared_w_y.bytes, ZERO.bytes, e_squared.bytes); + sc_mul(minus_e_squared_w_y.bytes, minus_e_squared_w_y.bytes, weight.bytes); + sc_mul(minus_e_squared_w_y.bytes, minus_e_squared_w_y.bytes, y_MN.bytes); + for (size_t i = 0; i < MN; ++i) + { + rct::key g_scalar = copy(e_r1_w_y); + rct::key h_scalar; + + // Use the binary decomposition of the index + sc_muladd(g_scalar.bytes, g_scalar.bytes, challenges_cache[i].bytes, e_squared_z_w.bytes); + sc_muladd(h_scalar.bytes, e_s1_w.bytes, challenges_cache[(~i) & (MN-1)].bytes, minus_e_squared_z_w.bytes); + + // Complete the scalar derivation + sc_add(Gi_scalars[i].bytes, Gi_scalars[i].bytes, g_scalar.bytes); + sc_muladd(h_scalar.bytes, minus_e_squared_w_y.bytes, d[i].bytes, h_scalar.bytes); + sc_add(Hi_scalars[i].bytes, Hi_scalars[i].bytes, h_scalar.bytes); + + // Update iterated values + sc_mul(e_r1_w_y.bytes, e_r1_w_y.bytes, yinv.bytes); + sc_mul(minus_e_squared_w_y.bytes, minus_e_squared_w_y.bytes, yinv.bytes); + } + + // L_j: -weight*e*e*challenges[j]**2 + // R_j: -weight*e*e*challenges[j]**(-2) + for (size_t j = 0; j < rounds; ++j) + { + sc_mul(temp.bytes, pd.challenges[j].bytes, pd.challenges[j].bytes); + sc_mul(temp.bytes, temp.bytes, minus_weight_e_squared.bytes); + multiexp_data.emplace_back(temp, proof8_L[j]); + + sc_mul(temp.bytes, challenges_inv[j].bytes, challenges_inv[j].bytes); + sc_mul(temp.bytes, temp.bytes, minus_weight_e_squared.bytes); + multiexp_data.emplace_back(temp, proof8_R[j]); + } + } + + // Verify all proofs in the weighted batch + multiexp_data.emplace_back(G_scalar, rct::G); + multiexp_data.emplace_back(H_scalar, rct::H); + for (size_t i = 0; i < maxMN; ++i) + { + multiexp_data[i * 2] = {Gi_scalars[i], Gi_p3[i]}; + multiexp_data[i * 2 + 1] = {Hi_scalars[i], Hi_p3[i]}; + } + if (!(multiexp(multiexp_data, 2 * maxMN) == rct::identity())) + { + MERROR("Verification failure"); + return false; + } + + return true; + } + + bool bulletproof_plus_VERIFY(const std::vector &proofs) + { + std::vector proof_pointers; + proof_pointers.reserve(proofs.size()); + for (const BulletproofPlus &proof: proofs) + proof_pointers.push_back(&proof); + return bulletproof_plus_VERIFY(proof_pointers); + } + + bool bulletproof_plus_VERIFY(const BulletproofPlus &proof) + { + std::vector proofs; + proofs.push_back(&proof); + return bulletproof_plus_VERIFY(proofs); + } +} \ No newline at end of file diff --git a/src/serialization/json_utils.h b/src/ringct/bulletproofs_plus.h old mode 100755 new mode 100644 similarity index 66% rename from src/serialization/json_utils.h rename to src/ringct/bulletproofs_plus.h index 82b4eb26141..93090670da1 --- a/src/serialization/json_utils.h +++ b/src/ringct/bulletproofs_plus.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -25,36 +25,25 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include -#include "json_archive.h" +#ifndef BULLETPROOFS_PLUS_H +#define BULLETPROOFS_PLUS_H -namespace serialization { +#include "rctTypes.h" -/// Subclass of json_archiver that writes to a std::ostringstream and returns the string on -/// demand. -class json_string_archiver : public json_archiver { - std::ostringstream oss; -public: - /// Constructor; takes no arguments. - json_string_archiver() : json_archiver{oss} {} +namespace rct +{ - /// Returns the string from the std::ostringstream - std::string str() { return oss.str(); } -}; +BulletproofPlus bulletproof_plus_PROVE(const rct::key &v, const rct::key &gamma); +BulletproofPlus bulletproof_plus_PROVE(uint64_t v, const rct::key &gamma); +BulletproofPlus bulletproof_plus_PROVE(const rct::keyV &v, const rct::keyV &gamma); +BulletproofPlus bulletproof_plus_PROVE(const std::vector &v, const rct::keyV &gamma); +bool bulletproof_plus_VERIFY(const BulletproofPlus &proof); +bool bulletproof_plus_VERIFY(const std::vector &proofs); +bool bulletproof_plus_VERIFY(const std::vector &proofs); -/*! serializes the data in v to a string. Throws on error. -*/ -template -std::string dump_json(T& v) -{ - json_string_archiver oar; - serialize(oar, v); - return oar.str(); } -} // namespace serialization +#endif \ No newline at end of file diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index e57115b6cda..bfd39e6d2a2 100755 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -36,6 +36,7 @@ #include "bulletproofs.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_config.h" +#include "bulletproofs_plus.h" #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "ringct" @@ -74,6 +75,37 @@ namespace return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I}; } + + rct::BulletproofPlus make_dummy_bulletproof_plus(const std::vector &outamounts, rct::keyV &C, rct::keyV &masks) + { + const size_t n_outs = outamounts.size(); + const rct::key I = rct::identity(); + size_t nrl = 0; + while ((1u << nrl) < n_outs) + ++nrl; + nrl += 6; + + C.resize(n_outs); + masks.resize(n_outs); + for (size_t i = 0; i < n_outs; ++i) + { + masks[i] = I; + rct::key sv8, sv; + sv = rct::zero(); + sv.bytes[0] = outamounts[i] & 255; + sv.bytes[1] = (outamounts[i] >> 8) & 255; + sv.bytes[2] = (outamounts[i] >> 16) & 255; + sv.bytes[3] = (outamounts[i] >> 24) & 255; + sv.bytes[4] = (outamounts[i] >> 32) & 255; + sv.bytes[5] = (outamounts[i] >> 40) & 255; + sv.bytes[6] = (outamounts[i] >> 48) & 255; + sv.bytes[7] = (outamounts[i] >> 56) & 255; + sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes); + rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H); + } + + return rct::BulletproofPlus{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I)}; + } } namespace rct { @@ -103,6 +135,32 @@ namespace rct { catch (...) { return false; } } + BulletproofPlus proveRangeBulletproofPlus(keyV &C, keyV &masks, const std::vector &amounts, epee::span sk, hw::device &hwdev) + { + CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes"); + masks.resize(amounts.size()); + for (size_t i = 0; i < masks.size(); ++i) + masks[i] = hwdev.genCommitmentMask(sk[i]); + BulletproofPlus proof = bulletproof_plus_PROVE(amounts, masks); + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); + C = proof.V; + return proof; + } + + bool verBulletproofPlus(const BulletproofPlus &proof) + { + try { return bulletproof_plus_VERIFY(proof); } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (...) { return false; } + } + + bool verBulletproofPlus(const std::vector &proofs) + { + try { return bulletproof_plus_VERIFY(proofs); } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (...) { return false; } + } + //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; @@ -213,9 +271,9 @@ namespace rct { keyV mu_P_to_hash(2*n+4); // domain, I, D, P, C, C_offset keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset sc_0(mu_P_to_hash[0].bytes); - memcpy(mu_P_to_hash[0].bytes, config::HASH_KEY_CLSAG_AGG_0.data(), config::HASH_KEY_CLSAG_AGG_0.size()); + memcpy(mu_P_to_hash[0].bytes, cryptonote::hashkey::CLSAG_AGG_0.data(),cryptonote::hashkey::CLSAG_AGG_0.size()); sc_0(mu_C_to_hash[0].bytes); - memcpy(mu_C_to_hash[0].bytes, config::HASH_KEY_CLSAG_AGG_1.data(), config::HASH_KEY_CLSAG_AGG_1.size()); + memcpy(mu_C_to_hash[0].bytes, cryptonote::hashkey::CLSAG_AGG_1.data(), cryptonote::hashkey::CLSAG_AGG_1.size()); for (size_t i = 1; i < n+1; ++i) { mu_P_to_hash[i] = P[i-1]; mu_C_to_hash[i] = P[i-1]; @@ -238,7 +296,7 @@ namespace rct { keyV c_to_hash(2*n+5); // domain, P, C, C_offset, message, aG, aH key c; sc_0(c_to_hash[0].bytes); - memcpy(c_to_hash[0].bytes, config::HASH_KEY_CLSAG_ROUND.data(), config::HASH_KEY_CLSAG_ROUND.size()); + memcpy(c_to_hash[0].bytes, cryptonote::hashkey::CLSAG_ROUND.data(), cryptonote::hashkey::CLSAG_ROUND.size()); for (size_t i = 1; i < n+1; ++i) { c_to_hash[i] = P[i-1]; @@ -607,10 +665,29 @@ namespace rct { kv.push_back(p.t); } } + else if (rv.type == RCTType::BulletproofPlus) + { + kv.reserve((6 * 2 + 6) * rv.p.bulletproofs_plus.size()); + for (const auto &p : rv.p.bulletproofs_plus) + { + // V are not hashed as they're expanded from outPk.mask + // (and thus hashed as part of rctSigBase above) + kv.push_back(p.A); + kv.push_back(p.A1); + kv.push_back(p.B); + kv.push_back(p.r1); + kv.push_back(p.s1); + kv.push_back(p.d1); + for (size_t n = 0; n < p.L.size(); ++n) + kv.push_back(p.L[n]); + for (size_t n = 0; n < p.R.size(); ++n) + kv.push_back(p.R[n]); + } + } else { - kv.reserve((64*3+1) * rv.p.rangeSigs.size()); - for (const auto &r: rv.p.rangeSigs) + kv.reserve((64 * 3 + 1) * rv.p.rangeSigs.size()); + for (const auto &r : rv.p.rangeSigs) { for (size_t n = 0; n < 64; ++n) kv.push_back(r.asig.s0[n]); @@ -918,9 +995,9 @@ namespace rct { keyV mu_P_to_hash(2*n+4); // domain, I, D, P, C, C_offset keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset sc_0(mu_P_to_hash[0].bytes); - memcpy(mu_P_to_hash[0].bytes, config::HASH_KEY_CLSAG_AGG_0.data(), config::HASH_KEY_CLSAG_AGG_0.size()); + memcpy(mu_P_to_hash[0].bytes, cryptonote::hashkey::CLSAG_AGG_0.data(), cryptonote::hashkey::CLSAG_AGG_0.size()); sc_0(mu_C_to_hash[0].bytes); - memcpy(mu_C_to_hash[0].bytes, config::HASH_KEY_CLSAG_AGG_1.data(), config::HASH_KEY_CLSAG_AGG_1.size()); + memcpy(mu_C_to_hash[0].bytes, cryptonote::hashkey::CLSAG_AGG_1.data(), cryptonote::hashkey::CLSAG_AGG_1.size()); for (size_t i = 1; i < n+1; ++i) { mu_P_to_hash[i] = pubs[i-1].dest; mu_C_to_hash[i] = pubs[i-1].dest; @@ -942,7 +1019,7 @@ namespace rct { // Set up round hash keyV c_to_hash(2*n+5); // domain, P, C, C_offset, message, L, R sc_0(c_to_hash[0].bytes); - memcpy(c_to_hash[0].bytes, config::HASH_KEY_CLSAG_ROUND.data(), config::HASH_KEY_CLSAG_ROUND.size()); + memcpy(c_to_hash[0].bytes, cryptonote::hashkey::CLSAG_ROUND.data(), cryptonote::hashkey::CLSAG_ROUND.size()); for (size_t i = 1; i < n+1; ++i) { c_to_hash[i] = pubs[i-1].dest; @@ -1086,13 +1163,13 @@ namespace rct { //compute range proof rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG - if (!bulletproof) + if (!bulletproof_or_plus) CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus); } //set txn fee @@ -1123,7 +1200,7 @@ namespace rct { //RCT simple //for post-rct only rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector &inamounts, const std::vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { - const bool bulletproof = rct_config.range_proof_type != RangeProofType::Borromean; + const bool bulletproof_or_plus = rct_config.range_proof_type > RangeProofType::Borromean; CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -1139,11 +1216,14 @@ namespace rct { } rctSig rv; - if (bulletproof) + if (bulletproof_or_plus) { switch (rct_config.bp_version) { case 0: + case 4: + rv.type = RCTType::BulletproofPlus; + break; case 3: rv.type = RCTType::CLSAG; break; @@ -1162,7 +1242,7 @@ namespace rct { rv.message = message; rv.outPk.resize(destinations.size()); - if (!bulletproof) + if (!bulletproof_or_plus) rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); @@ -1174,7 +1254,7 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - if (!bulletproof) + if (!bulletproof_or_plus) rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); #ifdef DBG if (!bulletproof) @@ -1183,8 +1263,10 @@ namespace rct { } rv.p.bulletproofs.clear(); - if (bulletproof) + rv.p.bulletproofs_plus.clear(); + if (bulletproof_or_plus) { + const bool plus = rv.type == RCTType::BulletproofPlus; size_t n_amounts = outamounts.size(); size_t amounts_proved = 0; if (rct_config.range_proof_type == RangeProofType::PaddedBulletproof) @@ -1193,14 +1275,23 @@ namespace rct { if (hwdev.get_mode() == hw::device::mode::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks)); + if (plus) + rv.p.bulletproofs_plus.push_back(make_dummy_bulletproof_plus(outamounts, C, masks)); + else + rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks)); } else { const epee::span keys{&amount_keys[0], amount_keys.size()}; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev)); + if (plus) + rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, outamounts, keys, hwdev)); + else + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev)); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + if (plus) + CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof"); + else + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif } for (i = 0; i < outamounts.size(); ++i) @@ -1213,7 +1304,7 @@ namespace rct { { size_t batch_size = 1; if (rct_config.range_proof_type == RangeProofType::MultiOutputBulletproof) - while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) + while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= (plus ? cryptonote::TX_BULLETPROOF_PLUS_MAX_OUTPUTS : cryptonote::TX_BULLETPROOF_MAX_OUTPUTS)) batch_size *= 2; rct::keyV C, masks; std::vector batch_amounts(batch_size); @@ -1222,20 +1313,29 @@ namespace rct { if (hwdev.get_mode() == hw::device::mode::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks)); + if (plus) + rv.p.bulletproofs_plus.push_back(make_dummy_bulletproof_plus(batch_amounts, C, masks)); + else + rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks)); } else { const epee::span keys{&amount_keys[amounts_proved], batch_size}; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev)); + if (plus) + rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, batch_amounts, keys, hwdev)); + else + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev)); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + if (plus) + CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof"); + else + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif } for (i = 0; i < batch_size; ++i) { - rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); - outSk[i + amounts_proved].mask = masks[i]; + rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); + outSk[i + amounts_proved].mask = masks[i]; } amounts_proved += batch_size; } @@ -1249,7 +1349,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(outamounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus); } //set txn fee @@ -1257,9 +1357,9 @@ namespace rct { // TODO: unused ?? // key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + keyV &pseudoOuts = bulletproof_or_plus ? rv.p.pseudoOuts : rv.pseudoOuts; pseudoOuts.resize(inamounts.size()); - if (rv.type == RCTType::CLSAG) + if (is_rct_clsag(rv.type)) rv.p.CLSAGs.resize(inamounts.size()); else rv.p.MGs.resize(inamounts.size()); @@ -1278,11 +1378,11 @@ namespace rct { if (msout) { msout->c.resize(inamounts.size()); - msout->mu_p.resize(rv.type == RCTType::CLSAG ? inamounts.size() : 0); + msout->mu_p.resize(is_rct_clsag(rv.type) ? inamounts.size() : 0); } for (i = 0 ; i < inamounts.size(); i++) { - if (rv.type == RCTType::CLSAG) + if (is_rct_clsag(rv.type)) { rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev); } @@ -1387,19 +1487,25 @@ namespace rct { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; std::deque results; - std::vector proofs; + std::vector bp_proofs; + std::vector bpp_proofs; size_t max_non_bp_proofs = 0, offset = 0; for (const rctSig *rvp: rvv) { CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); const rctSig &rv = *rvp; - CHECK_AND_ASSERT_MES(rct::is_rct_simple(rv.type), false, "verRctSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTType::Simple || rv.type == RCTType::Bulletproof || rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus, + false, "verRctSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); - if (bulletproof) + const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); + if (bulletproof || bulletproof_plus) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); - if (rv.type == RCTType::CLSAG) + if (bulletproof_plus) + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_plus_amounts(rv.p.bulletproofs_plus), false, "Mismatched sizes of outPk and bulletproofs_plus"); + else + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); + if (is_rct_clsag(rv.type)) { CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG"); CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs"); @@ -1419,7 +1525,7 @@ namespace rct { } CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - if (!bulletproof) + if (!bulletproof && !bulletproof_plus) max_non_bp_proofs += rv.p.rangeSigs.size(); } @@ -1429,11 +1535,12 @@ namespace rct { const rctSig &rv = *rvp; const bool bulletproof = is_rct_bulletproof(rv.type); - const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); + const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts; rct::keyV masks(rv.outPk.size()); for (size_t i = 0; i < rv.outPk.size(); i++) { - masks[i] = rv.outPk[i].mask; + masks[i] = rv.outPk[i].mask; } key sumOutpks = addKeys(masks); DP(sumOutpks); @@ -1449,10 +1556,15 @@ namespace rct { return false; } - if (bulletproof) + if (bulletproof_plus) + { + for (size_t i = 0; i < rv.p.bulletproofs_plus.size(); i++) + bpp_proofs.push_back(&rv.p.bulletproofs_plus[i]); + } + else if (bulletproof) { for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) - proofs.push_back(&rv.p.bulletproofs[i]); + bp_proofs.push_back(&rv.p.bulletproofs[i]); } else { @@ -1461,7 +1573,12 @@ namespace rct { offset += rv.p.rangeSigs.size(); } } - if (!proofs.empty() && !verBulletproof(proofs)) + if (!bpp_proofs.empty() && !verBulletproofPlus(bpp_proofs)) + { + LOG_PRINT_L1("Aggregate range proof verified failed"); + return false; + } + if (!bp_proofs.empty() && !verBulletproof(bp_proofs)) { LOG_PRINT_L1("Aggregate range proof verified failed"); return false; @@ -1502,10 +1619,12 @@ namespace rct { { PERF_TIMER(verRctNonSemanticsSimple); - CHECK_AND_ASSERT_MES(rct::is_rct_simple(rv.type), false, "verRctNonSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTType::Simple || rv.type == RCTType::Bulletproof || rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus, + false, "verRctNonSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); + const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type); // semantics check is early, and mixRing/MGs aren't resolved yet - if (bulletproof) + if (bulletproof || bulletproof_plus) CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); else CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); @@ -1516,7 +1635,7 @@ namespace rct { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts; const key message = get_pre_clsag_hash(rv, hw::get_device("default")); @@ -1524,7 +1643,7 @@ namespace rct { results.resize(rv.mixRing.size()); for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { tpool.submit(&waiter, [&, i] { - if (rv.type == RCTType::CLSAG) + if (is_rct_clsag(rv.type)) results[i] = verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], pseudoOuts[i]); else results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); @@ -1571,7 +1690,7 @@ namespace rct { //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG); + hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -1595,13 +1714,14 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rct::is_rct_simple(rv.type), false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTType::Simple || rv.type == RCTType::Bulletproof || rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus, + false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG); + hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTType::Bulletproof2 || rv.type == RCTType::CLSAG || rv.type == RCTType::BulletproofPlus); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -1627,6 +1747,7 @@ namespace rct { bool signMultisigMLSAG(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { CHECK_AND_ASSERT_MES(tools::equals_any(rv.type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2), false, "unsupported rct type"); + CHECK_AND_ASSERT_MES(!is_rct_clsag(rv.type), false, "CLSAG signature type in MLSAG signature function"); CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size"); CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); @@ -1651,7 +1772,7 @@ namespace rct { } bool signMultisigCLSAG(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { - CHECK_AND_ASSERT_MES(rv.type == RCTType::CLSAG, false, "unsupported rct type"); + CHECK_AND_ASSERT_MES(is_rct_clsag(rv.type), false, "unsupported rct type"); CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); CHECK_AND_ASSERT_MES(k.size() == rv.p.CLSAGs.size(), false, "Mismatched k/CLSAGs size"); CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); @@ -1673,7 +1794,7 @@ namespace rct { } bool signMultisig(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { - if (rv.type == RCTType::CLSAG) + if (is_rct_clsag(rv.type)) return signMultisigCLSAG(rv, indices, k, msout, secret_key); else return signMultisigMLSAG(rv, indices, k, msout, secret_key); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index e23a7c8df10..9b21f175d5f 100755 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -188,19 +188,22 @@ namespace rct { return vali; } - size_t n_bulletproof_amounts(const Bulletproof &proof) + static size_t n_bulletproof_amounts_base(const size_t L_size, const size_t R_size, const size_t V_size, const size_t max_outputs) { - CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); - CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size"); + CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size"); static const size_t extra_bits = 4; - static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date"); - CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size"); - CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); - CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); - CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof"); - return proof.V.size(); + CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); + CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(V_size <= (1u<<(L_size-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(V_size * 2 > (1u<<(L_size-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(V_size > 0, 0, "Empty bulletproof"); + return V_size; } + size_t n_bulletproof_amounts(const Bulletproof &proof) { return n_bulletproof_amounts_base(proof.L.size(), proof.R.size(), proof.V.size(), cryptonote::TX_BULLETPROOF_MAX_OUTPUTS); } + size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof) { return n_bulletproof_amounts_base(proof.L.size(), proof.R.size(), proof.V.size(), cryptonote::TX_BULLETPROOF_PLUS_MAX_OUTPUTS); } + size_t n_bulletproof_amounts(const std::vector &proofs) { size_t n = 0; @@ -215,16 +218,33 @@ namespace rct { return n; } - size_t n_bulletproof_max_amounts(const Bulletproof &proof) + size_t n_bulletproof_plus_amounts(const std::vector &proofs) + { + size_t n = 0; + for (const BulletproofPlus &proof: proofs) + { + size_t n2 = n_bulletproof_plus_amounts(proof); + CHECK_AND_ASSERT_MES(n2 < std::numeric_limits::max() - n, 0, "Invalid number of bulletproofs"); + if (n2 == 0) + return 0; + n += n2; + } + return n; + } + + static size_t n_bulletproof_max_amounts_base(size_t L_size, size_t R_size, size_t max_outputs) { - CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); - CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size"); + CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size"); static const size_t extra_bits = 4; - static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date"); - CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size"); - return 1 << (proof.L.size() - 6); + CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); + CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); + return 1 << (L_size - 6); } + size_t n_bulletproof_max_amounts(const Bulletproof &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), cryptonote::TX_BULLETPROOF_MAX_OUTPUTS); } + size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), cryptonote::TX_BULLETPROOF_PLUS_MAX_OUTPUTS); } + size_t n_bulletproof_max_amounts(const std::vector &proofs) { size_t n = 0; @@ -239,4 +259,18 @@ namespace rct { return n; } + size_t n_bulletproof_plus_max_amounts(const std::vector &proofs) + { + size_t n = 0; + for (const BulletproofPlus &proof: proofs) + { + size_t n2 = n_bulletproof_plus_max_amounts(proof); + CHECK_AND_ASSERT_MES(n2 < std::numeric_limits::max() - n, 0, "Invalid number of bulletproofs"); + if (n2 == 0) + return 0; + n += n2; + } + return n; + } + } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 95366aa01ab..1794efa815b 100755 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -46,6 +46,7 @@ extern "C" { #include "common/hex.h" #include "serialization/variant.h" #include "common/util.h" +#include //Define this flag when debugging to get additional info on the console @@ -79,7 +80,8 @@ namespace rct { unsigned char operator[](int i) const { return bytes[i]; } - bool operator==(const key &k) const { return !crypto_verify_32(bytes, k.bytes); } + bool operator==(const key& k) const { return !crypto_verify_32(bytes, k.bytes); } + bool operator!=(const key& k) const { return !operator==(k); } unsigned char bytes[32]; }; typedef std::vector keyV; //vector of keys @@ -234,11 +236,48 @@ namespace rct { END_SERIALIZE() }; + struct BulletproofPlus + { + rct::keyV V; + rct::key A, A1, B; + rct::key r1, s1, d1; + rct::keyV L, R; + + BulletproofPlus() {} + BulletproofPlus(const rct::key &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): + V({V}), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {} + BulletproofPlus(const rct::keyV &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): + V(V), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {} + + bool operator==(const BulletproofPlus &other) const { return V == other.V && A == other.A && A1 == other.A1 && B == other.B && r1 == other.r1 && s1 == other.s1 && d1 == other.d1 && L == other.L && R == other.R; } + + BEGIN_SERIALIZE_OBJECT() + // Commitments aren't saved, they're restored via outPk + // FIELD(V) + FIELD(A) + FIELD(A1) + FIELD(B) + FIELD(r1) + FIELD(s1) + FIELD(d1) + FIELD(L) + FIELD(R) + + if (L.empty() || L.size() != R.size()) + throw std::runtime_error("Bad bulletproofplus serialization"); + END_SERIALIZE() + }; + size_t n_bulletproof_amounts(const Bulletproof &proof); size_t n_bulletproof_max_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const std::vector &proofs); size_t n_bulletproof_max_amounts(const std::vector &proofs); + size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof); + size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof); + size_t n_bulletproof_plus_amounts(const std::vector &proofs); + size_t n_bulletproof_plus_max_amounts(const std::vector &proofs); + template auto start_array(Archive& ar, std::string_view tag, std::vector& v, size_t size) { ar.tag(tag); @@ -263,11 +302,14 @@ namespace rct { Bulletproof = 3, Bulletproof2 = 4, CLSAG = 5, + BulletproofPlus = 6, }; - inline bool is_rct_simple(RCTType type) { return tools::equals_any(type, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG); } + inline bool is_rct_simple(RCTType type) { return tools::equals_any(type, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG, RCTType::BulletproofPlus); } inline bool is_rct_bulletproof(RCTType type) { return tools::equals_any(type, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG); } inline bool is_rct_borromean(RCTType type) { return tools::equals_any(type, RCTType::Simple, RCTType::Full); } + inline bool is_rct_bulletproof_plus(RCTType type) { return tools::equals_any(type, RCTType::BulletproofPlus); } + inline bool is_rct_clsag(RCTType type) { return tools::equals_any(type, RCTType::CLSAG, RCTType::BulletproofPlus); } enum class RangeProofType : uint8_t { Borromean = 0, Bulletproof = 1, MultiOutputBulletproof = 2, PaddedBulletproof = 3 }; struct RCTConfig { @@ -290,7 +332,7 @@ namespace rct { field_varint(ar, "type", type); if (type == RCTType::Null) return; - if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG)) + if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG, RCTType::BulletproofPlus)) throw std::invalid_argument{"invalid ringct type"}; field_varint(ar, "txnFee", txnFee); @@ -302,35 +344,36 @@ namespace rct { { auto arr = start_array(ar, "pseudoOuts", pseudoOuts, inputs); for (auto& e : pseudoOuts) - value(arr.element(), e); + value(ar, e); } { auto arr = start_array(ar, "ecdhInfo", ecdhInfo, outputs); - if (tools::equals_any(type, RCTType::Bulletproof2, RCTType::CLSAG)) + if (tools::equals_any(type, RCTType::Bulletproof2, RCTType::CLSAG, RCTType::BulletproofPlus)) { for (auto& e : ecdhInfo) { - auto obj = arr.element().begin_object(); + auto obj = ar.begin_object(); if (Archive::is_deserializer) memset(e.amount.bytes, 0, sizeof(e.amount.bytes)); field(ar, "amount", reinterpret_cast(e.amount)); } } else { for (auto& e : ecdhInfo) - value(arr.element(), e); + value(ar, e); } } { auto arr = start_array(ar, "outPk", outPk, outputs); for (auto& e : outPk) - value(arr.element(), e.mask); + value(ar, e.mask); } } }; struct rctSigPrunable { std::vector rangeSigs; std::vector bulletproofs; + std::vector bulletproofs_plus; std::vector MGs; // simple rct has N, full has 1 std::vector CLSAGs; keyV pseudoOuts; //C - for simple rct @@ -341,9 +384,22 @@ namespace rct { { if (type == RCTType::Null) return; - if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG)) + if (!tools::equals_any(type, RCTType::Full, RCTType::Simple, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG, RCTType::BulletproofPlus)) throw std::invalid_argument{"invalid ringct type"}; - if (rct::is_rct_bulletproof(type)) + if (rct::is_rct_bulletproof_plus(type)) + { + uint32_t nbp = bulletproofs_plus.size(); + field_varint(ar, "nbp", nbp); + if (nbp > outputs) + throw std::invalid_argument{"too many bulletproofs_plus"}; + auto arr = start_array(ar, "bpp", bulletproofs_plus, nbp); + for (auto& b : bulletproofs_plus) + value(ar, b); + if (auto n_max = n_bulletproof_plus_max_amounts(bulletproofs_plus); n_max < outputs) + throw std::invalid_argument{"invalid bulletproofs_plus: n_max (" + std::to_string(n_max) + ") < outputs (" + std::to_string(outputs) + ")"}; + + } + else if (rct::is_rct_bulletproof(type)) { uint32_t nbp = bulletproofs.size(); if (tools::equals_any(type, RCTType::Bulletproof2, RCTType::CLSAG)) @@ -355,7 +411,7 @@ namespace rct { auto arr = start_array(ar, "bp", bulletproofs, nbp); for (auto& b : bulletproofs) - value(arr.element(), b); + value(ar, b); if (auto n_max = n_bulletproof_max_amounts(bulletproofs); n_max < outputs) throw std::invalid_argument{"invalid bulletproofs: n_max (" + std::to_string(n_max) + ") < outputs (" + std::to_string(outputs) + ")"}; @@ -364,10 +420,10 @@ namespace rct { { auto arr = start_array(ar, "rangeSigs", rangeSigs, outputs); for (auto& s : rangeSigs) - value(arr.element(), s); + value(ar, s); } - if (type == RCTType::CLSAG) + if (type == RCTType::CLSAG || type == RCTType::BulletproofPlus) { auto arr = start_array(ar, "CLSAGs", CLSAGs, inputs); @@ -376,11 +432,11 @@ namespace rct { // we save the CLSAGs contents directly, because we want it to save its // arrays without the size prefixes, and the load can't know what size // to expect if it's not in the data - auto obj = arr.element().begin_object(); + auto obj = ar.begin_object(); { auto arr_s = start_array(ar, "s", clsag.s, mixin + 1); for (auto& x : clsag.s) - value(arr_s.element(), x); + value(ar, x); } field(ar, "c1", clsag.c1); field(ar, "D", clsag.D); @@ -401,7 +457,7 @@ namespace rct { auto arr = start_array(ar, "MGs", MGs, mg_elements); for (auto& mg : MGs) { - auto obj = arr.element().begin_object(); + auto obj = ar.begin_object(); // we save the MGs contents directly, because we want it to save its // arrays and matrices without the size prefixes, and the load can't @@ -410,14 +466,14 @@ namespace rct { auto arr_ss = start_array(ar, "ss", mg.ss, mixin + 1); for (auto& ss : mg.ss) { - auto arr_ss2 = arr_ss.element().begin_array(); + auto arr_ss2 = ar.begin_array(); if constexpr (Archive::is_deserializer) ss.resize(mg_ss2_elements); else if (ss.size() != mg_ss2_elements) throw std::invalid_argument{"invalid mg_ss2 size: have " + std::to_string(ss.size()) + ", expected " + std::to_string(mg_ss2_elements)}; for (auto& x : ss) - value(arr_ss2.element(), x); + value(ar, x); } } field(ar, "cc", mg.cc); @@ -425,26 +481,34 @@ namespace rct { } } } - if (tools::equals_any(type, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG)) + if (tools::equals_any(type, RCTType::Bulletproof, RCTType::Bulletproof2, RCTType::CLSAG, RCTType::BulletproofPlus)) { auto arr = start_array(ar, "pseudoOuts", pseudoOuts, inputs); for (auto& o : pseudoOuts) - value(arr.element(), o); + value(ar, o); } } + BEGIN_SERIALIZE_OBJECT() + FIELD(rangeSigs) + FIELD(bulletproofs) + FIELD(bulletproofs_plus) + FIELD(MGs) + FIELD(CLSAGs) + FIELD(pseudoOuts) + END_SERIALIZE() }; struct rctSig: public rctSigBase { rctSigPrunable p; keyV& get_pseudo_outs() { - return rct::is_rct_bulletproof(type) ? p.pseudoOuts : pseudoOuts; + return (type == RCTType::Bulletproof || type == RCTType::Bulletproof2 || type == RCTType::CLSAG || type == RCTType::BulletproofPlus) ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return rct::is_rct_bulletproof(type) ? p.pseudoOuts : pseudoOuts; + return (type == RCTType::Bulletproof || type == RCTType::Bulletproof2 || type == RCTType::CLSAG || type == RCTType::BulletproofPlus) ? p.pseudoOuts : pseudoOuts; } }; @@ -604,3 +668,4 @@ VARIANT_TAG(rct::Bulletproof, "rct_bulletproof", 0x9c); VARIANT_TAG(rct::multisig_kLRki, "rct_multisig_kLR", 0x9d); VARIANT_TAG(rct::multisig_out, "rct_multisig_out", 0x9e); VARIANT_TAG(rct::clsag, "rct_clsag", 0x9f); +VARIANT_TAG(rct::BulletproofPlus, "rct_bulletproof_plus", 0xa0); diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index cefd00b8c6c..1670dd6fd74 100755 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -27,13 +27,12 @@ # 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. +add_subdirectory(common) + add_library(rpc_commands core_rpc_server_commands_defs.cpp - ) - -add_library(rpc_server_base - rpc_args.cpp - http_server_base.cpp + core_rpc_server_binary_commands.cpp + core_rpc_server_command_parser.cpp ) add_library(rpc @@ -43,7 +42,7 @@ add_library(rpc add_library(daemon_rpc_server http_server.cpp - lmq_server.cpp + omq_server.cpp ) add_library(rpc_http_client @@ -51,24 +50,18 @@ add_library(rpc_http_client ) target_link_libraries(rpc_commands PUBLIC + rpc_common common cpr::cpr PRIVATE cryptonote_protocol extra) -target_link_libraries(rpc_server_base - PUBLIC - common - uWebSockets - PRIVATE - extra) - target_link_libraries(rpc PUBLIC cryptonote_core rpc_commands - rpc_server_base + rpc_common net version PRIVATE @@ -79,7 +72,8 @@ target_link_libraries(rpc target_link_libraries(daemon_rpc_server PRIVATE - rpc_server_base + oxenc + rpc_common rpc Boost::thread extra) diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp index 8b7755d4b5b..7f6a6761485 100755 --- a/src/rpc/bootstrap_daemon.cpp +++ b/src/rpc/bootstrap_daemon.cpp @@ -35,18 +35,19 @@ namespace cryptonote std::optional bootstrap_daemon::get_height() { // query bootstrap daemon's height - rpc::GET_HEIGHT::response res{}; - if (!invoke({}, res)) + rpc::GET_HEIGHT get_height; + if (!invoke_json({}, get_height.response)) { return std::nullopt; } - if (res.status != cryptonote::rpc::STATUS_OK) + if (get_height.response["status"] != cryptonote::rpc::STATUS_OK) { return std::nullopt; } - return res.height; + return get_height.response["height"].get(); + } bool bootstrap_daemon::set_server(std::string url, const std::optional> &credentials /* = std::nullopt */) diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h index 5bac73c5c39..4013502ca57 100755 --- a/src/rpc/bootstrap_daemon.h +++ b/src/rpc/bootstrap_daemon.h @@ -5,7 +5,7 @@ #include "rpc/http_client.h" #include "rpc/core_rpc_server_commands_defs.h" - +#include "rpc/core_rpc_server_binary_commands.h" namespace cryptonote { @@ -20,6 +20,23 @@ namespace cryptonote // Called when a request has failed either internally or for some external reason; the next // request will attempt to use a different bootstrap server (if configured). void set_failed() { m_failed = true; } + + // New JSON-based bootstrap invocation (for new RPC format) + template , int> = 0> + bool invoke_json(const nlohmann::json& req, nlohmann::json& res) + { + if (!switch_server_if_needed()) + return false; + + try { + res = m_http_client.json_rpc(RPC::names().front(), req); + } catch (const std::exception& e) { + MWARNING("bootstrap daemon request failed: " << e.what()); + set_failed(); + return false; + } + return true; + } template , int> = 0> bool invoke(const typename RPC::request& req, typename RPC::response& res) @@ -28,12 +45,7 @@ namespace cryptonote return false; try { - if constexpr (std::is_base_of_v) - // TODO: post-8.x hard fork we can remove this one and let everything go through the - // non-binary json_rpc version instead (because all legacy json commands are callable via - // json_rpc as of daemon 8.x). - res = m_http_client.json(RPC::names().front(), req); - else if constexpr (std::is_base_of_v) + if constexpr (std::is_base_of_v) res = m_http_client.binary(RPC::names().front(), req); else res = m_http_client.json_rpc(RPC::names().front(), req); diff --git a/src/rpc/common/CMakeLists.txt b/src/rpc/common/CMakeLists.txt new file mode 100644 index 00000000000..6e90f577954 --- /dev/null +++ b/src/rpc/common/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(rpc_common + command_decorators.cpp + json_bt.cpp + rpc_args.cpp + http_server_base.cpp + ) + +target_link_libraries(rpc_common + PUBLIC + common + uWebSockets + oxenmq::oxenmq + PRIVATE + extra) \ No newline at end of file diff --git a/src/rpc/common/command_decorators.cpp b/src/rpc/common/command_decorators.cpp new file mode 100644 index 00000000000..9bee00c966a --- /dev/null +++ b/src/rpc/common/command_decorators.cpp @@ -0,0 +1,12 @@ + +#include "command_decorators.h" + +namespace cryptonote::rpc { + +void RPC_COMMAND::set_bt() { + bt = true; + response_b64.format = tools::json_binary_proxy::fmt::bt; + response_hex.format = tools::json_binary_proxy::fmt::bt; +} + +} // namespace cryptonote::rpc \ No newline at end of file diff --git a/src/rpc/common/command_decorators.h b/src/rpc/common/command_decorators.h new file mode 100644 index 00000000000..72d8837d1dd --- /dev/null +++ b/src/rpc/common/command_decorators.h @@ -0,0 +1,93 @@ +#pragma once + +#include "common/json_binary_proxy.h" + +#include + +namespace cryptonote::rpc { + + /// Returns a constexpr std::array of string_views from an arbitrary list of string literals + /// Used to specify RPC names as: + /// static constexpr auto names() { return NAMES("primary_name", "some_alias"); } + template + constexpr std::array NAMES(const char (&...names)[N]) { + static_assert(sizeof...(N) > 0, "RPC command must have at least one name"); + return {std::string_view{names, N-1}...}; + } + + /// Base class that all RPC commands must inherit from (either directly or via one or more of the + /// below tags). Inheriting from this (and no others) gives you a private, json, non-legacy RPC + /// command. For OMQ RPC the command will be available at `admin.whatever`; for HTTP RPC it'll be + /// at `whatever`. This base class is also where response objects are stored. + struct RPC_COMMAND { + private: + bool bt = false; + public: + /// Indicates whether this response is to be bt (true) or json (false) encoded. Do not set. + bool is_bt() const { return bt; } + + /// Called early in the request to indicate that this request is a bt-encoded one. + void set_bt(); + + /// The response data. For bt-encoded responses we convert this on the fly, with the + /// following notes: + /// - boolean values become 0 or 1 + /// - key-value pairs with null values are omitted from the object + /// - other null values are not permitted at all: an exception will be raised if the json + /// contains such a value. + /// - double values are not permitted; if a double is absolutely needed then check `is_bt` + /// and, when bt, encode it in some documented, endpoint-specific way. + /// - binary values in strings *are* permitted, but the caller must take care because they + /// will not be permitted for actual json responses (json serialization will fail): the caller + /// is expected to do something like: + /// + /// std::string binary = some_binary_data(); + /// cmd.response["binary_value"] = is_bt ? binary : oxenmq::to_hex(binary); + /// + /// or, more conveniently, using the shortcut interface: + /// + /// cmd.response_hex["binary_value"] = some_binary_data(); + /// + nlohmann::json response; + + /// Proxy object that is used to set binary data in `response`, encoding it as hex if this + /// data is being returned as json. If this response is to be bt-encoded then the binary + /// value is left as-is (which isn't valid for json, but can be transported inside the json + /// value as we never dump() when going to bt-encoded). + /// + /// Usage: + /// std::string data = "abc"; + /// rpc.response_hex["foo"]["bar"] = data; // json: "616263", bt: "abc" + tools::json_binary_proxy response_hex{response, tools::json_binary_proxy::fmt::hex}; + + /// Proxy object that encodes binary data as base64 for json, leaving it as binary for + /// bt-encoded responses. + /// + /// Usage: + /// std::string data = "abc"; + /// rpc.response_b64["foo"]["bar"] = data; // json: "YWJj", bt: "abc" + tools::json_binary_proxy response_b64{response, tools::json_binary_proxy::fmt::base64}; + }; + + /// Tag types that are used (via inheritance) to set rpc endpoint properties + + /// Specifies that the RPC call is public (i.e. available through restricted rpc). If this is + /// *not* inherited from then the command is restricted (i.e. only available to admins). For OMQ, + /// PUBLIC commands are available at `rpc.command` (versus non-PUBLIC ones at `admin.command`). + struct PUBLIC : virtual RPC_COMMAND {}; + + /// For Wallet RPC, specifies that the RPC call is restricted, meaning the user must authenticate + /// to the RPC listener by some means. + struct RESTRICTED : virtual RPC_COMMAND {}; + + /// Specifies that the RPC call takes no input arguments. (A dictionary of parameters may still + /// be passed, but will be ignored). + struct NO_ARGS : virtual RPC_COMMAND {}; + + /// Specifies a "legacy" JSON RPC command, available via HTTP JSON at /whatever (in addition to + /// json_rpc as "whatever"). When accessed via legacy mode the result is just the .result element + /// of the JSON RPC response. (Only applies to the HTTP RPC interface, and does nothing if BINARY + /// if specified). + struct LEGACY : virtual RPC_COMMAND {}; + +} // namespace cryptonote::rpc \ No newline at end of file diff --git a/src/rpc/http_server_base.cpp b/src/rpc/common/http_server_base.cpp similarity index 92% rename from src/rpc/http_server_base.cpp rename to src/rpc/common/http_server_base.cpp index d6a8f19aee4..d91c3531c2a 100755 --- a/src/rpc/http_server_base.cpp +++ b/src/rpc/common/http_server_base.cpp @@ -2,12 +2,15 @@ #include "http_server_base.h" #include #include +#include #include "common/string_util.h" // epee: #include "epee/net/jsonrpc_structs.h" #include "epee/storages/portable_storage_template_helper.h" +#include + namespace cryptonote::rpc { /// Checks an Authorization header for Basic login credentials. @@ -74,23 +77,20 @@ namespace cryptonote::rpc { } // Similar to the above, but for JSON errors (which are 200 OK + error embedded in JSON) - void http_server_base::jsonrpc_error_response(HttpResponse& res, int code, std::string message, std::optional id) const + void http_server_base::jsonrpc_error_response(HttpResponse& res, int code, std::string message, nlohmann::json id) const { - epee::json_rpc::error_response rsp; - rsp.jsonrpc = "2.0"; - if (id) - rsp.id = *id; - rsp.error.code = code; - rsp.error.message = std::move(message); - std::string body; - epee::serialization::store_t_to_json(rsp, body); - if (body.capacity() > body.size()) - body += '\n'; res.writeStatus("200 OK"sv); res.writeHeader("Server", m_server_header); res.writeHeader("Content-Type", "application/json"); if (m_closing) res.writeHeader("Connection", "close"); - res.end(body); + res.end(nlohmann::json{ + {"jsonrpc", "2.0"}, + {"id", std::move(id)}, + {"error", nlohmann::json{ + {"code", code}, + {"message", std::move(message)} + }} + }.dump()); if (m_closing) res.close(); } @@ -114,7 +114,7 @@ namespace cryptonote::rpc { // for example, localhost becomes `::1` instead of `0:0:0:0:0:0:0:1`). std::array a; std::memcpy(a.data(), addr.data(), 16); - for (auto& x : a) boost::endian::big_to_native_inplace(x); + for (auto& x : a) oxenc::big_to_host_inplace(x); size_t zero_start = 0, zero_end = 0; for (size_t i = 0, start = 0, end = 0; i < a.size(); i++) { diff --git a/src/rpc/http_server_base.h b/src/rpc/common/http_server_base.h similarity index 97% rename from src/rpc/http_server_base.h rename to src/rpc/common/http_server_base.h index af08bb1f669..ecc33201688 100755 --- a/src/rpc/http_server_base.h +++ b/src/rpc/common/http_server_base.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -40,7 +41,7 @@ namespace cryptonote::rpc { HttpResponse& res, int code, std::string message, - std::optional = std::nullopt) const; + nlohmann::json id) const; // Posts a callback to the uWebSockets thread loop controlling this connection; all writes must // be done from that thread, and so this method is provided to defer a callback from another diff --git a/src/rpc/common/json_bt.cpp b/src/rpc/common/json_bt.cpp new file mode 100644 index 00000000000..faabc446862 --- /dev/null +++ b/src/rpc/common/json_bt.cpp @@ -0,0 +1,33 @@ +#include "json_bt.h" + +namespace beldex { + +oxenc::bt_value json_to_bt(json&& j) { + if (j.is_object()) { + oxenc::bt_dict res; + for (auto& [k, v] : j.items()) { + if (v.is_null()) + continue; // skip k-v pairs with a null v (for other nulls we fail). + res[k] = json_to_bt(std::move(v)); + } + return res; + } + if (j.is_array()) { + oxenc::bt_list res; + for (auto& v : j) + res.push_back(json_to_bt(std::move(v))); + return res; + } + if (j.is_string()) { + return std::move(j.get_ref()); + } + if (j.is_boolean()) + return j.get() ? 1 : 0; + if (j.is_number_unsigned()) + return j.get(); + if (j.is_number_integer()) + return j.get(); + throw std::domain_error{"internal error: encountered some unhandled/invalid type in json-to-bt translation"}; +} + +} // namespace beldex \ No newline at end of file diff --git a/src/rpc/common/json_bt.h b/src/rpc/common/json_bt.h new file mode 100644 index 00000000000..4e155d7c1bb --- /dev/null +++ b/src/rpc/common/json_bt.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +using nlohmann::json; + +namespace beldex { + +oxenc::bt_value json_to_bt(json&& j); + +} // namespace beldex \ No newline at end of file diff --git a/src/rpc/common/param_parser.hpp b/src/rpc/common/param_parser.hpp new file mode 100644 index 00000000000..da4d61dbe01 --- /dev/null +++ b/src/rpc/common/param_parser.hpp @@ -0,0 +1,272 @@ +#pragma once + +#include "common/json_binary_proxy.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace cryptonote::rpc { + using nlohmann::json; + using oxenc::bt_dict_consumer; + using oxenc::bt_list_consumer; + using rpc_input = std::variant; + using json_range = std::pair; + + // Checks that key names are given in ascending order + template + void check_ascending_names(std::string_view name1, std::string_view name2, const Ignore&...) { + if (!(name2 > name1)) + throw std::runtime_error{"Internal error: request values must be retrieved in ascending order"}; + } + + // Wrapper around a reference for get_values that is used to indicate that the value is + // required, in which case an exception will be raised if the value is not found. Usage: + // + // int a_optional = 0, b_required; + // get_values(input, + // "a", a_optional, + // "b", required{b_required}, + // // ... + // ); + template + struct required { + T& value; + required(T& ref) : value{ref} {} + }; + template + constexpr bool is_required_wrapper = false; + template + constexpr bool is_required_wrapper> = true; + + template + constexpr bool is_std_optional = false; + template + constexpr bool is_std_optional> = true; + + // Wrapper around a reference for get_values that adds special handling to act as if the value was + // not given at all if the value is given as an empty string. This sucks, but is necessary for + // backwards compatibility (especially with wallet2 clients). + // + // Usage: + // + // std::string x; + // get_values(input, + // "x", ignore_empty_string{x}, + // // ... + // ); + template + struct ignore_empty_string { + T& value; + ignore_empty_string(T& ref) : value{ref} {} + + bool should_ignore(bt_dict_consumer& d) { + if (d.is_string()) { + auto d2{d}; // Copy because we want to leave d intact + if (d2.consume_string_view().empty()) + return true; + } + return false; + } + + bool should_ignore(json_range& it_range) { + auto& e = *it_range.first; + return (e.is_string() && e.get().empty()); + } + }; + template + constexpr bool is_ignore_empty_string_wrapper = false; + template + constexpr bool is_ignore_empty_string_wrapper> = true; + + // Advances the dict consumer to the first element >= the given name. Returns true if found, + // false if it advanced beyond the requested name. This is exactly the same as + // `d.skip_until(name)`, but is here so we can also overload an equivalent function for json + // iteration. + inline bool skip_until(bt_dict_consumer& d, std::string_view name) { + return d.skip_until(name); + } + // Equivalent to the above but for a json object iterator. + inline bool skip_until(json_range& it_range, std::string_view name) { + auto& [it, end] = it_range; + while (it != end && it.key() < name) + ++it; + return it != end && it.key() == name; + } + + // List types that are expandable; for these we emplace_back for each element of the input + template constexpr bool is_expandable_list = false; + template constexpr bool is_expandable_list> = true; +// Don't currently need these, but they will work fine if uncommented: +// template constexpr bool is_expandable_list> = true; +// template constexpr bool is_expandable_list> = true; +// template constexpr bool is_expandable_list> = true; + + // Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the + // list length matches exactly. + template constexpr bool is_tuple_like = false; + template constexpr bool is_tuple_like> = true; + template constexpr bool is_tuple_like> = true; + template constexpr bool is_tuple_like> = true; + + template + void load_tuple_values(bt_list_consumer&, TupleLike&, std::index_sequence); + + // Consumes the next value from the dict consumer into `val` + template + || std::is_same_v, + int> = 0> + void load_value(BTConsumer& c, T& val) { + if constexpr (std::is_integral_v) + val = c.template consume_integer(); + else if constexpr (std::is_same_v || std::is_same_v) + val = c.consume_string_view(); + else if constexpr (tools::json_is_binary) + tools::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, val); + else if constexpr (is_expandable_list) { + auto lc = c.consume_list_consumer(); + val.clear(); + while (!lc.is_finished()) + load_value(lc, val.emplace_back()); + } + else if constexpr (is_tuple_like) { + auto lc = c.consume_list_consumer(); + load_tuple_values(lc, val, std::make_index_sequence>{}); + } + else + static_assert(std::is_same_v, "Unsupported load_value type"); + } + // Copies the next value from the json range into `val`, and advances the iterator. Throws + // on unconvertible values. + template + void load_value(json_range& r, T& val) { + auto& key = r.first.key(); + auto& e = *r.first; + if constexpr (std::is_same_v) { + if (e.is_boolean()) + val = e.get(); + else if (e.is_number_unsigned()) { + // Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't + // have a distinct bool type). + auto b = e.get(); + if (b <= 1) + val = b; + else + throw std::domain_error{"Invalid value for '" + key + "': expected boolean"}; + } else { + throw std::domain_error{"Invalid value for '" + key + "': expected boolean"}; + } + } else if constexpr (std::is_unsigned_v) { + if (!e.is_number_unsigned()) + throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"}; + auto i = e.get(); + if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits::max()) + throw std::domain_error{"Invalid value for '" + key + "': value too large"}; + val = i; + } else if constexpr (std::is_integral_v) { + if (!e.is_number_integer()) + throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"}; + auto i = e.get(); + if (sizeof(T) < sizeof(int64_t)) { + if (i < std::numeric_limits::lowest()) + throw std::domain_error{"Invalid value for '" + key + "': negative value magnitude is too large"}; + else if (i > std::numeric_limits::max()) + throw std::domain_error{"Invalid value for '" + key + "': value is too large"}; + } + val = i; + } else if constexpr (std::is_same_v || std::is_same_v) { + val = e.get(); + } else if constexpr (tools::json_is_binary || is_expandable_list || is_tuple_like) { + try { e.get_to(val); } + catch (const std::exception& e) { throw std::domain_error{"Invalid values in '" + key + "'"}; } + } else { + static_assert(std::is_same_v, "Unsupported load type"); + } + ++r.first; + } + + template + void load_tuple_values(bt_list_consumer& c, TupleLike& val, std::index_sequence) { + (load_value(c, std::get(val)), ...); + } + + // Takes a json object iterator or bt_dict_consumer and loads the current value at the iterator. + // This calls itself recursively, if needed, to unwrap optional/required/ignore_empty_string + // wrappers. + template + void load_curr_value(In& in, T& val) { + if constexpr (is_required_wrapper) { + load_curr_value(in, val.value); + } else if constexpr (is_ignore_empty_string_wrapper) { + if (!val.should_ignore(in)) + load_curr_value(in, val.value); + } else if constexpr (is_std_optional) { + load_curr_value(in, val.emplace()); + } else { + load_value(in, val); + } + } + + // Gets the next value from a json object iterator or bt_dict_consumer. Leaves the iterator at + // the next value, i.e. found + 1 if found, or the next greater value if not found. (NB: + // nlohmann::json objects are backed by an *ordered* map and so both nlohmann iterators and + // bt_dict_consumer behave analogously here). + template + void get_next_value(In& in, [[maybe_unused]]std::string_view name, T& val) { + if constexpr (std::is_same_v) + ; + else if (skip_until(in, name)) + load_curr_value(in, val); + else if constexpr (is_required_wrapper) + throw std::runtime_error{"Required key '" + std::string{name} + "' not found"}; + } + + /// Accessor for simple, flat value retrieval from a json or bt_dict_consumer. In the later + /// case note that the given bt_dict_consumer will be advanced, so you *must* take care to + /// process keys in order, both for the keys passed in here *and* for use before and after this + /// call. + template + void get_values(Input& in, std::string_view name, T&& val, More&&... more) { + if constexpr (std::is_same_v) { + if (auto* json_in = std::get_if(&in)) { + json_range r{json_in->cbegin(), json_in->cend()}; + get_values(r, name, val, std::forward(more)...); + } else if (auto* dict = std::get_if(&in)) { + get_values(*dict, name, val, std::forward(more)...); + } else { + // A monostate indicates that no parameters field was provided at all + get_values(var::get(in), name, val, std::forward(more)...); + } + } + else if constexpr (std::is_same_v) + { + if (in.front() == 'd') + { + bt_dict_consumer d{in}; + get_values(d, name, val, std::forward(more)...); + } + else + { + auto json_in = json::parse(in); + json_range r{json_in.cbegin(), json_in.cend()}; + get_values(r, name, val, std::forward(more)...); + } + } else { + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v); + get_next_value(in, name, val); + if constexpr (sizeof...(More) > 0) { + check_ascending_names(name, more...); + get_values(in, std::forward(more)...); + } + } + } +} // namespace cryptonote::rpc diff --git a/src/rpc/rpc_args.cpp b/src/rpc/common/rpc_args.cpp similarity index 100% rename from src/rpc/rpc_args.cpp rename to src/rpc/common/rpc_args.cpp diff --git a/src/rpc/rpc_args.h b/src/rpc/common/rpc_args.h similarity index 100% rename from src/rpc/rpc_args.h rename to src/rpc/common/rpc_args.h diff --git a/src/rpc/common/rpc_command.h b/src/rpc/common/rpc_command.h new file mode 100644 index 00000000000..de89413cc49 --- /dev/null +++ b/src/rpc/common/rpc_command.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#include +#include "json_bt.h" + + +namespace cryptonote::rpc { + +using nlohmann::json; +using beldex::json_to_bt; + +using rpc_input = std::variant; + +/// Exception when trying to invoke an RPC command that indicate a parameter parse failure (will +/// give an invalid params error for JSON-RPC, for example). +struct parse_error : std::runtime_error { using std::runtime_error::runtime_error; }; + +/// Exception used to signal various types of errors with a request back to the caller. This +/// exception indicates that the caller did something wrong: bad data, invalid value, etc., but +/// don't indicate a local problem (and so we'll log them only at debug). For more serious, +/// internal errors a command should throw some other stl error (e.g. std::runtime_error or +/// perhaps std::logic_error), which will result in a local daemon warning (and a generic internal +/// error response to the user). +/// +/// For JSON RPC these become an error response with the code as the error.code value and the +/// string as the error.message. +/// For HTTP JSON these become a 500 Internal Server Error response with the message as the body. +/// For OxenMQ the code becomes the first part of the response and the message becomes the +/// second part of the response. +struct rpc_error : std::runtime_error { + /// \param code - a signed, 16-bit numeric code. 0 must not be used (as it is used for a + /// success code in OxenMQ), and values in the -32xxx range are reserved by JSON-RPC. + /// + /// \param message - a message to send along with the error code (see general description above). + rpc_error(int16_t code, std::string message) + : std::runtime_error{"RPC error " + std::to_string(code) + ": " + message}, + code{code}, message{std::move(message)} {} + + int16_t code; + std::string message; +}; + +enum struct rpc_source : uint8_t { internal, http, omq }; + +/// Contains the context of the invocation, which must be filled out by the glue code (e.g. HTTP +/// RPC server) with requester-specific context details. +struct rpc_context { + // Specifies that the requestor has admin permissions (e.g. is on an unrestricted RPC port, or + // is a local internal request). This can be used to provide different results for an admin + // versus non-admin when invoking a public RPC command. (Note that non-public RPC commands do + // not need to check this field for authentication: a non-public invoke() is not called in the + // first place if attempted by a public requestor). + bool admin = false; + + // The RPC engine source of the request, i.e. internal, HTTP, OMQ + rpc_source source = rpc_source::internal; + + // A free-form identifier (meant for humans) identifiying the remote address of the request; + // this might be IP:PORT, or could contain a pubkey, or ... + std::string remote; +}; + +struct rpc_request { + // The request body: + // - for an HTTP, non-JSONRPC POST request the string or string_view will be populated with the + // unparsed request body. + // - for an HTTP JSONRPC request with a "params" value the nlohmann::json will be set to the + // parsed "params" value of the request. + // - for OMQ requests with a data part the string or string_view will be set to the provided value + // - for all other requests (i.e. JSONRPC with no params; HTTP GET requests; no-data OMQ + // requests) the variant will contain a std::monostate. + // + // If something goes wrong, throw. + std::variant body; + + // Returns a string_view of the body, if the body is a string or string_view. Returns + // std::nullopt if the body is empty (std::monostate) or parsed jsonrpc params. + std::optional body_view() const { + if (auto* sv = std::get_if(&body)) return *sv; + if (auto* s = std::get_if(&body)) return *s; + return std::nullopt; + } + + // Values to pass through to the invoke() call + rpc_context context; +}; + +// Note: to use, parse_request(RPC, rpc_input) must be defined for each typename RPC +// this is used on. +template +auto make_invoke() { + return [](rpc_request&& request, RPCServer& server) -> typename RPCCallback::result_type { + RPC rpc{}; + + try { + if (auto body = request.body_view()) { + if (body->front() == 'd') { // Looks like a bt dict + rpc.set_bt(); + parse_request(rpc, oxenc::bt_dict_consumer{*body}); + } + else + parse_request(rpc, json::parse(*body)); + } else if (auto* j = std::get_if(&request.body)) { + parse_request(rpc, std::move(*j)); + } else { + assert(std::holds_alternative(request.body)); + parse_request(rpc, std::monostate{}); + } + } catch (const std::exception& e) { + throw parse_error{"Failed to parse request parameters: "s + e.what()}; + } + + server.invoke(rpc, std::move(request.context)); + + if (rpc.response.is_null()) + rpc.response = json::object(); + + if (rpc.is_bt()) + return json_to_bt(std::move(rpc.response)); + else + return std::move(rpc.response); + }; +} + +} // namespace cryptonote::rpc \ No newline at end of file diff --git a/src/rpc/common/rpc_version.h b/src/rpc/common/rpc_version.h new file mode 100644 index 00000000000..5fa296dc825 --- /dev/null +++ b/src/rpc/common/rpc_version.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace cryptonote::rpc { + + using version_t = std::pair; + + /// Makes a version array from a packed 32-bit integer version + constexpr version_t make_version(uint32_t version) + { + return {static_cast(version >> 16), static_cast(version & 0xffff)}; + } + /// Packs a version array into a packed 32-bit integer version + constexpr uint32_t pack_version(version_t version) + { + return (uint32_t(version.first) << 16) | version.second; + } + +} // namespace cryptonote::rpc \ No newline at end of file diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 10ceec0474b..799aca942b2 100755 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -31,22 +31,33 @@ #include #include -#include -#include #include #include #include #include #include #include +#include "common/json_binary_proxy.h" +#include +#include "epee/net/network_throttle.hpp" +#include "common/string_util.h" +#include "bootstrap_daemon.h" #include "crypto/crypto.h" #include "cryptonote_basic/hardfork.h" #include "cryptonote_basic/tx_extra.h" +#include "cryptonote_config.h" #include "cryptonote_core/beldex_name_system.h" #include "cryptonote_core/pos.h" +#include "cryptonote_core/master_node_rules.h" #include "beldex_economy.h" #include "epee/string_tools.h" #include "core_rpc_server.h" +#include "core_rpc_server_binary_commands.h" +#include "core_rpc_server_command_parser.h" +#include "core_rpc_server_error_codes.h" +#include "rpc/common/rpc_args.h" +#include "rpc/common/json_bt.h" +#include "rpc/common/rpc_command.h" #include "common/command_line.h" #include "common/beldex.h" #include "common/sha256sum.h" @@ -59,9 +70,8 @@ #include "cryptonote_core/uptime_proof.h" #include "net/parse.h" #include "crypto/hash.h" -#include "rpc/rpc_args.h" -#include "core_rpc_server_error_codes.h" #include "p2p/net_node.h" +#include "serialization/json_archive.h" #include "version.h" #include @@ -69,122 +79,66 @@ #define BELDEX_DEFAULT_LOG_CATEGORY "daemon.rpc" -namespace cryptonote { namespace rpc { +namespace cryptonote::rpc { + using nlohmann::json; + using tools::json_binary_proxy; namespace { - // Helper loaders for RPC registration; this lets us reduce the amount of compiled code by - // avoiding the need to instantiate {JSON,binary} loading code for {binary,JSON} commands. - // This first one is for JSON, the specialization below is for binary. - template - struct reg_helper { - using Request = typename RPC::request; - - Request load(rpc_request& request) { - Request req{}; - if (auto body = request.body_view()) { - if (!epee::serialization::load_t_from_json(req, *body)) - throw parse_error{"Failed to parse JSON parameters"}; - } else { - // This is nasty. TODO: get rid of epee's horrible serialization code. - auto& epee_stuff = var::get(request.body); - auto& storage_entry = epee_stuff.second; - // Epee nomenclature translactions: - // - // - "storage_entry" is a variant over values (ints, doubles, string, storage_entries, or - // array_entry). - // - // - "array_entry" is a variant over vectors of all of those values. - // - // Epee's json serialization also has a metric ton of limitations: for example it can't - // properly deserialize signed integer (unless *all* values are negative), or doubles - // (unless *all* values do not look like ints), and for both serialization and - // deserialization doesn't support lists of lists, and any mixed types in lists (for - // example '[bool, 1, "hi"]`). - // - // Conclusion: it needs to go. - if (auto* section = std::get_if(&storage_entry)) - req.load(epee_stuff.first, section); - else - throw std::runtime_error{"only top-level JSON object values are currently supported"}; - } - return req; - } + + template + void register_rpc_command(std::unordered_map>& regs) + { + static_assert(std::is_base_of_v && !std::is_base_of_v); + auto cmd = std::make_shared(); + cmd->is_public = std::is_base_of_v; + cmd->is_legacy = std::is_base_of_v; - // store_t_to_json can't store a string. Go epee. - template ::value, int> = 0> - std::string serialize(std::string&& res) { - std::ostringstream o; - epee::serialization::dump_as_json(o, std::move(res), 0 /*indent*/, false /*newlines*/); - return o.str(); - } + // Temporary: remove once RPC conversion is complete + static_assert(!FIXME_has_nested_response_v); - template ::value, int> = 0> - std::string serialize(typename RPC::response&& res) { - std::string response; - epee::serialization::store_t_to_json(res, response, 0 /*indent*/, false /*newlines*/); - return response; - } - }; + cmd->invoke = make_invoke(); + + for (const auto& name : RPC::names()) + regs.emplace(name, cmd); + } - // binary command specialization template - struct reg_helper::value>> { - using Request = typename RPC::request; - Request load(rpc_request& request) { - LOG_PRINT_L2("reg_helper load" << __func__); - Request req{}; + void register_binary_rpc_command(std::unordered_map>& regs) + { + static_assert(std::is_base_of_v && !std::is_base_of_v); + auto cmd = std::make_shared(); + cmd->is_public = std::is_base_of_v; + cmd->is_binary = true; + + // Legacy binary request; these still use epee serialization, and should be considered + // deprecated (tentatively to be removed in Beldex 11). + cmd->invoke = [](rpc_request&& request, core_rpc_server& server) -> rpc_command::result_type { + typename RPC::request req{}; std::string_view data; if (auto body = request.body_view()) data = *body; else throw std::runtime_error{"Internal error: can't load binary a RPC command with non-string body"}; - if (sizeof(data)>0){ - if (!epee::serialization::load_t_from_binary(req, data)) - throw parse_error{"Failed to parse binary data parameterS"}; - } - else{ - - } + if (!epee::serialization::load_t_from_binary(req, data)) + throw parse_error{"Failed to parse binary data parameters"}; - return req; - } + auto res = server.invoke(std::move(req), std::move(request.context)); - std::string serialize(typename RPC::response&& res) { std::string response; epee::serialization::store_t_to_binary(res, response); return response; - } - }; - - template , int> = 0> - void register_rpc_command(std::unordered_map>& regs) - { - using Request = typename RPC::request; - using Response = typename RPC::response; - /// check that core_rpc_server.invoke(Request, rpc_context) returns a Response; the code below - /// will fail anyway if this isn't satisfied, but that compilation failure might be more cryptic. - using invoke_return_type = decltype(std::declval().invoke(std::declval(), rpc_context{})); - static_assert(std::is_same::value, - "Unable to register RPC command: core_rpc_server::invoke(Request) is not defined or does not return a Response"); - auto cmd = std::make_shared(); - cmd->is_public = std::is_base_of_v; - cmd->is_binary = std::is_base_of_v; - cmd->is_legacy = std::is_base_of_v; - cmd->invoke = [](rpc_request&& request, core_rpc_server& server) { - reg_helper helper; - Response res = server.invoke(helper.load(request), std::move(request.context)); - return helper.serialize(std::move(res)); }; for (const auto& name : RPC::names()) regs.emplace(name, cmd); } - template - std::unordered_map> register_rpc_commands(tools::type_list) { + template + std::unordered_map> register_rpc_commands(tools::type_list, tools::type_list) { std::unordered_map> regs; (register_rpc_command(regs), ...); + (register_binary_rpc_command(regs), ...); return regs; } @@ -194,12 +148,11 @@ namespace cryptonote { namespace rpc { } - const std::unordered_map> rpc_commands = register_rpc_commands(rpc::core_rpc_types{}); + const std::unordered_map> rpc_commands = register_rpc_commands(rpc::core_rpc_types{}, rpc::core_rpc_binary_types{}); const command_line::arg_descriptor core_rpc_server::arg_bootstrap_daemon_address = { "bootstrap-daemon-address" - , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced.\n" - "Use 'auto' to enable automatic public nodes discovering and bootstrap daemon switching" + , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced." , "" }; @@ -209,11 +162,6 @@ namespace cryptonote { namespace rpc { , "" }; - std::optional rpc_request::body_view() const { - if (auto* sv = std::get_if(&body)) return *sv; - if (auto* s = std::get_if(&body)) return *s; - return std::nullopt; - } //----------------------------------------------------------------------------------- void core_rpc_server::init_options(boost::program_options::options_description& desc, boost::program_options::options_description& hidden) @@ -232,6 +180,7 @@ namespace cryptonote { namespace rpc { , m_should_use_bootstrap_daemon(false) , m_was_bootstrap_ever_used(false) {} + bool core_rpc_server::set_bootstrap_daemon(const std::string &address, std::string_view username_password) { std::string_view username, password; @@ -253,8 +202,6 @@ namespace cryptonote { namespace rpc { if (address.empty()) m_bootstrap_daemon.reset(); - else if (address == "auto") - m_bootstrap_daemon = std::make_unique([this]{ return get_random_public_node(); }); else m_bootstrap_daemon = std::make_unique(address, credentials); @@ -263,54 +210,21 @@ namespace cryptonote { namespace rpc { return true; } //------------------------------------------------------------------------------------------------------------------------------ - std::optional core_rpc_server::get_random_public_node() - { - GET_PUBLIC_NODES::response response{}; - try - { - GET_PUBLIC_NODES::request request{}; - request.gray = true; - request.white = true; - - rpc_context context = {}; - context.admin = true; - response = invoke(std::move(request), context); - } - catch(const std::exception &e) - { - return std::nullopt; - } - - const auto get_random_node_address = [](const std::vector& public_nodes) -> std::string { - const auto& random_node = public_nodes[crypto::rand_idx(public_nodes.size())]; - const auto address = random_node.host + ":" + std::to_string(random_node.rpc_port); - return address; - }; - - if (!response.white.empty()) - { - return get_random_node_address(response.white); - } - - MDEBUG("No white public node found, checking gray peers"); - - if (!response.gray.empty()) - { - return get_random_node_address(response.gray); - } - - MERROR("Failed to find any suitable public node"); - return std::nullopt; - } - //------------------------------------------------------------------------------------------------------------------------------ - void core_rpc_server::init(const boost::program_options::variables_map& vm) + bool core_rpc_server::init(const boost::program_options::variables_map& vm) { if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), command_line::get_arg(vm, arg_bootstrap_daemon_login))) { MERROR("Failed to parse bootstrap daemon address"); + return false; } m_was_bootstrap_ever_used = false; + return true; + } + //--------------------------------------------------------------------------------- + bool core_rpc_server::deinit() + { + return true; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_ready() @@ -322,157 +236,166 @@ namespace cryptonote { namespace rpc { #define CHECK_CORE_READY() do { if(!check_core_ready()){ res.status = STATUS_BUSY; return res; } } while(0) //------------------------------------------------------------------------------------------------------------------------------ - GET_HEIGHT::response core_rpc_server::invoke(GET_HEIGHT::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_HEIGHT& get_height, rpc_context context) { - GET_HEIGHT::response res{}; - PERF_TIMER(on_get_height); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - crypto::hash hash; - m_core.get_blockchain_top(res.height, hash); - ++res.height; // block height to chain height - res.hash = tools::type_to_hex(hash); - res.status = STATUS_OK; + if (use_bootstrap_daemon_if_necessary({}, get_height.response)) + return; + + auto [height, hash] = m_core.get_blockchain_top(); + ++height; // block height to chain height + get_height.response["status"] = STATUS_OK; + get_height.response["height"] = height; + get_height.response_hex["hash"] = hash; - res.immutable_height = 0; + uint64_t immutable_height = 0; cryptonote::checkpoint_t checkpoint; - if (m_core.get_blockchain_storage().get_db().get_immutable_checkpoint(&checkpoint, res.height - 1)) + if (m_core.get_blockchain_storage().get_db().get_immutable_checkpoint(&checkpoint, height - 1)) { - res.immutable_height = checkpoint.height; - res.immutable_hash = tools::type_to_hex(checkpoint.block_hash); + get_height.response["immutable_height"] = checkpoint.height; + get_height.response_hex["immutable_hash"] = checkpoint.block_hash; } - - return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_INFO::response core_rpc_server::invoke(GET_INFO::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_INFO& info, rpc_context context) { - GET_INFO::response res{}; - PERF_TIMER(on_get_info); - if (use_bootstrap_daemon_if_necessary(req, res)) + + if (use_bootstrap_daemon_if_necessary({}, info.response)) { if (context.admin) { - crypto::hash top_hash; - m_core.get_blockchain_top(res.height_without_bootstrap.emplace(), top_hash); - ++*res.height_without_bootstrap; // turn top block height into blockchain height - res.was_bootstrap_ever_used = true; + auto [height, top_hash] = m_core.get_blockchain_top(); + info.response["height_without_bootstrap"] = ++height; // turn top block height into blockchain height + info.response["was_bootstrap_ever_used"] = true; std::shared_lock lock{m_bootstrap_daemon_mutex}; - if (m_bootstrap_daemon.get() != nullptr) - { - res.bootstrap_daemon_address = m_bootstrap_daemon->address(); - } + if (m_bootstrap_daemon) + info.response["bootstrap_daemon_address"] = m_bootstrap_daemon->address(); } - return res; + return; } - crypto::hash top_hash; - m_core.get_blockchain_top(res.height, top_hash); - auto prev_ts = m_core.get_blockchain_storage().get_db().get_block_timestamp(res.height); - ++res.height; // turn top block height into blockchain height - res.top_block_hash = tools::type_to_hex(top_hash); - res.target_height = m_core.get_target_blockchain_height(); + auto [top_height, top_hash] = m_core.get_blockchain_top(); + + auto& bs = m_core.get_blockchain_storage(); + auto& db = bs.get_db(); + + auto prev_ts = db.get_block_timestamp(top_height); + auto height = top_height + 1; // turn top block height into blockchain height + + info.response["height"] = height; + info.response_hex["top_block_hash"] = top_hash; + info.response["target_height"] = m_core.get_target_blockchain_height(); bool next_block_is_POS = false; - if (POS::timings t; POS::get_round_timings(m_core.get_blockchain_storage(), res.height, prev_ts, t)) { - res.POS_ideal_timestamp = tools::to_seconds(t.ideal_timestamp.time_since_epoch()); - res.POS_target_timestamp = tools::to_seconds(t.r0_timestamp.time_since_epoch()); + if (POS::timings t; + POS::get_round_timings(bs, height, prev_ts, t)) { + info.response["POS_ideal_timestamp"] = tools::to_seconds(t.ideal_timestamp.time_since_epoch()); + info.response["POS_target_timestamp"] = tools::to_seconds(t.r0_timestamp.time_since_epoch()); next_block_is_POS = POS::clock::now() < t.miner_fallback_timestamp; } - res.immutable_height = 0; - cryptonote::checkpoint_t checkpoint; - if (m_core.get_blockchain_storage().get_db().get_immutable_checkpoint(&checkpoint, res.height - 1)) + if (cryptonote::checkpoint_t checkpoint; + db.get_immutable_checkpoint(&checkpoint, top_height)) { - res.immutable_height = checkpoint.height; - res.immutable_block_hash = tools::type_to_hex(checkpoint.block_hash); + info.response["immutable_height"] = checkpoint.height; + info.response_hex["immutable_block_hash"] = checkpoint.block_hash; } - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(next_block_is_POS); - res.target = tools::to_seconds((next_block_is_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)); - res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase - res.tx_pool_size = m_core.get_pool().get_transactions_count(); + if (next_block_is_POS) + info.response["POS"] = true; + else + info.response["difficulty"] = bs.get_difficulty_for_next_block(next_block_is_POS); + + info.response["target"] = tools::to_seconds((next_block_is_POS ? TARGET_BLOCK_TIME : old::TARGET_BLOCK_TIME_12)); + // This count seems broken: blocks with no outputs (after batching) shouldn't be subtracted, and + // 0-output txes (MN state changes) arguably shouldn't be, either. + info.response["tx_count"] = m_core.get_blockchain_storage().get_total_transactions() - height; //without coinbase + info.response["tx_pool_size"] = m_core.get_pool().get_transactions_count(); + if (context.admin) { - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_public_connections_count(); - res.outgoing_connections_count = m_p2p.get_public_outgoing_connections_count(); - res.incoming_connections_count = (total_conn - *res.outgoing_connections_count); - res.white_peerlist_size = m_p2p.get_public_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_public_gray_peers_count(); + info.response["alt_blocks_count"] = bs.get_alternative_blocks_count(); + auto total_conn = m_p2p.get_public_connections_count(); + auto outgoing_conns = m_p2p.get_public_outgoing_connections_count(); + info.response["outgoing_connections_count"] = outgoing_conns; + info.response["incoming_connections_count"] = total_conn - outgoing_conns; + info.response["white_peerlist_size"] = m_p2p.get_public_white_peers_count(); + info.response["grey_peerlist_size"] = m_p2p.get_public_gray_peers_count(); } cryptonote::network_type nettype = m_core.get_nettype(); - res.mainnet = nettype == MAINNET; - res.testnet = nettype == TESTNET; - res.devnet = nettype == DEVNET; - res.nettype = nettype == MAINNET ? "mainnet" : nettype == TESTNET ? "testnet" : nettype == DEVNET ? "devnet" : "fakechain"; + info.response["mainnet"] = nettype == network_type::MAINNET; + if (nettype == network_type::TESTNET) info.response["testnet"] = true; + else if (nettype == network_type::DEVNET) info.response["devnet"] = true; + else if (nettype != network_type::MAINNET) info.response["fakechain"] = true; + info.response["nettype"] = nettype == network_type::MAINNET ? "mainnet" : nettype == network_type::TESTNET ? "testnet" : nettype == network_type::DEVNET ? "devnet" : "fakechain"; try { - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); + auto cd = db.get_block_cumulative_difficulty(top_height); + info.response["cumulative_difficulty"] = cd; } catch(std::exception const &e) { - res.status = "Error retrieving cumulative difficulty at height " + std::to_string(res.height - 1); - return res; + info.response["status"] = "Error retrieving cumulative difficulty at height " + std::to_string(top_height); + return; } - res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); - res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); + info.response["block_size_limit"] = bs.get_current_cumulative_block_weight_limit(); + info.response["block_size_median"] = bs.get_current_cumulative_block_weight_median(); - auto bns_counts = m_core.get_blockchain_storage().name_system_db().get_mapping_counts(res.height); - res.bns_counts = bns_counts; + info.response["bns_counts"] = bs.name_system_db().get_mapping_counts(height); if (context.admin) { - res.master_node = m_core.master_node(); - res.start_time = (uint64_t)m_core.get_start_time(); - res.last_storage_server_ping = (uint64_t)m_core.m_last_storage_server_ping; - res.last_belnet_ping = (uint64_t)m_core.m_last_belnet_ping; - res.free_space = m_core.get_free_space(); - res.height_without_bootstrap = res.height; - std::shared_lock lock{m_bootstrap_daemon_mutex}; - if (m_bootstrap_daemon) - { - res.bootstrap_daemon_address = m_bootstrap_daemon->address(); + bool mn = m_core.master_node(); + info.response["master_node"] = mn; + info.response["start_time"] = m_core.get_start_time(); + if (mn) { + info.response["last_storage_server_ping"] = m_core.m_last_storage_server_ping.load(); + info.response["last_belnet_ping"] = m_core.m_last_belnet_ping.load(); + } + info.response["free_space"] = m_core.get_free_space(); + + if (std::shared_lock lock{m_bootstrap_daemon_mutex}; m_bootstrap_daemon) { + info.response["bootstrap_daemon_address"] = m_bootstrap_daemon->address(); + info.response["height_without_bootstrap"] = height; + info.response["was_bootstrap_ever_used"] = m_was_bootstrap_ever_used; } - res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.offline = m_core.offline(); - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - if (!context.admin) - res.database_size = round_up(res.database_size, 1'000'000'000); - res.version = context.admin ? BELDEX_VERSION_FULL : std::to_string(BELDEX_VERSION[0]); - res.status_line = context.admin ? m_core.get_status_string() : - "v" + std::to_string(BELDEX_VERSION[0]) + "; Height: " + std::to_string(res.height); + if (m_core.offline()) + info.response["offline"] = true; + auto db_size = db.get_database_size(); + info.response["database_size"] = context.admin ? db_size : round_up(db_size, 1'000'000'000); + info.response["version"] = context.admin ? BELDEX_VERSION_FULL : std::to_string(BELDEX_VERSION[0]); + info.response["status_line"] = context.admin ? m_core.get_status_string() : + "v" + std::to_string(BELDEX_VERSION[0]) + "; Height: " + std::to_string(height); - res.status = STATUS_OK; - return res; + info.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_NET_STATS::response core_rpc_server::invoke(GET_NET_STATS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_NET_STATS& get_net_stats, rpc_context context) { - GET_NET_STATS::response res{}; - PERF_TIMER(on_get_net_stats); // No bootstrap daemon check: Only ever get stats about local server - res.start_time = (uint64_t)m_core.get_start_time(); + get_net_stats.response["start_time"] = m_core.get_start_time(); { std::lock_guard lock{epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in}; - epee::net_utils::network_throttle_manager::get_global_throttle_in().get_stats(res.total_packets_in, res.total_bytes_in); + auto [packets, bytes] = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_stats(); + get_net_stats.response["total_packets_in"] = packets; + get_net_stats.response["total_bytes_in"] = bytes; } { std::lock_guard lock{epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_out}; - epee::net_utils::network_throttle_manager::get_global_throttle_out().get_stats(res.total_packets_out, res.total_bytes_out); + auto [packets, bytes] = epee::net_utils::network_throttle_manager::get_global_throttle_out().get_stats(); + get_net_stats.response["total_packets_out"] = packets; + get_net_stats.response["total_bytes_out"] = bytes; } - res.status = STATUS_OK; - return res; + get_net_stats.response["status"] = STATUS_OK; } namespace { //------------------------------------------------------------------------------------------------------------------------------ @@ -486,17 +409,17 @@ namespace cryptonote { namespace rpc { }; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCKS_FAST::response core_rpc_server::invoke(GET_BLOCKS_FAST::request&& req, rpc_context context) + GET_BLOCKS_BIN::response core_rpc_server::invoke(GET_BLOCKS_BIN::request&& req, rpc_context context) { - GET_BLOCKS_FAST::response res{}; + GET_BLOCKS_BIN::response res{}; PERF_TIMER(on_get_blocks); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; std::vector, std::vector > > > bs; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, GET_BLOCKS_FAST::MAX_COUNT)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, GET_BLOCKS_BIN::MAX_COUNT)) { res.status = "Failed"; return res; @@ -510,18 +433,16 @@ namespace cryptonote { namespace rpc { res.blocks.resize(res.blocks.size()+1); res.blocks.back().block = bd.first.first; size += bd.first.first.size(); - res.output_indices.push_back(GET_BLOCKS_FAST::block_output_indices()); + res.output_indices.push_back(GET_BLOCKS_BIN::block_output_indices()); ntxes += bd.second.size(); res.output_indices.back().indices.reserve(1 + bd.second.size()); if (req.no_miner_tx) - res.output_indices.back().indices.push_back(GET_BLOCKS_FAST::tx_output_indices()); + res.output_indices.back().indices.push_back(GET_BLOCKS_BIN::tx_output_indices()); res.blocks.back().txs.reserve(bd.second.size()); - for (std::vector>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) + for (auto& [txhash, txdata] : bd.second) { - res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash}); - i->second.clear(); - i->second.shrink_to_fit(); - size += res.blocks.back().txs.back().size(); + auto& entry = res.blocks.back().txs.emplace_back(std::move(txdata), crypto::null_hash); + size += entry.size(); } const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1); @@ -543,12 +464,13 @@ namespace cryptonote { namespace rpc { res.status = STATUS_OK; return res; } - GET_ALT_BLOCKS_HASHES::response core_rpc_server::invoke(GET_ALT_BLOCKS_HASHES::request&& req, rpc_context context) + //------------------------------------------------------------------------------------------------------------------------------ + GET_ALT_BLOCKS_HASHES_BIN::response core_rpc_server::invoke(GET_ALT_BLOCKS_HASHES_BIN::request&& req, rpc_context context) { - GET_ALT_BLOCKS_HASHES::response res{}; + GET_ALT_BLOCKS_HASHES_BIN::response res{}; PERF_TIMER(on_get_alt_blocks_hashes); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; std::vector blks; @@ -571,12 +493,12 @@ namespace cryptonote { namespace rpc { return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCKS_BY_HEIGHT::response core_rpc_server::invoke(GET_BLOCKS_BY_HEIGHT::request&& req, rpc_context context) + GET_BLOCKS_BY_HEIGHT_BIN::response core_rpc_server::invoke(GET_BLOCKS_BY_HEIGHT_BIN::request&& req, rpc_context context) { - GET_BLOCKS_BY_HEIGHT::response res{}; + GET_BLOCKS_BY_HEIGHT_BIN::response res{}; PERF_TIMER(on_get_blocks_by_height); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; res.status = "Failed"; @@ -595,8 +517,7 @@ namespace cryptonote { namespace rpc { return res; } std::vector txs; - std::vector missed_txs; - m_core.get_transactions(blk.tx_hashes, txs, missed_txs); + m_core.get_transactions(blk.tx_hashes, txs); res.blocks.resize(res.blocks.size() + 1); res.blocks.back().block = block_to_blob(blk); for (auto& tx : txs) @@ -606,12 +527,12 @@ namespace cryptonote { namespace rpc { return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_HASHES_FAST::response core_rpc_server::invoke(GET_HASHES_FAST::request&& req, rpc_context context) + GET_HASHES_BIN::response core_rpc_server::invoke(GET_HASHES_BIN::request&& req, rpc_context context) { - GET_HASHES_FAST::response res{}; + GET_HASHES_BIN::response res{}; PERF_TIMER(on_get_hashes); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; res.start_height = req.start_height; @@ -643,51 +564,81 @@ namespace cryptonote { namespace rpc { return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_OUTPUTS::response core_rpc_server::invoke(GET_OUTPUTS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_OUTPUTS& get_outputs, rpc_context context) { - GET_OUTPUTS::response res{}; PERF_TIMER(on_get_outs); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + json params{ + {"get_txid", get_outputs.request.get_txid}, + {"as_tuple", get_outputs.request.as_tuple}, + {"output_indices", json::array()} + }; - if (!context.admin && req.outputs.size() > GET_OUTPUTS::MAX_COUNT) { - res.status = "Too many outs requested"; - return res; + for (const auto& h: get_outputs.request.output_indices) + params["output_indices"].push_back(tools::type_to_hex(h)); + + if (use_bootstrap_daemon_if_necessary(params, get_outputs.response)) + return; + + if (!context.admin && get_outputs.request.output_indices.size() > GET_OUTPUTS::MAX_COUNT) { + get_outputs.response["status"] = "Too many outs requested"; + return; } + // This is nasty. WTF are core methods taking *local rpc* types? + // FIXME: make core methods take something sensible, like a std::vector. (We really + // don't need the pair since amount is also 0 for Beldex since the beginning of the chain; only in + // ancient Monero blocks was it non-zero). GET_OUTPUTS_BIN::request req_bin{}; - req_bin.outputs = req.outputs; - req_bin.get_txid = req.get_txid; - GET_OUTPUTS_BIN::response res_bin{}; - if (!m_core.get_outs(req_bin, res_bin)) - { - res.status = "Failed"; - return res; - } + req_bin.get_txid = get_outputs.request.get_txid; + req_bin.outputs.reserve(get_outputs.request.output_indices.size()); + for (auto oi : get_outputs.request.output_indices) + req_bin.outputs.push_back({0, oi}); - // convert to text - for (const auto &i: res_bin.outs) - { - res.outs.emplace_back(); - auto& outkey = res.outs.back(); - outkey.key = tools::type_to_hex(i.key); - outkey.mask = tools::type_to_hex(i.mask); - outkey.unlocked = i.unlocked; - outkey.height = i.height; - outkey.txid = tools::type_to_hex(i.txid); + GET_OUTPUTS_BIN::response res_bin{}; + if (!m_core.get_outs(req_bin, res_bin)){ + get_outputs.response["status"] = STATUS_FAILED; + return; + } + + auto binary_format = get_outputs.is_bt() ? json_binary_proxy::fmt::bt : json_binary_proxy::fmt::hex; + + auto& outs = (get_outputs.response["outs"] = json::array()); + if (!get_outputs.request.as_tuple) { + for (auto& outkey : res_bin.outs) { + json o; + json_binary_proxy b{o, binary_format}; + b["key"] = std::move(outkey.key); + b["mask"] = std::move(outkey.mask); + o["unlocked"] = outkey.unlocked; + o["height"] = outkey.height; + if (get_outputs.request.get_txid) + b["txid"] = std::move(outkey.txid); + outs.push_back(std::move(o)); + } + } else { + for (auto& outkey : res_bin.outs) { + auto o = json::array(); + json_binary_proxy b{o, binary_format}; + b.push_back(std::move(outkey.key)); + b.push_back(std::move(outkey.mask)); + o.push_back(outkey.unlocked); + o.push_back(outkey.height); + if (get_outputs.request.get_txid) + b.push_back(std::move(outkey.txid)); + outs.push_back(o); + } } - res.status = STATUS_OK; - return res; + get_outputs.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_TX_GLOBAL_OUTPUTS_INDEXES::response core_rpc_server::invoke(GET_TX_GLOBAL_OUTPUTS_INDEXES::request&& req, rpc_context context) + GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::response core_rpc_server::invoke(GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::request&& req, rpc_context context) { - GET_TX_GLOBAL_OUTPUTS_INDEXES::response res{}; + GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::response res{}; PERF_TIMER(on_get_indexes); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); @@ -697,7 +648,7 @@ namespace cryptonote { namespace rpc { return res; } res.status = STATUS_OK; - LOG_PRINT_L2("GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"); + LOG_PRINT_L2("GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN: [" << res.o_indexes.size() << "]"); return res; } @@ -717,122 +668,152 @@ namespace cryptonote { namespace rpc { } struct extra_extractor { - GET_TRANSACTIONS::extra_entry& entry; + nlohmann::json& entry; const network_type nettype; - const uint8_t hf_version; + const cryptonote::hf hf_version; + json_binary_proxy::fmt format; + + // If we encounter duplicate values then we want to produce an array of values, but with just + // a single one we want just the value itself; this does that. Returns a reference to the + // assigned value (whether as a top-level value or array element). + template + json& set( + const std::string& key, + T&& value, + /*[[maybe_unused]]*/ bool binary = tools::json_is_binary || tools::json_is_binary_container) { + auto* x = &entry[key]; + if (!x->is_null() && !x->is_array()) + x = &(entry[key] = json::array({std::move(*x)})); + if (x->is_array()) + x = &x->emplace_back(); + if constexpr (tools::json_is_binary || tools::json_is_binary_container || std::is_convertible_v) { + if (binary) + return json_binary_proxy{*x, format} = std::forward(value); + } + assert(!binary); + return *x = std::forward(value); + } - void operator()(const tx_extra_pub_key& x) { entry.pubkey = tools::type_to_hex(x.pub_key); } + void operator()(const tx_extra_pub_key& x) { set("pubkey", x.pub_key); } void operator()(const tx_extra_nonce& x) { if ((x.nonce.size() == sizeof(crypto::hash) + 1 && x.nonce[0] == TX_EXTRA_NONCE_PAYMENT_ID) || (x.nonce.size() == sizeof(crypto::hash8) + 1 && x.nonce[0] == TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID)) - entry.payment_id = oxenc::to_hex(x.nonce.begin() + 1, x.nonce.end()); + set("payment_id", std::string_view{x.nonce.data() + 1, x.nonce.size() - 1}, true); else - entry.extra_nonce = oxenc::to_hex(x.nonce); - } - void operator()(const tx_extra_merge_mining_tag& x) { entry.mm_depth = x.depth; entry.mm_root = tools::type_to_hex(x.merkle_root); } - void operator()(const tx_extra_additional_pub_keys& x) { entry.additional_pubkeys = hexify(x.data); } - void operator()(const tx_extra_burn& x) { entry.burn_amount = x.amount; } - void operator()(const tx_extra_master_node_winner& x) { entry.mn_winner = tools::type_to_hex(x.m_master_node_key); } - void operator()(const tx_extra_master_node_pubkey& x) { entry.mn_pubkey = tools::type_to_hex(x.m_master_node_key); } - void operator()(const tx_extra_security_signature& x) { entry.security_sig = tools::type_to_hex(x.m_security_signature); } + set("extra_nonce", x.nonce, true); + } + void operator()(const tx_extra_merge_mining_tag& x) { set("mm_depth", x.depth); set("mm_root", x.merkle_root); } + void operator()(const tx_extra_additional_pub_keys& x) { set("additional_pubkeys", x.data); } + void operator()(const tx_extra_burn& x) { set("burn_amount", x.amount); } + void operator()(const tx_extra_master_node_winner& x) { set("mn_winner", x.m_master_node_key); } + void operator()(const tx_extra_master_node_pubkey& x) { set("mn_pubkey", x.m_master_node_key); } + void operator()(const tx_extra_security_signature& x) { set("security_sig", tools::type_to_hex(x.m_security_signature)); } void operator()(const tx_extra_master_node_register& x) { - auto& reg = entry.mn_registration.emplace(); - reg.fee = microportion(x.m_portions_for_operator); - reg.expiry = x.m_expiration_timestamp; - for (size_t i = 0; i < x.m_portions.size(); i++) { - auto& [wallet, portion] = reg.contributors.emplace_back(); - wallet = get_account_address_as_str(nettype, false, {x.m_public_spend_keys[i], x.m_public_view_keys[i]}); - portion = microportion(x.m_portions[i]); - } + json reservations{}; + for (size_t i = 0; i < x.m_portions.size(); i++) + reservations[get_account_address_as_str(nettype, false, {x.m_public_spend_keys[i], x.m_public_view_keys[i]})] + = microportion(x.m_portions[i]); + set("mn_registration", json{ + {"fee", microportion(x.m_portions_for_operator)}, + {"expiry", x.m_expiration_timestamp}, + {"reservations", std::move(reservations)}}); } void operator()(const tx_extra_master_node_contributor& x) { - entry.mn_contributor = get_account_address_as_str(nettype, false, {x.m_spend_public_key, x.m_view_public_key}); + set("mn_contributor", get_account_address_as_str(nettype, false, {x.m_spend_public_key, x.m_view_public_key})); } template auto& _state_change(const T& x) { // Common loading code for nearly-identical state_change and deregister_old variables: - auto& sc = entry.mn_state_change.emplace(); - sc.height = x.block_height; - sc.index = x.master_node_index; - sc.voters.reserve(x.votes.size()); + auto voters = json::array(); for (auto& v : x.votes) - sc.voters.push_back(v.validator_index); - return sc; + voters.push_back(v.validator_index); + + json sc{ + {"height", x.block_height}, + {"index", x.master_node_index}, + {"voters", std::move(voters)}}; + return set("mn_state_change", std::move(sc)); } void operator()(const tx_extra_master_node_deregister_old& x) { auto& sc = _state_change(x); - sc.old_dereg = true; - sc.type = "dereg"; + sc["old_dereg"] = true; + sc["type"] = "dereg"; } void operator()(const tx_extra_master_node_state_change& x) { auto& sc = _state_change(x); if (x.reason_consensus_all) - sc.reasons = cryptonote::coded_reasons(x.reason_consensus_all); + sc["reasons"] = cryptonote::coded_reasons(x.reason_consensus_all); // If `any` has reasons not included in all then list the extra ones separately: if (uint16_t reasons_maybe = x.reason_consensus_any & ~x.reason_consensus_all) - sc.reasons_maybe = cryptonote::coded_reasons(reasons_maybe); + sc["reasons_maybe"] = cryptonote::coded_reasons(reasons_maybe); switch (x.state) { - case master_nodes::new_state::decommission: sc.type = "decom"; break; - case master_nodes::new_state::recommission: sc.type = "recom"; break; - case master_nodes::new_state::deregister: sc.type = "dereg"; break; - case master_nodes::new_state::ip_change_penalty: sc.type = "ip"; break; + case master_nodes::new_state::decommission: sc["type"] = "decom"; break; + case master_nodes::new_state::recommission: sc["type"] = "recom"; break; + case master_nodes::new_state::deregister: sc["type"] = "dereg"; break; + case master_nodes::new_state::ip_change_penalty: sc["type"] = "ip"; break; case master_nodes::new_state::_count: /*leave blank*/ break; } } - void operator()(const tx_extra_tx_secret_key& x) { entry.tx_secret_key = tools::type_to_hex(x.key); } + void operator()(const tx_extra_tx_secret_key& x) { set("tx_secret_key", tools::view_guts(x.key), true); } void operator()(const tx_extra_tx_key_image_proofs& x) { - entry.locked_key_images.reserve(x.proofs.size()); - for (auto& proof : x.proofs) entry.locked_key_images.emplace_back(tools::type_to_hex(proof.key_image)); - } - void operator()(const tx_extra_tx_key_image_unlock& x) { entry.key_image_unlock = tools::type_to_hex(x.key_image); } - void _load_owner(std::optional& entry, const bns::generic_owner& owner) { + std::vector kis; + kis.reserve(x.proofs.size()); + for (auto& proof : x.proofs) + kis.push_back(proof.key_image); + set("locked_key_images", std::move(kis)); + } + void operator()(const tx_extra_tx_key_image_unlock& x) { set("key_image_unlock", x.key_image); } + void _load_owner(json& parent, const std::string& key, const bns::generic_owner& owner) { if (!owner) return; if (owner.type == bns::generic_owner_sig_type::monero) - entry = get_account_address_as_str(nettype, owner.wallet.is_subaddress, owner.wallet.address); + parent[key] = get_account_address_as_str(nettype, owner.wallet.is_subaddress, owner.wallet.address); else if (owner.type == bns::generic_owner_sig_type::ed25519) - entry = tools::type_to_hex(owner.ed25519); + json_binary_proxy{parent[key], json_binary_proxy::fmt::hex} = owner.ed25519; } + //TODO CHECK ON HF void operator()(const tx_extra_beldex_name_system& x) { - auto& bns = entry.bns.emplace(); - bns.version = x.version; + json bns{}; + bns["version"] = x.version; if ((x.is_buying() || x.is_renewing()) && (x.version == 1)) - bns.blocks = bns::expiry_blocks(nettype, x.mapping_years) ; + bns["blocks"] = bns::expiry_blocks(nettype, x.mapping_years) ; if(x.version == 0) switch (x.type) { case bns::mapping_type::belnet: [[fallthrough]]; case bns::mapping_type::belnet_2years: [[fallthrough]]; case bns::mapping_type::belnet_5years: [[fallthrough]]; - case bns::mapping_type::belnet_10years: bns.type = "belnet"; break; + case bns::mapping_type::belnet_10years: bns["type"] = "belnet"; break; - case bns::mapping_type::bchat: bns.type = "bchat"; break; - case bns::mapping_type::wallet: bns.type = "wallet"; break; - case bns::mapping_type::eth_addr: bns.type = "eth_addr"; break; + case bns::mapping_type::bchat: bns["type"] = "bchat"; break; + case bns::mapping_type::wallet: bns["type"] = "wallet"; break; + case bns::mapping_type::eth_addr: bns["type"] = "eth_addr"; break; case bns::mapping_type::update_record_internal: [[fallthrough]]; case bns::mapping_type::_count: break; } if (x.is_buying()) - bns.buy = true; + bns["buy"] = true; else if (x.is_updating()) - bns.update = true; + bns["update"] = true; else if (x.is_renewing()) - bns.renew = true; - bns.name_hash = tools::type_to_hex(x.name_hash); + bns["renew"] = true; + // ✅ Always store name_hash as hex string (RPC-compatible) + bns["name_hash"] = oxenc::to_hex(std::string_view{x.name_hash.data, sizeof(x.name_hash.data)}); if (!x.encrypted_bchat_value.empty()) - bns.value_bchat = oxenc::to_hex(x.encrypted_bchat_value); + bns["value_bchat"] = oxenc::to_hex(x.encrypted_bchat_value); if (!x.encrypted_wallet_value.empty()) - bns.value_wallet = oxenc::to_hex(x.encrypted_wallet_value); + bns["value_wallet"] = oxenc::to_hex(x.encrypted_wallet_value); if (!x.encrypted_belnet_value.empty()) - bns.value_belnet = oxenc::to_hex(x.encrypted_belnet_value); + bns["value_belnet"] = oxenc::to_hex(x.encrypted_belnet_value); if (!x.encrypted_eth_addr_value.empty()) - bns.value_eth_addr = oxenc::to_hex(x.encrypted_eth_addr_value); - _load_owner(bns.owner, x.owner); - _load_owner(bns.backup_owner, x.backup_owner); - } + bns["value_eth_addr"] = oxenc::to_hex(x.encrypted_eth_addr_value); + _load_owner(bns, "owner", x.owner); + _load_owner(bns, "backup_owner", x.backup_owner); + set("bns", std::move(bns)); + } // Ignore these fields: void operator()(const tx_extra_padding&) {} @@ -840,125 +821,191 @@ namespace cryptonote { namespace rpc { }; - bool load_tx_extra_data(GET_TRANSACTIONS::extra_entry& e, const transaction& tx, network_type nettype,uint8_t hf_version) + void load_tx_extra_data(nlohmann::json& e, const transaction& tx, network_type nettype, cryptonote::hf hf_version, bool is_bt) { + e = json::object(); std::vector extras; if (!parse_tx_extra(tx.extra, extras)) - return false; - extra_extractor visitor{e, nettype, hf_version}; + return; + extra_extractor visitor{e, nettype, hf_version, is_bt ? json_binary_proxy::fmt::bt : json_binary_proxy::fmt::hex}; for (const auto& extra : extras) var::visit(visitor, extra); - return true; } } + + struct tx_info { + txpool_tx_meta_t meta; + std::string tx_blob; // Blob containing the transaction data. + bool flash; // True if this is a signed flash transaction + }; + + static std::unordered_map get_pool_txs_impl(cryptonote::core& core) { + auto& bc = core.get_blockchain_storage(); + auto& pool = core.get_pool(); + + std::unordered_map tx_infos; + tx_infos.reserve(bc.get_txpool_tx_count()); + + bc.for_all_txpool_txes( + [&tx_infos, &pool] + (const crypto::hash& txid, const txpool_tx_meta_t& meta, const cryptonote::blobdata* bd) { + transaction tx; + if (!parse_and_validate_tx_from_blob(*bd, tx)) + { + MERROR("Failed to parse tx from txpool"); + // continue + return true; + } + auto& txi = tx_infos[txid]; + txi.meta = meta; + txi.tx_blob = *bd; + tx.set_hash(txid); + txi.flash = pool.has_flash(txid); + return true; + }, true); + + return tx_infos; + } + + static auto pool_locks(cryptonote::core& core) { + auto& pool = core.get_pool(); + std::unique_lock tx_lock{pool, std::defer_lock}; + std::unique_lock bc_lock{core.get_blockchain_storage(), std::defer_lock}; + auto flash_lock = pool.flash_shared_lock(std::defer_lock); + std::lock(tx_lock, bc_lock, flash_lock); + return std::make_tuple(std::move(tx_lock), std::move(bc_lock), std::move(flash_lock)); + } + + static std::pair, tx_memory_pool::key_images_container> get_pool_txs_kis(cryptonote::core& core) { + auto locks = pool_locks(core); + return {get_pool_txs_impl(core), core.get_pool().get_spent_key_images(true)}; + } + + /* + static std::unordered_map get_pool_txs( + cryptonote::core& core, std::function post_process = {}) { + auto locks = pool_locks(core); + return get_pool_txs_impl(core, std::move(post_process)); + } + */ + + static tx_memory_pool::key_images_container get_pool_kis( + cryptonote::core& core, std::function post_process = {}) { + auto locks = pool_locks(core); + return core.get_pool().get_spent_key_images(true); + } //------------------------------------------------------------------------------------------------------------------------------ - GET_TRANSACTIONS::response core_rpc_server::invoke(GET_TRANSACTIONS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_TRANSACTIONS& get, rpc_context context) { - GET_TRANSACTIONS::response res{}; - - PERF_TIMER(on_get_transactions); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + PERF_TIMER(on_get_transactions); + json params{ + {"tx_hashes", json::array()}, + {"memory_pool",get.request.memory_pool}, + {"tx_extra",get.request.tx_extra}, + {"tx_extra_raw",get.request.tx_extra_raw}, + {"data",get.request.data}, + {"split",get.request.split}, + {"prune",get.request.prune} + }; + for (const auto& h: get.request.tx_hashes) + params["tx_hashes"].push_back(tools::type_to_hex(h)); - std::vector vh; - for(const auto& tx_hex_str: req.txs_hashes) - { - if (!tools::hex_to_type(tx_hex_str, vh.emplace_back())) + if (use_bootstrap_daemon_if_necessary(params, get.response)) + return; + + std::unordered_set missed_txs; + using split_tx = std::tuple; + std::vector txs; + if (!get.request.tx_hashes.empty()) { + if (!m_core.get_split_transactions_blobs(get.request.tx_hashes, txs, &missed_txs)) { - res.status = "Failed to parse hex representation of transaction hash"; - return res; + get.response["status"] = STATUS_FAILED; + return; } + LOG_PRINT_L2("Found " << txs.size() << "/" << get.request.tx_hashes.size() << " transactions on the blockchain"); } - std::vector missed_txs; - std::vector> txs; - bool r = m_core.get_split_transactions_blobs(vh, txs, missed_txs); - if(!r) - { - res.status = "Failed"; - return res; - } - LOG_PRINT_L2("Found " << txs.size() << "/" << vh.size() << " transactions on the blockchain"); // try the pool for any missing txes - auto &pool = m_core.get_pool(); - size_t found_in_pool = 0; - std::unordered_map per_tx_pool_tx_info; - if (!missed_txs.empty()) - { - std::vector pool_tx_info; - std::vector pool_key_image_info; - bool r = pool.get_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, nullptr, context.admin); - if(r) - { - // sort to match original request - std::vector> sorted_txs; - unsigned txs_processed = 0; - for (const crypto::hash &h: vh) - { - auto missed_it = std::find(missed_txs.begin(), missed_txs.end(), h); - if (missed_it == missed_txs.end()) - { - if (txs.size() == txs_processed) - { - res.status = "Failed: internal error - txs is empty"; - return res; - } - // core returns the ones it finds in the right order - if (std::get<0>(txs[txs_processed]) != h) - { - res.status = "Failed: tx hash mismatch"; - return res; - } - sorted_txs.push_back(std::move(txs[txs_processed])); - ++txs_processed; - continue; + auto& pool = m_core.get_pool(); + std::unordered_map found_in_pool; + if (!missed_txs.empty() || get.request.memory_pool) + { + try { + auto [pool_txs, pool_kis] = get_pool_txs_kis(m_core); + + auto split_mempool_tx = [](std::pair& info) { + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_from_blob(info.second.tx_blob, tx)) + throw std::runtime_error{"Unable to parse and validate tx from blob"}; + serialization::binary_string_archiver ba; + try { + tx.serialize_base(ba); + } catch (const std::exception& e) { + throw std::runtime_error{"Failed to serialize transaction base: "s + e.what()}; } - const std::string hash_string = tools::type_to_hex(h); - auto ptx_it = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), - [&hash_string](const tx_info &txi) { return hash_string == txi.id_hash; }); - if (ptx_it != pool_tx_info.end()) - { - cryptonote::transaction tx; - if (!cryptonote::parse_and_validate_tx_from_blob(ptx_it->tx_blob, tx)) - { - res.status = "Failed to parse and validate tx from blob"; - return res; - } - serialization::binary_string_archiver ba; - try { - tx.serialize_base(ba); - } catch (const std::exception& e) { - res.status = "Failed to serialize transaction base: "s + e.what(); - return res; + std::string pruned = ba.str(); + std::string pruned2{info.second.tx_blob, pruned.size()}; + return split_tx{info.first, std::move(pruned), get_transaction_prunable_hash(tx), std::move(pruned2)}; + }; + + if (!get.request.tx_hashes.empty()) { + // sort to match original request + std::vector sorted_txs; + unsigned txs_processed = 0; + for (const auto& h: get.request.tx_hashes) { + if (auto missed_it = missed_txs.find(h); missed_it == missed_txs.end()) { + if (txs.size() == txs_processed) { + get.response["status"] = "Failed: internal error - txs is empty"; + return; + } + // core returns the ones it finds in the right order + if (std::get<0>(txs[txs_processed]) != h) { + get.response["status"] = "Failed: internal error - tx hash mismatch"; + return; + } + sorted_txs.push_back(std::move(txs[txs_processed])); + ++txs_processed; + } else if (auto ptx_it = pool_txs.find(h); ptx_it != pool_txs.end()) { + sorted_txs.push_back(split_mempool_tx(*ptx_it)); + missed_txs.erase(missed_it); + found_in_pool.emplace(h, std::move(ptx_it->second)); } - std::string pruned = ba.str(); - std::string pruned2{ptx_it->tx_blob, pruned.size()}; - sorted_txs.emplace_back(h, std::move(pruned), get_transaction_prunable_hash(tx), std::move(pruned2)); - missed_txs.erase(missed_it); - per_tx_pool_tx_info.emplace(h, *ptx_it); - ++found_in_pool; + } + txs = std::move(sorted_txs); + get.response_hex["missed_tx"] = missed_txs; // non-plural here intentional to not break existing clients + LOG_PRINT_L2("Found " << found_in_pool.size() << "/" << get.request.tx_hashes.size() << " transactions in the pool"); + } else if (get.request.memory_pool) { + txs.reserve(pool_txs.size()); + std::transform(pool_txs.begin(), pool_txs.end(), std::back_inserter(txs), split_mempool_tx); + found_in_pool = std::move(pool_txs); + + auto mki = get.response_hex["mempool_key_images"]; + for (auto& [ki, txids] : pool_kis) { + // The *key* is also binary (hex for json): + std::string key{get.is_bt() ? tools::view_guts(ki) : tools::type_to_hex(ki)}; + mki[key] = txids; } } - txs = sorted_txs; + } catch (const std::exception& e) { + MERROR(e.what()); + get.response["status"] = "Failed: "s + e.what(); + return; } - LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool"); } - - res.missed_tx.reserve(missed_txs.size()); - for(const auto& miss_tx: missed_txs) - res.missed_tx.push_back(tools::type_to_hex(miss_tx)); - uint64_t immutable_height = m_core.get_blockchain_storage().get_immutable_height(); auto flash_lock = pool.flash_shared_lock(std::defer_lock); // Defer until/unless we actually need it + + auto& txs_out = get.response["txs"]; + txs_out = json::array(); auto height = m_core.get_current_blockchain_height(); auto net = nettype(); auto hf_version = get_network_version(net, height); - cryptonote::blobdata tx_data; - for(const auto& [tx_hash, unprunable_data, prunable_hash, prunable_data]: txs) + for (const auto& [tx_hash, unprunable_data, prunable_hash, prunable_data]: txs) { - auto& e = res.txs.emplace_back(); - e.tx_hash = tools::type_to_hex(tx_hash); - e.size = unprunable_data.size() + prunable_data.size(); + auto& e = txs_out.emplace_back(); + auto e_bin = get.response_hex["txs"].back(); + e_bin["tx_hash"] = tx_hash; + e["size"] = unprunable_data.size() + prunable_data.size(); // If the transaction was pruned then the prunable part will be empty but the prunable hash // will be non-null. (Some txes, like coinbase txes, are non-prunable and will have empty @@ -966,526 +1013,409 @@ namespace cryptonote { namespace rpc { bool prunable = prunable_hash != crypto::null_hash; bool pruned = prunable && prunable_data.empty(); - if (pruned || (prunable && (req.split || req.prune))) - e.prunable_hash = tools::type_to_hex(prunable_hash); + if (pruned || (prunable && (get.request.split || get.request.prune))) + e_bin["prunable_hash"] = prunable_hash; - if (req.split || req.prune || pruned) + std::string tx_data = unprunable_data; + if (!get.request.prune) + tx_data += prunable_data; + + if (get.request.split || get.request.prune) { - if (req.decode_as_json) - { - tx_data = unprunable_data; - if (!req.prune) - tx_data += prunable_data; - } - else - { - e.pruned_as_hex = oxenc::to_hex(unprunable_data); - if (!req.prune && prunable && !pruned) - e.prunable_as_hex = oxenc::to_hex(prunable_data); - } + e_bin["pruned"] = unprunable_data; + if (get.request.split) + e_bin["prunable"] = prunable_data; } - else - { - // use non-splitted form, leaving pruned_as_hex and prunable_as_hex as empty - tx_data = unprunable_data; - tx_data += prunable_data; - if (!req.decode_as_json) - e.as_hex = oxenc::to_hex(tx_data); + + if (get.request.data) { + if (pruned || get.request.prune) { + if (!e.count("pruned")) + e_bin["pruned"] = unprunable_data; + } else { + e_bin["data"] = tx_data; + } } - cryptonote::transaction t; - if (req.decode_as_json || req.tx_extra || req.stake_info) + cryptonote::transaction tx; + if (get.request.prune || pruned) { - if (req.prune || pruned) + if (!cryptonote::parse_and_validate_tx_base_from_blob(tx_data, tx)) { - if (!cryptonote::parse_and_validate_tx_base_from_blob(tx_data, t)) - { - res.status = "Failed to parse and validate base tx data"; - return res; - } - if (req.decode_as_json) - e.as_json = obj_to_json_str(pruned_transaction{t}); + get.response["status"] = "Failed to parse and validate base tx data"; + return; } - else + } + else + { + if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx)) { - if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, t)) - { - res.status = "Failed to parse and validate tx data"; - return res; - } - if (req.decode_as_json) - e.as_json = obj_to_json_str(t); + get.response["status"] = "Failed to parse and validate tx data"; + return; } - - if (req.tx_extra) - load_tx_extra_data(e.extra.emplace(), t, net, hf_version); } - auto ptx_it = per_tx_pool_tx_info.find(tx_hash); - e.in_pool = ptx_it != per_tx_pool_tx_info.end(); - bool might_be_flash = true; - if (e.in_pool) + std::optional extra; + if (get.request.tx_extra) + load_tx_extra_data(extra.emplace(), tx, nettype(), hf_version, get.is_bt()); + if (get.request.tx_extra_raw) + e_bin["tx_extra_raw"] = std::string_view{reinterpret_cast(tx.extra.data()), tx.extra.size()}; + { - e.block_height = e.block_timestamp = std::numeric_limits::max(); - e.double_spend_seen = ptx_it->second.double_spend_seen; - e.relayed = ptx_it->second.relayed; - e.received_timestamp = ptx_it->second.receive_time; + // Serialize *without* extra because we don't want/care about it in the RPC output (we + // already have all the extra info in more useful form from the other bits of this + // code). + std::vector saved_extra; + std::swap(tx.extra, saved_extra); + + serialization::json_archiver ja{ + get.is_bt() ? json_binary_proxy::fmt::bt : json_binary_proxy::fmt::hex}; + + serialize(ja, tx); + auto dumped = std::move(ja).json(); + e.update(dumped); + std::swap(saved_extra, tx.extra); } + + if (extra) + e["extra"] = std::move(*extra); else - { - e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); - e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); - e.received_timestamp = 0; - e.double_spend_seen = false; - e.relayed = false; - if (e.block_height <= immutable_height) - might_be_flash = false; - } + e.erase("extra"); + auto ptx_it = found_in_pool.find(tx_hash); + bool in_pool = ptx_it != found_in_pool.end(); + auto height = std::numeric_limits::max(); - if (req.stake_info) { - auto hf_version = get_network_version(nettype(), e.in_pool ? m_core.get_current_blockchain_height() : e.block_height); - master_nodes::staking_components sc; - if (master_nodes::tx_get_staking_components_and_amounts(nettype(), hf_version, t, e.block_height, &sc) - && sc.transferred > 0) - e.stake_amount = sc.transferred; + if (uint64_t fee, burned; get_tx_miner_fee(tx, fee, hf_version >= feature::FEE_BURNING, &burned)) { + e["fee"] = fee; + e["burned"] = burned; } - if (might_be_flash) + if (in_pool) { - if (!flash_lock) flash_lock.lock(); - e.flash = pool.has_flash(tx_hash); - } + const auto& meta = ptx_it->second.meta; + e["in_pool"] = true; + e["weight"] = meta.weight; + e["relayed"] = (bool) meta.relayed; + e["received_timestamp"] = meta.receive_time; + e["flash"] = ptx_it->second.flash; + e["double_spend_seen"] = meta.double_spend_seen ? true : false; + if (meta.do_not_relay) e["do_not_relay"] = true; + if (meta.last_relayed_time) e["last_relayed_time"] = meta.last_relayed_time; + if (meta.kept_by_block) e["kept_by_block"] = (bool) meta.kept_by_block; + if (meta.last_failed_id) e_bin["last_failed_block"] = meta.last_failed_id; + if (meta.last_failed_height) e["last_failed_height"] = meta.last_failed_height; + if (meta.max_used_block_id) e_bin["max_used_block"] = meta.max_used_block_id; + if (meta.max_used_block_height) e["max_used_height"] = meta.max_used_block_height; - // output indices too if not in pool - if (!e.in_pool) + } + else { - bool r = m_core.get_tx_outputs_gindexs(tx_hash, e.output_indices); - if (!r) - { - res.status = "Failed"; - return res; + height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); + e["block_height"] = height; + e["block_timestamp"] = m_core.get_blockchain_storage().get_db().get_block_timestamp(height); + if (height > immutable_height) { + if (!flash_lock) flash_lock.lock(); + e["flash"] = pool.has_flash(tx_hash); } } - } - - LOG_PRINT_L2(res.txs.size() << " transactions found, " << res.missed_tx.size() << " not found"); - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - IS_KEY_IMAGE_SPENT::response core_rpc_server::invoke(IS_KEY_IMAGE_SPENT::request&& req, rpc_context context) - { - IS_KEY_IMAGE_SPENT::response res{}; - - PERF_TIMER(on_is_key_image_spent); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - std::vector key_images; - for(const auto& ki_hex_str: req.key_images) - { - if (!tools::hex_to_type(ki_hex_str, key_images.emplace_back())) { - res.status = "Failed to parse hex representation of key image"; - return res; + master_nodes::staking_components sc; + if (master_nodes::tx_get_staking_components_and_amounts(nettype(), hf_version, tx, height, &sc) + && sc.transferred > 0) + e["stake_amount"] = sc.transferred; } - } - std::vector spent_status; - bool r = m_core.are_key_images_spent(key_images, spent_status); - if(!r) - { - res.status = "Failed"; - return res; - } - res.spent_status.clear(); - for (size_t n = 0; n < spent_status.size(); ++n) - res.spent_status.push_back(spent_status[n] ? IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN : IS_KEY_IMAGE_SPENT::UNSPENT); - // check the pool too - std::vector txs; - std::vector ki; - r = m_core.get_pool().get_transactions_and_spent_keys_info(txs, ki, nullptr, context.admin); - if(!r) - { - res.status = "Failed"; - return res; - } - for (std::vector::const_iterator i = ki.begin(); i != ki.end(); ++i) - { - crypto::hash hash; - crypto::key_image spent_key_image; - if (tools::hex_to_type(i->id_hash, hash)) + // output indices too if not in pool + if (!in_pool) { - memcpy(&spent_key_image, &hash, sizeof(hash)); // a bit dodgy, should be other parse functions somewhere - for (size_t n = 0; n < res.spent_status.size(); ++n) + std::vector indices; + if (m_core.get_tx_outputs_gindexs(tx_hash, indices)) + e["output_indices"] = std::move(indices); + else { - if (res.spent_status[n] == IS_KEY_IMAGE_SPENT::UNSPENT) - { - if (key_images[n] == spent_key_image) - { - res.spent_status[n] = IS_KEY_IMAGE_SPENT::SPENT_IN_POOL; - break; - } - } + get.response["status"] = STATUS_FAILED; + return; } } - else - MERROR("Invalid hash: " << i->id_hash); } - res.status = STATUS_OK; - return res; + LOG_PRINT_L2(get.response["txs"].size() << " transactions found, " << missed_txs.size() << " not found"); + get.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - SEND_RAW_TX::response core_rpc_server::invoke(SEND_RAW_TX::request&& req, rpc_context context) + void core_rpc_server::invoke(IS_KEY_IMAGE_SPENT& spent, rpc_context context) { - SEND_RAW_TX::response res{}; - - PERF_TIMER(on_send_raw_tx); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - CHECK_CORE_READY(); + PERF_TIMER(on_is_key_image_spent); + json params{ + {"key_images", json::array()} + }; - if (!oxenc::is_hex(req.tx_as_hex)) { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex); - res.status = "Failed"; - return res; + for (const auto& h: spent.request.key_images) + params["key_images"].push_back(tools::type_to_hex(h)); + + if (use_bootstrap_daemon_if_necessary(params, spent.response)) + return; + + spent.response["status"] = STATUS_FAILED; + + std::vector blockchain_spent; + if (!m_core.are_key_images_spent(spent.request.key_images, blockchain_spent)) + return; + std::optional kis; + auto spent_status = json::array(); + for (size_t n = 0; n < spent.request.key_images.size(); n++) { + if (blockchain_spent[n]) + spent_status.push_back(IS_KEY_IMAGE_SPENT::SPENT::BLOCKCHAIN); + else { + if (!kis) { + try { + kis = get_pool_kis(m_core); + } catch (const std::exception& e) { + MERROR("Failed to get pool key images: " << e.what()); + return; + } + } + spent_status.push_back(kis->count(spent.request.key_images[n]) + ? IS_KEY_IMAGE_SPENT::SPENT::POOL : IS_KEY_IMAGE_SPENT::SPENT::UNSPENT); + } + } + + spent.response["status"] = STATUS_OK; + spent.response["spent_status"] = std::move(spent_status); + } + //------------------------------------------------------------------------------------------------------------------------------ + void core_rpc_server::invoke(SUBMIT_TRANSACTION& tx, rpc_context context) + { + PERF_TIMER(on_submit_transaction); + + json params{ + {"tx_as_hex", oxenc::to_hex(tx.request.tx)}, + {"flash", tx.request.flash} + }; + if (use_bootstrap_daemon_if_necessary(params, tx.response)) + return; + + if (!check_core_ready()) { + tx.response["status"] = STATUS_BUSY; + return; } - auto tx_blob = oxenc::from_hex(req.tx_as_hex); - if (req.flash) + if (tx.request.flash) { - auto future = m_core.handle_flash_tx(tx_blob); + auto future = m_core.handle_flash_tx(tx.request.tx); + // FIXME: blocking here for 10s is nasty; we need to stash this request and come back to it + // when the flash tx result comes back, and wait for longer (maybe 30s). + // + // FIXME 2: on timeout, we should check the mempool to see if it arrived that way so that we + // return success if it got out to the network, even if we didn't get the flash quorum reply + // for some reason. auto status = future.wait_for(10s); if (status != std::future_status::ready) { - res.status = "Failed"; - res.reason = "Flash quorum timeout"; - res.flash_status = flash_result::timeout; - return res; + tx.response["status"] = STATUS_FAILED; + tx.response["reason"] = "Flash quorum timeout"; + tx.response["flash_status"] = flash_result::timeout; + return; } try { auto result = future.get(); - res.flash_status = result.first; + tx.response["flash_status"] = result.first; if (result.first == flash_result::accepted) { - res.status = STATUS_OK; + tx.response["status"] = STATUS_OK; } else { - res.status = "Failed"; - res.reason = !result.second.empty() ? result.second : result.first == flash_result::timeout ? "Flash quorum timeout" : "Transaction rejected by flash quorum"; + tx.response["status"] = STATUS_FAILED; + tx.response["reason"] = !result.second.empty() ? result.second : result.first == flash_result::timeout ? "Flash quorum timeout" : "Transaction rejected by flash quorum"; } } catch (const std::exception &e) { - res.flash_status = flash_result::rejected; - res.status = "Failed"; - res.reason = std::string{"Transaction failed: "} + e.what(); + tx.response["flash_status"] = flash_result::rejected; + tx.response["status"] = STATUS_FAILED; + tx.response["reason"] = "Transaction failed: "s + e.what(); } - return res; + return; } tx_verification_context tvc{}; - if(!m_core.handle_incoming_tx(tx_blob, tvc, tx_pool_options::new_tx(req.do_not_relay)) || tvc.m_verifivation_failed) - { - const vote_verification_context &vvc = tvc.m_vote_ctx; - res.status = "Failed"; - std::string reason = print_tx_verification_context (tvc); - reason += print_vote_verification_context(vvc); - res.tvc = tvc; - const std::string punctuation = res.reason.empty() ? "" : ": "; - if (tvc.m_verifivation_failed) - { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << reason); - } - else - { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << reason); - } - return res; - } - - if(!tvc.m_should_be_relayed) + if (!m_core.handle_incoming_tx(tx.request.tx, tvc, tx_pool_options::new_tx()) || tvc.m_verifivation_failed || !tvc.m_should_be_relayed) { - LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); - res.reason = "Not relayed"; - res.not_relayed = true; - res.status = STATUS_OK; - return res; + tx.response["status"] = STATUS_FAILED; + const vote_verification_context& vvc = tvc.m_vote_ctx; + std::string reason = print_tx_verification_context(tvc); + reason += print_vote_verification_context(vvc); + LOG_PRINT_L0("[on_submit_transaction]: " << (tvc.m_verifivation_failed ? "tx verification failed" : "Failed to process tx") << reason); + tx.response["reason"] = std::move(reason); + tx.response["reason_codes"] = tx_verification_failure_codes(tvc); + return; } + // Why is is the RPC handler's responsibility to tell the p2p protocol to relay a transaction?! NOTIFY_NEW_TRANSACTIONS::request r{}; - r.txs.push_back(tx_blob); + r.txs.push_back(std::move(tx.request.tx)); cryptonote_connection_context fake_context{}; m_core.get_protocol()->relay_transactions(r, fake_context); - //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes - res.status = STATUS_OK; - return res; + tx.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - START_MINING::response core_rpc_server::invoke(START_MINING::request&& req, rpc_context context) + void core_rpc_server::invoke(START_MINING& start_mining, rpc_context context) { - START_MINING::response res{}; - PERF_TIMER(on_start_mining); - CHECK_CORE_READY(); + + if(!check_core_ready()){ + start_mining.response["status"] = STATUS_BUSY; + return; + } + cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_core.get_nettype(), req.miner_address)) - { - res.status = "Failed, wrong address"; - LOG_PRINT_L0(res.status); - return res; + if(!get_account_address_from_str(info, m_core.get_nettype(), start_mining.request.miner_address)){ + start_mining.response["status"] = "Failed, invalid address"; + LOG_PRINT_L0(start_mining.response["status"]); + return; } if (info.is_subaddress) { - res.status = "Mining to subaddress isn't supported yet"; - LOG_PRINT_L0(res.status); - return res; + start_mining.response["status"] = "Mining to subaddress isn't supported yet"; + LOG_PRINT_L0(start_mining.response["status"]); + return; } - unsigned int concurrency_count = std::thread::hardware_concurrency() * 4; + int max_concurrency_count = std::thread::hardware_concurrency() * 4; // if we couldn't detect threads, set it to a ridiculously high number - if(concurrency_count == 0) - { - concurrency_count = 257; - } + if(max_concurrency_count == 0) + max_concurrency_count = 257; // if there are more threads requested than the hardware supports // then we fail and log that. - if(req.threads_count > concurrency_count) - { - res.status = "Failed, too many threads relative to CPU cores."; - LOG_PRINT_L0(res.status); - return res; + if (start_mining.request.threads_count > max_concurrency_count) { + start_mining.response["status"] = "Failed, too many threads relative to CPU cores."; + LOG_PRINT_L0(start_mining.response["status"]); + return; } - cryptonote::miner &miner= m_core.get_miner(); + auto& miner = m_core.get_miner(); if (miner.is_mining()) { - res.status = "Already mining"; - return res; + start_mining.response["status"] = "Already mining"; + return; } - if(!miner.start(info.address, static_cast(req.threads_count), req.num_blocks)) + + if(!miner.start(info.address, start_mining.request.threads_count, start_mining.request.num_blocks, start_mining.request.slow_mining)) { - res.status = "Failed, mining not started"; - LOG_PRINT_L0(res.status); - return res; + start_mining.response["status"] = "Failed, mining not started"; + LOG_PRINT_L0(start_mining.response["status"]); + return; } - res.status = STATUS_OK; - return res; + + start_mining.response["status"] = STATUS_OK; + LOG_PRINT_L0(start_mining.response["status"]); + return; } //------------------------------------------------------------------------------------------------------------------------------ - STOP_MINING::response core_rpc_server::invoke(STOP_MINING::request&& req, rpc_context context) + void core_rpc_server::invoke(STOP_MINING& stop_mining, rpc_context context) { - STOP_MINING::response res{}; - PERF_TIMER(on_stop_mining); cryptonote::miner &miner= m_core.get_miner(); if(!miner.is_mining()) { - res.status = "Mining never started"; - LOG_PRINT_L0(res.status); - return res; + stop_mining.response["status"] = "Mining never started"; + LOG_PRINT_L0(stop_mining.response["status"]); + return; } if(!miner.stop()) { - res.status = "Failed, mining not stopped"; - LOG_PRINT_L0(res.status); - return res; + stop_mining.response["status"] = "Failed, mining not stopped"; + LOG_PRINT_L0(stop_mining.response["status"]); + return; } - res.status = STATUS_OK; - return res; + stop_mining.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - MINING_STATUS::response core_rpc_server::invoke(MINING_STATUS::request&& req, rpc_context context) + void core_rpc_server::invoke(MINING_STATUS& mining_status, rpc_context context) { - MINING_STATUS::response res{}; - PERF_TIMER(on_mining_status); const miner& lMiner = m_core.get_miner(); - res.active = lMiner.is_mining(); - res.block_target = tools::to_seconds(TARGET_BLOCK_TIME_OLD); // old_block_time - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(false /*POS*/); + mining_status.response["active"] = lMiner.is_mining(); + mining_status.response["block_target"] = tools::to_seconds(old::TARGET_BLOCK_TIME_12); // old_block_time + mining_status.response["difficulty"] = m_core.get_blockchain_storage().get_difficulty_for_next_block(false /*POS*/); if ( lMiner.is_mining() ) { - res.speed = lMiner.get_speed(); - res.threads_count = lMiner.get_threads_count(); - res.block_reward = lMiner.get_block_reward(); + mining_status.response["speed"] = std::lround(lMiner.get_speed()); + mining_status.response["threads_count"] = lMiner.get_threads_count(); + mining_status.response["block_reward"] = lMiner.get_block_reward(); } const account_public_address& lMiningAdr = lMiner.get_mining_address(); if (lMiner.is_mining()) - res.address = get_account_address_as_str(nettype(), false, lMiningAdr); - const uint8_t major_version = m_core.get_blockchain_storage().get_network_version(); + mining_status.response["address"] = get_account_address_as_str(nettype(), false, lMiningAdr); + const auto major_version = m_core.get_blockchain_storage().get_network_version(); - res.pow_algorithm = - major_version >= network_version_13_checkpointing ? "RandomX (BELDEX variant)" : - major_version == network_version_11_infinite_staking ? "Cryptonight Turtle Light (Variant 2)" : + mining_status.response["pow_algorithm"] = + major_version >= hf::hf13_checkpointing ? "RandomX (BELDEX variant)" : + major_version == hf::hf11_infinite_staking ? "Cryptonight Turtle Light (Variant 2)" : "Cryptonight Heavy (Variant 2)"; - res.status = STATUS_OK; - return res; + mining_status.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - SAVE_BC::response core_rpc_server::invoke(SAVE_BC::request&& req, rpc_context context) + void core_rpc_server::invoke(SAVE_BC& save_bc, rpc_context context) { - SAVE_BC::response res{}; - PERF_TIMER(on_save_bc); if( !m_core.get_blockchain_storage().store_blockchain() ) { - res.status = "Error while storing blockchain"; - return res; + save_bc.response["status"] = "Error while storing blockchain"; + LOG_PRINT_L0(save_bc.response["status"]); + return; } - res.status = STATUS_OK; - return res; + save_bc.response["status"] = STATUS_OK; + } + + static nlohmann::json json_peer_info(const nodetool::peerlist_entry& peer) { + auto addr_type = peer.adr.get_type_id(); + nlohmann::json p{ + {"id", peer.id}, + {"host", peer.adr.host_str()}, + {"port", peer.adr.port()}, + {"last_seen", peer.last_seen} + }; + if (peer.pruning_seed) p["pruning_seed"] = peer.pruning_seed; + return p; } + //------------------------------------------------------------------------------------------------------------------------------ - GET_PEER_LIST::response core_rpc_server::invoke(GET_PEER_LIST::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_PEER_LIST& pl, rpc_context context) { - GET_PEER_LIST::response res{}; - PERF_TIMER(on_get_peer_list); - std::vector white_list; - std::vector gray_list; + std::vector white_list, gray_list; - if (req.public_only) - { + if (pl.request.public_only) m_p2p.get_public_peerlist(gray_list, white_list); - } else - { m_p2p.get_peerlist(gray_list, white_list); - } - - for (auto & entry : white_list) - { - if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) - res.white_list.emplace_back(entry.id, entry.adr.as().ip(), - entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) - res.white_list.emplace_back(entry.id, entry.adr.as().host_str(), - entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - else - res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - } - - for (auto & entry : gray_list) - { - if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) - res.gray_list.emplace_back(entry.id, entry.adr.as().ip(), - entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) - res.gray_list.emplace_back(entry.id, entry.adr.as().host_str(), - entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - else - res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port); - } - - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - GET_PUBLIC_NODES::response core_rpc_server::invoke(GET_PUBLIC_NODES::request&& req, rpc_context context) - { - PERF_TIMER(on_get_public_nodes); - - GET_PEER_LIST::response peer_list_res = invoke(GET_PEER_LIST::request{}, context); - GET_PUBLIC_NODES::response res{}; - res.status = std::move(peer_list_res.status); - - const auto collect = [](const std::vector &peer_list, std::vector &public_nodes) - { - for (const auto &entry : peer_list) - { - if (entry.rpc_port != 0) - { - public_nodes.emplace_back(entry); - } - } - }; - if (req.white) - { - collect(peer_list_res.white_list, res.white); - } - if (req.gray) - { - collect(peer_list_res.gray_list, res.gray); - } + std::transform(white_list.begin(), white_list.end(), std::back_inserter(pl.response["white_list"]), json_peer_info); + std::transform(gray_list.begin(), gray_list.end(), std::back_inserter(pl.response["gray_list"]), json_peer_info); - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - SET_LOG_HASH_RATE::response core_rpc_server::invoke(SET_LOG_HASH_RATE::request&& req, rpc_context context) - { - SET_LOG_HASH_RATE::response res{}; - - PERF_TIMER(on_set_log_hash_rate); - if(m_core.get_miner().is_mining()) - { - m_core.get_miner().do_print_hashrate(req.visible); - res.status = STATUS_OK; - } - else - { - res.status = STATUS_NOT_MINING; - } - return res; + pl.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - SET_LOG_LEVEL::response core_rpc_server::invoke(SET_LOG_LEVEL::request&& req, rpc_context context) + void core_rpc_server::invoke(SET_LOG_LEVEL& set_log_level, rpc_context context) { - SET_LOG_LEVEL::response res{}; - PERF_TIMER(on_set_log_level); - if (req.level < 0 || req.level > 4) + if (set_log_level.request.level < 0 || set_log_level.request.level > 4) { - res.status = "Error: log level not valid"; - return res; + set_log_level.response["status"] = "Error: log level not valid"; + return; } - mlog_set_log_level(req.level); - res.status = STATUS_OK; - return res; + mlog_set_log_level(set_log_level.request.level); + set_log_level.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - SET_LOG_CATEGORIES::response core_rpc_server::invoke(SET_LOG_CATEGORIES::request&& req, rpc_context context) + void core_rpc_server::invoke(SET_LOG_CATEGORIES& set_log_categories, rpc_context context) { - SET_LOG_CATEGORIES::response res{}; - PERF_TIMER(on_set_log_categories); - mlog_set_log(req.categories.c_str()); - res.categories = mlog_get_categories(); - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - GET_TRANSACTION_POOL::response core_rpc_server::invoke(GET_TRANSACTION_POOL::request&& req, rpc_context context) - { - GET_TRANSACTION_POOL::response res{}; - - PERF_TIMER(on_get_transaction_pool); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - - std::function load_extra; - if (req.tx_extra || req.stake_info) - load_extra = [this, &req, net=nettype()](const transaction& tx, tx_info& txi) { - auto height = m_core.get_current_blockchain_height(); - auto hf_version = get_network_version(net, height); - if (req.tx_extra) - load_tx_extra_data(txi.extra.emplace(), tx, net, hf_version); - if (req.stake_info) { - - master_nodes::staking_components sc; - if (master_nodes::tx_get_staking_components_and_amounts(net, hf_version, tx, height, &sc) - && sc.transferred > 0) - txi.stake_amount = sc.transferred; - } - }; - - m_core.get_pool().get_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, load_extra, context.admin); - for (tx_info& txi : res.transactions) - txi.tx_blob = oxenc::to_hex(txi.tx_blob); - res.status = STATUS_OK; - return res; + mlog_set_log(set_log_categories.request.categories.c_str()); + set_log_categories.response["categories"] = mlog_get_categories(); + set_log_categories.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ GET_TRANSACTION_POOL_HASHES_BIN::response core_rpc_server::invoke(GET_TRANSACTION_POOL_HASHES_BIN::request&& req, rpc_context context) @@ -1504,69 +1434,87 @@ namespace cryptonote { namespace rpc { return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_TRANSACTION_POOL_HASHES::response core_rpc_server::invoke(GET_TRANSACTION_POOL_HASHES::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_TRANSACTION_POOL_HASHES& get_transaction_pool_hashes, rpc_context context) { - GET_TRANSACTION_POOL_HASHES::response res{}; - PERF_TIMER(on_get_transaction_pool_hashes); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + if (use_bootstrap_daemon_if_necessary({}, get_transaction_pool_hashes.response)) + return; std::vector tx_hashes; m_core.get_pool().get_transaction_hashes(tx_hashes, context.admin); - res.tx_hashes.reserve(tx_hashes.size()); - for (const crypto::hash &tx_hash: tx_hashes) - res.tx_hashes.push_back(tools::type_to_hex(tx_hash)); - res.status = STATUS_OK; - return res; + get_transaction_pool_hashes.response_hex["tx_hashes"] = tx_hashes; + get_transaction_pool_hashes.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_TRANSACTION_POOL_STATS::response core_rpc_server::invoke(GET_TRANSACTION_POOL_STATS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_TRANSACTION_POOL_STATS& stats, rpc_context context) { - GET_TRANSACTION_POOL_STATS::response res{}; - PERF_TIMER(on_get_transaction_pool_stats); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + json params{ + {"include_unrelayed", stats.request.include_unrelayed} + }; + if (use_bootstrap_daemon_if_necessary(params, stats.response)) + return; + + auto txpool = m_core.get_pool().get_transaction_stats(stats.request.include_unrelayed); + json pool_stats{ + {"bytes_total", txpool.bytes_total}, + {"bytes_min", txpool.bytes_min}, + {"bytes_max", txpool.bytes_max}, + {"bytes_med", txpool.bytes_med}, + {"fee_total", txpool.fee_total}, + {"oldest", txpool.oldest}, + {"txs_total", txpool.txs_total}, + {"num_failing", txpool.num_failing}, + {"num_10m", txpool.num_10m}, + {"num_not_relayed", txpool.num_not_relayed}, + {"histo", std::move(txpool.histo)}, + {"num_double_spends", txpool.num_double_spends}}; + + if (txpool.histo_98pc) + pool_stats["histo_98pc"] = txpool.histo_98pc; + else + pool_stats["histo_max"] = std::time(nullptr) - txpool.oldest; + + stats.response["pool_stats"] = std::move(pool_stats); + stats.response["status"] = STATUS_OK; - m_core.get_pool().get_transaction_stats(res.pool_stats, context.admin); - res.status = STATUS_OK; - return res; } //------------------------------------------------------------------------------------------------------------------------------ - SET_BOOTSTRAP_DAEMON::response core_rpc_server::invoke(SET_BOOTSTRAP_DAEMON::request&& req, rpc_context context) - { + void core_rpc_server::invoke(SET_BOOTSTRAP_DAEMON& set_bootstrap, rpc_context context){ PERF_TIMER(on_set_bootstrap_daemon); + const auto& req = set_bootstrap.request; if (!set_bootstrap_daemon(req.address, req.username, req.password)) - throw rpc_error{ERROR_WRONG_PARAM, "Failed to set bootstrap daemon to address = " + req.address}; + { + // If setting failed, throw an RPC error + throw rpc_error{ERROR_WRONG_PARAM, + "Failed to set bootstrap daemon to address = " + req.address}; + } - SET_BOOTSTRAP_DAEMON::response res{}; - res.status = STATUS_OK; - return res; + // On success, populate the response + set_bootstrap.response["status"] = STATUS_OK; + set_bootstrap.response["address"] = req.address.empty() ? "none" : req.address; } //------------------------------------------------------------------------------------------------------------------------------ - STOP_DAEMON::response core_rpc_server::invoke(STOP_DAEMON::request&& req, rpc_context context) - { - STOP_DAEMON::response res{}; + void core_rpc_server::invoke(STOP_DAEMON& stop_daemon, rpc_context context) + { PERF_TIMER(on_stop_daemon); m_p2p.send_stop_signal(); - res.status = STATUS_OK; - return res; + stop_daemon.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ // // Beldex // - GET_OUTPUT_BLACKLIST::response core_rpc_server::invoke(GET_OUTPUT_BLACKLIST::request&& req, rpc_context context) + GET_OUTPUT_BLACKLIST_BIN::response core_rpc_server::invoke(GET_OUTPUT_BLACKLIST_BIN::request&& req, rpc_context context) { - GET_OUTPUT_BLACKLIST::response res{}; + GET_OUTPUT_BLACKLIST_BIN::response res{}; PERF_TIMER(on_get_output_blacklist_bin); - if (use_bootstrap_daemon_if_necessary(req, res)) + if (use_bootstrap_daemon_if_necessary(req, res)) return res; try @@ -1583,228 +1531,43 @@ namespace cryptonote { namespace rpc { return res; } //------------------------------------------------------------------------------------------------------------------------------ - GETBLOCKCOUNT::response core_rpc_server::invoke(GETBLOCKCOUNT::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK_COUNT& getblockcount, rpc_context context) { - GETBLOCKCOUNT::response res{}; - PERF_TIMER(on_getblockcount); - { - std::shared_lock lock{m_bootstrap_daemon_mutex}; - if (m_should_use_bootstrap_daemon) - { - res.status = "This command is unsupported for bootstrap daemon"; - return res; - } - } - res.count = m_core.get_current_blockchain_height(); - res.status = STATUS_OK; - return res; + // { + // std::shared_lock lock{m_bootstrap_daemon_mutex}; + // if (m_should_use_bootstrap_daemon) + // { + // getblockcount.response["status"] = "This command is unsupported for bootstrap daemon"; + // return; + // } + // } + getblockcount.response["count"] = m_core.get_current_blockchain_height(); + getblockcount.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GETBLOCKHASH::response core_rpc_server::invoke(GETBLOCKHASH::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK_HASH& getblockhash, rpc_context context) { - GETBLOCKHASH::response res{}; - PERF_TIMER(on_getblockhash); - { - std::shared_lock lock{m_bootstrap_daemon_mutex}; - if (m_should_use_bootstrap_daemon) - { - res = "This command is unsupported for bootstrap daemon"; - return res; - } - } - if(req.height.size() != 1) - throw rpc_error{ERROR_WRONG_PARAM, "Wrong parameters, expected height"}; - - uint64_t h = req.height[0]; - if(m_core.get_current_blockchain_height() <= h) - throw rpc_error{ERROR_TOO_BIG_HEIGHT, - "Requested block height: " + std::to_string(h) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1)}; - - res = tools::type_to_hex(m_core.get_block_id_by_height(h)); - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - GETBLOCKTEMPLATE::response core_rpc_server::invoke(GETBLOCKTEMPLATE::request&& req, rpc_context context) - { - GETBLOCKTEMPLATE::response res{}; - - PERF_TIMER(on_getblocktemplate); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - - if(!check_core_ready()) - throw rpc_error{ERROR_CORE_BUSY, "Core is busy"}; - - if(req.reserve_size > 255) - throw rpc_error{ERROR_TOO_BIG_RESERVE_SIZE, "Too big reserved size, maximum 255"}; - - if(req.reserve_size && !req.extra_nonce.empty()) - throw rpc_error{ERROR_WRONG_PARAM, "Cannot specify both a reserve_size and an extra_nonce"}; - - if(req.extra_nonce.size() > 510) - throw rpc_error{ERROR_TOO_BIG_RESERVE_SIZE, "Too big extra_nonce size, maximum 510 hex chars"}; - - cryptonote::address_parse_info info; - - if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_core.get_nettype(), req.wallet_address)) - throw rpc_error{ERROR_WRONG_WALLET_ADDRESS, "Failed to parse wallet address"}; - if (info.is_subaddress) - throw rpc_error{ERROR_MINING_TO_SUBADDRESS, "Mining to subaddress is not supported yet"}; - - block b; - cryptonote::blobdata blob_reserve; - if(!req.extra_nonce.empty()) - { - if(!oxenc::is_hex(req.extra_nonce)) - throw rpc_error{ERROR_WRONG_PARAM, "Parameter extra_nonce should be a hex string"}; - blob_reserve = oxenc::from_hex(req.extra_nonce); - } - else - blob_reserve.resize(req.reserve_size, 0); - cryptonote::difficulty_type diff; - crypto::hash prev_block; - if (!req.prev_block.empty()) - { - if (!tools::hex_to_type(req.prev_block, prev_block)) - throw rpc_error{ERROR_INTERNAL, "Invalid prev_block"}; - } - if(!m_core.create_miner_block_template(b, req.prev_block.empty() ? NULL : &prev_block, info.address, diff, res.height, res.expected_reward, blob_reserve)) - { - LOG_ERROR("Failed to create block template"); - throw rpc_error{ERROR_INTERNAL, "Internal error: failed to create block template"}; - } - - if (b.major_version >= network_version_13_checkpointing) - { - uint64_t seed_height, next_height; - crypto::hash seed_hash; - crypto::rx_seedheights(res.height, &seed_height, &next_height); - seed_hash = m_core.get_block_id_by_height(seed_height); - res.seed_hash = tools::type_to_hex(seed_hash); - if (next_height != seed_height) { - seed_hash = m_core.get_block_id_by_height(next_height); - res.next_seed_hash = tools::type_to_hex(seed_hash); - } - } - res.difficulty = diff; - - blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); - if(tx_pub_key == crypto::null_pkey) - { - LOG_ERROR("Failed to get tx pub key in coinbase extra"); - throw rpc_error{ERROR_INTERNAL, "Internal error: failed to create block template"}; - } - res.reserved_offset = block_blob.find(tx_pub_key.data, 0, sizeof(tx_pub_key.data)); - if (res.reserved_offset == block_blob.npos) - { - LOG_ERROR("Failed to find tx pub key in blockblob"); - throw rpc_error{ERROR_INTERNAL, "Internal error: failed to create block template"}; - } - if (req.reserve_size) - res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - else - res.reserved_offset = 0; - if(res.reserved_offset + req.reserve_size > block_blob.size()) - { - LOG_ERROR("Failed to calculate offset for "); - throw rpc_error{ERROR_INTERNAL, "Internal error: failed to create block template"}; - } - blobdata hashing_blob = get_block_hashing_blob(b); - res.prev_hash = tools::type_to_hex(b.prev_id); - res.blocktemplate_blob = oxenc::to_hex(block_blob); - res.blockhashing_blob = oxenc::to_hex(hashing_blob); - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - SUBMITBLOCK::response core_rpc_server::invoke(SUBMITBLOCK::request&& req, rpc_context context) - { - SUBMITBLOCK::response res{}; - - PERF_TIMER(on_submitblock); - { - std::shared_lock lock{m_bootstrap_daemon_mutex}; - if (m_should_use_bootstrap_daemon) - { - res.status = "This command is unsupported for bootstrap daemon"; - return res; - } - } - CHECK_CORE_READY(); - if(req.blob.size()!=1) - throw rpc_error{ERROR_WRONG_PARAM, "Wrong param"}; - if(!oxenc::is_hex(req.blob[0])) - throw rpc_error{ERROR_WRONG_BLOCKBLOB, "Wrong block blob"}; - auto blockblob =oxenc::from_hex(req.blob[0]); - // Fixing of high orphan issue for most pools - // Thanks Boolberry! - block b; - if(!parse_and_validate_block_from_blob(blockblob, b)) - throw rpc_error{ERROR_WRONG_BLOCKBLOB, "Wrong block blob"}; - - // Fix from Boolberry neglects to check block - // size, do that with the function below - if(!m_core.check_incoming_block_size(blockblob)) - throw rpc_error{ERROR_WRONG_BLOCKBLOB_SIZE, "Block blob size is too big, rejecting block"}; - - block_verification_context bvc; - if(!m_core.handle_block_found(b, bvc)) - throw rpc_error{ERROR_BLOCK_NOT_ACCEPTED, "Block not accepted"}; - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - GENERATEBLOCKS::response core_rpc_server::invoke(GENERATEBLOCKS::request&& req, rpc_context context) - { - GENERATEBLOCKS::response res{}; - - PERF_TIMER(on_generateblocks); - - CHECK_CORE_READY(); - - res.status = STATUS_OK; - - if(m_core.get_nettype() != FAKECHAIN) - throw rpc_error{ERROR_REGTEST_REQUIRED, "Regtest required when generating blocks"}; - - SUBMITBLOCK::request submit_req{}; - submit_req.blob.emplace_back(); // string vector containing exactly one block blob - - res.height = m_core.get_blockchain_storage().get_current_blockchain_height(); - - for(size_t i = 0; i < req.amount_of_blocks; i++) - { - GETBLOCKTEMPLATE::request template_req{}; - template_req.reserve_size = 1; - template_req.wallet_address = req.wallet_address; - template_req.prev_block = i == 0 ? req.prev_block : res.blocks.back(); - auto template_res = invoke(std::move(template_req), context); - res.status = template_res.status; - - blobdata blockblob; - if(!oxenc::is_hex(template_res.blocktemplate_blob)) - throw rpc_error{ERROR_WRONG_BLOCKBLOB, "Wrong block blob"}; - block b; - if(!parse_and_validate_block_from_blob(oxenc::from_hex(template_res.blocktemplate_blob), b)) - throw rpc_error{ERROR_WRONG_BLOCKBLOB, "Wrong block blob"}; - b.nonce = req.starting_nonce; - miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { - hash = cryptonote::get_block_longhash_w_blockchain(cryptonote::FAKECHAIN, &(m_core.get_blockchain_storage()), b, height, threads); - return true; - }, b, template_res.difficulty, template_res.height); - - submit_req.blob[0] = oxenc::to_hex(block_to_blob(b)); - auto submit_res = invoke(std::move(submit_req), context); - res.status = submit_res.status; + // { + // std::shared_lock lock{m_bootstrap_daemon_mutex}; + // if (m_should_use_bootstrap_daemon) + // { + // getblockhash.response["status"] = "This command is unsupported for bootstrap daemon"; + // return; + // } + // } + + auto curr_height = m_core.get_current_blockchain_height(); + for (auto h : getblockhash.request.heights) { + if (h >= curr_height) + throw rpc_error{ERROR_TOO_BIG_HEIGHT, + "Requested block height: " + tools::int_to_string(h) + " greater than current top block height: " + tools::int_to_string(curr_height - 1)}; - res.blocks.push_back(tools::type_to_hex(get_block_hash(b))); - res.height = template_res.height; + getblockhash.response_hex[tools::int_to_string(h)] = m_core.get_block_id_by_height(h); } - - return res; + getblockhash.response["height"] = curr_height; + getblockhash.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ uint64_t core_rpc_server::get_block_reward(const block& blk) @@ -1817,10 +1580,17 @@ namespace cryptonote { namespace rpc { return reward; } //------------------------------------------------------------------------------------------------------------------------------ - void core_rpc_server::fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash, bool get_tx_hashes) + void core_rpc_server::fill_block_header_response( + const block& blk, + bool orphan_status, + uint64_t height, + const crypto::hash& hash, + block_header_response& response, + bool fill_pow_hash, + bool get_tx_hashes) { PERF_TIMER(fill_block_header_response); - response.major_version = blk.major_version; + response.major_version = static_cast(blk.major_version); response.minor_version = blk.minor_version; response.timestamp = blk.timestamp; response.prev_hash = tools::type_to_hex(blk.prev_id); @@ -1834,11 +1604,17 @@ namespace cryptonote { namespace rpc { response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.reward = get_block_reward(blk); if(blk.nonce != 0) - response.miner_reward = blk.miner_tx.vout[0].amount; + response.coinbase_payouts = blk.miner_tx.vout[0].amount; response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); if (fill_pow_hash) - response.pow_hash = tools::type_to_hex(get_block_longhash_w_blockchain(m_core.get_nettype(), &(m_core.get_blockchain_storage()), blk, height, 0)); + response.pow_hash = tools::type_to_hex( + get_block_longhash_w_blockchain( + m_core.get_nettype(), + &m_core.get_blockchain_storage(), + blk, + height, + 0)); response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height); response.miner_tx_hash = tools::type_to_hex(cryptonote::get_transaction_hash(blk.miner_tx)); response.master_node_winner = tools::type_to_hex(cryptonote::get_master_node_winner_from_tx_extra(blk.miner_tx.extra)); @@ -1910,6 +1686,23 @@ namespace cryptonote { namespace rpc { // // The RPC type must have a `bool untrusted` member. // + template + bool core_rpc_server::use_bootstrap_daemon_if_necessary(const nlohmann::json& req, nlohmann::json& res) + { + res["untrusted"] = false; // If compilation fails here then the type being instantiated doesn't support using a bootstrap daemon + + auto bs_lock = should_bootstrap_lock(); + if (!bs_lock) + return false; // No bootstrap daemon available + + if (!m_bootstrap_daemon->invoke_json(req, res)) + throw std::runtime_error{"Bootstrap request failed"}; + + m_was_bootstrap_ever_used = true; + res["untrusted"] = true; + return true; + } + template bool core_rpc_server::use_bootstrap_daemon_if_necessary(const typename RPC::request& req, typename RPC::response& res) { @@ -1928,36 +1721,56 @@ namespace cryptonote { namespace rpc { return true; } //------------------------------------------------------------------------------------------------------------------------------ - GET_LAST_BLOCK_HEADER::response core_rpc_server::invoke(GET_LAST_BLOCK_HEADER::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_LAST_BLOCK_HEADER& get_last_block_header, rpc_context context) { - GET_LAST_BLOCK_HEADER::response res{}; - PERF_TIMER(on_get_last_block_header); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - CHECK_CORE_READY(); - uint64_t last_block_height; - crypto::hash last_block_hash; - m_core.get_blockchain_top(last_block_height, last_block_hash); + json params{ + {"fill_pow_hash", get_last_block_header.request.fill_pow_hash}, + {"get_tx_hashes", get_last_block_header.request.get_tx_hashes} + }; + + if (use_bootstrap_daemon_if_necessary(params, get_last_block_header.response)) + return; + + if(!check_core_ready()) + { + get_last_block_header.response["status"] = STATUS_BUSY; + return; + } + + auto [last_block_height, last_block_hash] = m_core.get_blockchain_top(); block last_block; bool have_last_block = m_core.get_block_by_height(last_block_height, last_block); if (!have_last_block) throw rpc_error{ERROR_INTERNAL, "Internal error: can't get last block."}; - fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header, req.fill_pow_hash && context.admin, req.get_tx_hashes); - res.status = STATUS_OK; - return res; + + block_header_response header{}; + fill_block_header_response(last_block, false, last_block_height, last_block_hash, header, get_last_block_header.request.fill_pow_hash && context.admin, get_last_block_header.request.get_tx_hashes); + + nlohmann::json header_as_json = header; + get_last_block_header.response["block_header"] = header_as_json; + get_last_block_header.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCK_HEADER_BY_HASH::response core_rpc_server::invoke(GET_BLOCK_HEADER_BY_HASH::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK_HEADER_BY_HASH& get_block_header_by_hash, rpc_context context) { - GET_BLOCK_HEADER_BY_HASH::response res{}; - PERF_TIMER(on_get_block_header_by_hash); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + + json params{ + {"hash",get_block_header_by_hash.request.hash}, + {"hashes", json::array()}, + {"fill_pow_hash",get_block_header_by_hash.request.fill_pow_hash}, + {"get_tx_hashes",get_block_header_by_hash.request.get_tx_hashes} + }; + for (const auto& h: get_block_header_by_hash.request.hashes) + params["hashes"].push_back(h); - auto get = [this, &req, admin=context.admin](const std::string &hash, block_header_response &block_header) { + if (use_bootstrap_daemon_if_necessary(params, get_block_header_by_hash.response)) + return; + + auto get = [this, &get_block_header_by_hash, admin=context.admin](const std::string &hash, block_header_response &block_header) { crypto::hash block_hash; if (!tools::hex_to_type(hash, block_hash)) throw rpc_error{ERROR_WRONG_PARAM, "Failed to parse hex representation of block hash. Hex = " + hash + '.'}; @@ -1969,32 +1782,53 @@ namespace cryptonote { namespace rpc { if (blk.miner_tx.vin.size() != 1 || !std::holds_alternative(blk.miner_tx.vin.front())) throw rpc_error{ERROR_INTERNAL, "Internal error: coinbase transaction in the block has the wrong type"}; uint64_t block_height = var::get(blk.miner_tx.vin.front()).height; - fill_block_header_response(blk, orphan, block_height, block_hash, block_header, req.fill_pow_hash && admin, req.get_tx_hashes); + fill_block_header_response(blk, orphan, block_height, block_hash, block_header, get_block_header_by_hash.request.fill_pow_hash && admin, get_block_header_by_hash.request.get_tx_hashes); }; - if (!req.hash.empty()) - get(req.hash, res.block_header.emplace()); + if (!get_block_header_by_hash.request.hash.empty()) + { + block_header_response block_header; + get(get_block_header_by_hash.request.hash, block_header); + get_block_header_by_hash.response["block_header"] = block_header; + } - res.block_headers.reserve(req.hashes.size()); - for (const std::string &hash: req.hashes) - get(hash, res.block_headers.emplace_back()); + std::vector block_headers; + for (const std::string &hash: get_block_header_by_hash.request.hashes) + get(hash, block_headers.emplace_back()); - res.status = STATUS_OK; - return res; + get_block_header_by_hash.response["block_headers"] = block_headers; + get_block_header_by_hash.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCK_HEADERS_RANGE::response core_rpc_server::invoke(GET_BLOCK_HEADERS_RANGE::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK_HEADERS_RANGE& get_block_headers_range, rpc_context context) { - GET_BLOCK_HEADERS_RANGE::response res{}; - PERF_TIMER(on_get_block_headers_range); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - + json params{ + {"start_height", get_block_headers_range.request.start_height}, + {"end_height", get_block_headers_range.request.end_height}, + {"fill_pow_hash", get_block_headers_range.request.fill_pow_hash}, + {"get_tx_hashes", get_block_headers_range.request.get_tx_hashes} + }; + if (use_bootstrap_daemon_if_necessary(params, get_block_headers_range.response)) + return; + const uint64_t bc_height = m_core.get_current_blockchain_height(); - if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height) + uint64_t start_height = get_block_headers_range.request.start_height; + uint64_t end_height = get_block_headers_range.request.end_height; + if (start_height >= bc_height || end_height >= bc_height || start_height > end_height) throw rpc_error{ERROR_TOO_BIG_HEIGHT, "Invalid start/end heights."}; - for (uint64_t h = req.start_height; h <= req.end_height; ++h) + + if (end_height - start_height >= GET_BLOCK_HEADERS_RANGE::MAX_COUNT) + throw rpc_error{ + ERROR_TOO_BIG_HEIGHT, + "Invalid start/end heights: requested range of " + + std::to_string(end_height - start_height + 1) + + " blocks exceeds limit " + + std::to_string(GET_BLOCK_HEADERS_RANGE::MAX_COUNT)}; + + std::vector headers; + for (uint64_t h = start_height; h <= end_height; ++h) { block blk; bool have_block = m_core.get_block_by_height(h, blk); @@ -2006,22 +1840,31 @@ namespace cryptonote { namespace rpc { uint64_t block_height = var::get(blk.miner_tx.vin.front()).height; if (block_height != h) throw rpc_error{ERROR_INTERNAL, "Internal error: coinbase transaction in the block has the wrong height"}; - res.headers.push_back(block_header_response()); - fill_block_header_response(blk, false, block_height, get_block_hash(blk), res.headers.back(), req.fill_pow_hash && context.admin, req.get_tx_hashes); + auto& hdr = headers.emplace_back(); + fill_block_header_response(blk, false, block_height, get_block_hash(blk), hdr, get_block_headers_range.request.fill_pow_hash && context.admin, get_block_headers_range.request.get_tx_hashes); } - res.status = STATUS_OK; - return res; + get_block_headers_range.response["headers"] = headers; + get_block_headers_range.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCK_HEADER_BY_HEIGHT::response core_rpc_server::invoke(GET_BLOCK_HEADER_BY_HEIGHT::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK_HEADER_BY_HEIGHT& get_block_header_by_height, rpc_context context) { - GET_BLOCK_HEADER_BY_HEIGHT::response res{}; - PERF_TIMER(on_get_block_header_by_height); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - auto get = [this, curr_height=m_core.get_current_blockchain_height(), pow=req.fill_pow_hash && context.admin, tx_hashes=req.get_tx_hashes] + json params{ + {"height",get_block_header_by_height.request.height}, + {"heights", json::array()}, + {"fill_pow_hash",get_block_header_by_height.request.fill_pow_hash}, + {"get_tx_hashes",get_block_header_by_height.request.get_tx_hashes} + }; + for (const auto& h: get_block_header_by_height.request.heights) + params["heights"].push_back(h); + + if (use_bootstrap_daemon_if_necessary(params, get_block_header_by_height.response)) + return; + + auto get = [this, curr_height=m_core.get_current_blockchain_height(), pow=get_block_header_by_height.request.fill_pow_hash && context.admin, tx_hashes=get_block_header_by_height.request.get_tx_hashes] (uint64_t height, block_header_response& bhr) { if (height >= curr_height) throw rpc_error{ERROR_TOO_BIG_HEIGHT, @@ -2033,137 +1876,172 @@ namespace cryptonote { namespace rpc { fill_block_header_response(blk, false, height, get_block_hash(blk), bhr, pow, tx_hashes); }; - if (req.height) - get(*req.height, res.block_header.emplace()); - if (!req.heights.empty()) - res.block_headers.reserve(req.heights.size()); - for (auto height : req.heights) - get(height, res.block_headers.emplace_back()); + block_header_response header; + if (get_block_header_by_height.request.height) + { + get(*get_block_header_by_height.request.height, header); + get_block_header_by_height.response["block_header"] = header; + } + std::vector headers; + if (!get_block_header_by_height.request.heights.empty()) + headers.reserve(get_block_header_by_height.request.heights.size()); + for (auto height : get_block_header_by_height.request.heights) + get(height, headers.emplace_back()); - res.status = STATUS_OK; - return res; + get_block_header_by_height.response["status"] = STATUS_OK; + get_block_header_by_height.response["block_headers"] = headers; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BLOCK::response core_rpc_server::invoke(GET_BLOCK::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BLOCK& get_block, rpc_context context) { - GET_BLOCK::response res{}; - PERF_TIMER(on_get_block); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - block blk; uint64_t block_height; bool orphan = false; crypto::hash block_hash; - if (!req.hash.empty()) + json params{ + {"hash", get_block.request.hash}, + {"height", get_block.request.height}, + {"fill_pow_hash", get_block.request.fill_pow_hash} + }; + if (use_bootstrap_daemon_if_necessary(params, get_block.response)) + return; + + if (!get_block.request.hash.empty()) { - if (!tools::hex_to_type(req.hash, block_hash)) - throw rpc_error{ERROR_WRONG_PARAM, "Failed to parse hex representation of block hash. Hex = " + req.hash + '.'}; + if (!tools::hex_to_type(get_block.request.hash, block_hash)) + throw rpc_error{ERROR_WRONG_PARAM, "Failed to parse hex representation of block hash. Hex = " + get_block.request.hash + '.'}; if (!m_core.get_block_by_hash(block_hash, blk, &orphan)) - throw rpc_error{ERROR_INTERNAL, "Internal error: can't get block by hash. Hash = " + req.hash + '.'}; + throw rpc_error{ERROR_INTERNAL, "Internal error: can't get block by hash. Hash = " + get_block.request.hash + '.'}; if (blk.miner_tx.vin.size() != 1 || !std::holds_alternative(blk.miner_tx.vin.front())) throw rpc_error{ERROR_INTERNAL, "Internal error: coinbase transaction in the block has the wrong type"}; block_height = var::get(blk.miner_tx.vin.front()).height; } else { - if (auto curr_height = m_core.get_current_blockchain_height(); req.height >= curr_height) - throw rpc_error{ERROR_TOO_BIG_HEIGHT, std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(curr_height - 1)}; - if (!m_core.get_block_by_height(req.height, blk)) - throw rpc_error{ERROR_INTERNAL, "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.'}; + if (auto curr_height = m_core.get_current_blockchain_height(); get_block.request.height >= curr_height) + throw rpc_error{ERROR_TOO_BIG_HEIGHT, std::string("Requested block height: ") + std::to_string(get_block.request.height) + " greater than current top block height: " + std::to_string(curr_height - 1)}; + if (!m_core.get_block_by_height(get_block.request.height, blk)) + throw rpc_error{ERROR_INTERNAL, "Internal error: can't get block by height. Height = " + std::to_string(get_block.request.height) + '.'}; block_hash = get_block_hash(blk); - block_height = req.height; - } - fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && context.admin, false /*tx hashes*/); - res.tx_hashes.reserve(blk.tx_hashes.size()); - for (const auto& tx_hash : blk.tx_hashes) - res.tx_hashes.push_back(tools::type_to_hex(tx_hash)); - res.blob = oxenc::to_hex(t_serializable_object_to_blob(blk)); - res.json = obj_to_json_str(blk); - res.status = STATUS_OK; - return res; + block_height = get_block.request.height; + } + block_header_response header; + fill_block_header_response(blk, orphan, block_height, block_hash, header, get_block.request.fill_pow_hash && context.admin, false /*tx hashes*/); + get_block.response["block_header"] = header; + std::vector tx_hashes; + tx_hashes.reserve(blk.tx_hashes.size()); + std::transform(blk.tx_hashes.begin(), blk.tx_hashes.end(), std::back_inserter(tx_hashes), [](const auto& x) { return tools::type_to_hex(x); }); + get_block.response["tx_hashes"] = std::move(tx_hashes); + get_block.response["blob"] = oxenc::to_hex(t_serializable_object_to_blob(blk)); + get_block.response["json"] = obj_to_json_str(blk); + get_block.response["status"] = STATUS_OK; + return; + } + + static json json_connection_info(const connection_info& ci) { + json info{ + {"incoming", ci.incoming}, + {"ip", ci.ip}, + {"address_type", ci.address_type}, + {"peer_id", ci.peer_id}, + {"recv_count", ci.recv_count}, + {"recv_idle_ms", ci.recv_idle_time.count()}, + {"send_count", ci.send_count}, + {"send_idle_ms", ci.send_idle_time.count()}, + {"state", ci.state}, + {"live_ms", ci.live_time.count()}, + {"avg_download", ci.avg_download}, + {"current_download", ci.current_download}, + {"avg_upload", ci.avg_upload}, + {"current_upload", ci.current_upload}, + {"connection_id", ci.connection_id}, + {"height", ci.height}, + }; + if (ci.ip != ci.host) info["host"] = ci.host; + if (ci.localhost) info["localhost"] = true; + if (ci.local_ip) info["local_ip"] = true; + if (uint16_t port; tools::parse_int(ci.port, port) && port > 0) info["port"] = port; + if (ci.pruning_seed) info["pruning_seed"] = ci.pruning_seed; + return info; } + //------------------------------------------------------------------------------------------------------------------------------ - GET_CONNECTIONS::response core_rpc_server::invoke(GET_CONNECTIONS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_CONNECTIONS& get_connections, rpc_context context) { - GET_CONNECTIONS::response res{}; - PERF_TIMER(on_get_connections); + auto& c = get_connections.response["connections"]; + c = json::array(); + for (auto& ci : m_p2p.get_payload_object().get_connections()) + c.push_back(json_connection_info(ci)); - res.connections = m_p2p.get_payload_object().get_connections(); - - res.status = STATUS_OK; - - return res; + get_connections.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - HARD_FORK_INFO::response core_rpc_server::invoke(HARD_FORK_INFO::request&& req, rpc_context context) + void core_rpc_server::invoke(HARD_FORK_INFO& hfinfo, rpc_context context) { - HARD_FORK_INFO::response res{}; - PERF_TIMER(on_hard_fork_info); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + + json params{ + {"version", hfinfo.request.version}, + {"height", hfinfo.request.height} + }; - const Blockchain &blockchain = m_core.get_blockchain_storage(); - uint8_t version = - req.version > 0 ? req.version : - req.height > 0 ? blockchain.get_network_version(req.height) : + if (use_bootstrap_daemon_if_necessary(params, hfinfo.response)) + return; + + const auto& blockchain = m_core.get_blockchain_storage(); + auto version = + hfinfo.request.version > 0 ? static_cast(hfinfo.request.version) : + hfinfo.request.height > 0 ? blockchain.get_network_version(hfinfo.request.height) : blockchain.get_network_version(); - res.version = version; - res.enabled = blockchain.get_network_version() >= version; + hfinfo.response["version"] = version; + hfinfo.response["enabled"] = blockchain.get_network_version() >= version; auto heights = get_hard_fork_heights(m_core.get_nettype(), version); - res.earliest_height = heights.first; - res.last_height = heights.second; - res.status = STATUS_OK; - return res; + if (heights.first) + hfinfo.response["earliest_height"] = *heights.first; + if (heights.second) + hfinfo.response["latest_height"] = *heights.second; + hfinfo.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GETBANS::response core_rpc_server::invoke(GETBANS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_BANS& get_bans, rpc_context context) { - GETBANS::response res{}; - PERF_TIMER(on_get_bans); + get_bans.response["bans"] = nlohmann::json::array(); auto now = time(nullptr); std::map blocked_hosts = m_p2p.get_blocked_hosts(); for (std::map::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) { if (i->second > now) { - GETBANS::ban b; + ban b; b.host = i->first; - b.ip = 0; - uint32_t ip; - if (epee::string_tools::get_ip_int32_from_string(ip, b.host)) - b.ip = ip; b.seconds = i->second - now; - res.bans.push_back(b); + get_bans.response["bans"].push_back(b); } } std::map blocked_subnets = m_p2p.get_blocked_subnets(); for (std::map::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i) { if (i->second > now) { - GETBANS::ban b; + ban b; b.host = i->first.host_str(); - b.ip = 0; b.seconds = i->second - now; - res.bans.push_back(b); + get_bans.response["bans"].push_back(b); } } - res.status = STATUS_OK; - return res; + get_bans.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - BANNED::response core_rpc_server::invoke(BANNED::request&& req, rpc_context context) + void core_rpc_server::invoke(BANNED& banned, rpc_context context) { - BANNED::response res{}; - PERF_TIMER(on_banned); - auto na_parsed = net::get_network_address(req.address, 0); + auto na_parsed = net::get_network_address(banned.request.address, 0); if (!na_parsed) throw rpc_error{ERROR_WRONG_PARAM, "Unsupported host type"}; epee::net_utils::network_address na = std::move(*na_parsed); @@ -2171,74 +2049,57 @@ namespace cryptonote { namespace rpc { time_t seconds; if (m_p2p.is_host_blocked(na, &seconds)) { - res.banned = true; - res.seconds = seconds; + banned.response["banned"] = true; + banned.response["seconds"] = seconds; } else { - res.banned = false; - res.seconds = 0; + banned.response["banned"] = false; + banned.response["seconds"] = 0; } - res.status = STATUS_OK; - return res; + banned.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - SETBANS::response core_rpc_server::invoke(SETBANS::request&& req, rpc_context context) + void core_rpc_server::invoke(SET_BANS& set_bans, rpc_context context) { - SETBANS::response res{}; - PERF_TIMER(on_set_bans); - for (auto i = req.bans.begin(); i != req.bans.end(); ++i) - { - epee::net_utils::network_address na; - - // try subnet first - if (!i->host.empty()) - { - auto ns_parsed = net::get_ipv4_subnet_address(i->host); - if (ns_parsed) - { - if (i->ban) - m_p2p.block_subnet(*ns_parsed, i->seconds); - else - m_p2p.unblock_subnet(*ns_parsed); - continue; - } - } + epee::net_utils::network_address na; - // then host - if (!i->host.empty()) - { - auto na_parsed = net::get_network_address(i->host, 0); - if (!na_parsed) - throw rpc_error{ERROR_WRONG_PARAM, "Unsupported host/subnet type"}; - na = std::move(*na_parsed); - } - else - { - na = epee::net_utils::ipv4_network_address{i->ip, 0}; - } - if (i->ban) - m_p2p.block_host(na, i->seconds); + // try subnet first + auto ns_parsed = net::get_ipv4_subnet_address(set_bans.request.host); + if (ns_parsed) + { + if (set_bans.request.ban) + m_p2p.block_subnet(*ns_parsed, set_bans.request.seconds); else - m_p2p.unblock_host(na); + m_p2p.unblock_subnet(*ns_parsed); + set_bans.response["status"] = STATUS_OK; + return; } - res.status = STATUS_OK; - return res; + // then host + auto na_parsed = net::get_network_address(set_bans.request.host, 0); + if (!na_parsed) + throw rpc_error{ERROR_WRONG_PARAM, "Unsupported host/subnet type"}; + na = std::move(*na_parsed); + if (set_bans.request.ban) + m_p2p.block_host(na, set_bans.request.seconds); + else + m_p2p.unblock_host(na); + + set_bans.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - FLUSH_TRANSACTION_POOL::response core_rpc_server::invoke(FLUSH_TRANSACTION_POOL::request&& req, rpc_context context) + void core_rpc_server::invoke(FLUSH_TRANSACTION_POOL& flush_transaction_pool, rpc_context context) { - FLUSH_TRANSACTION_POOL::response res{}; - PERF_TIMER(on_flush_txpool); bool failed = false; std::vector txids; - if (req.txids.empty()) + if (flush_transaction_pool.request.txids.empty()) { std::vector pool_txs; m_core.get_pool().get_transactions(pool_txs); @@ -2249,7 +2110,7 @@ namespace cryptonote { namespace rpc { } else { - for (const auto &txid_hex: req.txids) + for (const auto &txid_hex: flush_transaction_pool.request.txids) { if(!tools::hex_to_type(txid_hex, txids.emplace_back())) { @@ -2260,262 +2121,251 @@ namespace cryptonote { namespace rpc { } if (!m_core.get_blockchain_storage().flush_txes_from_pool(txids)) { - res.status = "Failed to remove one or more tx(es)"; - return res; + flush_transaction_pool.response["status"] = "Failed to remove one or more tx(es)"; + return; } - res.status = failed + flush_transaction_pool.response["status"] = failed ? txids.empty() ? "Failed to parse txid" : "Failed to parse some of the txids" : STATUS_OK; - return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_OUTPUT_HISTOGRAM::response core_rpc_server::invoke(GET_OUTPUT_HISTOGRAM::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_context context) { - GET_OUTPUT_HISTOGRAM::response res{}; - PERF_TIMER(on_get_output_histogram); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + json params{ + {"amounts", json::array()}, + {"min_count", get_output_histogram.request.min_count}, + {"max_count", get_output_histogram.request.max_count}, + {"unlocked", get_output_histogram.request.unlocked}, + {"recent_cutoff", get_output_histogram.request.recent_cutoff} + }; + + for (const auto& amt : get_output_histogram.request.amounts) + params["amounts"].push_back(amt); - if (!context.admin && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION) + if (use_bootstrap_daemon_if_necessary(params, get_output_histogram.response)) + return; + + if (!context.admin && get_output_histogram.request.recent_cutoff > 0 && get_output_histogram.request.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION) { - res.status = "Recent cutoff is too old"; - return res; + get_output_histogram.response["status"] = "Recent cutoff is too old"; + return; } std::map> histogram; try { auto net = nettype(); - histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff, req.min_count,net); - } + histogram = m_core.get_blockchain_storage().get_output_histogram( + get_output_histogram.request.amounts, + get_output_histogram.request.unlocked, + get_output_histogram.request.recent_cutoff, + get_output_histogram.request.min_count, + net + ); + } catch (const std::exception &e) { - res.status = "Failed to get output histogram"; - return res; + get_output_histogram.response["status"] = "Failed to get output histogram"; + return; } - res.histogram.clear(); - res.histogram.reserve(histogram.size()); - for (const auto &i: histogram) + std::vector response_histogram; + response_histogram.reserve(histogram.size()); + for (const auto &[amount, histogram_tuple]: histogram) { - if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0)) - res.histogram.push_back(GET_OUTPUT_HISTOGRAM::entry(i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second))); + auto& [total_instances, unlocked_instances, recent_instances] = histogram_tuple; + + if (total_instances >= get_output_histogram.request.min_count && (total_instances <= get_output_histogram.request.max_count || get_output_histogram.request.max_count == 0)) + response_histogram.push_back(GET_OUTPUT_HISTOGRAM::entry{amount, total_instances, unlocked_instances, recent_instances}); } - res.status = STATUS_OK; - return res; + get_output_histogram.response["histogram"] = response_histogram; + get_output_histogram.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_VERSION::response core_rpc_server::invoke(GET_VERSION::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_VERSION& version, rpc_context context) { - GET_VERSION::response res{}; - PERF_TIMER(on_get_version); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + if (use_bootstrap_daemon_if_necessary({}, version.response)) + return; - res.version = pack_version(VERSION); - res.status = STATUS_OK; - return res; + version.response["version"] = pack_version(VERSION); + version.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_NODE_STATUS::response core_rpc_server::invoke(GET_MASTER_NODE_STATUS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_NODE_STATUS& mns, rpc_context context) { - GET_MASTER_NODE_STATUS::response res{}; - - PERF_TIMER(on_get_master_node_status); - auto get_master_node_key_res = invoke(GET_MASTER_KEYS::request{}, context); - - GET_MASTER_NODES::request get_master_nodes_req{}; - get_master_nodes_req.include_json = req.include_json; - get_master_nodes_req.master_node_pubkeys.push_back(std::move(get_master_node_key_res.master_node_pubkey)); - - auto get_master_nodes_res = invoke(std::move(get_master_nodes_req), context); - res.status = get_master_nodes_res.status; - - if (get_master_nodes_res.master_node_states.empty()) // Started in master node but not staked, no information on the blockchain yet - { - res.master_node_state.master_node_pubkey = std::move(get_master_node_key_res.master_node_pubkey); - res.master_node_state.public_ip = epee::string_tools::get_ip_string_from_int32(m_core.mn_public_ip()); - res.master_node_state.storage_port = m_core.storage_https_port(); - res.master_node_state.storage_lmq_port = m_core.storage_omq_port(); - res.master_node_state.quorumnet_port = m_core.quorumnet_port(); - res.master_node_state.pubkey_ed25519 = std::move(get_master_node_key_res.master_node_ed25519_pubkey); - res.master_node_state.pubkey_x25519 = std::move(get_master_node_key_res.master_node_x25519_pubkey); - res.master_node_state.master_node_version = BELDEX_VERSION; - } - else - { - res.master_node_state = std::move(get_master_nodes_res.master_node_states[0]); + auto [top_height, top_hash] = m_core.get_blockchain_top(); + mns.response["height"] = top_height; + mns.response_hex["block_hash"] = top_hash; + const auto& keys = m_core.get_master_keys(); + if (!keys.pub) { + mns.response["status"] = "Not a master node"; + return; + } + mns.response["status"] = STATUS_OK; + + auto mn_infos = m_core.get_master_node_list_state({{keys.pub}}); + if (!mn_infos.empty()) + fill_mn_response_entry(mns.response["master_node_state"] = json::object(), mns.is_bt(), {} /*all fields*/, mn_infos.front(), top_height); + else { + mns.response["master_node_state"] = json{ + {"public_ip", epee::string_tools::get_ip_string_from_int32(m_core.mn_public_ip())}, + {"storage_port", m_core.storage_https_port()}, + {"storage_lmq_port", m_core.storage_omq_port()}, + {"quorumnet_port", m_core.quorumnet_port()}, + {"master_node_version", BELDEX_VERSION} + }; + auto rhex = mns.response_hex["master_node_state"]; + rhex["master_node_pubkey"] = keys.pub; + rhex["pubkey_ed25519"] = keys.pub_ed25519; + rhex["pubkey_x25519"] = keys.pub_x25519; } - - res.height = get_master_nodes_res.height; - res.block_hash = get_master_nodes_res.block_hash; - res.status = get_master_nodes_res.status; - res.as_json = get_master_nodes_res.as_json; - - return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_COINBASE_TX_SUM::response core_rpc_server::invoke(GET_COINBASE_TX_SUM::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_COINBASE_TX_SUM& get_coinbase_tx_sum, rpc_context context) { - GET_COINBASE_TX_SUM::response res{}; - PERF_TIMER(on_get_coinbase_tx_sum); - if (auto sums = m_core.get_coinbase_tx_sum(req.height, req.count)) { - std::tie(res.emission_amount, res.fee_amount, res.burn_amount) = *sums; - res.status = STATUS_OK; + if (auto sums = m_core.get_coinbase_tx_sum(get_coinbase_tx_sum.request.height, get_coinbase_tx_sum.request.count)) { + std::tie(get_coinbase_tx_sum.response["emission_amount"], get_coinbase_tx_sum.response["fee_amount"], get_coinbase_tx_sum.response["burn_amount"]) = *sums; + get_coinbase_tx_sum.response["status"] = STATUS_OK; } else { - res.status = STATUS_BUSY; // some other request is already calculating it + get_coinbase_tx_sum.response["status"] = STATUS_BUSY; // some other request is already calculating it } - return res; } //------------------------------------------------------------------------------------------------------------------------------ - GET_BASE_FEE_ESTIMATE::response core_rpc_server::invoke(GET_BASE_FEE_ESTIMATE::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_FEE_ESTIMATE& get_fee_estimate, rpc_context context) { - GET_BASE_FEE_ESTIMATE::response res{}; - - PERF_TIMER(on_get_base_fee_estimate); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + PERF_TIMER(on_get_fee_estimate); + + json params{ + {"grace_blocks", get_fee_estimate.request.grace_blocks} + }; + + if (use_bootstrap_daemon_if_necessary(params, get_fee_estimate.response)) + return; - auto fees = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks); - res.fee_per_byte = fees.first; - res.fee_per_output = fees.second; - res.flash_fee_fixed = FLASH_BURN_FIXED; - constexpr auto flash_percent = FLASH_MINER_TX_FEE_PERCENT + FLASH_BURN_TX_FEE_PERCENT_OLD; - res.flash_fee_per_byte = res.fee_per_byte * flash_percent / 100; - res.flash_fee_per_output = res.fee_per_output * flash_percent / 100; - res.quantization_mask = Blockchain::get_fee_quantization_mask(); - res.status = STATUS_OK; - return res; + auto fees = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(get_fee_estimate.request.grace_blocks); + get_fee_estimate.response["fee_per_byte"] = fees.first; + get_fee_estimate.response["fee_per_output"] = fees.second; + get_fee_estimate.response["flash_fee_fixed"] = beldex::FLASH_BURN_FIXED; + constexpr auto flash_percent = beldex::FLASH_MINER_TX_FEE_PERCENT + beldex::FLASH_BURN_TX_FEE_PERCENT_OLD; + get_fee_estimate.response["flash_fee_per_byte"] = fees.first * flash_percent / 100; + get_fee_estimate.response["flash_fee_per_output"] = fees.second * flash_percent / 100; + get_fee_estimate.response["quantization_mask"] = Blockchain::get_fee_quantization_mask(); + get_fee_estimate.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_ALTERNATE_CHAINS::response core_rpc_server::invoke(GET_ALTERNATE_CHAINS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_ALTERNATE_CHAINS& get_alternate_chains, rpc_context context) { - GET_ALTERNATE_CHAINS::response res{}; - PERF_TIMER(on_get_alternate_chains); try { - std::vector>> chains = m_core.get_blockchain_storage().get_alternative_chains(); - for (const auto &i: chains) + std::vector chains; + std::vector>> alt_chains = m_core.get_blockchain_storage().get_alternative_chains(); + for (const auto &i: alt_chains) { - res.chains.push_back(GET_ALTERNATE_CHAINS::chain_info{tools::type_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()}); - res.chains.back().block_hashes.reserve(i.second.size()); + chains.push_back(GET_ALTERNATE_CHAINS::chain_info{tools::type_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()}); + chains.back().block_hashes.reserve(i.second.size()); for (const crypto::hash &block_id: i.second) - res.chains.back().block_hashes.push_back(tools::type_to_hex(block_id)); + chains.back().block_hashes.push_back(tools::type_to_hex(block_id)); if (i.first.height < i.second.size()) { - res.status = "Error finding alternate chain attachment point"; - return res; + get_alternate_chains.response["status"] = "Error finding alternate chain attachment point"; + return; } cryptonote::block main_chain_parent_block; try { main_chain_parent_block = m_core.get_blockchain_storage().get_db().get_block_from_height(i.first.height - i.second.size()); } - catch (const std::exception &e) { res.status = "Error finding alternate chain attachment point"; return res; } - res.chains.back().main_chain_parent_block = tools::type_to_hex(get_block_hash(main_chain_parent_block)); + catch (const std::exception &e) { get_alternate_chains.response["status"] = "Error finding alternate chain attachment point"; return; } + chains.back().main_chain_parent_block = tools::type_to_hex(get_block_hash(main_chain_parent_block)); } - res.status = STATUS_OK; + get_alternate_chains.response["chains"] = chains; + get_alternate_chains.response["status"] = STATUS_OK; } catch (...) { - res.status = "Error retrieving alternate chains"; + get_alternate_chains.response["status"] = "Error retrieving alternate chains"; } - return res; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_LIMIT::response core_rpc_server::invoke(GET_LIMIT::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_LIMIT& limit, rpc_context context) { - GET_LIMIT::response res{}; - PERF_TIMER(on_get_limit); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; - - res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit(); - res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit(); - res.status = STATUS_OK; - return res; + if (use_bootstrap_daemon_if_necessary({}, limit.response)) + return; + + limit.response = { + {"limit_down", epee::net_utils::connection_basic::get_rate_down_limit()}, + {"limit_up", epee::net_utils::connection_basic::get_rate_up_limit()}, + {"status", STATUS_OK}}; } //------------------------------------------------------------------------------------------------------------------------------ - SET_LIMIT::response core_rpc_server::invoke(SET_LIMIT::request&& req, rpc_context context) + void core_rpc_server::invoke(SET_LIMIT& limit, rpc_context context) { - SET_LIMIT::response res{}; - PERF_TIMER(on_set_limit); + // -1 = reset to default // 0 = do not modify - - if (req.limit_down < -1 || req.limit_up < -1) - throw rpc_error{ERROR_WRONG_PARAM, "Invalid limit_down or limit_up value: value must be >= -1"}; - - if (req.limit_down != 0) + if (limit.request.limit_down != 0) epee::net_utils::connection_basic::set_rate_down_limit( - req.limit_down == -1 ? nodetool::default_limit_down : req.limit_down); - if (req.limit_up != 0) + limit.request.limit_down == -1 ? nodetool::default_limit_down : limit.request.limit_down); + + if (limit.request.limit_up != 0) epee::net_utils::connection_basic::set_rate_up_limit( - req.limit_up == -1 ? nodetool::default_limit_up : req.limit_up); + limit.request.limit_up == -1 ? nodetool::default_limit_up : limit.request.limit_up); - res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit(); - res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit(); - res.status = STATUS_OK; - return res; + limit.response = { + {"limit_down", epee::net_utils::connection_basic::get_rate_down_limit()}, + {"limit_up", epee::net_utils::connection_basic::get_rate_up_limit()}, + {"status", STATUS_OK}}; } //------------------------------------------------------------------------------------------------------------------------------ - OUT_PEERS::response core_rpc_server::invoke(OUT_PEERS::request&& req, rpc_context context) + void core_rpc_server::invoke(OUT_PEERS& out_peers, rpc_context context) { - OUT_PEERS::response res{}; - PERF_TIMER(on_out_peers); - if (req.set) - m_p2p.change_max_out_public_peers(req.out_peers); - res.status = STATUS_OK; - return res; + if (out_peers.request.set) + m_p2p.change_max_out_public_peers(out_peers.request.out_peers); + out_peers.response["out_peers"] = m_p2p.get_max_out_public_peers(); + out_peers.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - IN_PEERS::response core_rpc_server::invoke(IN_PEERS::request&& req, rpc_context context) + void core_rpc_server::invoke(IN_PEERS& in_peers, rpc_context context) { - IN_PEERS::response res{}; - PERF_TIMER(on_in_peers); - if (req.set) - m_p2p.change_max_in_public_peers(req.in_peers); - res.status = STATUS_OK; - return res; + if (in_peers.request.set) + m_p2p.change_max_in_public_peers(in_peers.request.in_peers); + in_peers.response["in_peers"] = m_p2p.get_max_in_public_peers(); + in_peers.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - POP_BLOCKS::response core_rpc_server::invoke(POP_BLOCKS::request&& req, rpc_context context) + void core_rpc_server::invoke(POP_BLOCKS& pop_blocks, rpc_context context) { - POP_BLOCKS::response res{}; - PERF_TIMER(on_pop_blocks); - m_core.get_blockchain_storage().pop_blocks(req.nblocks); - - res.height = m_core.get_current_blockchain_height(); - res.status = STATUS_OK; + m_core.get_blockchain_storage().pop_blocks(pop_blocks.request.nblocks); - return res; + pop_blocks.response["height"] = m_core.get_current_blockchain_height(); + pop_blocks.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - RELAY_TX::response core_rpc_server::invoke(RELAY_TX::request&& req, rpc_context context) + void core_rpc_server::invoke(RELAY_TX& relay_tx, rpc_context context) { - RELAY_TX::response res{}; - PERF_TIMER(on_relay_tx); - res.status = ""; - for (const auto &txid_hex: req.txids) + std::string status = ""; + for (const auto &str: relay_tx.request.txids) { crypto::hash txid; - if (!tools::hex_to_type(txid_hex, txid)) + if (!tools::hex_to_type(str, txid)) { - if (!res.status.empty()) res.status += ", "; - res.status += "invalid transaction id: " + txid; + if (!status.empty()) status += ", "; + status += "invalid transaction id: " + str; continue; } cryptonote::blobdata txblob; @@ -2529,59 +2379,51 @@ namespace cryptonote { namespace rpc { } else { - if (!res.status.empty()) res.status += ", "; - res.status += "transaction not found in pool: " + txid_hex; + if (!status.empty()) status += ", "; + status += "transaction not found in pool: " + str; + continue; } } - if (res.status.empty()) - res.status = STATUS_OK; + if (status.empty()) + status = STATUS_OK; - return res; + relay_tx.response["status"] = status; + return; } //------------------------------------------------------------------------------------------------------------------------------ - SYNC_INFO::response core_rpc_server::invoke(SYNC_INFO::request&& req, rpc_context context) + void core_rpc_server::invoke(SYNC_INFO& sync, rpc_context context) { - SYNC_INFO::response res{}; - PERF_TIMER(on_sync_info); - crypto::hash top_hash; - m_core.get_blockchain_top(res.height, top_hash); - ++res.height; // turn top block height into blockchain height - res.target_height = m_core.get_target_blockchain_height(); - res.next_needed_pruning_seed = m_p2p.get_payload_object().get_next_needed_pruning_stripe().second; - - for (const auto &c: m_p2p.get_payload_object().get_connections()) - res.peers.push_back({c}); - const cryptonote::block_queue &block_queue = m_p2p.get_payload_object().get_block_queue(); - block_queue.foreach([&](const cryptonote::block_queue::span &span) { - const std::string span_connection_id = tools::type_to_hex(span.connection_id); - uint32_t speed = (uint32_t)(100.0f * block_queue.get_speed(span.connection_id) + 0.5f); - std::string address = ""; - for (const auto &c: m_p2p.get_payload_object().get_connections()) - if (c.connection_id == span_connection_id) - address = c.address; - res.spans.push_back({span.start_block_height, span.nblocks, span_connection_id, (uint32_t)(span.rate + 0.5f), speed, span.size, address}); - return true; + auto [top_height, top_hash] = m_core.get_blockchain_top(); + sync.response["height"] = top_height + 1; // turn top block height into blockchain height + if (auto target_height = m_core.get_target_blockchain_height(); target_height > top_height + 1) + sync.response["target_height"] = target_height; + // Don't put this into the response until it actually does something on Beldex: + if (false) + sync.response["next_needed_pruning_seed"] = m_p2p.get_payload_object().get_next_needed_pruning_stripe().second; + + auto& peers = sync.response["peers"]; + peers = json{}; + for (auto& ci : m_p2p.get_payload_object().get_connections()) + peers[ci.connection_id] = json_connection_info(ci); + const auto& block_queue = m_p2p.get_payload_object().get_block_queue(); + auto spans = json::array(); + block_queue.foreach([&spans, &block_queue](const auto& span) { + uint32_t speed = (uint32_t)(100.0f * block_queue.get_speed(span.connection_id) + 0.5f); + spans.push_back(json{ + {"start_block_height", span.start_block_height}, + {"nblocks", span.nblocks}, + {"connection_id", tools::type_to_hex(span.connection_id)}, + {"rate", std::lround(span.rate)}, + {"speed", speed}, + {"size", span.size}}); + return true; }); - res.overview = block_queue.get_overview(res.height); - - res.status = STATUS_OK; - return res; - } - //------------------------------------------------------------------------------------------------------------------------------ - GET_TRANSACTION_POOL_BACKLOG::response core_rpc_server::invoke(GET_TRANSACTION_POOL_BACKLOG::request&& req, rpc_context context) - { - GET_TRANSACTION_POOL_BACKLOG::response res{}; - - PERF_TIMER(on_get_txpool_backlog); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + sync.response["overview"] = block_queue.get_overview(top_height + 1); - m_core.get_pool().get_transaction_backlog(res.backlog); - res.status = STATUS_OK; - return res; + sync.response["status"] = STATUS_OK; } namespace { @@ -2696,27 +2538,35 @@ namespace cryptonote { namespace rpc { } //------------------------------------------------------------------------------------------------------------------------------ - GET_OUTPUT_DISTRIBUTION::response core_rpc_server::invoke(GET_OUTPUT_DISTRIBUTION::request&& req, rpc_context context, bool binary) + void core_rpc_server::invoke(GET_OUTPUT_DISTRIBUTION& get_output_distribution, rpc_context context) { - GET_OUTPUT_DISTRIBUTION::response res{}; - PERF_TIMER(on_get_output_distribution); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + json params{ + {"amounts", json::array()}, + {"from_height", get_output_distribution.request.from_height}, + {"to_height", get_output_distribution.request.to_height}, + {"cumulative", get_output_distribution.request.cumulative} + }; + + for (const auto& amt : get_output_distribution.request.amounts) + params["amounts"].push_back(amt); + + if (use_bootstrap_daemon_if_necessary(params, get_output_distribution.response)) + return; try { // 0 is placeholder for the whole chain - const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); - for (uint64_t amount: req.amounts) + const uint64_t req_to_height = get_output_distribution.request.to_height ? get_output_distribution.request.to_height : (m_core.get_current_blockchain_height() - 1); + for (uint64_t amount: get_output_distribution.request.amounts) { auto data = detail::get_output_distribution( [this](auto&&... args) { return m_core.get_output_distribution(std::forward(args)...); }, amount, - req.from_height, + get_output_distribution.request.from_height, req_to_height, [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, - req.cumulative, + get_output_distribution.request.cumulative, m_core.get_current_blockchain_height()); if (!data) throw rpc_error{ERROR_INTERNAL, "Failed to get output distribution"}; @@ -2724,16 +2574,16 @@ namespace cryptonote { namespace rpc { // Force binary & compression off if this is a JSON request because trying to pass binary // data through JSON explodes it in terms of size (most values under 0x20 have to be encoded // using 6 chars such as "\u0002"). - res.distributions.push_back({std::move(*data), amount, "", binary && req.binary, binary && req.compress}); + GET_OUTPUT_DISTRIBUTION::distribution distributions = {std::move(*data), amount}; + get_output_distribution.response["distributions"].push_back(distributions); } } catch (const std::exception &e) { throw rpc_error{ERROR_INTERNAL, "Failed to get output distribution"}; } - - res.status = STATUS_OK; - return res; + get_output_distribution.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ GET_OUTPUT_DISTRIBUTION_BIN::response core_rpc_server::invoke(GET_OUTPUT_DISTRIBUTION_BIN::request&& req, rpc_context context) @@ -2751,52 +2601,83 @@ namespace cryptonote { namespace rpc { if (use_bootstrap_daemon_if_necessary(req, res)) return res; - return invoke(std::move(static_cast(req)), context, true); + try + { + // 0 is placeholder for the whole chain + const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); + for (uint64_t amount: req.amounts) + { + auto data = detail::get_output_distribution( + [this](auto&&... args) { return m_core.get_output_distribution(std::forward(args)...); }, + amount, + req.from_height, + req_to_height, + [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, + req.cumulative, + m_core.get_current_blockchain_height()); + if (!data) + throw rpc_error{ERROR_INTERNAL, "Failed to get output distribution"}; + + // Force binary & compression off if this is a JSON request because trying to pass binary + // data through JSON explodes it in terms of size (most values under 0x20 have to be encoded + // using 6 chars such as "\u0002"). + res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); + } + } + catch (const std::exception &e) + { + throw rpc_error{ERROR_INTERNAL, "Failed to get output distribution"}; + } + + res.status = STATUS_OK; + return res; } //------------------------------------------------------------------------------------------------------------------------------ - PRUNE_BLOCKCHAIN::response core_rpc_server::invoke(PRUNE_BLOCKCHAIN::request&& req, rpc_context context) + void core_rpc_server::invoke(PRUNE_BLOCKCHAIN& prune_blockchain, rpc_context context) { - PRUNE_BLOCKCHAIN::response res{}; - try { - if (!(req.check ? m_core.check_blockchain_pruning() : m_core.prune_blockchain())) - throw rpc_error{ERROR_INTERNAL, req.check ? "Failed to check blockchain pruning" : "Failed to prune blockchain"}; - res.pruning_seed = m_core.get_blockchain_pruning_seed(); - res.pruned = res.pruning_seed != 0; + if (!(prune_blockchain.request.check ? m_core.check_blockchain_pruning() : m_core.prune_blockchain())) + throw rpc_error{ERROR_INTERNAL, prune_blockchain.request.check ? "Failed to check blockchain pruning" : "Failed to prune blockchain"}; + auto pruning_seed = m_core.get_blockchain_pruning_seed(); + prune_blockchain.response["pruning_seed"] = pruning_seed; + prune_blockchain.response["pruned"] = pruning_seed != 0; } catch (const std::exception &e) { throw rpc_error{ERROR_INTERNAL, "Failed to prune blockchain"}; } - res.status = STATUS_OK; - return res; + prune_blockchain.response["status"] = STATUS_OK; } - - - GET_QUORUM_STATE::response core_rpc_server::invoke(GET_QUORUM_STATE::request&& req, rpc_context context) + //------------------------------------------------------------------------------------------------------------------------------ + void core_rpc_server::invoke(GET_QUORUM_STATE& get_quorum_state, rpc_context context) { - GET_QUORUM_STATE::response res{}; - PERF_TIMER(on_get_quorum_state); - if (req.quorum_type >= tools::enum_count && - req.quorum_type != GET_QUORUM_STATE::ALL_QUORUMS_SENTINEL_VALUE) - throw rpc_error{ERROR_WRONG_PARAM, - "Quorum type specifies an invalid value: " + std::to_string(req.quorum_type)}; + json params; + if (get_quorum_state.request.start_height.has_value()) + params["start_height"] = *get_quorum_state.request.start_height; + if (get_quorum_state.request.end_height.has_value()) + params["end_height"] = *get_quorum_state.request.end_height; + if (get_quorum_state.request.quorum_type.has_value()) + params["quorum_type"] = *get_quorum_state.request.quorum_type; + + if (use_bootstrap_daemon_if_necessary(params, get_quorum_state.response)) + return; + + const auto& quorum_type = get_quorum_state.request.quorum_type; - auto requested_type = [&req](master_nodes::quorum_type type) { - return req.quorum_type == GET_QUORUM_STATE::ALL_QUORUMS_SENTINEL_VALUE || - req.quorum_type == static_cast(type); + auto is_requested_type = [&quorum_type](master_nodes::quorum_type type) { + return !quorum_type || quorum_type == static_cast(type); }; bool latest = false; uint64_t latest_ob = 0, latest_cp = 0, latest_bl = 0; - uint64_t start = req.start_height, end = req.end_height; + auto& start = get_quorum_state.request.start_height; + auto& end = get_quorum_state.request.end_height; uint64_t curr_height = m_core.get_blockchain_storage().get_current_blockchain_height(); - if (start == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE && - end == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE) + if (!start && !end) { latest = true; // Our start block for the latest quorum of each type depends on the type being requested: @@ -2806,36 +2687,33 @@ namespace cryptonote { namespace rpc { // POS: current height (i.e. top block height + 1) uint64_t top_height = curr_height - 1; latest_ob = top_height; - latest_cp = std::min(start, top_height - top_height % master_nodes::CHECKPOINT_INTERVAL); - latest_bl = std::min(start, top_height - top_height % master_nodes::FLASH_QUORUM_INTERVAL); - if (requested_type(master_nodes::quorum_type::checkpointing)) - start = std::min(start, latest_cp); - if (requested_type(master_nodes::quorum_type::flash)) - start = std::min(start, latest_bl); + latest_cp = top_height - top_height % master_nodes::CHECKPOINT_INTERVAL; + latest_bl = top_height - top_height % master_nodes::FLASH_QUORUM_INTERVAL; + if (is_requested_type(master_nodes::quorum_type::checkpointing)) + start = latest_cp; + if (is_requested_type(master_nodes::quorum_type::flash)) + start = start ? std::min(*start, latest_bl) : latest_bl; end = curr_height; } - else if (start == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE) - { - start = end; - end = end + 1; - } - else if (end == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE) - { - end = start + 1; - } - else - { - if (end > start) end++; - else if (end != 0) end--; - } + else if (!start) + start = (*end)++; + else if (!end) + end = *start + 1; + else if (*end > *start) + ++*end; + else if (end > 0) + --*end; + + if (!start || *start > curr_height) + start = curr_height; - start = std::min(curr_height, start); // We can also provide the POS quorum for the current block being produced, so if asked for // that make a note. - bool add_curr_POS = (latest || end > curr_height) && requested_type(master_nodes::quorum_type::POS); - end = std::min(curr_height, end); + bool add_curr_POS = (latest || end > curr_height) && is_requested_type(master_nodes::quorum_type::POS); + if (!end || *end > curr_height) + end = curr_height; - uint64_t count = (start > end) ? start - end : end - start; + uint64_t count = (*start > *end) ? *start - *end : *end - *start; if (!context.admin && count > GET_QUORUM_STATE::MAX_COUNT) throw rpc_error{ERROR_WRONG_PARAM, "Number of requested quorums greater than the allowed limit: " @@ -2843,56 +2721,51 @@ namespace cryptonote { namespace rpc { + ", requested: " + std::to_string(count)}; bool at_least_one_succeeded = false; - res.quorums.reserve(std::min((uint64_t)16, count)); + std::vector quorums; + quorums.reserve(std::min((uint64_t)16, count)); auto net = nettype(); - for (size_t height = start; height != end;) + for (size_t height = *start; height < *end; height++) { - uint8_t hf_version = get_network_version(net, height); + auto hf_version = get_network_version(net, height); + auto start_quorum_iterator = static_cast(0); + auto end_quorum_iterator = master_nodes::max_quorum_type_for_hf(hf_version); + + if (quorum_type) { - auto start_quorum_iterator = static_cast(0); - auto end_quorum_iterator = master_nodes::max_quorum_type_for_hf(hf_version); + start_quorum_iterator = static_cast(*quorum_type); + end_quorum_iterator = start_quorum_iterator; + } - if (req.quorum_type != GET_QUORUM_STATE::ALL_QUORUMS_SENTINEL_VALUE) - { - start_quorum_iterator = static_cast(req.quorum_type); - end_quorum_iterator = start_quorum_iterator; + for (int quorum_int = (int)start_quorum_iterator; quorum_int <= (int)end_quorum_iterator; quorum_int++) + { + auto type = static_cast(quorum_int); + if (latest) + { // Latest quorum requested, so skip if this is isn't the latest height for *this* quorum type + if (type == master_nodes::quorum_type::obligations && height != latest_ob) continue; + if (type == master_nodes::quorum_type::checkpointing && height != latest_cp) continue; + if (type == master_nodes::quorum_type::flash && height != latest_bl) continue; + if (type == master_nodes::quorum_type::POS) continue; } - - for (int quorum_int = (int)start_quorum_iterator; quorum_int <= (int)end_quorum_iterator; quorum_int++) + if (std::shared_ptr quorum = m_core.get_quorum(type, height, true /*include_old*/)) { - auto type = static_cast(quorum_int); - if (latest) - { // Latest quorum requested, so skip if this is isn't the latest height for *this* quorum type - if (type == master_nodes::quorum_type::obligations && height != latest_ob) continue; - if (type == master_nodes::quorum_type::checkpointing && height != latest_cp) continue; - if (type == master_nodes::quorum_type::flash && height != latest_bl) continue; - if (type == master_nodes::quorum_type::POS) continue; - } - if (std::shared_ptr quorum = m_core.get_quorum(type, height, true /*include_old*/)) - { - auto& entry = res.quorums.emplace_back(); - entry.height = height; - entry.quorum_type = static_cast(quorum_int); - entry.quorum.validators = hexify(quorum->validators); - entry.quorum.workers = hexify(quorum->workers); + auto& entry = quorums.emplace_back(); + entry.height = height; + entry.quorum_type = static_cast(quorum_int); + entry.quorum.validators = hexify(quorum->validators); + entry.quorum.workers = hexify(quorum->workers); - at_least_one_succeeded = true; - } + at_least_one_succeeded = true; } } - - if (end >= start) height++; - else height--; } - if (uint8_t hf_version; add_curr_POS - && (hf_version = get_network_version(nettype(), curr_height)) >= network_version_17_POS) + if (auto hf_version = get_network_version(nettype(), curr_height); add_curr_POS && hf_version >= hf::hf17_POS) { - cryptonote::Blockchain const &blockchain = m_core.get_blockchain_storage(); - cryptonote::block_header const &top_header = blockchain.get_db().get_block_header_from_height(curr_height - 1); + const auto& blockchain = m_core.get_blockchain_storage(); + const auto& top_header = blockchain.get_db().get_block_header_from_height(curr_height - 1); - POS::timings next_timings = {}; - uint8_t POS_round = 0; + POS::timings next_timings{}; + uint8_t POS_round = 0; if (POS::get_round_timings(blockchain, curr_height, top_header.timestamp, next_timings) && POS::convert_time_to_round(POS::clock::now(), next_timings.r0_timestamp, &POS_round)) { @@ -2901,7 +2774,7 @@ namespace cryptonote { namespace rpc { auto quorum = generate_POS_quorum(m_core.get_nettype(), mn_list.get_block_leader().key, hf_version, mn_list.active_master_nodes_infos(), entropy, POS_round); if (verify_POS_quorum_sizes(quorum)) { - auto& entry = res.quorums.emplace_back(); + auto& entry = quorums.emplace_back(); entry.height = curr_height; entry.quorum_type = static_cast(master_nodes::quorum_type::POS); @@ -2916,43 +2789,49 @@ namespace cryptonote { namespace rpc { if (!at_least_one_succeeded) throw rpc_error{ERROR_WRONG_PARAM, "Failed to query any quorums at all"}; - res.status = STATUS_OK; - return res; + get_quorum_state.response["quorums"] = quorums; + get_quorum_state.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - FLUSH_CACHE::response core_rpc_server::invoke(FLUSH_CACHE::request&& req, rpc_context context) + void core_rpc_server::invoke(FLUSH_CACHE& flush_cache, rpc_context context) { - FLUSH_CACHE::response res{}; - if (req.bad_txs) + if (flush_cache.request.bad_txs) m_core.flush_bad_txs_cache(); - if (req.bad_blocks) + if (flush_cache.request.bad_blocks) m_core.flush_invalid_blocks(); - res.status = STATUS_OK; - return res; + flush_cache.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_NODE_REGISTRATION_CMD_RAW::response core_rpc_server::invoke(GET_MASTER_NODE_REGISTRATION_CMD_RAW::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_NODE_REGISTRATION_CMD_RAW& get_master_node_registration_cmd_raw, rpc_context context) { - GET_MASTER_NODE_REGISTRATION_CMD_RAW::response res{}; - PERF_TIMER(on_get_master_node_registration_cmd_raw); if (!m_core.master_node()) throw rpc_error{ERROR_WRONG_PARAM, "Daemon has not been started in master node mode, please relaunch with --master-node flag."}; - uint8_t hf_version = get_network_version(nettype(), m_core.get_current_blockchain_height()); - if (!master_nodes::make_registration_cmd(m_core.get_nettype(), hf_version, req.staking_requirement, req.args, m_core.get_master_keys(), res.registration_cmd, req.make_friendly)) + auto hf_version = get_network_version(nettype(), m_core.get_current_blockchain_height()); + std::string registration_cmd; + if (!master_nodes::make_registration_cmd(m_core.get_nettype(), + hf_version, + get_master_node_registration_cmd_raw.request.staking_requirement, + get_master_node_registration_cmd_raw.request.args, + m_core.get_master_keys(), + registration_cmd, + get_master_node_registration_cmd_raw.request.make_friendly)) throw rpc_error{ERROR_INTERNAL, "Failed to make registration command"}; - res.status = STATUS_OK; - return res; + get_master_node_registration_cmd_raw.response["registration_cmd"] = registration_cmd; + get_master_node_registration_cmd_raw.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_NODE_REGISTRATION_CMD::response core_rpc_server::invoke(GET_MASTER_NODE_REGISTRATION_CMD::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_NODE_REGISTRATION_CMD& get_master_node_registration_cmd, rpc_context context) { - GET_MASTER_NODE_REGISTRATION_CMD::response res{}; - PERF_TIMER(on_get_master_node_registration_cmd); + + if (!m_core.master_node()) + throw rpc_error{ERROR_WRONG_PARAM, "Daemon has not been started in master node mode, please relaunch with --master-node flag."}; std::vector args; @@ -2961,79 +2840,74 @@ namespace cryptonote { namespace rpc { { uint64_t portions_cut; - if (!master_nodes::get_portions_from_percent_str(req.operator_cut, portions_cut)) + if (!master_nodes::get_portions_from_percent_str(get_master_node_registration_cmd.request.operator_cut, portions_cut)) { - res.status = "Invalid value: " + req.operator_cut + ". Should be between [0-100]"; - MERROR(res.status); - return res; + get_master_node_registration_cmd.response["status"] = "Invalid value: " + get_master_node_registration_cmd.request.operator_cut + ". Should be between [0-100]"; + MERROR(get_master_node_registration_cmd.response["status"]); + return; } args.push_back(std::to_string(portions_cut)); } - for (const auto& [address, amount] : req.contributions) + auto& addresses = get_master_node_registration_cmd.request.contributor_addresses; + auto& amounts = get_master_node_registration_cmd.request.contributor_amounts; + + if (addresses.size() != amounts.size()) { + throw std::runtime_error("Mismatch in sizes of addresses and amounts"); + } + + for (size_t i = 0; i < addresses.size(); ++i) { - uint64_t num_portions = master_nodes::get_portions_to_make_amount(staking_requirement, amount); - args.push_back(address); - args.push_back(std::to_string(num_portions)); + uint64_t num_portions = master_nodes::get_portions_to_make_amount(staking_requirement, amounts[i]); + args.push_back(addresses[i]); + args.push_back(std::to_string(num_portions)); } - GET_MASTER_NODE_REGISTRATION_CMD_RAW::request req_old{}; + GET_MASTER_NODE_REGISTRATION_CMD_RAW req_old{}; - req_old.staking_requirement = req.staking_requirement; - req_old.args = std::move(args); - req_old.make_friendly = false; - return invoke(std::move(req_old), context); + req_old.request.staking_requirement = staking_requirement; + req_old.request.args = std::move(args); + req_old.request.make_friendly = false; + + invoke(req_old, context); + get_master_node_registration_cmd.response["status"] = req_old.response["status"]; + get_master_node_registration_cmd.response["registration_cmd"] = req_old.response["registration_cmd"]; } + //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::response core_rpc_server::invoke(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES& get_master_node_blacklisted_key_images, rpc_context context) { - GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::response res{}; - PERF_TIMER(on_get_master_node_blacklisted_key_images); auto &blacklist = m_core.get_master_node_blacklisted_key_images(); - res.status = STATUS_OK; - res.blacklist.reserve(blacklist.size()); - for (const master_nodes::key_image_blacklist_entry &entry : blacklist) - { - res.blacklist.emplace_back(); - auto &new_entry = res.blacklist.back(); - new_entry.key_image = tools::type_to_hex(entry.key_image); - new_entry.unlock_height = entry.unlock_height; - new_entry.amount = entry.amount; - } - return res; + get_master_node_blacklisted_key_images.response["status"] = STATUS_OK; + get_master_node_blacklisted_key_images.response["blacklist"] = blacklist; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_KEYS::response core_rpc_server::invoke(GET_MASTER_KEYS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_KEYS& get_master_keys, rpc_context context) { - GET_MASTER_KEYS::response res{}; - PERF_TIMER(on_get_master_node_key); - const auto& keys = m_core.get_master_keys(); if (keys.pub) - res.master_node_pubkey = tools::type_to_hex(keys.pub); - res.master_node_ed25519_pubkey = tools::type_to_hex(keys.pub_ed25519); - res.master_node_x25519_pubkey = tools::type_to_hex(keys.pub_x25519); - res.status = STATUS_OK; - return res; + get_master_keys.response["master_node_pubkey"] = tools::type_to_hex(keys.pub); + get_master_keys.response["master_node_ed25519_pubkey"] = tools::type_to_hex(keys.pub_ed25519); + get_master_keys.response["master_node_x25519_pubkey"] = tools::type_to_hex(keys.pub_x25519); + get_master_keys.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_PRIVKEYS::response core_rpc_server::invoke(GET_MASTER_PRIVKEYS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_PRIVKEYS& get_master_privkeys, rpc_context context) { - GET_MASTER_PRIVKEYS::response res{}; - PERF_TIMER(on_get_master_node_key); - const auto& keys = m_core.get_master_keys(); if (keys.key != crypto::null_skey) - res.master_node_privkey = tools::type_to_hex(keys.key.data); - res.master_node_ed25519_privkey = tools::type_to_hex(keys.key_ed25519.data); - res.master_node_x25519_privkey = tools::type_to_hex(keys.key_x25519.data); - res.status = STATUS_OK; - return res; + get_master_privkeys.response["master_node_privkey"] = tools::type_to_hex(keys.key.data); + get_master_privkeys.response["master_node_ed25519_privkey"] = tools::type_to_hex(keys.key_ed25519.data); + get_master_privkeys.response["master_node_x25519_privkey"] = tools::type_to_hex(keys.key_x25519.data); + get_master_privkeys.response["status"] = STATUS_OK; + return; } static time_t reachable_to_time_t( @@ -3047,188 +2921,236 @@ namespace cryptonote { namespace rpc { system_now + (t - steady_now))); } + static bool requested(const std::unordered_set& requested, const std::string& key) { + return requested.empty() || + (requested.count("all") + ? !requested.count("-" + key) + : requested.count(key)); + } + + template + static void set_if_requested(const std::unordered_set& reqed, Dict& dict, + const std::string& key, T&& value, More&&... more) { + if (requested(reqed, key)) + dict[key] = std::forward(value); + if constexpr (sizeof...(More) > 0) + set_if_requested(reqed, dict, std::forward(more)...); + } + //------------------------------------------------------------------------------------------------------------------------------ - void core_rpc_server::fill_mn_response_entry(GET_MASTER_NODES::response::entry& entry, const master_nodes::master_node_pubkey_info &mn_info, uint64_t current_height) { + void core_rpc_server::fill_mn_response_entry(json& entry, bool is_bt, const std::unordered_set& reqed, const master_nodes::master_node_pubkey_info& mn_info, uint64_t top_height) + { + auto hf_version = m_core.get_blockchain_storage().get_network_version(); + auto binary_format = is_bt ? json_binary_proxy::fmt::bt : json_binary_proxy::fmt::hex; + json_binary_proxy binary{entry, binary_format}; const auto &info = *mn_info.info; - entry.master_node_pubkey = tools::type_to_hex(mn_info.pubkey); - entry.registration_height = info.registration_height; - entry.requested_unlock_height = info.requested_unlock_height; - entry.last_reward_block_height = info.last_reward_block_height; - entry.last_reward_transaction_index = info.last_reward_transaction_index; - entry.active = info.is_active(); - entry.funded = info.is_fully_funded(); - entry.state_height = info.is_fully_funded() - ? (info.is_decommissioned() ? info.last_decommission_height : info.active_since_height) : info.last_reward_block_height; - uint8_t hf_version = m_core.get_blockchain_storage().get_network_version(); - entry.earned_downtime_blocks = master_nodes::quorum_cop::calculate_decommission_credit(info, current_height,hf_version); - entry.decommission_count = info.decommission_count; - entry.last_decommission_reason_consensus_all = info.last_decommission_reason_consensus_all; - entry.last_decommission_reason_consensus_any = info.last_decommission_reason_consensus_any; - m_core.get_master_node_list().access_proof(mn_info.pubkey, [ - this, &entry, is_me = (m_core.master_node() && m_core.get_master_keys().pub == mn_info.pubkey) - ](const auto &proof) { - if (is_me) { - entry.master_node_version = BELDEX_VERSION; - entry.belnet_version = m_core.belnet_version; - entry.storage_server_version = m_core.ss_version; - entry.public_ip = epee::string_tools::get_ip_string_from_int32(m_core.mn_public_ip()); - entry.storage_port = m_core.storage_https_port(); - entry.storage_lmq_port = m_core.storage_omq_port(); - entry.quorumnet_port = m_core.quorumnet_port(); - entry.pubkey_ed25519 = tools::type_to_hex(m_core.get_master_keys().pub_ed25519); - entry.pubkey_x25519 = tools::type_to_hex(m_core.get_master_keys().pub_x25519); - } - else{ - entry.master_node_version = proof.proof->version; - entry.belnet_version = proof.proof->belnet_version; - entry.storage_server_version = proof.proof->storage_server_version; - entry.public_ip = epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip); - entry.storage_port = proof.proof->storage_https_port; - entry.storage_lmq_port = proof.proof->storage_omq_port; - entry.pubkey_ed25519 = proof.proof->pubkey_ed25519 ? tools::type_to_hex(proof.proof->pubkey_ed25519) : ""; - entry.pubkey_x25519 = proof.pubkey_x25519 ? tools::type_to_hex(proof.pubkey_x25519) : ""; - entry.quorumnet_port = proof.proof->qnet_port; - } - // NOTE: Master Node Testing - entry.last_uptime_proof = proof.timestamp; - auto system_now = std::chrono::system_clock::now(); - auto steady_now = std::chrono::steady_clock::now(); - auto& netconf = m_core.get_net_config(); - entry.storage_server_reachable = !proof.ss_reachable.unreachable_for(netconf.UPTIME_PROOF_VALIDITY - netconf.UPTIME_PROOF_FREQUENCY, steady_now); - entry.storage_server_first_unreachable = reachable_to_time_t(proof.ss_reachable.first_unreachable, system_now, steady_now); - entry.storage_server_last_unreachable = reachable_to_time_t(proof.ss_reachable.last_unreachable, system_now, steady_now); - entry.storage_server_last_reachable = reachable_to_time_t(proof.ss_reachable.last_reachable, system_now, steady_now); - entry.belnet_reachable = !proof.belnet_reachable.unreachable_for(netconf.UPTIME_PROOF_VALIDITY - netconf.UPTIME_PROOF_FREQUENCY, steady_now); - entry.belnet_first_unreachable = reachable_to_time_t(proof.belnet_reachable.first_unreachable, system_now, steady_now); - entry.belnet_last_unreachable = reachable_to_time_t(proof.belnet_reachable.last_unreachable, system_now, steady_now); - entry.belnet_last_reachable = reachable_to_time_t(proof.belnet_reachable.last_reachable, system_now, steady_now); - - master_nodes::participation_history const &checkpoint_participation = proof.checkpoint_participation; - master_nodes::participation_history const &POS_participation = proof.POS_participation; - master_nodes::participation_history const ×tamp_participation = proof.timestamp_participation; - master_nodes::participation_history const ×ync_status = proof.timesync_status; - entry.checkpoint_participation = std::vector(checkpoint_participation.begin(), checkpoint_participation.end()); - entry.POS_participation = std::vector(POS_participation.begin(), POS_participation.end()); - entry.timestamp_participation = std::vector(timestamp_participation.begin(), timestamp_participation.end()); - entry.timesync_status = std::vector(timesync_status.begin(), timesync_status.end()); + set_if_requested(reqed, binary, "master_node_pubkey", mn_info.pubkey); + set_if_requested(reqed, entry, + "registration_height", info.registration_height, + "requested_unlock_height", info.requested_unlock_height, + "last_reward_block_height", info.last_reward_block_height, + "last_reward_transaction_index", info.last_reward_transaction_index, + "active", info.is_active(), + "funded", info.is_fully_funded(), + "state_height", info.is_fully_funded() + ? (info.is_decommissioned() ? info.last_decommission_height : info.active_since_height) + : info.last_reward_block_height, + "earned_downtime_blocks", master_nodes::quorum_cop::calculate_decommission_credit(info, top_height, hf_version), + "decommission_count", info.decommission_count, + "total_contributed", info.total_contributed, + "staking_requirement", info.staking_requirement, + "portions_for_operator", info.portions_for_operator, + "operator_fee", microportion(info.portions_for_operator), + "operator_address", cryptonote::get_account_address_as_str(m_core.get_nettype(), false/*subaddress*/, info.operator_address), + "swarm_id", info.swarm_id, + "swarm", tools::int_to_string(info.swarm_id, 16), + "registration_hf_version", info.registration_hf_version + ); + + if (requested(reqed, "total_reserved") && info.total_reserved != info.total_contributed) + entry["total_reserved"] = info.total_reserved; + + if (info.last_decommission_reason_consensus_any) { + set_if_requested(reqed, entry, + "last_decommission_reason_consensus_all", info.last_decommission_reason_consensus_all, + "last_decommission_reason_consensus_any", info.last_decommission_reason_consensus_any); + + if (requested(reqed, "last_decomm_reasons")) { + auto& reasons = (entry["last_decomm_reasons"] = json{ + {"all", cryptonote::coded_reasons(info.last_decommission_reason_consensus_all)}}); + if (auto some = cryptonote::coded_reasons(info.last_decommission_reason_consensus_any & ~info.last_decommission_reason_consensus_all); + !some.empty()) + reasons["some"] = std::move(some); + } + } + + auto& netconf = m_core.get_net_config(); + // FIXME: accessing proofs one-by-one like this is kind of gross. + m_core.get_master_node_list().access_proof(mn_info.pubkey, [&](const auto& proof) { + if (m_core.master_node() && m_core.get_master_keys().pub == mn_info.pubkey) { + // When returning our own info we always want to return the most current data because the + // data from the MN list could be stale (it only gets updated when we get verification of + // acceptance of our proof from the network). The rest of the network might not get the + // updated data until the next proof, but local callers like SS and Belnet want it updated + // immediately. + set_if_requested(reqed, entry, + "master_node_version", BELDEX_VERSION, + "belnet_version", m_core.belnet_version, + "storage_server_version", m_core.ss_version, + "public_ip", epee::string_tools::get_ip_string_from_int32(m_core.mn_public_ip()), + "storage_port", m_core.storage_https_port(), + "storage_lmq_port", m_core.storage_omq_port(), + "quorumnet_port", m_core.quorumnet_port()); + set_if_requested(reqed, binary, + "pubkey_ed25519", m_core.get_master_keys().pub_ed25519, + "pubkey_x25519", m_core.get_master_keys().pub_x25519); + } else { + if (proof.proof->public_ip != 0) + set_if_requested(reqed, entry, + "master_node_version", proof.proof->version, + "belnet_version", proof.proof->belnet_version, + "storage_server_version", proof.proof->storage_server_version, + "public_ip", epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip), + "storage_port", proof.proof->storage_https_port, + "storage_lmq_port", proof.proof->storage_omq_port, + "quorumnet_port", proof.proof->qnet_port); + if (proof.proof->pubkey_ed25519) + set_if_requested(reqed, binary, + "pubkey_ed25519", proof.proof->pubkey_ed25519, + "pubkey_x25519", proof.pubkey_x25519); + } + + auto system_now = std::chrono::system_clock::now(); + auto steady_now = std::chrono::steady_clock::now(); + set_if_requested(reqed, entry, "last_uptime_proof", proof.timestamp); + if (m_core.master_node()) { + set_if_requested(reqed, entry, + "storage_server_reachable", !proof.ss_reachable.unreachable_for(netconf.UPTIME_PROOF_VALIDITY - netconf.UPTIME_PROOF_FREQUENCY, steady_now), + "belnet_reachable", !proof.belnet_reachable.unreachable_for(netconf.UPTIME_PROOF_VALIDITY - netconf.UPTIME_PROOF_FREQUENCY, steady_now)); + if (proof.ss_reachable.first_unreachable != master_nodes::NEVER && requested(reqed, "storage_server_first_unreachable")) + entry["storage_server_first_unreachable"] = reachable_to_time_t(proof.ss_reachable.first_unreachable, system_now, steady_now); + if (proof.ss_reachable.last_unreachable != master_nodes::NEVER && requested(reqed, "storage_server_last_unreachable")) + entry["storage_server_last_unreachable"] = reachable_to_time_t(proof.ss_reachable.last_unreachable, system_now, steady_now); + if (proof.ss_reachable.last_reachable != master_nodes::NEVER && requested(reqed, "storage_server_last_reachable")) + entry["storage_server_last_reachable"] = reachable_to_time_t(proof.ss_reachable.last_reachable, system_now, steady_now); + if (proof.belnet_reachable.first_unreachable != master_nodes::NEVER && requested(reqed, "belnet_first_unreachable")) + entry["belnet_first_unreachable"] = reachable_to_time_t(proof.belnet_reachable.first_unreachable, system_now, steady_now); + if (proof.belnet_reachable.last_unreachable != master_nodes::NEVER && requested(reqed, "belnet_last_unreachable")) + entry["belnet_last_unreachable"] = reachable_to_time_t(proof.belnet_reachable.last_unreachable, system_now, steady_now); + if (proof.belnet_reachable.last_reachable != master_nodes::NEVER && requested(reqed, "belnet_last_reachable")) + entry["belnet_last_reachable"] = reachable_to_time_t(proof.belnet_reachable.last_reachable, system_now, steady_now); + } + + if (requested(reqed, "checkpoint_votes") && !proof.checkpoint_participation.empty()) { + std::vector voted, missed; + for (auto& cpp : proof.checkpoint_participation) + (cpp.pass() ? voted : missed).push_back(cpp.height); + std::sort(voted.begin(), voted.end()); + std::sort(missed.begin(), missed.end()); + entry["checkpoint_votes"] = json{ + {"voted", voted}, + {"missed", missed}}; + } + if (requested(reqed, "POS_votes") && !proof.POS_participation.empty()) { + std::vector> voted, missed; + for (auto& ppp : proof.POS_participation) + (ppp.pass() ? voted : missed).emplace_back(ppp.height, ppp.round); + std::sort(voted.begin(), voted.end()); + std::sort(missed.begin(), missed.end()); + entry["POS_votes"]["voted"] = voted; + entry["POS_votes"]["missed"] = missed; + } + if (requested(reqed, "quorumnet_tests") && !proof.timestamp_participation.empty()) { + auto fails = proof.timestamp_participation.failures(); + entry["quorumnet_tests"] = json::array({proof.timestamp_participation.size() - fails, fails}); + } + if (requested(reqed, "timesync_tests") && !proof.timesync_status.empty()) { + auto fails = proof.timesync_status.failures(); + entry["timesync_tests"] = json::array({proof.timesync_status.size() - fails, fails}); + } }); - entry.contributors.reserve(info.contributors.size()); - - using namespace master_nodes; - for (master_node_info::contributor_t const &contributor : info.contributors) - { - entry.contributors.push_back({}); - auto &new_contributor = entry.contributors.back(); - new_contributor.amount = contributor.amount; - new_contributor.reserved = contributor.reserved; - new_contributor.address = cryptonote::get_account_address_as_str(m_core.get_nettype(), false/*is_subaddress*/, contributor.address); - - new_contributor.locked_contributions.reserve(contributor.locked_contributions.size()); - for (master_node_info::contribution_t const &src : contributor.locked_contributions) - { - new_contributor.locked_contributions.push_back({}); - auto &dest = new_contributor.locked_contributions.back(); - dest.amount = src.amount; - dest.key_image = tools::type_to_hex(src.key_image); - dest.key_image_pub_key = tools::type_to_hex(src.key_image_pub_key); + if (requested(reqed, "contributors")) { + bool want_locked_c = requested(reqed, "locked_contributions"); + auto& contributors = (entry["contributors"] = json::array()); + for (const auto& contributor : info.contributors) { + auto& c = contributors.emplace_back(json{ + {"amount", contributor.amount}, + {"address", cryptonote::get_account_address_as_str(m_core.get_nettype(), false/*subaddress*/, contributor.address)}}); + if (contributor.reserved != contributor.amount) + c["reserved"] = contributor.reserved; + if (want_locked_c) { + auto& locked = (c["locked_contributions"] = json::array()); + for (const auto& src : contributor.locked_contributions) { + auto& lc = locked.emplace_back(json{{"amount", src.amount}}); + json_binary_proxy lc_binary{lc, binary_format}; + lc_binary["key_image"] = src.key_image; + lc_binary["key_image_pub_key"] = src.key_image_pub_key; + } + } } } - - entry.total_contributed = info.total_contributed; - entry.total_reserved = info.total_reserved; - entry.staking_requirement = info.staking_requirement; - entry.portions_for_operator = info.portions_for_operator; - entry.operator_address = cryptonote::get_account_address_as_str(m_core.get_nettype(), false/*is_subaddress*/, info.operator_address); - entry.swarm_id = info.swarm_id; - entry.registration_hf_version = info.registration_hf_version; - } - static constexpr GET_MASTER_NODES::requested_fields_t all_fields{true}; //------------------------------------------------------------------------------------------------------------------------------ - GET_MASTER_NODES::response core_rpc_server::invoke(GET_MASTER_NODES::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MASTER_NODES& mns, rpc_context context) { - GET_MASTER_NODES::response res{}; - - res.status = STATUS_OK; - res.height = m_core.get_current_blockchain_height() - 1; - res.target_height = m_core.get_target_blockchain_height(); - res.block_hash = tools::type_to_hex(m_core.get_block_id_by_height(res.height)); - auto [hf, mnode_rev] = get_network_version_revision(nettype(), res.height); - res.hardfork = hf; - res.mnode_revision = mnode_rev; - - if (!req.poll_block_hash.empty()) { - res.polling_mode = true; - if (req.poll_block_hash == res.block_hash) { - res.unchanged = true; - res.fields = req.fields.value_or(all_fields); - return res; - } - } + auto& req = mns.request; + mns.response["status"] = STATUS_OK; + auto [top_height, top_hash] = m_core.get_blockchain_top(); + auto [hf, mnode_rev] = get_network_version_revision(nettype(), top_height); + set_if_requested(req.fields, mns.response, + "height", top_height, + "target_height", m_core.get_target_blockchain_height(), + "hardfork", hf, + "mnode_revision", mnode_rev); + set_if_requested(req.fields, mns.response_hex, + "block_hash", top_hash); - std::vector pubkeys(req.master_node_pubkeys.size()); - for (size_t i = 0; i < req.master_node_pubkeys.size(); i++) - { - if (!tools::hex_to_type(req.master_node_pubkeys[i], pubkeys[i])) - throw rpc_error{ERROR_WRONG_PARAM, - "Could not convert to a public key, arg: " + std::to_string(i) - + " which is pubkey: " + req.master_node_pubkeys[i]}; + if (req.poll_block_hash) { + bool unchanged = req.poll_block_hash == top_hash; + mns.response["unchanged"] = unchanged; + if (unchanged) + return; + if (!requested(req.fields, "block_hash")) + mns.response_hex["block_hash"] = top_hash; // Force it on a poll request even if it wasn't a requested field } - auto mn_infos = m_core.get_master_node_list_state(pubkeys); + auto mn_infos = m_core.get_master_node_list_state(req.master_node_pubkeys); - if (req.active_only) { - const auto end = + if (req.active_only) + mn_infos.erase( std::remove_if(mn_infos.begin(), mn_infos.end(), [](const master_nodes::master_node_pubkey_info& mnpk_info) { return !mnpk_info.info->is_active(); - }); - - mn_infos.erase(end, mn_infos.end()); - } - - if (req.limit != 0) { - - const auto limit = std::min(mn_infos.size(), static_cast(req.limit)); - + }), + mn_infos.end()); + + const int top_mn_index = (int) mn_infos.size() - 1; + if (req.limit < 0 || req.limit > top_mn_index) { + // We asked for -1 (no limit but shuffle) or a value >= the count, so just shuffle the entire list + std::shuffle(mn_infos.begin(), mn_infos.end(), tools::rng); + } else if (req.limit > 0) { // We need to select N random elements, in random order, from yyyyyyyy. We could (and used // to) just shuffle the entire list and return the first N, but that is quite inefficient when // the list is large and N is small. So instead this algorithm is going to select a random - // element from yyyyyyyy, swap it to position 0, so we get: [x]yyyyyyyy where one of the new - // y's used to be at element 0. Then we select a random element from the new y's (i.e. all // the elements beginning at position 1), and swap it into element 1, to get [xx]yyyyyy, then // keep repeating until our set of x's is big enough, say [xxx]yyyyy. At that point we chop // of the y's to just be left with [xxx], and only required N swaps in total. - for (size_t i = 0; i < limit; i++) + for (int i = 0; i < req.limit; i++) { - size_t j = std::uniform_int_distribution{i, mn_infos.size()-1}(tools::rng); + int j = std::uniform_int_distribution{i, top_mn_index}(tools::rng); using std::swap; if (i != j) swap(mn_infos[i], mn_infos[j]); } - mn_infos.resize(limit); + mn_infos.resize(req.limit); } - res.master_node_states.reserve(mn_infos.size()); - res.fields = req.fields.value_or(all_fields); - - if (req.include_json) - { - if (mn_infos.empty()) - res.as_json = "{}"; - else - res.as_json = cryptonote::obj_to_json_str(mn_infos); - } - - for (auto &pubkey_info : mn_infos) { - res.master_node_states.emplace_back(); - fill_mn_response_entry(res.master_node_states.back(), pubkey_info, res.height); - } - - return res; + auto& mn_states = (mns.response["master_node_states"] = json::array()); + for (auto &pubkey_info : mn_infos) + fill_mn_response_entry(mn_states.emplace_back(json::object()), mns.is_bt(), req.fields, pubkey_info, top_height); } namespace { @@ -3236,26 +3158,32 @@ namespace cryptonote { namespace rpc { // after the ping had expired). `Success` is a callback that is invoked with a single boolean // argument: true if this ping should trigger an immediate proof send (i.e. first ping after // startup or after a ping expiry), false for an ordinary ping. - template - auto handle_ping( + template + std::string handle_ping( core& core, std::array cur_version, std::array required, std::string_view pubkey_ed25519, + std::string_view error, std::string_view name, std::atomic& update, std::chrono::seconds lifetime, Success success) { std::string our_pubkey_ed25519 = tools::type_to_hex(core.get_master_keys().pub_ed25519); - typename RPC::response res{}; - if (cur_version < required) { - res.status = fmt::format("Outdated {}. Current: {}.{}.{}, Required: {}.{}.{}",name, cur_version[0], cur_version[1], cur_version[2], required[0], required[1], required[2]); - MERROR(res.status); - } else if (!pubkey_ed25519.empty() && !(pubkey_ed25519.find_first_not_of('0') == std::string_view::npos) // TODO: once belnet & ss are always sending this we can remove this empty bypass - && (pubkey_ed25519 != our_pubkey_ed25519)) { - res.status = fmt::format("Invalid {} pubkey: expected {}, received {}", name, our_pubkey_ed25519, pubkey_ed25519); - MERROR(res.status); + std::string status{}; + if (!error.empty()) { + status = fmt::format("Error: {}", error); + MERROR(fmt::format("{0} reported an error: {1}. Check {0} logs for more details.", name, error)); + update = 0; // Reset our last ping time to 0 so that we won't send a ping until we get + // success back again (even if we had an earlier acceptable ping within the + // cutoff time). + } else if (cur_version < required) { + status = fmt::format("Outdated {}. Current: {}.{}.{}, Required: {}.{}.{}",name, cur_version[0], cur_version[1], cur_version[2], required[0], required[1], required[2]); + MERROR(status); + } else if (pubkey_ed25519 != our_pubkey_ed25519) { + status = fmt::format("Invalid {} pubkey: expected {}, received {}", name, our_pubkey_ed25519, pubkey_ed25519); + MERROR(status); } else { auto now = std::time(nullptr); auto old = update.exchange(now); @@ -3265,51 +3193,53 @@ namespace cryptonote { namespace rpc { else MDEBUG(fmt::format("Accepted ping from {} {}.{}.{}", name, cur_version[0], cur_version[1], cur_version[2])); success(significant); - res.status = STATUS_OK; + status = STATUS_OK; } - return res; + return status; } } //------------------------------------------------------------------------------------------------------------------------------ - STORAGE_SERVER_PING::response core_rpc_server::invoke(STORAGE_SERVER_PING::request&& req, rpc_context context) + void core_rpc_server::invoke(STORAGE_SERVER_PING& storage_server_ping, rpc_context context) { - m_core.ss_version = req.version; - return handle_ping(m_core, - req.version, master_nodes::MIN_STORAGE_SERVER_VERSION, - req.pubkey_ed25519, + m_core.ss_version = storage_server_ping.request.version; + storage_server_ping.response["status"] = handle_ping(m_core, + storage_server_ping.request.version, master_nodes::MIN_STORAGE_SERVER_VERSION, + storage_server_ping.request.pubkey_ed25519, + storage_server_ping.request.error, "Storage Server", m_core.m_last_storage_server_ping, m_core.get_net_config().UPTIME_PROOF_FREQUENCY, - [this, &req](bool significant) { - m_core.m_storage_https_port = req.https_port; - m_core.m_storage_omq_port = req.omq_port; + [this, &storage_server_ping](bool significant) { + m_core.m_storage_https_port = storage_server_ping.request.https_port; + m_core.m_storage_omq_port = storage_server_ping.request.omq_port; if (significant) m_core.reset_proof_interval(); }); } //------------------------------------------------------------------------------------------------------------------------------ - BELNET_PING::response core_rpc_server::invoke(BELNET_PING::request&& req, rpc_context context) + void core_rpc_server::invoke(BELNET_PING& belnet_ping, rpc_context context) { - m_core.belnet_version = req.version; - return handle_ping(m_core, - req.version, master_nodes::MIN_BELNET_VERSION, - req.pubkey_ed25519, + m_core.belnet_version = belnet_ping.request.version; + belnet_ping.response["status"] = handle_ping(m_core, + belnet_ping.request.version, master_nodes::MIN_BELNET_VERSION, + belnet_ping.request.pubkey_ed25519, + belnet_ping.request.error, "Belnet", m_core.m_last_belnet_ping, m_core.get_net_config().UPTIME_PROOF_FREQUENCY, [this](bool significant) { if (significant) m_core.reset_proof_interval(); }); } //------------------------------------------------------------------------------------------------------------------------------ - GET_STAKING_REQUIREMENT::response core_rpc_server::invoke(GET_STAKING_REQUIREMENT::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_STAKING_REQUIREMENT& get_staking_requirement, rpc_context context) { - GET_STAKING_REQUIREMENT::response res{}; - PERF_TIMER(on_get_staking_requirement); - res.height = req.height > 0 ? req.height : m_core.get_current_blockchain_height(); + get_staking_requirement.response["height"] = get_staking_requirement.request.height > 0 ? get_staking_requirement.request.height : m_core.get_current_blockchain_height(); - res.staking_requirement = master_nodes::get_staking_requirement(res.height); - res.status = STATUS_OK; - return res; + get_staking_requirement.response["staking_requirement"] = master_nodes::get_staking_requirement(get_staking_requirement.response["height"]); + get_staking_requirement.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - static void check_quantity_limit(size_t count, size_t max, char const *container_name = nullptr) + + template + static void check_quantity_limit(T count, T max, const char* container_name = "input") { if (count > max) { @@ -3320,87 +3250,96 @@ namespace cryptonote { namespace rpc { throw rpc_error{ERROR_WRONG_PARAM, err.str()}; } } + + template + static void check_quantity_limit(std::optional count, T max, const char* name = "input") { + if (count) + check_quantity_limit(*count, max, name); + } + //------------------------------------------------------------------------------------------------------------------------------ - GET_CHECKPOINTS::response core_rpc_server::invoke(GET_CHECKPOINTS::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_CHECKPOINTS& get_checkpoints, rpc_context context) { - GET_CHECKPOINTS::response res{}; + if (!context.admin) + check_quantity_limit(get_checkpoints.request.count, GET_CHECKPOINTS::MAX_COUNT); - if (use_bootstrap_daemon_if_necessary(req, res)) - return res; + json params; + if (get_checkpoints.request.start_height.has_value()) + params["start_height"] = *get_checkpoints.request.start_height; + if (get_checkpoints.request.end_height.has_value()) + params["end_height"] = *get_checkpoints.request.end_height; + if (get_checkpoints.request.count.has_value()) + params["count"] = *get_checkpoints.request.count; - if (!context.admin) - check_quantity_limit(req.count, GET_CHECKPOINTS::MAX_COUNT); + if (use_bootstrap_daemon_if_necessary(params, get_checkpoints.response)) + return; + + auto& start = get_checkpoints.request.start_height; + auto& end = get_checkpoints.request.end_height; + auto count = get_checkpoints.request.count.value_or(GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT); - res.status = STATUS_OK; - BlockchainDB const &db = m_core.get_blockchain_storage().get_db(); + get_checkpoints.response["status"] = STATUS_OK; + const auto& db = m_core.get_blockchain_storage().get_db(); std::vector checkpoints; - if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE && - req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE) - { - checkpoint_t top_checkpoint; - if (db.get_top_checkpoint(top_checkpoint)) - checkpoints = db.get_checkpoints_range(top_checkpoint.height, 0, req.count); - } - else if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE) - { - checkpoints = db.get_checkpoints_range(req.end_height, 0, req.count); - } - else if (req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE) + if (!start && !end) { - checkpoints = db.get_checkpoints_range(req.start_height, UINT64_MAX, req.count); + if (checkpoint_t top_checkpoint; db.get_top_checkpoint(top_checkpoint)) + checkpoints = db.get_checkpoints_range(top_checkpoint.height, 0, count); } + else if (!start) + checkpoints = db.get_checkpoints_range(*end, 0, count); + else if (!end) + checkpoints = db.get_checkpoints_range(*start, UINT64_MAX, count); else - { - checkpoints = db.get_checkpoints_range(req.start_height, req.end_height); - } + checkpoints = + context.admin + ? db.get_checkpoints_range(*start, *end) + : db.get_checkpoints_range(*start, *end, GET_CHECKPOINTS::MAX_COUNT); - res.checkpoints.reserve(checkpoints.size()); - for (checkpoint_t const &checkpoint : checkpoints) - res.checkpoints.push_back(checkpoint); + get_checkpoints.response["checkpoints"] = std::move(checkpoints); - return res; + return; } //------------------------------------------------------------------------------------------------------------------------------ - GET_MN_STATE_CHANGES::response core_rpc_server::invoke(GET_MN_STATE_CHANGES::request&& req, rpc_context context) + void core_rpc_server::invoke(GET_MN_STATE_CHANGES& get_mn_state_changes, rpc_context context) { - GET_MN_STATE_CHANGES::response res{}; + json params; + params["start_height"] = get_mn_state_changes.request.start_height; + if (get_mn_state_changes.request.end_height.has_value()) + params["end_height"] = *get_mn_state_changes.request.end_height; + + if (use_bootstrap_daemon_if_necessary(params, get_mn_state_changes.response)) + return; using blob_t = cryptonote::blobdata; using block_pair_t = std::pair; std::vector blocks; const auto& db = m_core.get_blockchain_storage(); - const uint64_t current_height = db.get_current_blockchain_height(); + auto start_height = get_mn_state_changes.request.start_height; + auto end_height = get_mn_state_changes.request.end_height.value_or(db.get_current_blockchain_height() - 1); - uint64_t end_height; - if (req.end_height == GET_MN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE) { - // current height is the block being mined, so exclude it from the results - end_height = current_height - 1; - } else { - end_height = req.end_height; - } - - if (end_height < req.start_height) + if (end_height < start_height) throw rpc_error{ERROR_WRONG_PARAM, "The provided end_height needs to be higher than start_height"}; - if (!db.get_blocks(req.start_height, end_height - req.start_height + 1, blocks)) - throw rpc_error{ERROR_INTERNAL, "Could not query block at requested height: " + std::to_string(req.start_height)}; + if (!db.get_blocks(start_height, end_height - start_height + 1, blocks)) + throw rpc_error{ERROR_INTERNAL, "Could not query block at requested height: " + std::to_string(start_height)}; - res.start_height = req.start_height; - res.end_height = end_height; + get_mn_state_changes.response["start_height"] = start_height; + get_mn_state_changes.response["end_height"] = end_height; std::vector blobs; - std::vector missed_ids; + int total_deregister = 0, total_decommission = 0, total_recommission = 0, total_ip_change_penalty = 0, total_unlock = 0; for (const auto& block : blocks) { blobs.clear(); - if (!db.get_transactions_blobs(block.second.tx_hashes, blobs, missed_ids)) + if (!db.get_transactions_blobs(block.second.tx_hashes, blobs)) { MERROR("Could not query block at requested height: " << cryptonote::get_block_height(block.second)); continue; } - const uint8_t hard_fork_version = block.second.major_version; + const auto hard_fork_version = block.second.major_version; for (const auto& blob : blobs) { cryptonote::transaction tx; @@ -3414,25 +3353,25 @@ namespace cryptonote { namespace rpc { cryptonote::tx_extra_master_node_state_change state_change; if (!cryptonote::get_master_node_state_change_from_tx_extra(tx.extra, state_change, hard_fork_version)) { - LOG_ERROR("Could not get state change from tx, possibly corrupt tx, hf_version "<< std::to_string(hard_fork_version)); + LOG_ERROR("Could not get state change from tx, possibly corrupt tx, hf_version "<< static_cast(hard_fork_version)); continue; } switch(state_change.state) { case master_nodes::new_state::deregister: - res.total_deregister++; + total_deregister++; break; case master_nodes::new_state::decommission: - res.total_decommission++; + total_decommission++; break; case master_nodes::new_state::recommission: - res.total_recommission++; + total_recommission++; break; case master_nodes::new_state::ip_change_penalty: - res.total_ip_change_penalty++; + total_ip_change_penalty++; break; default: @@ -3443,169 +3382,127 @@ namespace cryptonote { namespace rpc { if (tx.type == cryptonote::txtype::key_image_unlock) { - res.total_unlock++; + total_unlock++; } } } - res.status = STATUS_OK; - return res; + get_mn_state_changes.response["total_deregister"] = total_deregister; + get_mn_state_changes.response["total_decommission"] = total_decommission; + get_mn_state_changes.response["total_recommission"] = total_recommission; + get_mn_state_changes.response["total_ip_change_penalty"] = total_ip_change_penalty; + get_mn_state_changes.response["total_unlock"] = total_unlock; + get_mn_state_changes.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - REPORT_PEER_STATUS::response core_rpc_server::invoke(REPORT_PEER_STATUS::request&& req, rpc_context context) + void core_rpc_server::invoke(REPORT_PEER_STATUS& report_peer_status, rpc_context context) { - REPORT_PEER_STATUS::response res{}; - crypto::public_key pubkey; - if (!tools::hex_to_type(req.pubkey, pubkey)) { - MERROR("Could not parse public key: " << req.pubkey); + if (!tools::hex_to_type(report_peer_status.request.pubkey, pubkey)) { + MERROR("Could not parse public key: " << report_peer_status.request.pubkey); throw rpc_error{ERROR_WRONG_PARAM, "Could not parse public key"}; } bool success = false; - if (req.type == "belnet") - success = m_core.get_master_node_list().set_belnet_peer_reachable(pubkey, req.passed); - else if (req.type == "storage" || req.type == "reachability" /* TODO: old name, can be removed once SS no longer uses it */) - success = m_core.get_master_node_list().set_storage_server_peer_reachable(pubkey, req.passed); + if (report_peer_status.request.type == "belnet") + success = m_core.get_master_node_list().set_belnet_peer_reachable(pubkey, report_peer_status.request.passed); + else if (report_peer_status.request.type == "storage" || report_peer_status.request.type == "reachability" /* TODO: old name, can be removed once SS no longer uses it */) + success = m_core.get_master_node_list().set_storage_server_peer_reachable(pubkey, report_peer_status.request.passed); else throw rpc_error{ERROR_WRONG_PARAM, "Unknown status type"}; if (!success) throw rpc_error{ERROR_WRONG_PARAM, "Pubkey not found"}; - res.status = STATUS_OK; - return res; + report_peer_status.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - TEST_TRIGGER_P2P_RESYNC::response core_rpc_server::invoke(TEST_TRIGGER_P2P_RESYNC::request&& req, rpc_context context) + void core_rpc_server::invoke(TEST_TRIGGER_P2P_RESYNC& test_trigger_p2p_resync, rpc_context context) { - TEST_TRIGGER_P2P_RESYNC::response res{}; - m_p2p.reset_peer_handshake_timer(); - res.status = STATUS_OK; - return res; + test_trigger_p2p_resync.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - TEST_TRIGGER_UPTIME_PROOF::response core_rpc_server::invoke(TEST_TRIGGER_UPTIME_PROOF::request&& req, rpc_context context) + void core_rpc_server::invoke(TEST_TRIGGER_UPTIME_PROOF& test_trigger_uptime_proof, rpc_context context) { - if (m_core.get_nettype() != cryptonote::MAINNET) + if (m_core.get_nettype() != cryptonote::network_type::MAINNET) m_core.submit_uptime_proof(); - TEST_TRIGGER_UPTIME_PROOF::response res{}; - res.status = STATUS_OK; - return res; + test_trigger_uptime_proof.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - BNS_NAMES_TO_OWNERS::response core_rpc_server::invoke(BNS_NAMES_TO_OWNERS::request&& req, rpc_context context) + void core_rpc_server::invoke(BNS_NAMES_TO_OWNERS& names_to_owners, rpc_context context) { - BNS_NAMES_TO_OWNERS::response res{}; - if (!context.admin) - check_quantity_limit(req.entries.size(), BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES); + check_quantity_limit(names_to_owners.request.name_hash.size(), BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES); - std::optional height = m_core.get_current_blockchain_height(); - uint8_t hf_version = get_network_version(nettype(), *height); - if (req.include_expired) height = std::nullopt; + json params{ + {"name_hash", json::array()}, + {"include_expired", names_to_owners.request.include_expired}, + }; + for (const auto& name_hash: names_to_owners.request.name_hash) + params["name_hash"].push_back(name_hash); + + if (use_bootstrap_daemon_if_necessary(params, names_to_owners.response)) + return; - std::vector types; + std::optional height = m_core.get_current_blockchain_height(); + auto hf_version = get_network_version(nettype(), *height); + if (names_to_owners.request.include_expired) height = std::nullopt; bns::name_system_db &db = m_core.get_blockchain_storage().name_system_db(); - for (size_t request_index = 0; request_index < req.entries.size(); request_index++) + for (size_t request_index = 0; request_index < names_to_owners.request.name_hash.size(); request_index++) { - std::string const &req_name_hash = req.entries[request_index]; + const auto& request = names_to_owners.request.name_hash[request_index]; // This also takes 32 raw bytes, but that is undocumented (because it is painful to pass // through json). - auto name_hash = bns::name_hash_input_to_base64(req_name_hash); + auto name_hash = bns::name_hash_input_to_base64(names_to_owners.request.name_hash[request_index]); if (!name_hash) throw rpc_error{ERROR_WRONG_PARAM, "Invalid name_hash: expected hash as 64 hex digits or 43/44 base64 characters"}; std::vector records = db.get_mappings(*name_hash, height); for (auto const &record : records) { - auto& entry = res.entries.emplace_back(); - entry.entry_index = request_index; - entry.name_hash = record.name_hash; - entry.owner = record.owner.to_string(nettype()); - if (record.backup_owner) entry.backup_owner = record.backup_owner.to_string(nettype()); - entry.encrypted_bchat_value = oxenc::to_hex(record.encrypted_bchat_value.to_view()); - entry.encrypted_wallet_value = oxenc::to_hex(record.encrypted_wallet_value.to_view()); - entry.encrypted_belnet_value = oxenc::to_hex(record.encrypted_belnet_value.to_view()); - entry.encrypted_eth_addr_value = oxenc::to_hex(record.encrypted_eth_addr_value.to_view()); - entry.expiration_height = record.expiration_height; - entry.update_height = record.update_height; - entry.txid = tools::type_to_hex(record.txid); + auto& elem = names_to_owners.response["result"].emplace_back(); + elem["entry_index"] = request_index; + elem["name_hash"] = record.name_hash; + elem["owner"] = record.owner.to_string(nettype()); + if (record.backup_owner) elem["backup_owner"] = record.backup_owner.to_string(nettype()); + elem["encrypted_bchat_value"] = oxenc::to_hex(record.encrypted_bchat_value.to_view()); + elem["encrypted_wallet_value"] = oxenc::to_hex(record.encrypted_wallet_value.to_view()); + elem["encrypted_belnet_value"] = oxenc::to_hex(record.encrypted_belnet_value.to_view()); + elem["encrypted_eth_addr_value"] = oxenc::to_hex(record.encrypted_eth_addr_value.to_view()); + elem["expiration_height"] = record.expiration_height; + elem["update_height"] = record.update_height; + elem["txid"] = tools::type_to_hex(record.txid); } } - - res.status = STATUS_OK; - return res; + names_to_owners.response["status"] = STATUS_OK; } //------------------------------------------------------------------------------------------------------------------------------ - BNS_LOOKUP::response core_rpc_server::invoke(BNS_LOOKUP::request&& req, rpc_context context) + void core_rpc_server::invoke(BNS_OWNERS_TO_NAMES& owners_to_names, rpc_context context) { - BNS_LOOKUP::response res{}; - - std::string name = tools::lowercase_ascii_string(std::move(req.name)); + if (!context.admin) + check_quantity_limit(owners_to_names.request.entries.size(), BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES); - BNS_NAMES_TO_OWNERS::request name_to_owner_req{}; - name_to_owner_req.entries.push_back(bns::name_to_base64_hash(name)); - auto name_to_owner_res = invoke(std::move(name_to_owner_req), context); - - if(name_to_owner_res.entries.size() != 1){ - throw rpc_error{ERROR_INVALID_RESULT, "Invalid data returned from BNS_NAMES_TO_OWNERS"}; - } - - auto entries = name_to_owner_res.entries.back(); - { - res.name_hash = entries.name_hash; - res.owner = entries.owner; - if (entries.backup_owner) res.backup_owner = entries.backup_owner; - res.expiration_height = entries.expiration_height; - res.update_height = entries.update_height; - res.txid = entries.txid; - - if(!entries.encrypted_bchat_value.empty()){ - BNS_VALUE_DECRYPT::request bns_value_decrypt_req{name, "bchat", entries.encrypted_bchat_value}; - auto bns_value_decrypt_res = invoke(std::move(bns_value_decrypt_req), context); - res.bchat_value = bns_value_decrypt_res.value; - } - if(!entries.encrypted_belnet_value.empty()){ - BNS_VALUE_DECRYPT::request bns_value_decrypt_req{name, "belnet", entries.encrypted_belnet_value}; - auto bns_value_decrypt_res = invoke(std::move(bns_value_decrypt_req), context); - res.belnet_value = bns_value_decrypt_res.value; - } - if(!entries.encrypted_wallet_value.empty()){ - BNS_VALUE_DECRYPT::request bns_value_decrypt_req{name, "wallet", entries.encrypted_wallet_value}; - auto bns_value_decrypt_res = invoke(std::move(bns_value_decrypt_req), context); - res.wallet_value = bns_value_decrypt_res.value; - } - if(!entries.encrypted_eth_addr_value.empty()){ - BNS_VALUE_DECRYPT::request bns_value_decrypt_req{name, "eth_addr", entries.encrypted_eth_addr_value}; - auto bns_value_decrypt_res = invoke(std::move(bns_value_decrypt_req), context); - res.eth_addr_value = bns_value_decrypt_res.value; - } - } - - res.status = STATUS_OK; - return res; - } - - //------------------------------------------------------------------------------------------------------------------------------ - BNS_OWNERS_TO_NAMES::response core_rpc_server::invoke(BNS_OWNERS_TO_NAMES::request&& req, rpc_context context) - { - BNS_OWNERS_TO_NAMES::response res{}; + json params{ + {"entries", json::array()}, + {"include_expired", owners_to_names.request.include_expired}, + }; + for (const auto& name_hash: owners_to_names.request.entries) + params["entries"].push_back(name_hash); - if (!context.admin) - check_quantity_limit(req.entries.size(), BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES); + if (use_bootstrap_daemon_if_necessary(params, owners_to_names.response)) + return; std::unordered_map owner_to_request_index; std::vector owners; - owners.reserve(req.entries.size()); - for (size_t request_index = 0; request_index < req.entries.size(); request_index++) + owners.reserve(owners_to_names.request.entries.size()); + for (size_t request_index = 0; request_index < owners_to_names.request.entries.size(); request_index++) { - std::string const &owner = req.entries[request_index]; + std::string const &owner = owners_to_names.request.entries[request_index]; bns::generic_owner bns_owner = {}; std::string errmsg; if (!bns::parse_owner_to_generic_owner(m_core.get_nettype(), owner, bns_owner, &errmsg)) @@ -3621,8 +3518,9 @@ namespace cryptonote { namespace rpc { bns::name_system_db &db = m_core.get_blockchain_storage().name_system_db(); std::optional height; - if (!req.include_expired) height = m_core.get_current_blockchain_height(); + if (!owners_to_names.request.include_expired) height = m_core.get_current_blockchain_height(); + std::vector entries; std::vector records = db.get_mappings_by_owners(owners, height); for (auto &record : records) { @@ -3637,7 +3535,7 @@ namespace cryptonote { namespace rpc { (record.backup_owner ? ("BackupOwner=" + record.backup_owner.to_string(nettype()) + " ") : ""s) + " could not be mapped back a index in the request 'entries' array"}; - auto& entry = res.entries.emplace_back(); + auto& entry = entries.emplace_back(); entry.request_index = it->second; entry.name_hash = std::move(record.name_hash); if (record.owner) entry.owner = record.owner.to_string(nettype()); @@ -3651,41 +3549,98 @@ namespace cryptonote { namespace rpc { entry.txid = tools::type_to_hex(record.txid); } - res.status = STATUS_OK; - return res; + owners_to_names.response["entries"] = entries; + owners_to_names.response["status"] = STATUS_OK; + return; } //------------------------------------------------------------------------------------------------------------------------------ - BNS_RESOLVE::response core_rpc_server::invoke(BNS_RESOLVE::request&& req, rpc_context context) + void core_rpc_server::invoke(BNS_RESOLVE& resolve, rpc_context context) { - BNS_RESOLVE::response res{}; - - if (req.type >= static_cast>(bns::mapping_type::belnet_2years)) + auto& req = resolve.request; + if (req.type < 0 || req.type >= tools::enum_count) throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve BNS address: 'type' parameter not specified"}; auto name_hash = bns::name_hash_input_to_base64(req.name_hash); if (!name_hash) throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve BNS address: invalid 'name_hash' value '" + req.name_hash + "'"}; + json params{ + {"type", resolve.request.type}, + {"name_hash", *name_hash}, + }; + + if (use_bootstrap_daemon_if_necessary(params, resolve.response)) + return; - uint8_t hf_version = m_core.get_blockchain_storage().get_network_version(); + auto hf_version = m_core.get_blockchain_storage().get_network_version(); auto type = static_cast(req.type); if (auto mapping = m_core.get_blockchain_storage().name_system_db().resolve( type, *name_hash, m_core.get_current_blockchain_height())) { auto [val, nonce] = mapping->value_nonce(type); - res.encrypted_value = oxenc::to_hex(val); + resolve.response_hex["encrypted_value"] = val; if (val.size() < mapping->to_view().size()) - res.nonce = oxenc::to_hex(nonce); + resolve.response_hex["nonce"] = nonce; } - return res; } + //------------------------------------------------------------------------------------------------------------------------------ - BNS_VALUE_DECRYPT::response core_rpc_server::invoke(BNS_VALUE_DECRYPT::request&& req, rpc_context context) + void core_rpc_server::invoke(BNS_LOOKUP& lookup, rpc_context context) { - BNS_VALUE_DECRYPT::response res{}; + std::string name = tools::lowercase_ascii_string(std::move(lookup.request.name)); + json params{ + {"name", lookup.request.name} + }; + if (use_bootstrap_daemon_if_necessary(params, lookup.response)) + return; + + BNS_NAMES_TO_OWNERS name_to_owner{}; + name_to_owner.request.name_hash.push_back(bns::name_to_base64_hash(name)); + invoke(name_to_owner, context); + + if(name_to_owner.response["result"].size() != 1){ + throw rpc_error{ERROR_INVALID_RESULT, "Invalid data returned from BNS_NAMES_TO_OWNERS"}; + } + + auto entries = name_to_owner.response["result"].back(); + { + lookup.response["name_hash"] = entries["name_hash"]; + lookup.response["owner"] = entries["owner"]; + if (!entries["backup_owner"].empty()) + lookup.response["backup_owner"] = entries["backup_owner"]; + lookup.response["expiration_height"] = entries["expiration_height"]; + lookup.response["update_height"] = entries["update_height"]; + lookup.response["txid"] = entries["txid"]; + + for (const auto& [type, key] : std::vector>{ + {"bchat", "encrypted_bchat_value"}, + {"belnet", "encrypted_belnet_value"}, + {"wallet", "encrypted_wallet_value"}, + {"eth_addr", "encrypted_eth_addr_value"}}) + { + if (entries.contains(key) && !entries[key].get().empty()) { + BNS_VALUE_DECRYPT value_decrypt; + value_decrypt.request = {name, type, entries[key].get()}; + try { + invoke(value_decrypt, context); + lookup.response[type + "_value"] = value_decrypt.response["value"]; + } catch (const rpc_error& e) { + MERROR("Value decryption failed for type " << type << ": " << e.what()); + } + } + } + } + + lookup.response["status"] = STATUS_OK; + } + + //------------------------------------------------------------------------------------------------------------------------------ + void core_rpc_server::invoke(BNS_VALUE_DECRYPT& value_decrypt, rpc_context context) + { + auto& req = value_decrypt.request; // --------------------------------------------------------------------------------------------- // // Validate encrypted value @@ -3708,8 +3663,8 @@ namespace cryptonote { namespace rpc { std::string reason; bns::mapping_type type = {}; - std::optional hf_version = m_core.get_blockchain_storage().get_network_version(); - if (!bns::validate_mapping_type(req.type, *hf_version, &type, &reason)) + auto hf_version = m_core.get_blockchain_storage().get_network_version(); + if (!bns::validate_mapping_type(req.type, hf_version, &type, &reason)) throw rpc_error{ERROR_INVALID_VALUE_LENGTH, "Invalid BNS type: " + reason}; if (!bns::validate_bns_name(req.name, &reason)) @@ -3728,7 +3683,7 @@ namespace cryptonote { namespace rpc { if (!value.decrypt(req.name, type)) throw rpc_error{ERROR_INTERNAL, "Value decryption failure"}; - res.value = value.to_readable_value(nettype(), type); - return res; + value_decrypt.response["value"] = value.to_readable_value(nettype(), type); + value_decrypt.response["status"] = STATUS_OK; } -} } // namespace cryptonote +} // namespace cryptonote::rpc diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 26a8a3ef824..a9a5b332625 100755 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -37,11 +37,12 @@ #include #include -#include "bootstrap_daemon.h" -#include "core_rpc_server_commands_defs.h" #include "cryptonote_core/cryptonote_core.h" +#include "core_rpc_server_commands_defs.h" +#include "core_rpc_server_binary_commands.h" #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "rpc/common/rpc_command.h" #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) #include "common/beldex_integration_test_hooks.h" @@ -51,92 +52,30 @@ #define BELDEX_DEFAULT_LOG_CATEGORY "daemon.rpc" namespace boost::program_options { -class options_description; -class variables_map; + class options_description; + class variables_map; } -namespace cryptonote::rpc { - - /// Exception when trying to invoke an RPC command that indicate a parameter parse failure (will - /// give an invalid params error for JSON-RPC, for example). - struct parse_error : std::runtime_error { using std::runtime_error::runtime_error; }; - - /// Exception used to signal various types of errors with a request back to the caller. This - /// exception indicates that the caller did something wrong: bad data, invalid value, etc., but - /// don't indicate a local problem (and so we'll log them only at debug). For more serious, - /// internal errors a command should throw some other stl error (e.g. std::runtime_error or - /// perhaps std::logic_error), which will result in a local daemon warning (and a generic internal - /// error response to the user). - /// - /// For JSON RPC these become an error response with the code as the error.code value and the - /// string as the error.message. - /// For HTTP JSON these become a 500 Internal Server Error response with the message as the body. - /// For OxenMQ the code becomes the first part of the response and the message becomes the - /// second part of the response. - struct rpc_error : std::runtime_error { - /// \param code - a signed, 16-bit numeric code. 0 must not be used (as it is used for a - /// success code in OxenMQ), and values in the -32xxx range are reserved by JSON-RPC. - /// - /// \param message - a message to send along with the error code (see general description above). - rpc_error(int16_t code, std::string message) - : std::runtime_error{"RPC error " + std::to_string(code) + ": " + message}, - code{code}, message{std::move(message)} {} - - int16_t code; - std::string message; - }; - - /// Junk that epee makes us deal with to pass in a generically parsed json value - using jsonrpc_params = std::pair; - - enum struct rpc_source : uint8_t { internal, http, omq }; - - /// Contains the context of the invocation, which must be filled out by the glue code (e.g. HTTP - /// RPC server) with requester-specific context details. - struct rpc_context { - // Specifies that the requestor has admin permissions (e.g. is on an unrestricted RPC port, or - // is a local internal request). This can be used to provide different results for an admin - // versus non-admin when invoking a public RPC command. (Note that non-public RPC commands do - // not need to check this field for authentication: a non-public invoke() is not called in the - // first place if attempted by a public requestor). - bool admin = false; - - // The RPC engine source of the request, i.e. internal, HTTP, LMQ - rpc_source source = rpc_source::internal; - - // A free-form identifier (meant for humans) identifiying the remote address of the request; - // this might be IP:PORT, or could contain a pubkey, or ... - std::string remote; - }; +namespace cryptonote { + class bootstrap_daemon; +} - struct rpc_request { - // The request body; for a non-HTTP-JSON-RPC request the string or string_view will be populated - // with the unparsed request body (though may be empty, e.g. for GET requests). For HTTP - // JSON-RPC request, if the request has a "params" value then the epee storage pair will be set - // to the portable_storage entry and the storage entry containing "params". If "params" is - // omitted entirely (or, for LMQ, there is no data part) then the string will be set in the - // variant (and empty). - // - // The returned value in either case is the serialized value to return. - // - // If sometimes goes wrong, throw. - std::variant body; - - // Returns a string_view of the body, if the body is a string or string_view. Returns - // std::nullopt if the body is a jsonrpc_params. - std::optional body_view() const; - - // Values to pass through to the invoke() call - rpc_context context; - }; +namespace cryptonote::rpc { + // FIXME: temporary shim for converting RPC methods + template + struct FIXME_has_nested_response : std::false_type {}; + template + struct FIXME_has_nested_response> : std::true_type {}; + template constexpr bool FIXME_has_nested_response_v = FIXME_has_nested_response::value; class core_rpc_server; /// Stores an RPC command callback. These are set up in core_rpc_server.cpp. struct rpc_command { + using result_type = std::variant; // Called with the incoming command data; returns the response body if all goes well, // otherwise throws an exception. - std::string(*invoke)(rpc_request&&, core_rpc_server&); + result_type(*invoke)(rpc_request&&, core_rpc_server&); bool is_public; // callable via restricted RPC bool is_binary; // only callable at /name (for HTTP RPC), and binary data, not JSON. bool is_legacy; // callable at /name (for HTTP RPC), even though it is JSON (for backwards compat). @@ -182,7 +121,8 @@ namespace cryptonote::rpc { ); static void init_options(boost::program_options::options_description& desc, boost::program_options::options_description& hidden); - void init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); /// Returns a reference to the owning cryptonote core object core& get_core() { return m_core; } @@ -190,90 +130,87 @@ namespace cryptonote::rpc { network_type nettype() const { return m_core.get_nettype(); } - GET_HEIGHT::response invoke(GET_HEIGHT::request&& req, rpc_context context); - GET_BLOCKS_FAST::response invoke(GET_BLOCKS_FAST::request&& req, rpc_context context); - GET_ALT_BLOCKS_HASHES::response invoke(GET_ALT_BLOCKS_HASHES::request&& req, rpc_context context); - GET_BLOCKS_BY_HEIGHT::response invoke(GET_BLOCKS_BY_HEIGHT::request&& req, rpc_context context); - GET_HASHES_FAST::response invoke(GET_HASHES_FAST::request&& req, rpc_context context); - GET_TRANSACTIONS::response invoke(GET_TRANSACTIONS::request&& req, rpc_context context); - IS_KEY_IMAGE_SPENT::response invoke(IS_KEY_IMAGE_SPENT::request&& req, rpc_context context); - GET_TX_GLOBAL_OUTPUTS_INDEXES::response invoke(GET_TX_GLOBAL_OUTPUTS_INDEXES::request&& req, rpc_context context); - SEND_RAW_TX::response invoke(SEND_RAW_TX::request&& req, rpc_context context); - START_MINING::response invoke(START_MINING::request&& req, rpc_context context); - STOP_MINING::response invoke(STOP_MINING::request&& req, rpc_context context); - MINING_STATUS::response invoke(MINING_STATUS::request&& req, rpc_context context); - GET_OUTPUTS_BIN::response invoke(GET_OUTPUTS_BIN::request&& req, rpc_context context); - GET_OUTPUTS::response invoke(GET_OUTPUTS::request&& req, rpc_context context); - GET_INFO::response invoke(GET_INFO::request&& req, rpc_context context); - GET_NET_STATS::response invoke(GET_NET_STATS::request&& req, rpc_context context); - SAVE_BC::response invoke(SAVE_BC::request&& req, rpc_context context); - GET_PEER_LIST::response invoke(GET_PEER_LIST::request&& req, rpc_context context); - GET_PUBLIC_NODES::response invoke(GET_PUBLIC_NODES::request&& req, rpc_context context); - SET_LOG_HASH_RATE::response invoke(SET_LOG_HASH_RATE::request&& req, rpc_context context); - SET_LOG_LEVEL::response invoke(SET_LOG_LEVEL::request&& req, rpc_context context); - SET_LOG_CATEGORIES::response invoke(SET_LOG_CATEGORIES::request&& req, rpc_context context); - GET_TRANSACTION_POOL::response invoke(GET_TRANSACTION_POOL::request&& req, rpc_context context); - GET_TRANSACTION_POOL_HASHES_BIN::response invoke(GET_TRANSACTION_POOL_HASHES_BIN::request&& req, rpc_context context); - GET_TRANSACTION_POOL_HASHES::response invoke(GET_TRANSACTION_POOL_HASHES::request&& req, rpc_context context); - GET_TRANSACTION_POOL_STATS::response invoke(GET_TRANSACTION_POOL_STATS::request&& req, rpc_context context); - SET_BOOTSTRAP_DAEMON::response invoke(SET_BOOTSTRAP_DAEMON::request&& req, rpc_context context); - STOP_DAEMON::response invoke(STOP_DAEMON::request&& req, rpc_context context); - GET_LIMIT::response invoke(GET_LIMIT::request&& req, rpc_context context); - SET_LIMIT::response invoke(SET_LIMIT::request&& req, rpc_context context); - OUT_PEERS::response invoke(OUT_PEERS::request&& req, rpc_context context); - IN_PEERS::response invoke(IN_PEERS::request&& req, rpc_context context); - GET_OUTPUT_DISTRIBUTION::response invoke(GET_OUTPUT_DISTRIBUTION::request&& req, rpc_context context, bool binary = false); - GET_OUTPUT_DISTRIBUTION_BIN::response invoke(GET_OUTPUT_DISTRIBUTION_BIN::request&& req, rpc_context context); - POP_BLOCKS::response invoke(POP_BLOCKS::request&& req, rpc_context context); - GETBLOCKCOUNT::response invoke(GETBLOCKCOUNT::request&& req, rpc_context context); - GETBLOCKHASH::response invoke(GETBLOCKHASH::request&& req, rpc_context context); - GETBLOCKTEMPLATE::response invoke(GETBLOCKTEMPLATE::request&& req, rpc_context context); - SUBMITBLOCK::response invoke(SUBMITBLOCK::request&& req, rpc_context context); - GENERATEBLOCKS::response invoke(GENERATEBLOCKS::request&& req, rpc_context context); - GET_LAST_BLOCK_HEADER::response invoke(GET_LAST_BLOCK_HEADER::request&& req, rpc_context context); - GET_BLOCK_HEADER_BY_HASH::response invoke(GET_BLOCK_HEADER_BY_HASH::request&& req, rpc_context context); - GET_BLOCK_HEADER_BY_HEIGHT::response invoke(GET_BLOCK_HEADER_BY_HEIGHT::request&& req, rpc_context context); - GET_BLOCK_HEADERS_RANGE::response invoke(GET_BLOCK_HEADERS_RANGE::request&& req, rpc_context context); - GET_BLOCK::response invoke(GET_BLOCK::request&& req, rpc_context context); - GET_CONNECTIONS::response invoke(GET_CONNECTIONS::request&& req, rpc_context context); - HARD_FORK_INFO::response invoke(HARD_FORK_INFO::request&& req, rpc_context context); - SETBANS::response invoke(SETBANS::request&& req, rpc_context context); - GETBANS::response invoke(GETBANS::request&& req, rpc_context context); - BANNED::response invoke(BANNED::request&& req, rpc_context context); - FLUSH_TRANSACTION_POOL::response invoke(FLUSH_TRANSACTION_POOL::request&& req, rpc_context context); - GET_OUTPUT_HISTOGRAM::response invoke(GET_OUTPUT_HISTOGRAM::request&& req, rpc_context context); - GET_VERSION::response invoke(GET_VERSION::request&& req, rpc_context context); - GET_COINBASE_TX_SUM::response invoke(GET_COINBASE_TX_SUM::request&& req, rpc_context context); - GET_BASE_FEE_ESTIMATE::response invoke(GET_BASE_FEE_ESTIMATE::request&& req, rpc_context context); - GET_ALTERNATE_CHAINS::response invoke(GET_ALTERNATE_CHAINS::request&& req, rpc_context context); - RELAY_TX::response invoke(RELAY_TX::request&& req, rpc_context context); - SYNC_INFO::response invoke(SYNC_INFO::request&& req, rpc_context context); - GET_TRANSACTION_POOL_BACKLOG::response invoke(GET_TRANSACTION_POOL_BACKLOG::request&& req, rpc_context context); - PRUNE_BLOCKCHAIN::response invoke(PRUNE_BLOCKCHAIN::request&& req, rpc_context context); - GET_OUTPUT_BLACKLIST::response invoke(GET_OUTPUT_BLACKLIST::request&& req, rpc_context context); - GET_QUORUM_STATE::response invoke(GET_QUORUM_STATE::request&& req, rpc_context context); - GET_MASTER_NODE_REGISTRATION_CMD_RAW::response invoke(GET_MASTER_NODE_REGISTRATION_CMD_RAW::request&& req, rpc_context context); - GET_MASTER_NODE_REGISTRATION_CMD::response invoke(GET_MASTER_NODE_REGISTRATION_CMD::request&& req, rpc_context context); - GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::response invoke(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::request&& req, rpc_context context); - GET_MASTER_KEYS::response invoke(GET_MASTER_KEYS::request&& req, rpc_context context); - GET_MASTER_PRIVKEYS::response invoke(GET_MASTER_PRIVKEYS::request&& req, rpc_context context); - GET_MASTER_NODE_STATUS::response invoke(GET_MASTER_NODE_STATUS::request&& req, rpc_context context); - GET_MASTER_NODES::response invoke(GET_MASTER_NODES::request&& req, rpc_context context); - GET_STAKING_REQUIREMENT::response invoke(GET_STAKING_REQUIREMENT::request&& req, rpc_context context); - STORAGE_SERVER_PING::response invoke(STORAGE_SERVER_PING::request&& req, rpc_context context); - BELNET_PING::response invoke(BELNET_PING::request&& req, rpc_context context); - GET_CHECKPOINTS::response invoke(GET_CHECKPOINTS::request&& req, rpc_context context); - GET_MN_STATE_CHANGES::response invoke(GET_MN_STATE_CHANGES::request&& req, rpc_context context); - REPORT_PEER_STATUS::response invoke(REPORT_PEER_STATUS::request&& req, rpc_context context); - TEST_TRIGGER_P2P_RESYNC::response invoke(TEST_TRIGGER_P2P_RESYNC::request&& req, rpc_context context); - TEST_TRIGGER_UPTIME_PROOF::response invoke(TEST_TRIGGER_UPTIME_PROOF::request&& req, rpc_context context); - BNS_NAMES_TO_OWNERS::response invoke(BNS_NAMES_TO_OWNERS::request&& req, rpc_context context); - BNS_LOOKUP::response invoke(BNS_LOOKUP::request&& req, rpc_context context); - BNS_OWNERS_TO_NAMES::response invoke(BNS_OWNERS_TO_NAMES::request&& req, rpc_context context); - BNS_RESOLVE::response invoke(BNS_RESOLVE::request&& req, rpc_context context); - BNS_VALUE_DECRYPT::response invoke(BNS_VALUE_DECRYPT::request&& req, rpc_context context); - FLUSH_CACHE::response invoke(FLUSH_CACHE::request&& req, rpc_context); - + // JSON & bt-encoded RPC endpoints + void invoke(GET_HEIGHT& req, rpc_context context); + void invoke(GET_INFO& info, rpc_context context); + void invoke(GET_NET_STATS& get_net_stats, rpc_context context); + void invoke(GET_OUTPUTS& get_outputs, rpc_context context); + void invoke(HARD_FORK_INFO& hfinfo, rpc_context context); + void invoke(START_MINING& start_mining, rpc_context context); + void invoke(STOP_MINING& stop_mining, rpc_context context); + void invoke(SAVE_BC& save_bc, rpc_context context); + void invoke(STOP_DAEMON& stop_daemon, rpc_context context); + void invoke(GET_BLOCK_COUNT& getblockcount, rpc_context context); + void invoke(MINING_STATUS& mining_status, rpc_context context); + void invoke(GET_TRANSACTION_POOL_HASHES& get_transaction_pool_hashes, rpc_context context); + void invoke(GET_TRANSACTION_POOL_STATS& get_transaction_pool_stats, rpc_context context); + void invoke(GET_TRANSACTIONS& req, rpc_context context); + void invoke(GET_CONNECTIONS& get_connections, rpc_context context); + void invoke(SYNC_INFO& sync, rpc_context context); + void invoke(GET_MASTER_NODE_STATUS& sns, rpc_context context); + void invoke(GET_MASTER_NODES& sns, rpc_context context); + void invoke(GET_LIMIT& limit, rpc_context context); + void invoke(SET_LIMIT& limit, rpc_context context); + void invoke(IS_KEY_IMAGE_SPENT& spent, rpc_context context); + void invoke(SUBMIT_TRANSACTION& tx, rpc_context context); + void invoke(GET_BLOCK_HASH& req, rpc_context context); + void invoke(GET_PEER_LIST& pl, rpc_context context); + void invoke(SET_LOG_LEVEL& set_log_level, rpc_context context); + void invoke(SET_LOG_CATEGORIES& set_log_categories, rpc_context context); + void invoke(BANNED& banned, rpc_context context); + void invoke(FLUSH_TRANSACTION_POOL& flush_transaction_pool, rpc_context context); + void invoke(GET_VERSION& version, rpc_context context); + void invoke(GET_COINBASE_TX_SUM& get_coinbase_tx_sum, rpc_context context); + void invoke(GET_FEE_ESTIMATE& get_fee_estimate, rpc_context context); + void invoke(OUT_PEERS& out_peers, rpc_context context); + void invoke(IN_PEERS& in_peers, rpc_context context); + void invoke(GET_OUTPUT_DISTRIBUTION& get_output_distribution, rpc_context); + void invoke(POP_BLOCKS& pop_blocks, rpc_context context); + void invoke(BELNET_PING& lokinet_ping, rpc_context context); + void invoke(STORAGE_SERVER_PING& storage_server_ping, rpc_context context); + void invoke(PRUNE_BLOCKCHAIN& prune_blockchain, rpc_context context); + void invoke(TEST_TRIGGER_P2P_RESYNC& test_trigger_p2p_resync, rpc_context context); + void invoke(TEST_TRIGGER_UPTIME_PROOF& test_trigger_uptime_proof, rpc_context context); + void invoke(REPORT_PEER_STATUS& report_peer_status, rpc_context context); + void invoke(GET_MN_STATE_CHANGES& get_mn_state_changes, rpc_context context); + void invoke(FLUSH_CACHE& flush_cache, rpc_context context); + void invoke(GET_LAST_BLOCK_HEADER& get_last_block_header, rpc_context context); + void invoke(GET_BLOCK_HEADER_BY_HASH& get_block_header_by_hash, rpc_context context); + void invoke(GET_BANS& get_bans, rpc_context context); + void invoke(SET_BANS& set_bans, rpc_context context); + void invoke(GET_CHECKPOINTS& get_checkpoints, rpc_context context); + void invoke(GET_STAKING_REQUIREMENT& get_staking_requirement, rpc_context context); + void invoke(GET_MASTER_KEYS& get_master_keys, rpc_context context); + void invoke(GET_MASTER_PRIVKEYS& get_master_privkeys, rpc_context context); + void invoke(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES& get_master_node_blacklisted_key_images, rpc_context context); + void invoke(RELAY_TX& relay_tx, rpc_context context); + void invoke(GET_BLOCK_HEADERS_RANGE& get_block_headers_range, rpc_context context); + void invoke(GET_BLOCK_HEADER_BY_HEIGHT& get_block_header_by_height, rpc_context context); + void invoke(GET_BLOCK& get_block, rpc_context context); + void invoke(GET_MASTER_NODE_REGISTRATION_CMD& get_master_node_registration_cmd, rpc_context context); + void invoke(GET_MASTER_NODE_REGISTRATION_CMD_RAW& get_master_node_registration_cmd_raw, rpc_context context); + void invoke(GET_QUORUM_STATE& get_quorum_state, rpc_context context); + void invoke(GET_ALTERNATE_CHAINS& get_alternate_chains, rpc_context context); + void invoke(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_context context); + void invoke(BNS_OWNERS_TO_NAMES& bns_owners_to_names, rpc_context context); + void invoke(BNS_NAMES_TO_OWNERS& bns_names_to_owners, rpc_context context); + void invoke(BNS_RESOLVE& resolve, rpc_context context); + void invoke(BNS_LOOKUP& lookup, rpc_context context); + void invoke(BNS_VALUE_DECRYPT& value_decrypt, rpc_context context); + void invoke(SET_BOOTSTRAP_DAEMON& set_bootstrap, rpc_context context); + + + + // Deprecated Monero NIH binary endpoints: + GET_ALT_BLOCKS_HASHES_BIN::response invoke(GET_ALT_BLOCKS_HASHES_BIN::request&& req, rpc_context context); + GET_BLOCKS_BIN::response invoke(GET_BLOCKS_BIN::request&& req, rpc_context context); + GET_BLOCKS_BY_HEIGHT_BIN::response invoke(GET_BLOCKS_BY_HEIGHT_BIN::request&& req, rpc_context context); + GET_HASHES_BIN::response invoke(GET_HASHES_BIN::request&& req, rpc_context context); + GET_OUTPUT_BLACKLIST_BIN::response invoke(GET_OUTPUT_BLACKLIST_BIN::request&& req, rpc_context context); + GET_OUTPUT_DISTRIBUTION_BIN::response invoke(GET_OUTPUT_DISTRIBUTION_BIN::request&& req, rpc_context context); + GET_OUTPUTS_BIN::response invoke(GET_OUTPUTS_BIN::request&& req, rpc_context context); + GET_TRANSACTION_POOL_HASHES_BIN::response invoke(GET_TRANSACTION_POOL_HASHES_BIN::request&& req, rpc_context context); + GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::response invoke(GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::request&& req, rpc_context context); #if defined(BELDEX_ENABLE_INTEGRATION_TEST_HOOKS) void on_relay_uptime_and_votes() { @@ -315,19 +252,27 @@ namespace cryptonote::rpc { private: bool check_core_ready(); - void fill_mn_response_entry(GET_MASTER_NODES::response::entry& entry, const master_nodes::master_node_pubkey_info &mn_info, uint64_t current_height); + void fill_mn_response_entry( + nlohmann::json& entry, + bool is_bt, + const std::unordered_set& requested, + const master_nodes::master_node_pubkey_info& mn_info, + uint64_t top_height); //utils uint64_t get_block_reward(const block& blk); - std::optional get_random_public_node(); bool set_bootstrap_daemon(const std::string &address, std::string_view username_password); bool set_bootstrap_daemon(const std::string &address, std::string_view username, std::string_view password); void fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash, bool get_tx_hashes); std::unique_lock should_bootstrap_lock(); + // JSON version (new) template - bool use_bootstrap_daemon_if_necessary(const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res); + bool use_bootstrap_daemon_if_necessary(const nlohmann::json& req, nlohmann::json& res); + template + bool use_bootstrap_daemon_if_necessary(const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res); + core& m_core; nodetool::node_server >& m_p2p; std::shared_mutex m_bootstrap_daemon_mutex; diff --git a/src/rpc/core_rpc_server_binary_commands.cpp b/src/rpc/core_rpc_server_binary_commands.cpp new file mode 100644 index 00000000000..96c9d6f7953 --- /dev/null +++ b/src/rpc/core_rpc_server_binary_commands.cpp @@ -0,0 +1,214 @@ +#include "core_rpc_server_binary_commands.h" + +namespace cryptonote::rpc { + + KV_SERIALIZE_MAP_CODE_BEGIN(EMPTY) + KV_SERIALIZE_MAP_CODE_END() + + void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::tx_output_indices& toi) + { + j = nlohmann::json{{"indices", toi.indices}}; + } + + void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::block_output_indices& boi) + { + j = nlohmann::json{{"indices", boi.indices}}; + } + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BIN::request) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + KV_SERIALIZE(start_height) + KV_SERIALIZE(prune) + KV_SERIALIZE_OPT(no_miner_tx, false) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BIN::tx_output_indices) + KV_SERIALIZE(indices) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BIN::block_output_indices) + KV_SERIALIZE(indices) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BIN::response) + KV_SERIALIZE(blocks) + KV_SERIALIZE(start_height) + KV_SERIALIZE(current_height) + KV_SERIALIZE(status) + KV_SERIALIZE(output_indices) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BY_HEIGHT_BIN::request) + KV_SERIALIZE(heights) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BY_HEIGHT_BIN::response) + KV_SERIALIZE(blocks) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALT_BLOCKS_HASHES_BIN::response) + KV_SERIALIZE(blks_hashes) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_HASHES_BIN::request) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + KV_SERIALIZE(start_height) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_HASHES_BIN::response) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) + KV_SERIALIZE(start_height) + KV_SERIALIZE(current_height) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::request) + KV_SERIALIZE_VAL_POD_AS_BLOB(txid) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN::response) + KV_SERIALIZE(o_indexes) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(get_outputs_out) + KV_SERIALIZE(amount) + KV_SERIALIZE(index) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::request) + KV_SERIALIZE(outputs) + KV_SERIALIZE_OPT(get_txid, true) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::outkey) + KV_SERIALIZE_VAL_POD_AS_BLOB(key) + KV_SERIALIZE_VAL_POD_AS_BLOB(mask) + KV_SERIALIZE(unlocked) + KV_SERIALIZE(height) + KV_SERIALIZE_VAL_POD_AS_BLOB(txid) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::response) + KV_SERIALIZE(outs) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES_BIN::request) + KV_SERIALIZE_OPT(flashed_txs_only, false) + KV_SERIALIZE_OPT(long_poll, false) + KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(tx_pool_checksum, crypto::hash{}) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES_BIN::response) + KV_SERIALIZE(status) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_BLACKLIST_BIN::response) + KV_SERIALIZE(blacklist) + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION_BIN::request) + KV_SERIALIZE(amounts) + KV_SERIALIZE_OPT(from_height, (uint64_t)0) + KV_SERIALIZE_OPT(to_height, (uint64_t)0) + KV_SERIALIZE_OPT(cumulative, false) + KV_SERIALIZE_OPT(binary, true) + KV_SERIALIZE_OPT(compress, false) +KV_SERIALIZE_MAP_CODE_END() + + +namespace +{ + template + std::string compress_integer_array(const std::vector &v) + { + std::string s; + s.reserve(tools::VARINT_MAX_LENGTH); + auto ins = std::back_inserter(s); + for (const T &t: v) + tools::write_varint(ins, t); + return s; + } + + template + std::vector decompress_integer_array(const std::string &s) + { + std::vector v; + for (auto it = s.begin(); it < s.end(); ) + { + int read = tools::read_varint(it, s.end(), v.emplace_back()); + CHECK_AND_ASSERT_THROW_MES(read > 0, "Error decompressing data"); + } + return v; + } +} + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION_BIN::distribution) + KV_SERIALIZE(amount) + KV_SERIALIZE_N(data.start_height, "start_height") + KV_SERIALIZE(binary) + KV_SERIALIZE(compress) + if (binary) + { + if (is_store) + { + if (compress) + { + const_cast(compressed_data) = compress_integer_array(data.distribution); + KV_SERIALIZE(compressed_data) + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + else + { + if (compress) + { + KV_SERIALIZE(compressed_data) + const_cast&>(data.distribution) = decompress_integer_array(compressed_data); + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + } + else + KV_SERIALIZE_N(data.distribution, "distribution") + KV_SERIALIZE_N(data.base, "base") +KV_SERIALIZE_MAP_CODE_END() + + +KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION_BIN::response) + KV_SERIALIZE(status) + KV_SERIALIZE(distributions) + KV_SERIALIZE(untrusted) +KV_SERIALIZE_MAP_CODE_END() +} diff --git a/src/rpc/core_rpc_server_binary_commands.h b/src/rpc/core_rpc_server_binary_commands.h new file mode 100644 index 00000000000..6750928c92a --- /dev/null +++ b/src/rpc/core_rpc_server_binary_commands.h @@ -0,0 +1,331 @@ +// Copyright (c) 2018-2021, The Loki Project +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "core_rpc_server_commands_defs.h" + +namespace cryptonote::rpc { + + struct EMPTY { KV_MAP_SERIALIZABLE }; + + /// Specifies that the RPC call is legacy, deprecated Monero custom binary input/ouput. If not + /// given then the command is JSON/bt-encoded values. For HTTP RPC this also means the command is + /// *not* available via the HTTP JSON RPC. + struct BINARY : virtual RPC_COMMAND {}; + + /// Get all blocks info. Binary request. + /// + /// Inputs: + /// block_ids -- descending list of block IDs used to detect reorganizations and network status: + /// the first 10 are the 10 most recent blocks, after which height decreases by a power of 2. + struct GET_BLOCKS_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_blocks.bin", "getblocks.bin"); } + + static constexpr size_t MAX_COUNT = 1000; + + struct request { + std::list block_ids; // Descending list of block IDs used to detect reorganizations and network: the first 10 blocks id are sequential, then height drops by a power of 2 (2, 4, 8, 16, etc.) down to height 1, and then finally the genesis block id. + uint64_t start_height; // The height of the first block to fetch. + bool prune; // Prunes the blockchain, dropping off 7/8ths of the blocks. + bool no_miner_tx; // If specified and true, don't include miner transactions in transaction results. + + KV_MAP_SERIALIZABLE + }; + + struct tx_output_indices + { + std::vector indices; // Array of unsigned int. + + KV_MAP_SERIALIZABLE + }; + + struct block_output_indices + { + std::vector indices; // Array of TX output indices: + + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::vector blocks; // Array of block complete entries + uint64_t start_height; // The starting block's height. + uint64_t current_height; // The current block height. + std::string status; // General RPC error code. "OK" means everything looks good. + std::vector output_indices; // Array of indices. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::tx_output_indices& toi); + void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::block_output_indices& boi); + + BELDEX_RPC_DOC_INTROSPECT + // Get blocks by height. Binary request. + struct GET_BLOCKS_BY_HEIGHT_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_blocks_by_height.bin", "getblocks_by_height.bin"); } + + struct request + { + std::vector heights; // List of block heights + + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::vector blocks; // Array of block complete entries + std::string status; // General RPC error code. "OK" means everything looks good. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + + BELDEX_RPC_DOC_INTROSPECT + // Get the known blocks hashes which are not on the main chain. + struct GET_ALT_BLOCKS_HASHES_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_alt_blocks_hashes.bin"); } + + struct request : EMPTY {}; + struct response + { + std::vector blks_hashes; // List of alternative blocks hashes to main chain. + std::string status; // General RPC error code. "OK" means everything looks good. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + // Get hashes. Binary request. + struct GET_HASHES_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_hashes.bin", "gethashes.bin"); } + + struct request + { + std::list block_ids; // First 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + uint64_t start_height; // The starting block's height. + + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::vector m_block_ids; // Binary array of hashes, See block_ids above. + uint64_t start_height; // The starting block's height. + uint64_t current_height; // The current block height. + std::string status; // General RPC error code. "OK" means everything looks good. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + // Get global outputs of transactions. Binary request. + struct GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_o_indexes.bin"); } + + struct request + { + crypto::hash txid; // Binary txid. + + KV_MAP_SERIALIZABLE + }; + + + struct response + { + std::vector o_indexes; // List of output indexes + std::string status; // General RPC error code. "OK" means everything looks good. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + struct get_outputs_out + { + uint64_t amount; // Amount of Loki in TXID. + uint64_t index; + + KV_MAP_SERIALIZABLE + }; + + BELDEX_RPC_DOC_INTROSPECT + // Get outputs. Binary request. + struct GET_OUTPUTS_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_outs.bin"); } + + /// Maximum outputs that may be requested in a single request (unless admin) + static constexpr size_t MAX_COUNT = 5000; + + struct request + { + std::vector outputs; // Array of structure `get_outputs_out`. + bool get_txid; // TXID + + KV_MAP_SERIALIZABLE + }; + + struct outkey + { + crypto::public_key key; // The public key of the output. + rct::key mask; + bool unlocked; // States if output is locked (`false`) or not (`true`). + uint64_t height; // Block height of the output. + crypto::hash txid; // Transaction id. + + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::vector outs; // List of outkey information. + std::string status; // General RPC error code. "OK" means everything looks good. + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + // Get hashes from transaction pool. Binary request. + struct GET_TRANSACTION_POOL_HASHES_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_transaction_pool_hashes.bin"); } + + static constexpr std::chrono::seconds long_poll_timeout{15}; + + struct request + { + bool flashed_txs_only; // Optional: If true only transactions that were sent via flash and approved are queried. + bool long_poll; // Optional: If true, this call is blocking until timeout OR tx pool has changed since the last query. TX pool change is detected by comparing the hash of all the hashes in the tx pool. Ignored when using OMQ RPC. + crypto::hash tx_pool_checksum; // Optional: If `long_poll` is true the caller must pass the hashes of all their known tx pool hashes, XOR'ed together. Ignored when using OMQ RPC. + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::string status; // General RPC error code. "OK" means everything looks good. + std::vector tx_hashes; // List of transaction hashes, + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + // Exactly like GET_OUTPUT_DISTRIBUTION, but does a binary RPC transfer instead of JSON + struct GET_OUTPUT_DISTRIBUTION_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_output_distribution.bin"); } + struct request + { + std::vector amounts; // Amounts to look for in atomic units. + uint64_t from_height; // (optional, default is 0) starting height to check from. + uint64_t to_height; // (optional, default is 0) ending height to check up to. + bool cumulative; // (optional, default is false) States if the result should be cumulative (true) or not (false). + bool binary; + bool compress; + + KV_MAP_SERIALIZABLE + }; + + struct distribution + { + rpc::output_distribution_data data; + uint64_t amount; + std::string compressed_data; + bool binary; + bool compress; + + KV_MAP_SERIALIZABLE + }; + + struct response + { + std::string status; // General RPC error code. "OK" means everything looks good. + std::vector distributions; // + bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + + KV_MAP_SERIALIZABLE + }; + }; + + BELDEX_RPC_DOC_INTROSPECT + // Get information on output blacklist. + struct GET_OUTPUT_BLACKLIST_BIN : PUBLIC, BINARY + { + static constexpr auto names() { return NAMES("get_output_blacklist.bin"); } + struct request : EMPTY {}; + + struct response + { + std::vector blacklist; // (Developer): Array of indexes from the global output list, corresponding to blacklisted key images. + std::string status; // Generic RPC error code. "OK" is the success value. + bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. + + KV_MAP_SERIALIZABLE + }; + }; + + /// List of all supported rpc command structs to allow compile-time enumeration of all supported + /// RPC types. Every type added above that has an RPC endpoint needs to be added here, and needs + /// a core_rpc_server::invoke() overload that takes a ::request and returns a + /// ::response. The ::request has to be unique (for overload resolution); + /// ::response does not. + using core_rpc_binary_types = tools::type_list< + GET_ALT_BLOCKS_HASHES_BIN, + GET_BLOCKS_BIN, + GET_BLOCKS_BY_HEIGHT_BIN, + GET_HASHES_BIN, + GET_OUTPUTS_BIN, + GET_OUTPUT_BLACKLIST_BIN, + GET_OUTPUT_DISTRIBUTION_BIN, + GET_TRANSACTION_POOL_HASHES_BIN, + GET_TX_GLOBAL_OUTPUTS_INDEXES_BIN + >; + +} // namespace cryptonote::rpc \ No newline at end of file diff --git a/src/rpc/core_rpc_server_command_parser.cpp b/src/rpc/core_rpc_server_command_parser.cpp new file mode 100644 index 00000000000..a63ac5e0685 --- /dev/null +++ b/src/rpc/core_rpc_server_command_parser.cpp @@ -0,0 +1,436 @@ +#include "core_rpc_server_command_parser.h" +#include "rpc/common/param_parser.hpp" + + +namespace cryptonote::rpc { + using nlohmann::json; + + void parse_request(GET_MASTER_NODES& mns, rpc_input in) { + // Remember: key access must be in sorted order (even across get_values() calls). + get_values(in, + "active_only", mns.request.active_only); + + bool fields_dict = false; + if (auto* json_in = std::get_if(&in)) { + // Deprecated {"field":true, "field2":true, ...} handling: + if (auto fit = json_in->find("fields"); fit != json_in->end() && fit->is_object()) { + fields_dict = true; + for (auto& [k, v] : fit->items()) { + if (v.get()) { + if (k == "all") { + mns.request.fields.clear(); // Empty means all + break; // The old behaviour just ignored everything else if you specified all + } + mns.request.fields.insert(k); + } + } + } + } + if (!fields_dict) { + std::vector fields; + get_values(in, + "fields", fields); + + for (const auto& f : fields) + mns.request.fields.emplace(f); + // If the only thing given is "all" then just clear it (as a small optimization): + if (mns.request.fields.size() == 1 && *mns.request.fields.begin() == "all") + mns.request.fields.clear(); + } + + get_values(in, + "limit", mns.request.limit, + "master_node_pubkeys", mns.request.master_node_pubkeys, + "poll_block_hash", ignore_empty_string{mns.request.poll_block_hash}); + } + + void parse_request(START_MINING& start_mining, rpc_input in) { + get_values(in, + "miner_address", required{start_mining.request.miner_address}, + "num_blocks", start_mining.request.num_blocks, + "slow_mining", start_mining.request.slow_mining, + "threads_count", start_mining.request.threads_count); + } + + void parse_request(GET_OUTPUTS& get_outputs, rpc_input in) { + get_values(in, + "as_tuple", get_outputs.request.as_tuple, + "get_txid", get_outputs.request.get_txid); + + // "outputs" is trickier: for backwards compatibility we need to accept json of: + // [{"amount":0,"index":i1}, ...] + // but that is incredibly wasteful and so we also want the more efficient (and we only accept + // this for bt, since we don't have backwards compat to worry about): + // [i1, i2, ...] + bool legacy_outputs = false; + if (auto* json_in = std::get_if(&in)) { + if (auto outputs = json_in->find("outputs"); + outputs != json_in->end() && !outputs->empty() && outputs->is_array() && outputs->front().is_object()) { + legacy_outputs = true; + auto& reqoi = get_outputs.request.output_indices; + reqoi.reserve(outputs->size()); + for (auto& o : *outputs) + reqoi.push_back(o["index"].get()); + } + } + if (!legacy_outputs) + get_values(in, "output_indices", get_outputs.request.output_indices); + } + + void parse_request(GET_TRANSACTION_POOL_STATS& pstats, rpc_input in) { + get_values(in, + "include_unrelayed", pstats.request.include_unrelayed); + } + + void parse_request(HARD_FORK_INFO& hfinfo, rpc_input in) { + get_values(in, + "height", hfinfo.request.height, + "version", hfinfo.request.version); + + if (hfinfo.request.height && hfinfo.request.version) + throw std::runtime_error{"Error: at most one of 'height'" + std::to_string(hfinfo.request.height) + "/" + std::to_string(hfinfo.request.version) + " and 'version' may be specified"}; + } + + void parse_request(GET_TRANSACTIONS& get, rpc_input in) { + // Backwards compat for old stupid "txs_hashes" input name + if (auto* json_in = std::get_if(&in)) + if (auto it = json_in->find("txs_hashes"); it != json_in->end()) + (*json_in)["tx_hashes"] = std::move(*it); + + get_values(in, + "data", get.request.data, + "memory_pool", get.request.memory_pool, + "prune", get.request.prune, + "split", get.request.split, + "tx_extra", get.request.tx_extra, + "tx_extra_raw", get.request.tx_extra_raw, + "tx_hashes", get.request.tx_hashes); + + if (get.request.memory_pool && !get.request.tx_hashes.empty()) + throw std::runtime_error{"Error: 'memory_pool' and 'tx_hashes' are mutually exclusive"}; + } + + void parse_request(GET_TRANSACTION_POOL& get, rpc_input in) { + // Deprecated wrapper; GET_TRANSACTION_POOL is a no-member subclass of GET_TRANSACTIONS; it + // works identically, except that we force `memory_pool` to true. + parse_request(static_cast(get), std::move(in)); + if (!get.request.tx_hashes.empty()) + throw std::runtime_error{"Error: 'get_transaction_pool' does not support specifying 'tx_hashes'"}; + get.request.memory_pool = true; + } + + void parse_request(SET_LIMIT& limit, rpc_input in) { + get_values(in, + "limit_down", limit.request.limit_down, + "limit_up", limit.request.limit_up); + + if (limit.request.limit_down < -1) + throw std::domain_error{"limit_down must be >= -1"}; + if (limit.request.limit_down < -1) + throw std::domain_error{"limit_up must be >= -1"}; + } + + void parse_request(IS_KEY_IMAGE_SPENT& spent, rpc_input in){ + get_values(in, + "key_images", spent.request.key_images); + } + + void parse_request(SUBMIT_TRANSACTION& tx, rpc_input in) { + if (auto* json_in = std::get_if(&in)) + if (auto it = json_in->find("tx_as_hex"); it != json_in->end()) + (*json_in)["tx"] = std::move(*it); + + auto& tx_data = tx.request.tx; + get_values(in, + "flash", tx.request.flash, + "tx", required{tx_data}); + + if (tx_data.empty()) // required above will make sure it's specified, but doesn't guarantee against an empty value + throw std::domain_error{"Invalid 'tx' value: cannot be empty"}; + + // tx can be specified as base64, hex, or binary, so try to figure out which one we have by + // looking at the beginning. + // + // An encoded transaction always starts with the version byte, currently 0-4 (though 0 isn't + // actually used), with higher future values possible. That means in hex we get something like: + // `04...` and in base64 we get `B` (because the first 6 bits are 000001, and the b64 alphabet + // begins at `A` for 0). Thus the first bytes, for tx versions 0 through 48, are thus: + // + // binary: (31 binary control characters through 0x1f ... ) (space) ! " # $ % & ' ( ) * + , - . / 0 + // base64: A A A A B B B B C C C C D D D D E E E E F F F F G G G G H H H H I I I I J J J J K K K K L L L L M + // hex: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 + // + // and so we run into the first ambiguity at version 48. Since we are currently only at version + // 4 (and Oxen started at version 2) this is likely to be sufficient for an extremely long time. + // + // Thus our heuristic: + // 'A'-'L' => base64 + // '0'-'2' => hex + // \x00-\x2f => bytes + // anything else we reject as garbage. + auto tx0 = tx_data.front(); + bool good = false; + if (tx0 <= 0x2f) { + good = true; + } else if (tx0 >= 'A' && tx0 <= 'L') { + if (oxenc::is_base64(tx_data)) { + auto end = oxenc::from_base64(tx_data.begin(), tx_data.end(), tx_data.begin()); + tx_data.erase(end, tx_data.end()); + good = true; + } + } else if (tx0 >= '0' && tx0 <= '2') { + if (oxenc::is_hex(tx_data)) { + auto end = oxenc::from_hex(tx_data.begin(), tx_data.end(), tx_data.begin()); + tx_data.erase(end, tx_data.end()); + good = true; + } + } + + if (!good) + throw std::domain_error{"Invalid 'tx' value: expected hex, base64, or bytes"}; + } + + void parse_request(GET_BLOCK_HASH& bh, rpc_input in) { + get_values(in, "heights", bh.request.heights); + if (bh.request.heights.size() > bh.MAX_HEIGHTS) + throw std::domain_error{"Error: too many block heights requested at once"}; + } + + void parse_request(GET_PEER_LIST& pl, rpc_input in) { + get_values(in, + "public_only", pl.request.public_only); + } + + void parse_request(SET_LOG_LEVEL& set_log_level, rpc_input in) { + get_values(in, + "level", required{set_log_level.request.level}); + } + + void parse_request(SET_BOOTSTRAP_DAEMON& set_bootstrap_daemon, rpc_input in) { + get_values(in, + "address", required{set_bootstrap_daemon.request.address}, + "password", set_bootstrap_daemon.request.password, + "username", set_bootstrap_daemon.request.username); + } + + void parse_request(SET_LOG_CATEGORIES& set_log_categories, rpc_input in) { + get_values(in, + "categories", required{set_log_categories.request.categories}); + } + + void parse_request(BANNED& banned, rpc_input in) { + get_values(in, + "address", required{banned.request.address}); + } + + void parse_request(FLUSH_TRANSACTION_POOL& flush_transaction_pool, rpc_input in) { + get_values(in, + "txids", flush_transaction_pool.request.txids); + } + + void parse_request(GET_COINBASE_TX_SUM& get_coinbase_tx_sum, rpc_input in) { + get_values(in, + "count", get_coinbase_tx_sum.request.count, + "height", get_coinbase_tx_sum.request.height); + } + + void parse_request(GET_FEE_ESTIMATE& get_fee_estimate, rpc_input in) { + get_values(in, + "grace_blocks", get_fee_estimate.request.grace_blocks); + } + + void parse_request(OUT_PEERS& out_peers, rpc_input in){ + get_values(in, + "out_peers", out_peers.request.out_peers, + "set", out_peers.request.set); + } + + void parse_request(IN_PEERS& in_peers, rpc_input in){ + get_values(in, + "in_peers", in_peers.request.in_peers, + "set", in_peers.request.set); + } + + void parse_request(GET_OUTPUT_DISTRIBUTION& get_output_distribution, rpc_input in) { + get_values(in, + "amounts", get_output_distribution.request.amounts, + "cumulative", get_output_distribution.request.cumulative, + "from_height", get_output_distribution.request.from_height, + "to_height", get_output_distribution.request.to_height); + } + + void parse_request(POP_BLOCKS& pop_blocks, rpc_input in){ + get_values(in, + "nblocks", required{pop_blocks.request.nblocks}); + } + + void parse_request(BELNET_PING& belnet_ping, rpc_input in){ + get_values(in, + "error", belnet_ping.request.error, + "pubkey_ed25519", belnet_ping.request.pubkey_ed25519, + "version", required{belnet_ping.request.version}); + } + + void parse_request(STORAGE_SERVER_PING& storage_server_ping, rpc_input in){ + get_values(in, + "error", storage_server_ping.request.error, + "https_port", required{storage_server_ping.request.https_port}, + "omq_port", required{storage_server_ping.request.omq_port}, + "pubkey_ed25519", required{storage_server_ping.request.pubkey_ed25519}, + "version", required{storage_server_ping.request.version}); + } + + void parse_request(PRUNE_BLOCKCHAIN& prune_blockchain, rpc_input in){ + get_values(in, + "check", prune_blockchain.request.check); + } + + void parse_request(REPORT_PEER_STATUS& report_peer_status, rpc_input in) { + get_values(in, + "passed", report_peer_status.request.passed, + "pubkey", report_peer_status.request.pubkey, + "type", report_peer_status.request.type); + } + + void parse_request(GET_MN_STATE_CHANGES& get_mn_state_changes, rpc_input in) { + get_values(in, + "end_height", get_mn_state_changes.request.end_height, + "start_height", required{get_mn_state_changes.request.start_height}); + } + + void parse_request(FLUSH_CACHE& flush_cache, rpc_input in) { + get_values(in, + "bad_blocks", flush_cache.request.bad_blocks, + "bad_txs", flush_cache.request.bad_txs); + } + + void parse_request(GET_LAST_BLOCK_HEADER& get_last_block_header, rpc_input in) { + get_values(in, + "fill_pow_hash", get_last_block_header.request.fill_pow_hash, + "get_tx_hashes", get_last_block_header.request.get_tx_hashes); + } + + void parse_request(GET_BLOCK_HEADER_BY_HASH& get_block_header_by_hash, rpc_input in) { + get_values(in, + "fill_pow_hash", get_block_header_by_hash.request.fill_pow_hash, + "get_tx_hashes", get_block_header_by_hash.request.get_tx_hashes, + "hash", get_block_header_by_hash.request.hash, + "hashes", get_block_header_by_hash.request.hashes); + } + + void parse_request(SET_BANS& set_bans, rpc_input in) { + get_values(in, + "ban", required{set_bans.request.ban}, + "host", required{set_bans.request.host}, + "seconds", required{set_bans.request.seconds}); + } + + void parse_request(GET_STAKING_REQUIREMENT& get_staking_requirement, rpc_input in) { + get_values(in, + "height", get_staking_requirement.request.height); + } + + void parse_request(RELAY_TX& relay_tx, rpc_input in){ + get_values(in, + "txids", relay_tx.request.txids); + } + + void parse_request(GET_BLOCK_HEADERS_RANGE& get_block_headers_range, rpc_input in) { + get_values(in, + "end_height", get_block_headers_range.request.end_height, + "fill_pow_hash", get_block_headers_range.request.fill_pow_hash, + "get_tx_hashes", get_block_headers_range.request.get_tx_hashes, + "start_height", get_block_headers_range.request.start_height); + } + + void parse_request(GET_BLOCK_HEADER_BY_HEIGHT& get_block_header_by_height, rpc_input in) { + get_values(in, + "fill_pow_hash", get_block_header_by_height.request.fill_pow_hash, + "get_tx_hashes", get_block_header_by_height.request.get_tx_hashes, + "height", get_block_header_by_height.request.height, + "heights", get_block_header_by_height.request.heights); + } + + void parse_request(GET_BLOCK& get_block, rpc_input in) { + get_values(in, + "fill_pow_hash", get_block.request.fill_pow_hash, + "hash", get_block.request.hash, + "height", get_block.request.height); + } + + void parse_request(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_input in) { + get_values(in, + "amounts", get_output_histogram.request.amounts, + "max_count", get_output_histogram.request.max_count, + "min_count", get_output_histogram.request.min_count, + "recent_cutoff", get_output_histogram.request.recent_cutoff, + "unlocked", get_output_histogram.request.unlocked); + } + + void parse_request(BNS_OWNERS_TO_NAMES& owners_to_names, rpc_input in) { + get_values(in, + "entries", required{owners_to_names.request.entries}, + "include_expired", owners_to_names.request.include_expired); + } + + void parse_request(BNS_NAMES_TO_OWNERS& names_to_owners, rpc_input in) { + get_values(in, + "include_expired", names_to_owners.request.include_expired, + "name_hash", required{names_to_owners.request.name_hash}); + } + + void parse_request(BNS_RESOLVE& resolve, rpc_input in) { + get_values(in, + "name_hash", required{resolve.request.name_hash}, + "type", required{resolve.request.type}); + } + + void parse_request(BNS_LOOKUP& lookup, rpc_input in) { + get_values(in, + "name", required{lookup.request.name}); + } + + void parse_request(BNS_VALUE_DECRYPT& value_decrypt, rpc_input in) { + get_values(in, + "encrypted_value", required{value_decrypt.request.encrypted_value}, + "name", required{value_decrypt.request.name}, + "type", required{value_decrypt.request.type}); + } + + void parse_request(GET_QUORUM_STATE& qs, rpc_input in) { + get_values(in, + "end_height", qs.request.end_height, + "quorum_type", qs.request.quorum_type, + "start_height", qs.request.start_height); + + if (qs.request.quorum_type) { + if (*qs.request.quorum_type == 255) // backwards-compat magic value + qs.request.quorum_type = std::nullopt; + else if (*qs.request.quorum_type > tools::enum_count) + throw std::domain_error{ + "Quorum type specifies an invalid value: " + *qs.request.quorum_type}; + } + } + + void parse_request(GET_CHECKPOINTS& getcp, rpc_input in) { + get_values(in, + "count", getcp.request.count, + "end_height", getcp.request.end_height, + "start_height", getcp.request.start_height); + } + + void parse_request(GET_MASTER_NODE_REGISTRATION_CMD_RAW& cmd, rpc_input in) { + get_values(in, + "args", required{cmd.request.args}, + "make_friendly", cmd.request.make_friendly, + "staking_requirement", required{cmd.request.staking_requirement}); + } + + void parse_request(GET_MASTER_NODE_REGISTRATION_CMD& cmd, rpc_input in) { + get_values(in, + "contributor_addresses", required{cmd.request.contributor_addresses}, + "contributor_amounts", required{cmd.request.contributor_amounts}, + "operator_cut", required{cmd.request.operator_cut}, + "staking_requirement", required{cmd.request.staking_requirement}); + } +} \ No newline at end of file diff --git a/src/rpc/core_rpc_server_command_parser.h b/src/rpc/core_rpc_server_command_parser.h new file mode 100644 index 00000000000..5ff74e646fe --- /dev/null +++ b/src/rpc/core_rpc_server_command_parser.h @@ -0,0 +1,60 @@ +#pragma once + +#include "core_rpc_server_commands_defs.h" +#include +#include + +namespace cryptonote::rpc { + + using rpc_input = std::variant; + + inline void parse_request(NO_ARGS&, rpc_input) {}; + + void parse_request(BANNED& banned, rpc_input in); + void parse_request(FLUSH_CACHE& flush_cache, rpc_input in); + void parse_request(FLUSH_TRANSACTION_POOL& flush_transaction_pool, rpc_input in); + void parse_request(GET_FEE_ESTIMATE& get_fee_estimate, rpc_input in); + void parse_request(GET_BLOCK& get_block, rpc_input in); + void parse_request(GET_BLOCK_HASH& bh, rpc_input in); + void parse_request(GET_BLOCK_HEADERS_RANGE& get_block_headers_range, rpc_input in); + void parse_request(GET_BLOCK_HEADER_BY_HASH& get_block_header_by_hash, rpc_input in); + void parse_request(GET_BLOCK_HEADER_BY_HEIGHT& get_block_header_by_height, rpc_input in); + void parse_request(GET_CHECKPOINTS& getcp, rpc_input in); + void parse_request(GET_COINBASE_TX_SUM& get_coinbase_tx_sum, rpc_input in); + void parse_request(GET_QUORUM_STATE& get_quorum_state, rpc_input in); + void parse_request(GET_LAST_BLOCK_HEADER& get_last_block_header, rpc_input in); + void parse_request(GET_OUTPUTS& get_outputs, rpc_input in); + void parse_request(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_input in); + void parse_request(GET_PEER_LIST& bh, rpc_input in); + void parse_request(GET_MASTER_NODES& sns, rpc_input in); + void parse_request(GET_MASTER_NODE_REGISTRATION_CMD& cmd, rpc_input in); + void parse_request(GET_MASTER_NODE_REGISTRATION_CMD_RAW& cmd, rpc_input in); + void parse_request(GET_MN_STATE_CHANGES& get_mn_state_changes, rpc_input in); + void parse_request(GET_STAKING_REQUIREMENT& get_staking_requirement, rpc_input in); + void parse_request(GET_TRANSACTIONS& get, rpc_input in); + void parse_request(GET_TRANSACTION_POOL& get, rpc_input in); + void parse_request(GET_TRANSACTION_POOL_STATS& pstats, rpc_input in); + void parse_request(HARD_FORK_INFO& hfinfo, rpc_input in); + void parse_request(IN_PEERS& in_peers, rpc_input in); + void parse_request(IS_KEY_IMAGE_SPENT& spent, rpc_input in); + void parse_request(BELNET_PING& belnet_ping, rpc_input in); + void parse_request(BNS_OWNERS_TO_NAMES& owners_to_names, rpc_input in); + void parse_request(BNS_NAMES_TO_OWNERS& names_to_owners, rpc_input in); + void parse_request(BNS_RESOLVE& resolve, rpc_input in); + void parse_request(BNS_LOOKUP& lookup, rpc_input in); + void parse_request(BNS_VALUE_DECRYPT& value_decrypt, rpc_input in); + void parse_request(OUT_PEERS& out_peers, rpc_input in); + void parse_request(GET_OUTPUT_DISTRIBUTION& get_output_distribution, rpc_input in); + void parse_request(POP_BLOCKS& pop_blocks, rpc_input in); + void parse_request(PRUNE_BLOCKCHAIN& prune_blockchain, rpc_input in); + void parse_request(REPORT_PEER_STATUS& report_peer_status, rpc_input in); + void parse_request(SET_BANS& set_bans, rpc_input in); + void parse_request(SET_LIMIT& limit, rpc_input in); + void parse_request(SET_LOG_CATEGORIES& set_log_categories, rpc_input in); + void parse_request(SET_LOG_LEVEL& set_log_level, rpc_input in); + void parse_request(SET_BOOTSTRAP_DAEMON& set_bootstrap_daemon, rpc_input in); + void parse_request(START_MINING& start_mining, rpc_input in); + void parse_request(STORAGE_SERVER_PING& storage_server_ping, rpc_input in); + void parse_request(SUBMIT_TRANSACTION& tx, rpc_input in); + void parse_request(RELAY_TX& tx, rpc_input in); +} \ No newline at end of file diff --git a/src/rpc/core_rpc_server_commands_defs.cpp b/src/rpc/core_rpc_server_commands_defs.cpp index d71a335e500..bb11ddabe03 100755 --- a/src/rpc/core_rpc_server_commands_defs.cpp +++ b/src/rpc/core_rpc_server_commands_defs.cpp @@ -1,1424 +1,1426 @@ #include "core_rpc_server_commands_defs.h" +#include +#include + +namespace nlohmann { + + template + void to_json(nlohmann::json& j, const std::optional& v) + { + if (v.has_value()) + j = *v; + else + j = nullptr; + } + + template + void from_json(const nlohmann::json& j, std::optional& v) + { + if (j.is_null()) + v = std::nullopt; + else + v = j.get(); + } + +} -namespace cryptonote::rpc { +namespace cryptonote { + void to_json(nlohmann::json& j, const checkpoint_t& c) + { + j = nlohmann::json + { + {"version", c.version}, + {"type", c.type}, + {"height", c.height}, + {"block_hash", tools::type_to_hex(c.block_hash)}, + {"signatures", c.signatures}, + {"prev_height", c.prev_height}, + }; + }; +} -KV_SERIALIZE_MAP_CODE_BEGIN(STATUS) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() +namespace master_nodes { + void to_json(nlohmann::json& j, const key_image_blacklist_entry& b) { j = nlohmann::json{{"key_image", tools::type_to_hex(b.key_image)}, {"unlock_height", b.unlock_height}, {"amount", b.amount} }; }; + void to_json(nlohmann::json& j, const quorum_signature& s) + { + j = nlohmann::json + { + {"voter_index", s.voter_index}, + {"signature", tools::type_to_hex(s.signature)}, + }; + }; +} -KV_SERIALIZE_MAP_CODE_BEGIN(EMPTY) -KV_SERIALIZE_MAP_CODE_END() +namespace cryptonote::rpc { +void RPC_COMMAND::set_bt() { + bt = true; + response_b64.format = tools::json_binary_proxy::fmt::bt; + response_hex.format = tools::json_binary_proxy::fmt::bt; +} -KV_SERIALIZE_MAP_CODE_BEGIN(GET_HEIGHT::response) - KV_SERIALIZE(height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) - KV_SERIALIZE(hash) - KV_SERIALIZE(immutable_height) - KV_SERIALIZE(immutable_hash) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_FAST::request) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) - KV_SERIALIZE(start_height) - KV_SERIALIZE(prune) - KV_SERIALIZE_OPT(no_miner_tx, false) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_FAST::tx_output_indices) - KV_SERIALIZE(indices) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_FAST::block_output_indices) - KV_SERIALIZE(indices) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_FAST::response) - KV_SERIALIZE(blocks) - KV_SERIALIZE(start_height) - KV_SERIALIZE(current_height) - KV_SERIALIZE(status) - KV_SERIALIZE(output_indices) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BY_HEIGHT::request) - KV_SERIALIZE(heights) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BY_HEIGHT::response) - KV_SERIALIZE(blocks) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALT_BLOCKS_HASHES::response) - KV_SERIALIZE(blks_hashes) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_HASHES_FAST::request) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) - KV_SERIALIZE(start_height) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_HASHES_FAST::response) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) - KV_SERIALIZE(start_height) - KV_SERIALIZE(current_height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::request) - KV_SERIALIZE(txs_hashes) - KV_SERIALIZE(decode_as_json) - KV_SERIALIZE(tx_extra) - KV_SERIALIZE(prune) - KV_SERIALIZE(split) - KV_SERIALIZE(stake_info) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::mn_reg_info::contribution) - KV_SERIALIZE(wallet) - KV_SERIALIZE(portion) -KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::mn_reg_info) - KV_SERIALIZE(contributors) - KV_SERIALIZE(fee) - KV_SERIALIZE(expiry) -KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::state_change) - KV_SERIALIZE(old_dereg) - KV_SERIALIZE(type) - KV_SERIALIZE(height) - KV_SERIALIZE(index) - KV_SERIALIZE(voters) - KV_SERIALIZE(reasons); - KV_SERIALIZE(reasons_maybe); -KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::bns_details) - KV_SERIALIZE(version) - KV_SERIALIZE(buy) - KV_SERIALIZE(update) - KV_SERIALIZE(renew) - KV_SERIALIZE(type) - KV_SERIALIZE(blocks) - KV_SERIALIZE(name_hash) - KV_SERIALIZE(prev_txid) - KV_SERIALIZE(value_bchat) - KV_SERIALIZE(value_wallet) - KV_SERIALIZE(value_belnet) - KV_SERIALIZE(value_eth_addr) - KV_SERIALIZE(owner) - KV_SERIALIZE(backup_owner) -KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry) - KV_SERIALIZE(pubkey) - KV_SERIALIZE(burn_amount) - KV_SERIALIZE(extra_nonce) - KV_SERIALIZE(payment_id) - KV_SERIALIZE(mm_depth) - KV_SERIALIZE(mm_root) - KV_SERIALIZE(additional_pubkeys) - KV_SERIALIZE(mn_winner) - KV_SERIALIZE(mn_pubkey) - KV_SERIALIZE(mn_registration) - KV_SERIALIZE(mn_contributor) - KV_SERIALIZE(mn_state_change) - KV_SERIALIZE(tx_secret_key) - KV_SERIALIZE(locked_key_images) - KV_SERIALIZE(key_image_unlock) - KV_SERIALIZE(bns) -KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::entry) - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(as_hex) - KV_SERIALIZE(as_json) - KV_SERIALIZE(pruned_as_hex) - KV_SERIALIZE(prunable_as_hex) - KV_SERIALIZE(prunable_hash) - KV_SERIALIZE(size) - KV_SERIALIZE(in_pool) - KV_SERIALIZE(double_spend_seen) - if (!in_pool) +void to_json(nlohmann::json& j, const block_header_response& h) +{ + j = nlohmann::json { - KV_SERIALIZE(block_height) - KV_SERIALIZE(block_timestamp) - KV_SERIALIZE(output_indices) - } + {"major_version", h.major_version}, + {"minor_version", h.minor_version}, + {"timestamp", h.timestamp}, + {"prev_hash", h.prev_hash}, + {"nonce", h.nonce}, + {"orphan_status", h.orphan_status}, + {"height", h.height}, + {"depth", h.depth}, + {"hash", h.hash}, + {"difficulty", h.difficulty}, + {"cumulative_difficulty", h.cumulative_difficulty}, + {"reward", h.reward}, + {"coinbase_payouts", h.coinbase_payouts}, + {"block_size", h.block_size}, + {"block_weight", h.block_weight}, + {"num_txes", h.num_txes}, + {"long_term_weight", h.long_term_weight}, + {"miner_tx_hash", h.miner_tx_hash}, + {"miner_tx_hash", h.miner_tx_hash}, + {"tx_hashes", h.tx_hashes}, + {"master_node_winner", h.master_node_winner}, + }; + if (h.pow_hash) + j["pow_hash"] = *h.pow_hash; +}; + +void from_json(const nlohmann::json& j, block_header_response& h) +{ + j.at("major_version").get_to(h.major_version); + j.at("minor_version").get_to(h.minor_version); + j.at("timestamp").get_to(h.timestamp); + j.at("prev_hash").get_to(h.prev_hash); + j.at("nonce").get_to(h.nonce); + j.at("orphan_status").get_to(h.orphan_status); + j.at("height").get_to(h.height); + j.at("depth").get_to(h.depth); + j.at("hash").get_to(h.hash); + j.at("difficulty").get_to(h.difficulty); + j.at("cumulative_difficulty").get_to(h.cumulative_difficulty); + j.at("reward").get_to(h.reward); + j.at("coinbase_payouts").get_to(h.coinbase_payouts); + j.at("block_size").get_to(h.block_size); + j.at("block_weight").get_to(h.block_weight); + j.at("num_txes").get_to(h.num_txes); + if (auto it = j.find("pow_hash"); it == j.end() || it->is_null()) + h.pow_hash = std::nullopt; else - { - KV_SERIALIZE(relayed) - KV_SERIALIZE(received_timestamp) - } - KV_SERIALIZE(flash) - KV_SERIALIZE(extra) - KV_SERIALIZE(stake_amount) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::response) - KV_SERIALIZE(txs) - KV_SERIALIZE(missed_tx) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(IS_KEY_IMAGE_SPENT::request) - KV_SERIALIZE(key_images) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(IS_KEY_IMAGE_SPENT::response) - KV_SERIALIZE(spent_status) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TX_GLOBAL_OUTPUTS_INDEXES::request) - KV_SERIALIZE_VAL_POD_AS_BLOB(txid) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TX_GLOBAL_OUTPUTS_INDEXES::response) - KV_SERIALIZE(o_indexes) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(get_outputs_out) - KV_SERIALIZE(amount) - KV_SERIALIZE(index) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::request) - KV_SERIALIZE(outputs) - KV_SERIALIZE_OPT(get_txid, true) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::outkey) - KV_SERIALIZE_VAL_POD_AS_BLOB(key) - KV_SERIALIZE_VAL_POD_AS_BLOB(mask) - KV_SERIALIZE(unlocked) - KV_SERIALIZE(height) - KV_SERIALIZE_VAL_POD_AS_BLOB(txid) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS_BIN::response) - KV_SERIALIZE(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::request) - KV_SERIALIZE(outputs) - KV_SERIALIZE(get_txid) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::outkey) - KV_SERIALIZE(key) - KV_SERIALIZE(mask) - KV_SERIALIZE(unlocked) - KV_SERIALIZE(height) - KV_SERIALIZE(txid) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::response) - KV_SERIALIZE(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SEND_RAW_TX::request) - KV_SERIALIZE(tx_as_hex) - KV_SERIALIZE_OPT(do_not_relay, false) - KV_SERIALIZE_OPT(flash, false) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SEND_RAW_TX::response) - KV_SERIALIZE(status) - KV_SERIALIZE(reason) - KV_SERIALIZE(not_relayed) - KV_SERIALIZE(sanity_check_failed) - KV_SERIALIZE(untrusted) - KV_SERIALIZE(tvc) - KV_SERIALIZE_ENUM(flash_status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(START_MINING::request) - KV_SERIALIZE(miner_address) - KV_SERIALIZE(threads_count) - KV_SERIALIZE_OPT(num_blocks, uint64_t{0}) - KV_SERIALIZE_OPT(slow_mining, false) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(MINING_STATUS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(active) - KV_SERIALIZE(speed) - KV_SERIALIZE(threads_count) - KV_SERIALIZE(address) - KV_SERIALIZE(pow_algorithm) - KV_SERIALIZE(block_target) - KV_SERIALIZE(block_reward) - KV_SERIALIZE(difficulty) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_INFO::response) - KV_SERIALIZE(status) - KV_SERIALIZE(height) - KV_SERIALIZE(target_height) - KV_SERIALIZE(immutable_height) - KV_SERIALIZE(POS_ideal_timestamp) - KV_SERIALIZE(POS_target_timestamp) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(target) - KV_SERIALIZE(tx_count) - KV_SERIALIZE(tx_pool_size) - KV_SERIALIZE(alt_blocks_count) - KV_SERIALIZE(outgoing_connections_count) - KV_SERIALIZE(incoming_connections_count) - KV_SERIALIZE(white_peerlist_size) - KV_SERIALIZE(grey_peerlist_size) - KV_SERIALIZE(mainnet) - KV_SERIALIZE(testnet) - KV_SERIALIZE(devnet) - KV_SERIALIZE(nettype) - KV_SERIALIZE(top_block_hash) - KV_SERIALIZE(immutable_block_hash) - KV_SERIALIZE(cumulative_difficulty) - KV_SERIALIZE(block_size_limit) - KV_SERIALIZE(block_weight_limit) - KV_SERIALIZE(block_size_median) - KV_SERIALIZE(block_weight_median) - KV_SERIALIZE(bns_counts) - KV_SERIALIZE(start_time) - KV_SERIALIZE(master_node) - KV_SERIALIZE(last_storage_server_ping) - KV_SERIALIZE(last_belnet_ping) - KV_SERIALIZE(free_space) - KV_SERIALIZE(offline) - KV_SERIALIZE(untrusted) - KV_SERIALIZE(bootstrap_daemon_address) - KV_SERIALIZE(height_without_bootstrap) - KV_SERIALIZE(was_bootstrap_ever_used) - KV_SERIALIZE(database_size) - KV_SERIALIZE(version) - KV_SERIALIZE(status_line) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_NET_STATS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(start_time) - KV_SERIALIZE(total_packets_in) - KV_SERIALIZE(total_bytes_in) - KV_SERIALIZE(total_packets_out) - KV_SERIALIZE(total_bytes_out) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GETBLOCKCOUNT::response) - KV_SERIALIZE(count) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -bool GETBLOCKHASH::request::load(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) + h.pow_hash = it->get(); + j.at("long_term_weight").get_to(h.long_term_weight); + j.at("miner_tx_hash").get_to(h.miner_tx_hash); + j.at("miner_tx_hash").get_to(h.miner_tx_hash); + j.at("tx_hashes").get_to(h.tx_hashes); + j.at("master_node_winner").get_to(h.master_node_winner); +}; + +void to_json(nlohmann::json& j, const GET_QUORUM_STATE::quorum_t& q) { - return epee::serialization::perform_serialize(height, ps, hparent_section, "height"); -} -bool GETBLOCKHASH::request::store(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) + j = nlohmann::json{{"validators", q.validators}, {"workers", q.workers}}; +}; +void to_json(nlohmann::json& j, const GET_QUORUM_STATE::quorum_for_height& q) { - return epee::serialization::perform_serialize(height, ps, hparent_section, "height"); -} - - -KV_SERIALIZE_MAP_CODE_BEGIN(GETBLOCKTEMPLATE::request) - KV_SERIALIZE(reserve_size) - KV_SERIALIZE(wallet_address) - KV_SERIALIZE(prev_block) - KV_SERIALIZE(extra_nonce) -KV_SERIALIZE_MAP_CODE_END() + j = nlohmann::json{{"height", q.height}, {"quorum_type", q.quorum_type}, {"quorum", q.quorum}}; +}; - -KV_SERIALIZE_MAP_CODE_BEGIN(GETBLOCKTEMPLATE::response) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(height) - KV_SERIALIZE(reserved_offset) - KV_SERIALIZE(expected_reward) - KV_SERIALIZE(prev_hash) - KV_SERIALIZE(blocktemplate_blob) - KV_SERIALIZE(blockhashing_blob) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) - KV_SERIALIZE(seed_hash) - KV_SERIALIZE(next_seed_hash) -KV_SERIALIZE_MAP_CODE_END() - - -bool SUBMITBLOCK::request::load(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) +void to_json(nlohmann::json& j, const GET_ALTERNATE_CHAINS::chain_info& c) { - return epee::serialization::perform_serialize(blob, ps, hparent_section, "blob"); + j = nlohmann::json{ + {"block_hash", c.block_hash}, + {"height", c.height}, + {"length", c.length}, + {"difficulty", c.difficulty}, + {"block_hashes", c.block_hashes}, + {"main_chain_parent_block", c.main_chain_parent_block}, + }; } -bool SUBMITBLOCK::request::store(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) +void from_json(const nlohmann::json& j, GET_ALTERNATE_CHAINS::chain_info& c) { - return epee::serialization::perform_serialize(blob, ps, hparent_section, "blob"); + j.at("block_hash").get_to(c.block_hash); + j.at("height").get_to(c.height); + j.at("length").get_to(c.length); + j.at("difficulty").get_to(c.difficulty); + j.at("block_hashes").get_to(c.block_hashes); + j.at("main_chain_parent_block").get_to(c.main_chain_parent_block); } +void to_json(nlohmann::json& j, const GET_OUTPUT_HISTOGRAM::entry& e) +{ + j = nlohmann::json{ + {"amount", e.amount}, + {"total_instances", e.total_instances}, + {"unlocked_instances", e.unlocked_instances}, + {"recent_instances", e.recent_instances}, + }; +} -KV_SERIALIZE_MAP_CODE_BEGIN(GENERATEBLOCKS::request) - KV_SERIALIZE(amount_of_blocks) - KV_SERIALIZE(wallet_address) - KV_SERIALIZE(prev_block) - KV_SERIALIZE_OPT(starting_nonce, (uint32_t)0) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GENERATEBLOCKS::response) - KV_SERIALIZE(height) - KV_SERIALIZE(blocks) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(block_header_response) - KV_SERIALIZE(major_version) - KV_SERIALIZE(minor_version) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(prev_hash) - KV_SERIALIZE(nonce) - KV_SERIALIZE(orphan_status) - KV_SERIALIZE(height) - KV_SERIALIZE(depth) - KV_SERIALIZE(hash) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(cumulative_difficulty) - KV_SERIALIZE(reward) - KV_SERIALIZE(miner_reward) - KV_SERIALIZE(block_size) - KV_SERIALIZE_OPT(block_weight, (uint64_t)0) - KV_SERIALIZE(num_txes) - KV_SERIALIZE(pow_hash) - KV_SERIALIZE_OPT(long_term_weight, (uint64_t)0) - KV_SERIALIZE(miner_tx_hash) - KV_SERIALIZE(tx_hashes) - KV_SERIALIZE(master_node_winner) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_LAST_BLOCK_HEADER::request) - KV_SERIALIZE_OPT(fill_pow_hash, false); - KV_SERIALIZE_OPT(get_tx_hashes, false); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_LAST_BLOCK_HEADER::response) - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HASH::request) - KV_SERIALIZE(hash) - KV_SERIALIZE(hashes) - KV_SERIALIZE_OPT(fill_pow_hash, false); - KV_SERIALIZE_OPT(get_tx_hashes, false); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HASH::response) - KV_SERIALIZE(block_header) - KV_SERIALIZE(block_headers) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HEIGHT::request) - KV_SERIALIZE(height) - KV_SERIALIZE(heights) - KV_SERIALIZE_OPT(fill_pow_hash, false); - KV_SERIALIZE_OPT(get_tx_hashes, false); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HEIGHT::response) - KV_SERIALIZE(block_header) - KV_SERIALIZE(block_headers) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK::request) - KV_SERIALIZE(hash) - KV_SERIALIZE(height) - KV_SERIALIZE_OPT(fill_pow_hash, false); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK::response) - KV_SERIALIZE(block_header) - KV_SERIALIZE(tx_hashes) - KV_SERIALIZE(status) - KV_SERIALIZE(blob) - KV_SERIALIZE(json) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::peer) - KV_SERIALIZE(id) - KV_SERIALIZE(host) - KV_SERIALIZE(ip) - KV_SERIALIZE(port) - KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) - KV_SERIALIZE(last_seen) - KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::request) - KV_SERIALIZE_OPT(public_only, true) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::response) - KV_SERIALIZE(status) - KV_SERIALIZE(white_list) - KV_SERIALIZE(gray_list) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(public_node) - KV_SERIALIZE(host) - KV_SERIALIZE(last_seen) - KV_SERIALIZE(rpc_port) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_PUBLIC_NODES::request) - KV_SERIALIZE_OPT(gray, false) - KV_SERIALIZE_OPT(white, true) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_PUBLIC_NODES::response) - KV_SERIALIZE(status) - KV_SERIALIZE(gray) - KV_SERIALIZE(white) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_HASH_RATE::request) - KV_SERIALIZE(visible) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_LEVEL::request) - KV_SERIALIZE(level) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_CATEGORIES::request) - KV_SERIALIZE(categories) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_CATEGORIES::response) - KV_SERIALIZE(status) - KV_SERIALIZE(categories) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(tx_info) - KV_SERIALIZE(id_hash) - KV_SERIALIZE(tx_json) - KV_SERIALIZE(blob_size) - KV_SERIALIZE_OPT(weight, (uint64_t)0) - KV_SERIALIZE(fee) - KV_SERIALIZE(max_used_block_id_hash) - KV_SERIALIZE(max_used_block_height) - KV_SERIALIZE(kept_by_block) - KV_SERIALIZE(last_failed_height) - KV_SERIALIZE(last_failed_id_hash) - KV_SERIALIZE(receive_time) - KV_SERIALIZE(relayed) - KV_SERIALIZE(last_relayed_time) - KV_SERIALIZE(do_not_relay) - KV_SERIALIZE(double_spend_seen) - KV_SERIALIZE(tx_blob) - KV_SERIALIZE(extra) - KV_SERIALIZE(stake_amount) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(spent_key_image_info) - KV_SERIALIZE(id_hash) - KV_SERIALIZE(txs_hashes) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL::request) - KV_SERIALIZE(tx_extra) - KV_SERIALIZE(stake_info) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL::response) - KV_SERIALIZE(status) - KV_SERIALIZE(transactions) - KV_SERIALIZE(spent_key_images) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES_BIN::request) - KV_SERIALIZE_OPT(flashed_txs_only, false) - KV_SERIALIZE_OPT(long_poll, false) - KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(tx_pool_checksum, crypto::hash{}) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES_BIN::response) - KV_SERIALIZE(status) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES::response) - KV_SERIALIZE(status) - KV_SERIALIZE(tx_hashes) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_BACKLOG::response) - KV_SERIALIZE(status) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(txpool_histo) - KV_SERIALIZE(txs) - KV_SERIALIZE(bytes) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(txpool_stats) - KV_SERIALIZE(bytes_total) - KV_SERIALIZE(bytes_min) - KV_SERIALIZE(bytes_max) - KV_SERIALIZE(bytes_med) - KV_SERIALIZE(fee_total) - KV_SERIALIZE(oldest) - KV_SERIALIZE(txs_total) - KV_SERIALIZE(num_failing) - KV_SERIALIZE(num_10m) - KV_SERIALIZE(num_not_relayed) - KV_SERIALIZE(histo_98pc) - KV_SERIALIZE(histo) - KV_SERIALIZE(num_double_spends) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_STATS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(pool_stats) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_CONNECTIONS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(connections) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADERS_RANGE::request) - KV_SERIALIZE(start_height) - KV_SERIALIZE(end_height) - KV_SERIALIZE_OPT(fill_pow_hash, false); - KV_SERIALIZE_OPT(get_tx_hashes, false); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADERS_RANGE::response) - KV_SERIALIZE(status) - KV_SERIALIZE(headers) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_BOOTSTRAP_DAEMON::request) - KV_SERIALIZE(address) - KV_SERIALIZE(username) - KV_SERIALIZE(password) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_LIMIT::response) - KV_SERIALIZE(status) - KV_SERIALIZE(limit_up) - KV_SERIALIZE(limit_down) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LIMIT::request) - KV_SERIALIZE(limit_down) - KV_SERIALIZE(limit_up) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SET_LIMIT::response) - KV_SERIALIZE(status) - KV_SERIALIZE(limit_up) - KV_SERIALIZE(limit_down) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(OUT_PEERS::request) - KV_SERIALIZE_OPT(set, true) - KV_SERIALIZE(out_peers) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(OUT_PEERS::response) - KV_SERIALIZE(out_peers) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(IN_PEERS::request) - KV_SERIALIZE_OPT(set, true) - KV_SERIALIZE(in_peers) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(IN_PEERS::response) - KV_SERIALIZE(in_peers) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::request) - KV_SERIALIZE(version) - KV_SERIALIZE(height) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::response) - KV_SERIALIZE(version) - KV_SERIALIZE(enabled) - KV_SERIALIZE(earliest_height) - KV_SERIALIZE(last_height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GETBANS::ban) - KV_SERIALIZE(host) - KV_SERIALIZE(ip) - KV_SERIALIZE(seconds) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GETBANS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(bans) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SETBANS::ban) - KV_SERIALIZE(host) - KV_SERIALIZE(ip) - KV_SERIALIZE(ban) - KV_SERIALIZE(seconds) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(SETBANS::request) - KV_SERIALIZE(bans) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BANNED::request) - KV_SERIALIZE(address) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BANNED::response) - KV_SERIALIZE(status) - KV_SERIALIZE(banned) - KV_SERIALIZE(seconds) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(FLUSH_TRANSACTION_POOL::request) - KV_SERIALIZE(txids) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::request) - KV_SERIALIZE(amounts); - KV_SERIALIZE(min_count); - KV_SERIALIZE(max_count); - KV_SERIALIZE(unlocked); - KV_SERIALIZE(recent_cutoff); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::entry) - KV_SERIALIZE(amount); - KV_SERIALIZE(total_instances); - KV_SERIALIZE(unlocked_instances); - KV_SERIALIZE(recent_instances); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::response) - KV_SERIALIZE(status) - KV_SERIALIZE(histogram) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_VERSION::response) - KV_SERIALIZE(status) - KV_SERIALIZE(version) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_COINBASE_TX_SUM::request) - KV_SERIALIZE(height); - KV_SERIALIZE(count); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_COINBASE_TX_SUM::response) - KV_SERIALIZE(status) - KV_SERIALIZE(emission_amount) - KV_SERIALIZE(fee_amount) - KV_SERIALIZE(burn_amount) -KV_SERIALIZE_MAP_CODE_END() - +void from_json(const nlohmann::json& j, GET_OUTPUT_HISTOGRAM::entry& e) +{ + j.at("amount").get_to(e.amount); + j.at("total_instances").get_to(e.total_instances); + j.at("unlocked_instances").get_to(e.unlocked_instances); + j.at("recent_instances").get_to(e.recent_instances); +}; -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BASE_FEE_ESTIMATE::request) - KV_SERIALIZE(grace_blocks) -KV_SERIALIZE_MAP_CODE_END() +void to_json(nlohmann::json& j, const GET_OUTPUT_DISTRIBUTION::distribution& y) +{ + j = nlohmann::json{ + {"amount", y.amount}, + {"distribution", y.data.distribution}, + {"start_height", y.data.start_height}, + {"base", y.data.base} + }; +} +void to_json(nlohmann::json& j, const BNS_OWNERS_TO_NAMES::response_entry& r) +{ + j = nlohmann::json{ + {"request_index", r.request_index}, + {"name_hash", r.name_hash}, + {"owner", r.owner}, + {"backup_owner", r.backup_owner}, + {"encrypted_bchat_value", r.encrypted_bchat_value}, + {"encrypted_wallet_value", r.encrypted_wallet_value}, + {"encrypted_belnet_value", r.encrypted_belnet_value}, + {"encrypted_eth_addr_value", r.encrypted_eth_addr_value}, + {"update_height", r.update_height}, + {"expiration_height", r.expiration_height}, + {"txid", r.txid}, + }; +} -KV_SERIALIZE_MAP_CODE_BEGIN(GET_BASE_FEE_ESTIMATE::response) - KV_SERIALIZE(status) - KV_SERIALIZE(fee_per_byte) - KV_SERIALIZE(fee_per_output) - KV_SERIALIZE(flash_fee_per_byte) - KV_SERIALIZE(flash_fee_per_output) - KV_SERIALIZE(flash_fee_fixed) - KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(STATUS) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(EMPTY) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_HEIGHT::response) +// KV_SERIALIZE(height) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE(hash) +// KV_SERIALIZE(immutable_height) +// KV_SERIALIZE(immutable_hash) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::request) +// KV_SERIALIZE(txs_hashes) +// KV_SERIALIZE(decode_as_json) +// KV_SERIALIZE(tx_extra) +// KV_SERIALIZE(prune) +// KV_SERIALIZE(split) +// KV_SERIALIZE(stake_info) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::mn_reg_info::contribution) +// KV_SERIALIZE(wallet) +// KV_SERIALIZE(portion) +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::mn_reg_info) +// KV_SERIALIZE(contributors) +// KV_SERIALIZE(fee) +// KV_SERIALIZE(expiry) +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::state_change) +// KV_SERIALIZE(old_dereg) +// KV_SERIALIZE(type) +// KV_SERIALIZE(height) +// KV_SERIALIZE(index) +// KV_SERIALIZE(voters) +// KV_SERIALIZE(reasons); +// KV_SERIALIZE(reasons_maybe); +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::bns_details) +// KV_SERIALIZE(version) +// KV_SERIALIZE(buy) +// KV_SERIALIZE(update) +// KV_SERIALIZE(renew) +// KV_SERIALIZE(type) +// KV_SERIALIZE(blocks) +// KV_SERIALIZE(name_hash) +// KV_SERIALIZE(prev_txid) +// KV_SERIALIZE(value_bchat) +// KV_SERIALIZE(value_wallet) +// KV_SERIALIZE(value_belnet) +// KV_SERIALIZE(value_eth_addr) +// KV_SERIALIZE(owner) +// KV_SERIALIZE(backup_owner) +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry) +// KV_SERIALIZE(pubkey) +// KV_SERIALIZE(burn_amount) +// KV_SERIALIZE(extra_nonce) +// KV_SERIALIZE(payment_id) +// KV_SERIALIZE(mm_depth) +// KV_SERIALIZE(mm_root) +// KV_SERIALIZE(additional_pubkeys) +// KV_SERIALIZE(mn_winner) +// KV_SERIALIZE(mn_pubkey) +// KV_SERIALIZE(mn_registration) +// KV_SERIALIZE(mn_contributor) +// KV_SERIALIZE(mn_state_change) +// KV_SERIALIZE(tx_secret_key) +// KV_SERIALIZE(locked_key_images) +// KV_SERIALIZE(key_image_unlock) +// KV_SERIALIZE(bns) +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::entry) +// KV_SERIALIZE(tx_hash) +// KV_SERIALIZE(as_hex) +// KV_SERIALIZE(as_json) +// KV_SERIALIZE(pruned_as_hex) +// KV_SERIALIZE(prunable_as_hex) +// KV_SERIALIZE(prunable_hash) +// KV_SERIALIZE(size) +// KV_SERIALIZE(in_pool) +// KV_SERIALIZE(double_spend_seen) +// if (!in_pool) +// { +// KV_SERIALIZE(block_height) +// KV_SERIALIZE(block_timestamp) +// KV_SERIALIZE(output_indices) +// } +// else +// { +// KV_SERIALIZE(relayed) +// KV_SERIALIZE(received_timestamp) +// } +// KV_SERIALIZE(flash) +// KV_SERIALIZE(extra) +// KV_SERIALIZE(stake_amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::response) +// KV_SERIALIZE(txs) +// KV_SERIALIZE(missed_tx) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(IS_KEY_IMAGE_SPENT::request) +// KV_SERIALIZE(key_images) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(IS_KEY_IMAGE_SPENT::response) +// KV_SERIALIZE(spent_status) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::request) +// KV_SERIALIZE(outputs) +// KV_SERIALIZE(get_txid) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::outkey) +// KV_SERIALIZE(key) +// KV_SERIALIZE(mask) +// KV_SERIALIZE(unlocked) +// KV_SERIALIZE(height) +// KV_SERIALIZE(txid) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUTS::response) +// KV_SERIALIZE(outs) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SEND_RAW_TX::request) +// KV_SERIALIZE(tx_as_hex) +// KV_SERIALIZE_OPT(do_not_relay, false) +// KV_SERIALIZE_OPT(flash, false) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SEND_RAW_TX::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(reason) +// KV_SERIALIZE(not_relayed) +// KV_SERIALIZE(sanity_check_failed) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE(tvc) +// KV_SERIALIZE_ENUM(flash_status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(START_MINING::request) +// KV_SERIALIZE(miner_address) +// KV_SERIALIZE(threads_count) +// KV_SERIALIZE_OPT(num_blocks, uint64_t{0}) +// KV_SERIALIZE_OPT(slow_mining, false) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(MINING_STATUS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(active) +// KV_SERIALIZE(speed) +// KV_SERIALIZE(threads_count) +// KV_SERIALIZE(address) +// KV_SERIALIZE(pow_algorithm) +// KV_SERIALIZE(block_target) +// KV_SERIALIZE(block_reward) +// KV_SERIALIZE(difficulty) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_INFO::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(height) +// KV_SERIALIZE(target_height) +// KV_SERIALIZE(immutable_height) +// KV_SERIALIZE(POS_ideal_timestamp) +// KV_SERIALIZE(POS_target_timestamp) +// KV_SERIALIZE(difficulty) +// KV_SERIALIZE(target) +// KV_SERIALIZE(tx_count) +// KV_SERIALIZE(tx_pool_size) +// KV_SERIALIZE(alt_blocks_count) +// KV_SERIALIZE(outgoing_connections_count) +// KV_SERIALIZE(incoming_connections_count) +// KV_SERIALIZE(white_peerlist_size) +// KV_SERIALIZE(grey_peerlist_size) +// KV_SERIALIZE(mainnet) +// KV_SERIALIZE(testnet) +// KV_SERIALIZE(devnet) +// KV_SERIALIZE(nettype) +// KV_SERIALIZE(top_block_hash) +// KV_SERIALIZE(immutable_block_hash) +// KV_SERIALIZE(cumulative_difficulty) +// KV_SERIALIZE(block_size_limit) +// KV_SERIALIZE(block_weight_limit) +// KV_SERIALIZE(block_size_median) +// KV_SERIALIZE(block_weight_median) +// KV_SERIALIZE(bns_counts) +// KV_SERIALIZE(start_time) +// KV_SERIALIZE(master_node) +// KV_SERIALIZE(last_storage_server_ping) +// KV_SERIALIZE(last_belnet_ping) +// KV_SERIALIZE(free_space) +// KV_SERIALIZE(offline) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE(bootstrap_daemon_address) +// KV_SERIALIZE(height_without_bootstrap) +// KV_SERIALIZE(was_bootstrap_ever_used) +// KV_SERIALIZE(database_size) +// KV_SERIALIZE(version) +// KV_SERIALIZE(status_line) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_NET_STATS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(start_time) +// KV_SERIALIZE(total_packets_in) +// KV_SERIALIZE(total_bytes_in) +// KV_SERIALIZE(total_packets_out) +// KV_SERIALIZE(total_bytes_out) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_COUNT::response) +// KV_SERIALIZE(count) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// bool GETBLOCKHASH::request::load(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) +// { +// return epee::serialization::perform_serialize(height, ps, hparent_section, "height"); +// } +// bool GETBLOCKHASH::request::store(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section) +// { +// return epee::serialization::perform_serialize(height, ps, hparent_section, "height"); +// } + + +// KV_SERIALIZE_MAP_CODE_BEGIN(block_header_response) +// KV_SERIALIZE(major_version) +// KV_SERIALIZE(minor_version) +// KV_SERIALIZE(timestamp) +// KV_SERIALIZE(prev_hash) +// KV_SERIALIZE(nonce) +// KV_SERIALIZE(orphan_status) +// KV_SERIALIZE(height) +// KV_SERIALIZE(depth) +// KV_SERIALIZE(hash) +// KV_SERIALIZE(difficulty) +// KV_SERIALIZE(cumulative_difficulty) +// KV_SERIALIZE(reward) +// KV_SERIALIZE(coinbase_payouts) +// KV_SERIALIZE(block_size) +// KV_SERIALIZE_OPT(block_weight, (uint64_t)0) +// KV_SERIALIZE(num_txes) +// KV_SERIALIZE(pow_hash) +// KV_SERIALIZE_OPT(long_term_weight, (uint64_t)0) +// KV_SERIALIZE(miner_tx_hash) +// KV_SERIALIZE(tx_hashes) +// KV_SERIALIZE(master_node_winner) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_LAST_BLOCK_HEADER::request) +// KV_SERIALIZE_OPT(fill_pow_hash, false); +// KV_SERIALIZE_OPT(get_tx_hashes, false); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_LAST_BLOCK_HEADER::response) +// KV_SERIALIZE(block_header) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HASH::request) +// KV_SERIALIZE(hash) +// KV_SERIALIZE(hashes) +// KV_SERIALIZE_OPT(fill_pow_hash, false); +// KV_SERIALIZE_OPT(get_tx_hashes, false); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HASH::response) +// KV_SERIALIZE(block_header) +// KV_SERIALIZE(block_headers) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HEIGHT::request) +// KV_SERIALIZE(height) +// KV_SERIALIZE(heights) +// KV_SERIALIZE_OPT(fill_pow_hash, false); +// KV_SERIALIZE_OPT(get_tx_hashes, false); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADER_BY_HEIGHT::response) +// KV_SERIALIZE(block_header) +// KV_SERIALIZE(block_headers) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK::request) +// KV_SERIALIZE(hash) +// KV_SERIALIZE(height) +// KV_SERIALIZE_OPT(fill_pow_hash, false); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK::response) +// KV_SERIALIZE(block_header) +// KV_SERIALIZE(tx_hashes) +// KV_SERIALIZE(status) +// KV_SERIALIZE(blob) +// KV_SERIALIZE(json) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::peer) +// KV_SERIALIZE(id) +// KV_SERIALIZE(host) +// KV_SERIALIZE(ip) +// KV_SERIALIZE(port) +// KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) +// KV_SERIALIZE(last_seen) +// KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::request) +// KV_SERIALIZE_OPT(public_only, true) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_PEER_LIST::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(white_list) +// KV_SERIALIZE(gray_list) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_HASH_RATE::request) +// KV_SERIALIZE(visible) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_LEVEL::request) +// KV_SERIALIZE(level) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_CATEGORIES::request) +// KV_SERIALIZE(categories) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LOG_CATEGORIES::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(categories) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(tx_info) +// FIXME: delete +// KV_SERIALIZE_MAP_CODE_BEGIN(old_tx_info) +// KV_SERIALIZE(id_hash) +// KV_SERIALIZE(tx_json) +// KV_SERIALIZE(blob_size) +// KV_SERIALIZE_OPT(weight, (uint64_t)0) +// KV_SERIALIZE(fee) +// KV_SERIALIZE(max_used_block_id_hash) +// KV_SERIALIZE(max_used_block_height) +// KV_SERIALIZE(kept_by_block) +// KV_SERIALIZE(last_failed_height) +// KV_SERIALIZE(last_failed_id_hash) +// KV_SERIALIZE(receive_time) +// KV_SERIALIZE(relayed) +// KV_SERIALIZE(last_relayed_time) +// KV_SERIALIZE(do_not_relay) +// KV_SERIALIZE(double_spend_seen) +// KV_SERIALIZE(tx_blob) +// KV_SERIALIZE(extra) +// // KV_SERIALIZE(stake_amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(spent_key_image_info) +// KV_SERIALIZE(id_hash) +// KV_SERIALIZE(txs_hashes) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL::request) +// KV_SERIALIZE(tx_extra) +// KV_SERIALIZE(stake_info) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(transactions) +// KV_SERIALIZE(spent_key_images) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_HASHES::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(tx_hashes) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(txpool_histo) +// KV_SERIALIZE(txs) +// KV_SERIALIZE(bytes) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALTERNATE_CHAINS::chain_info) - KV_SERIALIZE(block_hash) - KV_SERIALIZE(height) - KV_SERIALIZE(length) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(block_hashes) - KV_SERIALIZE(main_chain_parent_block) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(txpool_stats) +// KV_SERIALIZE(bytes_total) +// KV_SERIALIZE(bytes_min) +// KV_SERIALIZE(bytes_max) +// KV_SERIALIZE(bytes_med) +// KV_SERIALIZE(fee_total) +// KV_SERIALIZE(oldest) +// KV_SERIALIZE(txs_total) +// KV_SERIALIZE(num_failing) +// KV_SERIALIZE(num_10m) +// KV_SERIALIZE(num_not_relayed) +// KV_SERIALIZE(histo_98pc) +// KV_SERIALIZE(histo) +// KV_SERIALIZE(num_double_spends) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALTERNATE_CHAINS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(chains) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTION_POOL_STATS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(pool_stats) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(RELAY_TX::request) - KV_SERIALIZE(txids) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_CONNECTIONS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(connections) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::peer) - KV_SERIALIZE(info) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADERS_RANGE::request) +// KV_SERIALIZE(start_height) +// KV_SERIALIZE(end_height) +// KV_SERIALIZE_OPT(fill_pow_hash, false); +// KV_SERIALIZE_OPT(get_tx_hashes, false); +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::span) - KV_SERIALIZE(start_block_height) - KV_SERIALIZE(nblocks) - KV_SERIALIZE(connection_id) - KV_SERIALIZE(rate) - KV_SERIALIZE(speed) - KV_SERIALIZE(size) - KV_SERIALIZE(remote_address) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCK_HEADERS_RANGE::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(headers) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_BOOTSTRAP_DAEMON::request) +// KV_SERIALIZE(address) +// KV_SERIALIZE(username) +// KV_SERIALIZE(password) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::response) - KV_SERIALIZE(status) - KV_SERIALIZE(height) - KV_SERIALIZE(target_height) - KV_SERIALIZE(next_needed_pruning_seed) - KV_SERIALIZE(peers) - KV_SERIALIZE(spans) - KV_SERIALIZE(overview) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_LIMIT::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(limit_up) +// KV_SERIALIZE(limit_down) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::request) - KV_SERIALIZE(amounts) - KV_SERIALIZE_OPT(from_height, (uint64_t)0) - KV_SERIALIZE_OPT(to_height, (uint64_t)0) - KV_SERIALIZE_OPT(cumulative, false) - KV_SERIALIZE_OPT(binary, true) - KV_SERIALIZE_OPT(compress, false) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LIMIT::request) +// KV_SERIALIZE(limit_down) +// KV_SERIALIZE(limit_up) +// KV_SERIALIZE_MAP_CODE_END() -namespace -{ - template - std::string compress_integer_array(const std::vector &v) - { - std::string s; - s.reserve(tools::VARINT_MAX_LENGTH); - auto ins = std::back_inserter(s); - for (const T &t: v) - tools::write_varint(ins, t); - return s; - } - - template - std::vector decompress_integer_array(const std::string &s) - { - std::vector v; - for (auto it = s.begin(); it < s.end(); ) - { - int read = tools::read_varint(it, s.end(), v.emplace_back()); - CHECK_AND_ASSERT_THROW_MES(read > 0, "Error decompressing data"); - } - return v; - } -} +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_LIMIT::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(limit_up) +// KV_SERIALIZE(limit_down) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::distribution) - KV_SERIALIZE(amount) - KV_SERIALIZE_N(data.start_height, "start_height") - KV_SERIALIZE(binary) - KV_SERIALIZE(compress) - if (binary) - { - if (is_store) - { - if (compress) - { - const_cast(compressed_data) = compress_integer_array(data.distribution); - KV_SERIALIZE(compressed_data) - } - else - KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") - } - else - { - if (compress) - { - KV_SERIALIZE(compressed_data) - const_cast&>(data.distribution) = decompress_integer_array(compressed_data); - } - else - KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") - } - } - else - KV_SERIALIZE_N(data.distribution, "distribution") - KV_SERIALIZE_N(data.base, "base") -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(OUT_PEERS::request) +// KV_SERIALIZE_OPT(set, true) +// KV_SERIALIZE(out_peers) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::response) - KV_SERIALIZE(status) - KV_SERIALIZE(distributions) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(OUT_PEERS::response) +// KV_SERIALIZE(out_peers) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(POP_BLOCKS::request) - KV_SERIALIZE(nblocks); -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(IN_PEERS::request) +// KV_SERIALIZE_OPT(set, true) +// KV_SERIALIZE(in_peers) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(POP_BLOCKS::response) - KV_SERIALIZE(status) - KV_SERIALIZE(height) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(IN_PEERS::response) +// KV_SERIALIZE(in_peers) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(PRUNE_BLOCKCHAIN::request) - KV_SERIALIZE_OPT(check, false) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::request) +// KV_SERIALIZE(version) +// KV_SERIALIZE(height) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(PRUNE_BLOCKCHAIN::response) - KV_SERIALIZE(status) - KV_SERIALIZE(pruned) - KV_SERIALIZE(pruning_seed) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(HARD_FORK_INFO::response) +// KV_SERIALIZE_ENUM(version) +// KV_SERIALIZE(revision) +// KV_SERIALIZE(enabled) +// KV_SERIALIZE(earliest_height) +// KV_SERIALIZE(last_height) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::request) - KV_SERIALIZE_OPT(start_height, HEIGHT_SENTINEL_VALUE) - KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) - KV_SERIALIZE_OPT(quorum_type, ALL_QUORUMS_SENTINEL_VALUE) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BANS::ban) +// KV_SERIALIZE(host) +// KV_SERIALIZE(ip) +// KV_SERIALIZE(seconds) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::quorum_t) - KV_SERIALIZE(validators) - KV_SERIALIZE(workers) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_BANS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(bans) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::quorum_for_height) - KV_SERIALIZE(height) - KV_SERIALIZE(quorum_type) - KV_SERIALIZE(quorum) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_BANS::ban) +// KV_SERIALIZE(host) +// KV_SERIALIZE(ip) +// KV_SERIALIZE(ban) +// KV_SERIALIZE(seconds) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::response) - KV_SERIALIZE(status) - KV_SERIALIZE(quorums) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(SET_BANS::request) +// KV_SERIALIZE(bans) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD_RAW::request) - KV_SERIALIZE(args) - KV_SERIALIZE(make_friendly) - KV_SERIALIZE(staking_requirement) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(BANNED::request) +// KV_SERIALIZE(address) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD_RAW::response) - KV_SERIALIZE(status) - KV_SERIALIZE(registration_cmd) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(BANNED::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(banned) +// KV_SERIALIZE(seconds) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD::contribution_t) - KV_SERIALIZE(address) - KV_SERIALIZE(amount) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(FLUSH_TRANSACTION_POOL::request) +// KV_SERIALIZE(txids) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD::request) - KV_SERIALIZE(operator_cut) - KV_SERIALIZE(contributions) - KV_SERIALIZE(staking_requirement) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::request) +// KV_SERIALIZE(amounts); +// KV_SERIALIZE(min_count); +// KV_SERIALIZE(max_count); +// KV_SERIALIZE(unlocked); +// KV_SERIALIZE(recent_cutoff); +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_KEYS::response) - KV_SERIALIZE(master_node_pubkey) - KV_SERIALIZE(master_node_ed25519_pubkey) - KV_SERIALIZE(master_node_x25519_pubkey) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::entry) +// KV_SERIALIZE(amount); +// KV_SERIALIZE(total_instances); +// KV_SERIALIZE(unlocked_instances); +// KV_SERIALIZE(recent_instances); +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_PRIVKEYS::response) - KV_SERIALIZE(master_node_privkey) - KV_SERIALIZE(master_node_ed25519_privkey) - KV_SERIALIZE(master_node_x25519_privkey) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(histogram) + // KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(master_node_contribution) - KV_SERIALIZE(key_image) - KV_SERIALIZE(key_image_pub_key) - KV_SERIALIZE(amount) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_VERSION::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(version) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(master_node_contributor) - KV_SERIALIZE(amount) - KV_SERIALIZE(reserved) - KV_SERIALIZE(address) - KV_SERIALIZE(locked_contributions) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_COINBASE_TX_SUM::request) +// KV_SERIALIZE(height); +// KV_SERIALIZE(count); +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::requested_fields_t) - KV_SERIALIZE(all) - if (!this_ref.all) - { - KV_SERIALIZE(master_node_pubkey) - KV_SERIALIZE(registration_height) - KV_SERIALIZE(registration_hf_version) - KV_SERIALIZE(requested_unlock_height) - KV_SERIALIZE(last_reward_block_height) - KV_SERIALIZE(last_reward_transaction_index) - KV_SERIALIZE(active) - KV_SERIALIZE(funded) - KV_SERIALIZE(state_height) - KV_SERIALIZE(decommission_count) - KV_SERIALIZE(earned_downtime_blocks) - KV_SERIALIZE(master_node_version) - KV_SERIALIZE(belnet_version) - KV_SERIALIZE(storage_server_version) - KV_SERIALIZE(contributors) - KV_SERIALIZE(total_contributed) - KV_SERIALIZE(total_reserved) - KV_SERIALIZE(staking_requirement) - KV_SERIALIZE(portions_for_operator) - KV_SERIALIZE(swarm_id) - KV_SERIALIZE(operator_address) - KV_SERIALIZE(public_ip) - KV_SERIALIZE(storage_port) - KV_SERIALIZE(storage_lmq_port) - KV_SERIALIZE(quorumnet_port) - KV_SERIALIZE(pubkey_ed25519) - KV_SERIALIZE(pubkey_x25519) - KV_SERIALIZE(block_hash) - KV_SERIALIZE(height) - KV_SERIALIZE(target_height) - KV_SERIALIZE(hardfork) - KV_SERIALIZE(mnode_revision) - - KV_SERIALIZE(last_uptime_proof) - KV_SERIALIZE(storage_server_reachable) - KV_SERIALIZE(storage_server_first_unreachable) - KV_SERIALIZE(storage_server_last_unreachable) - KV_SERIALIZE(storage_server_last_reachable) - KV_SERIALIZE(belnet_reachable) - KV_SERIALIZE(belnet_first_unreachable) - KV_SERIALIZE(belnet_last_unreachable) - KV_SERIALIZE(belnet_last_reachable) - KV_SERIALIZE(checkpoint_participation) - KV_SERIALIZE(POS_participation) - KV_SERIALIZE(timestamp_participation) - KV_SERIALIZE(timesync_status) - } -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::request) - KV_SERIALIZE(master_node_pubkeys); - KV_SERIALIZE(include_json); - KV_SERIALIZE(limit) - KV_SERIALIZE(active_only) - KV_SERIALIZE(fields) - KV_SERIALIZE(poll_block_hash) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::response::entry) - const auto* res = stg.template get_context(); - const bool all = !is_store || !res || res->fields.all; - - #define KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(var) if (all || res->fields.var) KV_SERIALIZE(var) - - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(master_node_pubkey); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(registration_height); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(registration_hf_version); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(requested_unlock_height); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_reward_block_height); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_reward_transaction_index); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(active); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(funded); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(state_height); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(decommission_count); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(earned_downtime_blocks); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(master_node_version); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_version) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_version) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(contributors); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(total_contributed); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(total_reserved); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(staking_requirement); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(portions_for_operator); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(swarm_id); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(operator_address); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(public_ip); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_port); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_lmq_port); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(quorumnet_port); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(pubkey_ed25519); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(pubkey_x25519); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_uptime_proof); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_reachable); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_first_unreachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_last_unreachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_last_reachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_reachable); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_first_unreachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_last_unreachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_last_reachable) - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(checkpoint_participation); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(POS_participation); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(timestamp_participation); - KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(timesync_status); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::response) - if (!unchanged) KV_SERIALIZE_DEPENDENT(master_node_states) - KV_SERIALIZE(status) - if (fields.height || fields.all) KV_SERIALIZE(height) - if (fields.target_height || fields.all) KV_SERIALIZE(target_height) - if (fields.block_hash || fields.all || (polling_mode && !unchanged)) KV_SERIALIZE(block_hash) - if (fields.hardfork || fields.all) KV_SERIALIZE(hardfork) - if (fields.mnode_revision || fields.all) KV_SERIALIZE(mnode_revision) - if (!as_json.empty()) KV_SERIALIZE(as_json) - if (polling_mode) KV_SERIALIZE(unchanged); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_STATUS::request) - KV_SERIALIZE(include_json); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_STATUS::response) - KV_SERIALIZE(master_node_state) - KV_SERIALIZE(height) - KV_SERIALIZE(block_hash) - KV_SERIALIZE(status) - KV_SERIALIZE(as_json) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(STORAGE_SERVER_PING::request) - KV_SERIALIZE(version); - KV_SERIALIZE(https_port); - KV_SERIALIZE(omq_port); - KV_SERIALIZE(pubkey_ed25519); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BELNET_PING::request) - KV_SERIALIZE(version); - KV_SERIALIZE(pubkey_ed25519); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_STAKING_REQUIREMENT::request) - KV_SERIALIZE(height) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_STAKING_REQUIREMENT::response) - KV_SERIALIZE(staking_requirement) - KV_SERIALIZE(height) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::entry) - KV_SERIALIZE(key_image) - KV_SERIALIZE(unlock_height) - KV_SERIALIZE(amount) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::response) - KV_SERIALIZE(blacklist) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_BLACKLIST::response) - KV_SERIALIZE(blacklist) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::request) - KV_SERIALIZE_OPT(start_height, HEIGHT_SENTINEL_VALUE) - KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) - KV_SERIALIZE_OPT(count, NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::quorum_signature_serialized) - KV_SERIALIZE(voter_index); - KV_SERIALIZE(signature); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::checkpoint_serialized) - KV_SERIALIZE(version); - KV_SERIALIZE(type); - KV_SERIALIZE(height); - KV_SERIALIZE(block_hash); - KV_SERIALIZE(signatures); - KV_SERIALIZE(prev_height); -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::response) - KV_SERIALIZE(checkpoints) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MN_STATE_CHANGES::request) - KV_SERIALIZE(start_height) - KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) -KV_SERIALIZE_MAP_CODE_END() +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_COINBASE_TX_SUM::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(emission_amount) +// KV_SERIALIZE(fee_amount) +// KV_SERIALIZE(burn_amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_FEE_ESTIMATE::request) +// KV_SERIALIZE(grace_blocks) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_FEE_ESTIMATE::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(fee_per_byte) +// KV_SERIALIZE(fee_per_output) +// KV_SERIALIZE(flash_fee_per_byte) +// KV_SERIALIZE(flash_fee_per_output) +// KV_SERIALIZE(flash_fee_fixed) +// KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALTERNATE_CHAINS::chain_info) +// KV_SERIALIZE(block_hash) +// KV_SERIALIZE(height) +// KV_SERIALIZE(length) +// KV_SERIALIZE(difficulty) +// KV_SERIALIZE(block_hashes) +// KV_SERIALIZE(main_chain_parent_block) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_ALTERNATE_CHAINS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(chains) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(RELAY_TX::request) +// KV_SERIALIZE(txids) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::peer) +// KV_SERIALIZE(info) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::span) +// KV_SERIALIZE(start_block_height) +// KV_SERIALIZE(nblocks) +// KV_SERIALIZE(connection_id) +// KV_SERIALIZE(rate) +// KV_SERIALIZE(speed) +// KV_SERIALIZE(size) +// KV_SERIALIZE(remote_address) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(SYNC_INFO::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(height) +// KV_SERIALIZE(target_height) +// KV_SERIALIZE(next_needed_pruning_seed) +// KV_SERIALIZE(peers) +// KV_SERIALIZE(spans) +// KV_SERIALIZE(overview) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::request) +// KV_SERIALIZE(amounts) +// KV_SERIALIZE_OPT(from_height, (uint64_t)0) +// KV_SERIALIZE_OPT(to_height, (uint64_t)0) +// KV_SERIALIZE_OPT(cumulative, false) +// KV_SERIALIZE_OPT(binary, true) +// KV_SERIALIZE_OPT(compress, false) +// KV_SERIALIZE_MAP_CODE_END() + + +// namespace +// { +// template +// std::string compress_integer_array(const std::vector &v) +// { +// std::string s; +// s.reserve(tools::VARINT_MAX_LENGTH); +// auto ins = std::back_inserter(s); +// for (const T &t: v) +// tools::write_varint(ins, t); +// return s; +// } + +// template +// std::vector decompress_integer_array(const std::string &s) +// { +// std::vector v; +// for (auto it = s.begin(); it < s.end(); ) +// { +// int read = tools::read_varint(it, s.end(), v.emplace_back()); +// CHECK_AND_ASSERT_THROW_MES(read > 0, "Error decompressing data"); +// } +// return v; +// } +// } + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::distribution) +// KV_SERIALIZE(amount) +// KV_SERIALIZE_N(data.start_height, "start_height") +// KV_SERIALIZE(binary) +// KV_SERIALIZE(compress) +// if (binary) +// { +// if (is_store) +// { +// if (compress) +// { +// const_cast(compressed_data) = compress_integer_array(data.distribution); +// KV_SERIALIZE(compressed_data) +// } +// else +// KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") +// } +// else +// { +// if (compress) +// { +// KV_SERIALIZE(compressed_data) +// const_cast&>(data.distribution) = decompress_integer_array(compressed_data); +// } +// else +// KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") +// } +// } +// else +// KV_SERIALIZE_N(data.distribution, "distribution") +// KV_SERIALIZE_N(data.base, "base") +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(distributions) + // KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(POP_BLOCKS::request) +// KV_SERIALIZE(nblocks); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(POP_BLOCKS::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(height) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(PRUNE_BLOCKCHAIN::request) +// KV_SERIALIZE_OPT(check, false) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(PRUNE_BLOCKCHAIN::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(pruned) +// KV_SERIALIZE(pruning_seed) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::request) +// KV_SERIALIZE_OPT(start_height, HEIGHT_SENTINEL_VALUE) +// KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) +// KV_SERIALIZE_OPT(quorum_type, ALL_QUORUMS_SENTINEL_VALUE) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::quorum_t) +// KV_SERIALIZE(validators) +// KV_SERIALIZE(workers) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::quorum_for_height) +// KV_SERIALIZE(height) +// KV_SERIALIZE(quorum_type) +// KV_SERIALIZE(quorum) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_QUORUM_STATE::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(quorums) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD_RAW::request) +// KV_SERIALIZE(args) +// KV_SERIALIZE(make_friendly) +// KV_SERIALIZE(staking_requirement) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD_RAW::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(registration_cmd) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD::contribution_t) +// KV_SERIALIZE(address) +// KV_SERIALIZE(amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_REGISTRATION_CMD::request) +// KV_SERIALIZE(operator_cut) +// KV_SERIALIZE(contributions) +// KV_SERIALIZE(staking_requirement) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_KEYS::response) +// KV_SERIALIZE(master_node_pubkey) +// KV_SERIALIZE(master_node_ed25519_pubkey) +// KV_SERIALIZE(master_node_x25519_pubkey) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_PRIVKEYS::response) +// KV_SERIALIZE(master_node_privkey) +// KV_SERIALIZE(master_node_ed25519_privkey) +// KV_SERIALIZE(master_node_x25519_privkey) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(master_node_contribution) +// KV_SERIALIZE(key_image) +// KV_SERIALIZE(key_image_pub_key) +// KV_SERIALIZE(amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(master_node_contributor) +// KV_SERIALIZE(amount) +// KV_SERIALIZE(reserved) +// KV_SERIALIZE(address) +// KV_SERIALIZE(locked_contributions) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::requested_fields_t) +// KV_SERIALIZE(all) +// if (!this_ref.all) +// { +// KV_SERIALIZE(master_node_pubkey) +// KV_SERIALIZE(registration_height) +// KV_SERIALIZE(registration_hf_version) +// KV_SERIALIZE(requested_unlock_height) +// KV_SERIALIZE(last_reward_block_height) +// KV_SERIALIZE(last_reward_transaction_index) +// KV_SERIALIZE(active) +// KV_SERIALIZE(funded) +// KV_SERIALIZE(state_height) +// KV_SERIALIZE(decommission_count) +// KV_SERIALIZE(earned_downtime_blocks) +// KV_SERIALIZE(master_node_version) +// KV_SERIALIZE(belnet_version) +// KV_SERIALIZE(storage_server_version) +// KV_SERIALIZE(contributors) +// KV_SERIALIZE(total_contributed) +// KV_SERIALIZE(total_reserved) +// KV_SERIALIZE(staking_requirement) +// KV_SERIALIZE(portions_for_operator) +// KV_SERIALIZE(swarm_id) +// KV_SERIALIZE(swarm) +// KV_SERIALIZE(operator_address) +// KV_SERIALIZE(public_ip) +// KV_SERIALIZE(storage_port) +// KV_SERIALIZE(storage_lmq_port) +// KV_SERIALIZE(quorumnet_port) +// KV_SERIALIZE(pubkey_ed25519) +// KV_SERIALIZE(pubkey_x25519) +// KV_SERIALIZE(block_hash) +// KV_SERIALIZE(height) +// KV_SERIALIZE(target_height) +// KV_SERIALIZE(hardfork) +// KV_SERIALIZE(mnode_revision) + +// KV_SERIALIZE(last_uptime_proof) +// KV_SERIALIZE(storage_server_reachable) +// KV_SERIALIZE(storage_server_first_unreachable) +// KV_SERIALIZE(storage_server_last_unreachable) +// KV_SERIALIZE(storage_server_last_reachable) +// KV_SERIALIZE(belnet_reachable) +// KV_SERIALIZE(belnet_first_unreachable) +// KV_SERIALIZE(belnet_last_unreachable) +// KV_SERIALIZE(belnet_last_reachable) +// KV_SERIALIZE(checkpoint_participation) +// KV_SERIALIZE(POS_participation) +// KV_SERIALIZE(timestamp_participation) +// KV_SERIALIZE(timesync_status) +// } +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::request) +// KV_SERIALIZE(master_node_pubkeys); +// KV_SERIALIZE(include_json); +// KV_SERIALIZE(limit) +// KV_SERIALIZE(active_only) +// KV_SERIALIZE(fields) +// KV_SERIALIZE(poll_block_hash) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::response::entry) +// const auto* res = stg.template get_context(); +// const bool all = !is_store || !res || res->fields.all; + +// #define KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(var) if (all || res->fields.var) KV_SERIALIZE(var) + +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(master_node_pubkey); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(registration_height); +// if (all || res->fields.registration_hf_version) KV_SERIALIZE_ENUM(registration_hf_version); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(requested_unlock_height); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_reward_block_height); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_reward_transaction_index); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(active); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(funded); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(state_height); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(decommission_count); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(earned_downtime_blocks); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(master_node_version); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_version) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_version) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(contributors); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(total_contributed); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(total_reserved); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(staking_requirement); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(portions_for_operator); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(swarm_id); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(swarm); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(operator_address); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(public_ip); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_port); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_lmq_port); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(quorumnet_port); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(pubkey_ed25519); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(pubkey_x25519); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(last_uptime_proof); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_reachable); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_first_unreachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_last_unreachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_last_reachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_reachable); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_first_unreachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_last_unreachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(belnet_last_reachable) +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(checkpoint_participation); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(POS_participation); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(timestamp_participation); +// KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(timesync_status); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODES::response) +// if (!unchanged) KV_SERIALIZE_DEPENDENT(master_node_states) +// KV_SERIALIZE(status) +// if (fields.height || fields.all) KV_SERIALIZE(height) +// if (fields.target_height || fields.all) KV_SERIALIZE(target_height) +// if (fields.block_hash || fields.all || (polling_mode && !unchanged)) KV_SERIALIZE(block_hash) +// if (fields.hardfork || fields.all) KV_SERIALIZE(hardfork) +// if (fields.mnode_revision || fields.all) KV_SERIALIZE(mnode_revision) +// if (!as_json.empty()) KV_SERIALIZE(as_json) +// if (polling_mode) KV_SERIALIZE(unchanged); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_STATUS::request) +// KV_SERIALIZE(include_json); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_STATUS::response) +// KV_SERIALIZE(master_node_state) +// KV_SERIALIZE(height) +// KV_SERIALIZE(block_hash) +// KV_SERIALIZE(status) +// KV_SERIALIZE(as_json) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(STORAGE_SERVER_PING::request) +// KV_SERIALIZE(version); +// KV_SERIALIZE(https_port); +// KV_SERIALIZE(omq_port); +// KV_SERIALIZE(pubkey_ed25519); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BELNET_PING::request) +// KV_SERIALIZE(version); +// KV_SERIALIZE(pubkey_ed25519); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_STAKING_REQUIREMENT::request) +// KV_SERIALIZE(height) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_STAKING_REQUIREMENT::response) +// KV_SERIALIZE(staking_requirement) +// KV_SERIALIZE(height) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::entry) +// KV_SERIALIZE(key_image) +// KV_SERIALIZE(unlock_height) +// KV_SERIALIZE(amount) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::response) +// KV_SERIALIZE(blacklist) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::request) +// KV_SERIALIZE_OPT(start_height, HEIGHT_SENTINEL_VALUE) +// KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) +// KV_SERIALIZE_OPT(count, NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::quorum_signature_serialized) +// KV_SERIALIZE(voter_index); +// KV_SERIALIZE(signature); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::checkpoint_serialized) +// KV_SERIALIZE(version); +// KV_SERIALIZE(type); +// KV_SERIALIZE(height); +// KV_SERIALIZE(block_hash); +// KV_SERIALIZE(signatures); +// KV_SERIALIZE(prev_height); +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::response) +// KV_SERIALIZE(checkpoints) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MN_STATE_CHANGES::request) +// KV_SERIALIZE(start_height) +// KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(GET_MN_STATE_CHANGES::response) +// KV_SERIALIZE(status) +// KV_SERIALIZE(untrusted) +// KV_SERIALIZE(total_deregister) +// KV_SERIALIZE(total_ip_change_penalty) +// KV_SERIALIZE(total_decommission) +// KV_SERIALIZE(total_recommission) +// KV_SERIALIZE(start_height) +// KV_SERIALIZE(end_height) +// KV_SERIALIZE_MAP_CODE_END() -KV_SERIALIZE_MAP_CODE_BEGIN(GET_MN_STATE_CHANGES::response) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) - KV_SERIALIZE(total_deregister) - KV_SERIALIZE(total_ip_change_penalty) - KV_SERIALIZE(total_decommission) - KV_SERIALIZE(total_recommission) - KV_SERIALIZE(start_height) - KV_SERIALIZE(end_height) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(REPORT_PEER_STATUS::request) - KV_SERIALIZE(type) - KV_SERIALIZE(pubkey) - KV_SERIALIZE(passed) -KV_SERIALIZE_MAP_CODE_END() - - - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::request) - KV_SERIALIZE(entries) - KV_SERIALIZE(include_expired) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::response_entry) - KV_SERIALIZE(entry_index) - KV_SERIALIZE(name_hash) - KV_SERIALIZE(owner) - KV_SERIALIZE(backup_owner) - KV_SERIALIZE(encrypted_bchat_value) - KV_SERIALIZE(encrypted_wallet_value) - KV_SERIALIZE(encrypted_belnet_value) - KV_SERIALIZE(encrypted_eth_addr_value) - KV_SERIALIZE(update_height) - KV_SERIALIZE(expiration_height) - KV_SERIALIZE(txid) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::response) - KV_SERIALIZE(entries) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_LOOKUP::request) - KV_SERIALIZE(name) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_LOOKUP::response) - KV_SERIALIZE(name_hash) - KV_SERIALIZE(owner) - KV_SERIALIZE(backup_owner) - KV_SERIALIZE(bchat_value) - KV_SERIALIZE(wallet_value) - KV_SERIALIZE(belnet_value) - KV_SERIALIZE(eth_addr_value) - KV_SERIALIZE(update_height) - KV_SERIALIZE(expiration_height) - KV_SERIALIZE(txid) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::request) - KV_SERIALIZE(entries) - KV_SERIALIZE(include_expired) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::response_entry) - KV_SERIALIZE(request_index) - KV_SERIALIZE(name_hash) - KV_SERIALIZE(owner) - KV_SERIALIZE(backup_owner) - KV_SERIALIZE(encrypted_bchat_value) - KV_SERIALIZE(encrypted_wallet_value) - KV_SERIALIZE(encrypted_belnet_value) - KV_SERIALIZE(update_height) - KV_SERIALIZE(expiration_height) - KV_SERIALIZE(txid) - KV_SERIALIZE(encrypted_eth_addr_value) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::response) - KV_SERIALIZE(entries) - KV_SERIALIZE(status) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_RESOLVE::request) - KV_SERIALIZE(name_hash) - KV_SERIALIZE_OPT(type, static_cast(-1)) -KV_SERIALIZE_MAP_CODE_END() - - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_RESOLVE::response) - KV_SERIALIZE(encrypted_value) - KV_SERIALIZE(nonce) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_VALUE_DECRYPT::request) - KV_SERIALIZE(name); - KV_SERIALIZE(type); - KV_SERIALIZE(encrypted_value); -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(BNS_VALUE_DECRYPT::response) - KV_SERIALIZE(value) -KV_SERIALIZE_MAP_CODE_END() - -KV_SERIALIZE_MAP_CODE_BEGIN(FLUSH_CACHE::request) - KV_SERIALIZE_OPT(bad_txs, false) - KV_SERIALIZE_OPT(bad_blocks, false) -KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(REPORT_PEER_STATUS::request) +// KV_SERIALIZE(type) +// KV_SERIALIZE(pubkey) +// KV_SERIALIZE(passed) +// KV_SERIALIZE_MAP_CODE_END() + + + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::request) +// KV_SERIALIZE(entries) +// KV_SERIALIZE(include_expired) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::response_entry) +// KV_SERIALIZE(entry_index) +// KV_SERIALIZE(name_hash) +// KV_SERIALIZE(owner) +// KV_SERIALIZE(backup_owner) +// KV_SERIALIZE(encrypted_bchat_value) +// KV_SERIALIZE(encrypted_wallet_value) +// KV_SERIALIZE(encrypted_belnet_value) +// KV_SERIALIZE(encrypted_eth_addr_value) +// KV_SERIALIZE(update_height) +// KV_SERIALIZE(expiration_height) +// KV_SERIALIZE(txid) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_NAMES_TO_OWNERS::response) +// KV_SERIALIZE(entries) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_LOOKUP::request) +// KV_SERIALIZE(name) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_LOOKUP::response) +// KV_SERIALIZE(name_hash) +// KV_SERIALIZE(owner) +// KV_SERIALIZE(backup_owner) +// KV_SERIALIZE(bchat_value) +// KV_SERIALIZE(wallet_value) +// KV_SERIALIZE(belnet_value) +// KV_SERIALIZE(eth_addr_value) +// KV_SERIALIZE(update_height) +// KV_SERIALIZE(expiration_height) +// KV_SERIALIZE(txid) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::request) +// KV_SERIALIZE(entries) +// KV_SERIALIZE(include_expired) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::response_entry) +// KV_SERIALIZE(request_index) +// KV_SERIALIZE(name_hash) +// KV_SERIALIZE(owner) +// KV_SERIALIZE(backup_owner) +// KV_SERIALIZE(encrypted_bchat_value) +// KV_SERIALIZE(encrypted_wallet_value) +// KV_SERIALIZE(encrypted_belnet_value) +// KV_SERIALIZE(update_height) +// KV_SERIALIZE(expiration_height) +// KV_SERIALIZE(txid) +// KV_SERIALIZE(encrypted_eth_addr_value) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_OWNERS_TO_NAMES::response) +// KV_SERIALIZE(entries) +// KV_SERIALIZE(status) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_RESOLVE::request) +// KV_SERIALIZE(name_hash) +// KV_SERIALIZE_OPT(type, static_cast(-1)) +// KV_SERIALIZE_MAP_CODE_END() + + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_RESOLVE::response) +// KV_SERIALIZE(encrypted_value) +// KV_SERIALIZE(nonce) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_VALUE_DECRYPT::request) +// KV_SERIALIZE(name); +// KV_SERIALIZE(type); +// KV_SERIALIZE(encrypted_value); +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(BNS_VALUE_DECRYPT::response) +// KV_SERIALIZE(value) +// KV_SERIALIZE_MAP_CODE_END() + +// KV_SERIALIZE_MAP_CODE_BEGIN(FLUSH_CACHE::request) +// KV_SERIALIZE_OPT(bad_txs, false) +// KV_SERIALIZE_OPT(bad_blocks, false) +// KV_SERIALIZE_MAP_CODE_END() } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e6475a12adb..cfe4e08bad4 100755 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -31,16 +31,34 @@ #pragma once +// vim help for nicely wrapping/formatting comments in here: +// Global options for wrapping and indenting lists within comments with gq: +// +// set formatoptions+=n +// set formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s\\+\\\\|^\\s*[-+*]\\s\\+ +// +// cpp-specific options to properly recognize `///` as a comment when wrapping, to go in +// ~/.vim/after/ftplugin/cpp.vim: +// +// setlocal comments-=:// +// setlocal comments+=:/// +// setlocal comments+=:// + +#include "rpc/common/rpc_version.h" +#include "rpc/common/command_decorators.h" + #include "crypto/crypto.h" #include "epee/string_tools.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/verification_context.h" #include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" #include "cryptonote_config.h" #include "cryptonote_core/master_node_voting.h" +#include "cryptonote_core/master_node_list.h" #include "common/varint.h" #include "common/perf_timer.h" #include "common/meta.h" @@ -51,33 +69,31 @@ #include "cryptonote_core/master_node_list.h" #include "common/beldex.h" +#include +#include +#include +#include + namespace cryptonote { + void to_json(nlohmann::json& j, const checkpoint_t& c); +} + +namespace master_nodes { + void to_json(nlohmann::json& j, const key_image_blacklist_entry& b); + void to_json(nlohmann::json& j, const quorum_signature& s); +} /// Namespace for core RPC commands. Every RPC commands gets defined here (including its name(s), /// access, and data type), and added to `core_rpc_types` list at the bottom of the file. +namespace cryptonote::rpc { -namespace rpc { - - using version_t = std::pair; - -// When making *any* change here, bump minor -// If the change is incompatible, then bump major and set minor to 0 -// This ensures rpc::VERSION always increases, that every change -// has its own version, and that clients can just test major to see -// whether they can talk to a given daemon without having to know in -// advance which version they will stop working with - constexpr version_t VERSION = {4, 0}; - - /// Makes a version array from a packed 32-bit integer version - constexpr version_t make_version(uint32_t version) - { - return {static_cast(version >> 16), static_cast(version & 0xffff)}; - } - /// Packs a version array into a packed 32-bit integer version - constexpr uint32_t pack_version(version_t version) - { - return (uint32_t(version.first) << 16) | version.second; - } + // When making *any* change here, bump minor + // If the change is incompatible, then bump major and set minor to 0 + // This ensures rpc::VERSION always increases, that every change + // has its own version, and that clients can just test major to see + // whether they can talk to a given daemon without having to know in + // advance which version they will stop working with + constexpr version_t VERSION = {4, 1}; const static std::string STATUS_OK = "OK", @@ -86,2634 +102,2637 @@ namespace rpc { STATUS_NOT_MINING = "NOT MINING", STATUS_TX_LONG_POLL_TIMED_OUT = "Long polling client timed out before txpool had an update"; + /// RPC: blockchain/get_height + /// + /// Get the node's current height. + /// + /// Inputs: none. + /// + /// Outputs: + /// + /// - `height` -- The current blockchain height according to the queried daemon. + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `untrusted` -- If the result is obtained using bootstrap mode then this will be set to true, otherwise will be omitted. + /// - `hash` -- Hash of the block at the current height + /// - `immutable_height` -- The latest height in the blockchain that cannot be reorganized because of a hardcoded checkpoint or 2 MN checkpoints. Omitted if not available. + /// - `immutable_hash` -- Hash of the highest block in the chain that cannot be reorganized. + /// + /// Example-JSON-Fetch + struct GET_HEIGHT : PUBLIC, LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("get_height", "getheight"); } + }; - namespace { - /// Returns a constexpr std::array of string_views from an arbitrary list of string literals - /// Used to specify RPC names as: - /// static constexpr auto names() { return NAMES("primary_name", "some_alias"); } - template - constexpr std::array NAMES(const char (&...names)[N]) { - static_assert(sizeof...(N) > 0, "RPC command must have at least one name"); - return {std::string_view{names, N-1}...}; - } - } - - /// Base command that all RPC commands must inherit from (either directly or via one or more of - /// the below tags). Inheriting from this (and no others) gives you a private, json, non-legacy - /// RPC command. For LMQ RPC the command will be available at `admin.whatever`; for HTTP RPC - /// it'll be at `whatever`. - struct RPC_COMMAND {}; + /// RPC: blockchain/get_transactions + /// + /// Look up one or more transactions by hash. + /// + /// Inputs: + /// + /// - `tx_hashes` -- List of transaction hashes to look up. (Will also be accepted as json input + /// key `"txs_hashes"` for backwards compatibility). Exclusive of `memory_pool`. + /// - `memory_pool` -- If true then return all transactions and spent key images currently in the + /// memory pool. This field is exclusive of `tx_hashes`. + /// - `tx_extra` -- If set to true then parse and return tx-extra information + /// - `tx_extra_raw` -- If set to true then include the raw tx-extra information in the + /// `tx_extra_raw` field. This will be hex-encoded for json, raw bytes for bt-encoded requests. + /// - `data` -- Controls whether the `data` (or `pruned`, if pruned) field containing raw tx data + /// is included. By default it is not included; you typically want `details` rather than this + /// field. + /// - `split` -- If set to true then always split transactions into non-prunable and prunable + /// parts in the response. + /// - `prune` -- Like `split`, but also omits the prunable part of transactions from the response + /// details. + /// + /// Outputs: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `untrusted` -- If the result is obtained using bootstrap mode then this will be set to + /// - `missed_tx` -- set of transaction hashes that were not found. If all were found then this + /// field is omitted. There is no particular ordering of hashes in this list. + /// - `txs` -- list of transaction details; each element is a dict containing: + /// - `tx_hash` -- Transaction hash. + /// - `size` -- Size of the transaction, in bytes. Note that if the transaction has been pruned + /// this is the post-pruning size, not the original size. + /// - `in_pool` -- Will be set to true if the transaction is in the transaction pool (`true`) + /// and omitted if mined into a block. + /// - `flash` -- True if this is an approved, flash transaction; this information is generally + /// only available for approved in-pool transactions and txes in very recent blocks. + /// - `fee` -- the transaction fee (in atomic BELDEX) incurred in this transaction (not including + /// any burned amount). + /// - `burned` -- the amount of BELDEX (in atomic units) burned by this transaction. + /// - `block_height` -- Block height including the transaction. Omitted for tx pool + /// transactions. + /// - `block_timestamp` -- Unix time at which the block has been added to the blockchain. + /// Omitted for tx pool transactions. + /// - `output_indices` -- List of transaction indexes. Omitted for tx pool transactions. + /// - `relayed` -- For `in_pool` transactions this field will be set to indicate whether the + /// transaction has been relayed to the network. + /// - `double_spend_seen` -- Will be set to true for tx pool transactions that are + /// double-spends (and thus cannot be added to the blockchain). Omitted for mined + /// transactions. + /// - `received_timestamp` -- Timestamp transaction was received in the pool. Omitted for + /// mined blocks. + /// - `max_used_block` -- the hash of the highest block referenced by this transaction; only + /// for mempool transactions. + /// - `max_used_height` -- the height of the highest block referenced by this transaction; only + /// for mempool transactions. + /// - `last_failed_block` -- the hash of the last block where this transaction was attempted to + /// be mined (but failed). + /// - `max_used_height` -- the height of the last block where this transaction failed to be + /// acceptable for a block. + /// - `weight` -- the transaction "weight" which is the size of the transaction with padding + /// removed. Only included for mempool transactions (for mined transactions the size and + /// weight at the same and so only `size` is included). + /// - `kept_by_block` will be present and true if this is a mempool transaction that was added + /// to the mempool after being popped off a block (e.g. because of a blockchain + /// reorganization). + /// - `last_relayed_time` indicates the last time this block was relayed to the network; only + /// for mempool transactions. + /// - `do_not_relay` -- set to true for mempool blocks that are marked "do not relay" + /// - `double_spend_seen` -- set to true if one or more outputs in this mempool transaction + /// have already been spent (and thus the tx cannot currently be added to the blockchain). + /// - `data` -- Full, unpruned transaction data. For a json request this is hex-encoded; for a + /// bt-encoded request this is raw bytes. This field is omitted if any of `tx_extra`, + /// `split`, or `prune` is requested; or if the transaction has been pruned in the database. + /// - `pruned` -- The non-prunable part of the transaction, encoded as hex (for json requests). + /// Always included if `split` or `prune` are specified; without those options it will be + /// included instead of `data` if the transaction has been pruned. + /// - `prunable` -- The prunable part of the transaction. Only included when `split` is + /// specified, the transaction is prunable, and the tx has not been pruned from the database. + /// - `prunable_hash` -- The hash of the prunable part of the transaction. Will be provided if + /// either: the tx has been pruned; or the tx is prunable and either of `prune` or `split` are + /// specified. + /// - `extra` -- Parsed "extra" transaction information; omitted unless specifically requested + /// (via the `tx_extra` request parameter). This is a dict containing one or more of the + /// following keys. + /// - `pubkey` -- The tx extra public key + /// - `burn_amount` -- The amount of BELDEX that this transaction burns, if any. + /// - `extra_nonce` -- Optional extra nonce value (in hex); will be empty if nonce is + /// recognized as a payment id + /// - `payment_id` -- The payment ID, if present. This is either a 16 hex character (8-byte) + /// encrypted payment id, or a 64 hex character (32-byte) deprecated, unencrypted payment ID + /// - `mm_depth` -- (Merge-mining) the merge-mined depth + /// - `mm_root` -- (Merge-mining) the merge mining merkle root hash + /// - `additional_pubkeys` -- Additional public keys + /// - `mn_winner` -- Master node block reward winner public key + /// - `mn_pubkey` -- Master node public key (e.g. for registrations, stakes, unlocks) + /// - `mn_contributor` -- Master node contributor wallet address (for stakes) + /// - `tx_secret_key` -- The transaction secret key, included in registrations/stakes to + /// decrypt transaction amounts and recipients + /// - `locked_key_images` -- Key image(s) locked by the transaction (for registrations, + /// stakes) + /// - `key_image_unlock` -- A key image being unlocked in a stake unlock request (an unlock + /// will be started for *all* key images locked in the same MN contributions). + /// - `mn_registration` -- Master node registration details; this is a dict containing: + /// - `fee` the operator fee expressed in millionths (i.e. 234567 == 23.4567%) + /// - `expiry` the unix timestamp at which the registration signature expires + /// - `contributors`: dict of (wallet => portion) pairs indicating the staking portions + /// reserved for the operator and any reserved contribution spots in the registration. + /// Portion is expressed in millionths (i.e. 250000 = 25% staking portion). + /// - `mn_state_change` -- Information for a "state change" transaction such as a + /// deregistration, decommission, recommission, or ip change reset transaction. This is a + /// dict containing: + /// - `old_dereg` will be set to true if this is an "old" deregistration transaction + /// (before the BDX 4 hardfork), omitted for more modern state change txes. + /// - `type` string indicating the state change type: "dereg", "decomm", "recomm", or "ip" + /// for a deregistration, decommission, recommission, or ip change penalty transaction. + /// - `height` the voting block height for the changing master node and voting master + /// nodes that produced this state change transaction. + /// - `index` the position of the affected node in the random list of tested nodes for this + /// `height`. + /// - `voters` the positions of validators in the testing quorum for this `height` who + /// tested and voted for this state change. This typically contains the first 7 voters + /// who voted for the state change (out of a possible set of 10). + /// - `reasons` list of reported reasons for a decommission or deregistration as reported + /// by the voting quorum. This contains any reasons that all 7+ voters agreed on, and + /// contains one or more of: + /// - `"uptime"` -- the master node was missing uptime proofs + /// - `"checkpoints"` -- the master node missed too many recent checkpoint votes + /// - `"pos"` -- the master node missed too many recent pos votes + /// - `"storage"` -- the master node's storage server was unreachable for too long + /// - `"belnet"` -- the master node's belnet router was unreachable for too long + /// - `"timecheck"` -- the master node's beldexd was not reachable for too many recent + /// time synchronization checks. (This generally means beldexd's quorumnet port is not + /// reachable). + /// - `"timesync"` -- the master node's clock was too far out of sync + /// The list is omitted entirely if there are no reasons at all or if there are no reasons + /// that were agreed upon by all voting master nodes. + /// - `reasons_maybe` list of reported reasons that some but not all master nodes provided + /// for the deregistration/decommission. Possible values are identical to the above. + /// This list is omitted entirely if it would be empty (i.e. there are no reasons at all, + /// or all voting master nodes agreed on all given reasons). + /// - `bns` -- BNS registration or update transaction details. This contains keys: + /// - `buy` -- set to true if this is an BNS buy record; omitted otherwise. + /// - `update` -- set to true if this is an BNS record update; omitted otherwise. + /// - `renew` -- set to true if this is an BNS renewal; omitted otherwise. + /// - `bchat_value` -- the BNS request type string. For registrations: "bchat", + /// for a record update: "update". + /// - `wallet_value` -- the BNS request type string. For registrations: "wallet", + /// for a record update: "update". + /// - `belnet_value` -- the BNS request type string. For registrations: "belnet", + /// for a record update: "update". + /// - `blocks` -- The registration length in blocks; omitted for registrations (such as + /// Session/Wallets) that do not expire. + /// - `name_hash` -- The hashed name of the record being purchased/updated. Encoded in hex + /// for json requests. Note that the actual name is not provided on the blockchain. + /// - `prev_txid` -- For an update this field is set to the txid of the previous BNS update + /// or registration (i.e. the most recent transaction that this record is updating). + /// - `value` -- The encrypted value of the record (in hex for json requests) being + /// set/updated. See [`bns_resolve`](#bns_resolve) for details on encryption/decryption. + /// - `owner` -- the owner of this record being set in a registration or update; this can + /// be a primary wallet address, wallet subaddress, or a plain public key. + /// - `backup_owner` -- an optional backup owner who also has permission to edit the + /// record. + /// - `stake_amount` -- Set to the calculated transaction stake amount (only applicable if the + /// transaction is a master node registration or stake). + /// - `mempool_key_images` -- dict of spent key images of mempool transactions. Only included + /// when `memory_pool` is set to true. Each key is the key image (in hex, for json requests) + /// and each value is a list of transaction hashes that spend that key image (typically just + /// one, but in the case of conflicting transactions there can be multiple). + struct GET_TRANSACTIONS : PUBLIC, LEGACY + { + static constexpr auto names() { return NAMES("get_transactions", "gettransactions"); } + struct request_parameters + { + std::vector tx_hashes; + bool memory_pool = false; + bool tx_extra = false; + bool tx_extra_raw = false; + bool data = false; + bool split = false; + bool prune = false; + } request; + }; + + /// RPC: daemon/get_transaction_pool + /// + /// DEPRECATED. This endpoint is for backwards compatibility for old clients obtaining + /// transactions in the transaction pool. The replacement is to use `get_transactions` with + /// `"memory_pool": true`. + /// + /// Inputs: + /// + /// - Takes all the same inputs as get_transactions, except for `memory_pool` and `tx_hashes`. + /// + /// Outputs: + /// + /// - Same as get_transactions with `"memory_pool": true`. + struct GET_TRANSACTION_POOL : GET_TRANSACTIONS + { + static constexpr auto names() { return NAMES("get_transaction_pool"); } + }; - /// Tag types that are used (via inheritance) to set rpc endpoint properties + /// RPC: blockchain/is_key_image_spent + /// + /// Queries whether outputs have been spent using the key image associated with the output. + /// + /// Inputs: + /// + /// - `key_images` -- list of key images to check. For json requests these must be hex or + /// base64-encoded; for bt-requests they can be hex/base64 or raw bytes. + /// + /// Outputs + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore + /// - `spent_status` -- array of status codes returned in the same order as the `key_images` input. + /// Each value is one of: + /// - `0` -- the key image is unspent + /// - `1` -- the key image is spent in a mined block + /// - `2` -- the key image is spent in a transaction currently in the mempool + struct IS_KEY_IMAGE_SPENT : PUBLIC, LEGACY + { + static constexpr auto names() { return NAMES("is_key_image_spent"); } - /// Specifies that the RPC call is public (i.e. available through restricted rpc). If this is - /// *not* inherited from then the command is restricted (i.e. only available to admins). For LMQ, - /// PUBLIC commands are available at `rpc.command` (versus non-PUBLIC ones at `admin.command`). - struct PUBLIC : RPC_COMMAND {}; + enum class SPENT : uint8_t { + UNSPENT = 0, + BLOCKCHAIN = 1, + POOL = 2, + }; + + struct request_parameters { + std::vector key_images; + } request; + }; + + + /// RPC: blockchain/get_outs + /// + /// Retrieve outputs + /// + /// Inputs: + /// + /// - `outputs` -- Array of output indices. For backwards compatibility these may also be passed as + /// an array of {"amount":0,"index":n} dicts. + /// - `get_txid` -- Request the TXID (i.e. hash) of the transaction as well. + /// - `as_tuple` -- Requests the returned outs variable as a tuple of values rather than a dict. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore + /// - `outs` -- List of outkey information; if `as_tuple` is not set then these are dicts containing + /// keys: + /// - `key` -- The public key of the output. + /// - `mask` -- + /// - `unlocked` -- States if output is locked (`false`) or not (`true`). + /// - `height` -- Block height of the output. + /// - `txid` -- Transaction id; only present if requested via the `get_txid` parameter. + /// Otherwise, when `as_tuple` is set, these are 4- or 5-element arrays (depending on whether + /// `get_txid` is desired) containing the values in the order listed above. + struct GET_OUTPUTS : PUBLIC, LEGACY + { + static constexpr auto names() { return NAMES("get_outs"); } - /// Specifies that the RPC call is binary input/ouput. If not given then the command is JSON. - /// For HTTP RPC this also means the command is *not* available via the HTTP JSON RPC. - struct BINARY : RPC_COMMAND {}; + /// Maximum outputs that may be requested in a single request (unless admin) + static constexpr size_t MAX_COUNT = 5000; - /// Specifies a "legacy" JSON RPC command, available via HTTP JSON at /whatever (in addition to - /// json_rpc as "whatever"). When accessed via legacy mode the result is just the .result element - /// of the JSON RPC response. (Only applies to the HTTP RPC interface, and does nothing if BINARY - /// if specified). - struct LEGACY : RPC_COMMAND {}; + struct request_parameters + { + bool get_txid = false; // If true the request will return the TXID/hash of the transaction as well. + bool as_tuple = false; + std::vector output_indices; + } request; + }; + + /// RPC: blockchain/submit_transaction + /// + /// Submit a transaction to be broadcast to the network. + /// + /// Inputs: + /// + /// - `tx` -- the full transaction data itself. Can be hex- or base64-encoded for json requests; + /// can also be those or raw bytes for bt-encoded requests. For backwards compatibility, + /// hex-encoded data can also be passed in a json request via the parameter `tx_as_hex` but + /// that is deprecated and will eventually be removed. + /// - `flash` -- Should be set to true if this transaction is a flash transaction that should be + /// submitted to a flash quorum rather than distributed through the mempool. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore + /// - `reason` -- String containing additional information on why a transaction failed. + /// - `flash_status` -- Set to the result of submitting this transaction to the flash quorum. 1 + /// means the quorum rejected the transaction; 2 means the quorum accepted it; 3 means there was + /// a timeout connecting to or waiting for a response from the flash quorum. Note that a + /// timeout response does *not* necessarily mean the transaction has not made it to the network. + /// - `not_relayed` -- will be set to true if some problem with the transactions prevents it from + /// being relayed to the network, omitted otherwise. + /// - `reason_codes` -- If the transaction was rejected this will be set to a set of reason string + /// codes indicating why the transaction failed: + /// - `"failed"` -- general "bad transaction" code + /// - `"altchain"` -- the transaction is spending outputs that exist on an altchain. + /// - `"mixin"` -- the transaction has the wrong number of decoys + /// - `"double_spend"` -- the transaction is spending outputs that are already spent + /// - `"invalid_input"` -- one or more inputs in the transaction are invalid + /// - `"invalid_output"` -- out or more outputs in the transaction are invalid + /// - `"too_few_outputs"` -- the transaction does not create enough outputs (at least two are + /// required, currently). + /// - `"too_big"` -- the transaction is too large + /// - `"overspend"` -- the transaction spends (via outputs + fees) more than the inputs + /// - `"fee_too_low"` -- the transaction fee is insufficient + /// - `"invalid_version"` -- the transaction version is invalid (the wallet likely needs an + /// update). + /// - `"invalid_type"` -- the transaction type is invalid + /// - `"mnode_locked"` -- one or more outputs are currently staked to a registred master node + /// and thus are not currently spendable on the blockchain. + /// - `"blacklisted"` -- the outputs are currently blacklisted (from being in the 30-day penalty + /// period following a master node deregistration). + /// - `"flash"` -- the flash transaction failed (see `flash_status`) + struct SUBMIT_TRANSACTION : PUBLIC, LEGACY + { + static constexpr auto names() { return NAMES("send_raw_transaction", "sendrawtransaction", "submit_transaction"); } + + struct request_parameters + { + std::string tx; + bool flash = false; + } request; + + }; + + /// RPC: daemon/start_mining + /// + /// Start mining on the daemon + /// + /// Inputs: + /// + /// - `miner_address` -- Account address to mine to. + /// - `threads_count` -- Number of mining threads to run. Defaults to 1 thread if omitted or 0. + /// - `num_blocks` -- Mine until the blockchain has this many new blocks, then stop (no limit if 0, the default). + /// - `slow_mining` -- Do slow mining (i.e. don't allocate RandomX cache); primarily intended for testing. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct START_MINING : LEGACY + { + static constexpr auto names() { return NAMES("start_mining"); } + + struct request_parameters { + std::string miner_address; + int threads_count = 1; + int num_blocks = 0; + bool slow_mining = false; + } request; + }; + + /// RPC: daemon/stop_mining + /// + /// Stop mining on the daemon. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct STOP_MINING : LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("stop_mining"); } + }; + /// RPC: daemon/mining_status + /// + /// Get the mining status of the daemon. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `active` -- States if mining is enabled (`true`) or disabled (`false`). + /// - `speed` -- Mining power in hashes per seconds. + /// - `threads_count` -- Number of running mining threads. + /// - `address` -- Account address daemon is mining to. Empty if not mining. + /// - `pow_algorithm` -- Current hashing algorithm name + /// - `block_target` -- The expected time to solve per block, i.e. TARGET_BLOCK_TIME + /// - `block_reward` -- Block reward for the current block being mined. + /// - `difficulty` -- The difficulty for the current block being mined. + struct MINING_STATUS : LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("mining_status"); } + }; - /// (Not a tag). Generic, serializable, no-argument request type, use as `struct request : EMPTY {};` - struct EMPTY { KV_MAP_SERIALIZABLE }; + /// RPC: network/get_info + /// + /// Retrieve general information about the state of the node and the network. + /// + /// Inputs: none. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `height` -- Current length of longest chain known to daemon. + /// - `target_height` -- The height of the next block in the chain. + /// - `immutable_height` -- The latest height in the blockchain that can not be reorganized (i.e. + /// is backed by at least 2 Master Node, or 1 hardcoded checkpoint, 0 if N/A). Omitted if it + /// cannot be determined (typically because the node is still syncing). + /// - `POS` -- will be true if the next expected block is a POS block, false otherwise. + /// - `POS_ideal_timestamp` -- For POS blocks this is the ideal timestamp of the next block, + /// that is, the timestamp if the network was operating with perfect 2-minute blocks since the + /// POS hard fork. + /// - `POS_target_timestamp` -- For POS blocks this is the target timestamp of the next block, + /// which targets 2 minutes after the previous block but will be slightly faster/slower if the + /// previous block is behind/ahead of the ideal timestamp. + /// - `difficulty` -- Network mining difficulty; omitted when the network is expecting a POS + /// block. + /// - `target` -- Current target for next proof of work. + /// - `tx_count` -- Total number of non-coinbase transaction in the chain. + /// - `tx_pool_size` -- Number of transactions that have been broadcast but not included in a block. + /// - `mainnet` -- Indicates whether the node is on the main network (`true`) or not (`false`). + /// - `testnet` -- Indicates that the node is on the test network (`true`). Will be omitted for + /// non-testnet. + /// - `devnet` -- Indicates that the node is on the dev network (`true`). Will be omitted for + /// non-devnet. + /// - `fakechain` -- States that the node is running in "fakechain" mode (`true`). Omitted + /// otherwise. + /// - `nettype` -- String value of the network type (mainnet, testnet, devnet, or fakechain). + /// - `top_block_hash` -- Hash of the highest block in the chain. Will be hex for JSON requests, + /// 32-byte binary value for bt requests. + /// - `immutable_block_hash` -- Hash of the highest block in the chain that can not be reorganized. + /// Hex string for json, bytes for bt. + /// - `cumulative_difficulty` -- Cumulative difficulty of all blocks in the blockchain. + /// - `block_size_limit` -- Maximum allowed block size. + /// - `block_size_median` -- Median block size of latest 100 blocks. + /// - `bns_counts` -- BNS registration counts. + /// - `offline` -- Indicates that the node is offline, if true. Omitted for online nodes. + /// - `untrusted` -- Indicates that the result was obtained using a bootstrap mode, and is therefore + /// - `database_size` -- Current size of Blockchain data. Over public RPC this is rounded up to the + /// next-largest GB value. + /// - `version` -- Current version of this daemon, as a string. For a public node this will just be + /// the major and minor version (e.g. "9"); for an admin rpc endpoint this will return the full + /// version (e.g. "9.2.1"). + /// - `status_line` -- A short one-line summary string of the node (requires an admin/unrestricted + /// connection for most details) + /// + /// If the endpoint is a restricted (i.e. admin) endpoint then the following fields are also + /// included: + /// + /// - `alt_blocks_count` -- Number of alternative blocks to main chain. + /// - `outgoing_connections_count` -- Number of peers that you are connected to and getting + /// information from. + /// - `incoming_connections_count` -- Number of peers connected to and pulling from your node. + /// - `white_peerlist_size` -- White Peerlist Size + /// - `grey_peerlist_size` -- Grey Peerlist Size + /// - `master_node` -- Will be true if the node is running in --master-node mode. + /// - `start_time` -- Start time of the daemon, as UNIX time. + /// - `last_storage_server_ping` -- Last ping time of the storage server (0 if never or not running + /// as a master node) + /// - `last_belnet_ping` -- Last ping time of belnet (0 if never or not running as a master node) + /// - `free_space` -- Available disk space on the node. + /// - `bootstrap_daemon_address` -- Bootstrap node to give immediate usability to wallets while + /// syncing by proxying RPC to it. (Note: the replies may be untrustworthy). + /// - `height_without_bootstrap` -- Current length of the local chain of the daemon. Only included + /// if a bootstrap daemon is configured. + /// - `was_bootstrap_ever_used` -- States if the bootstrap node has ever been used since the daemon + /// + /// Example-JSON-Fetch + struct GET_INFO : PUBLIC, LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("get_info", "getinfo"); } + }; - /// (Not a tag). Generic response which contains only a status string; use as `struct response : STATUS {};` - struct STATUS + /// RPC: daemon/get_net_stats + /// + /// Retrieve general information about the network statistics of the daemon. + /// + /// Inputs: none. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `start_time` -- something. + /// - `total_packets_in` -- something. + /// - `total_bytes_in` -- something. + /// - `total_packets_out` -- something. + /// - `total_bytes_out` -- something. + struct GET_NET_STATS : LEGACY, NO_ARGS { - std::string status; // General RPC error code. "OK" means everything looks good. + static constexpr auto names() { return NAMES("get_net_stats"); } + }; - KV_MAP_SERIALIZABLE + + /// RPC: daemon/save_bc + /// + /// Save the blockchain. The blockchain does not need saving and is always saved when modified, + /// however it does a sync to flush the filesystem cache onto the disk for safety purposes, + /// against Operating System or Hardware crashes. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct SAVE_BC : LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("save_bc"); } }; - BELDEX_RPC_DOC_INTROSPECT - // Get the node's current height. - struct GET_HEIGHT : PUBLIC, LEGACY + /// RPC: blockchain/get_block_count + /// + /// Look up how many blocks are in the longest chain known to the node. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `count` -- Number of blocks in logest chain seen by the node. + /// + /// Example-JSON-Fetch + struct GET_BLOCK_COUNT : PUBLIC, NO_ARGS { - static constexpr auto names() { return NAMES("get_height", "getheight"); } + static constexpr auto names() { return NAMES("get_block_count", "getblockcount"); } + }; - struct request : EMPTY {}; - struct response - { - uint64_t height; // The current blockchain height according to the queried daemon. - std::string status; // Generic RPC error code. "OK" is the success value. - bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. - std::string hash; // Hash of the block at the current height - uint64_t immutable_height; // The latest height in the blockchain that can not be reorganized from (backed by atleast 2 Service Node, or 1 hardcoded checkpoint, 0 if N/A). - std::string immutable_hash; // Hash of the highest block in the chain that can not be reorganized. - - KV_MAP_SERIALIZABLE - }; + /// RPC: blockchain/get_block_hash + /// + /// Look up one or more blocks' hashes by their height. + /// + /// Inputs: + /// - heights array of block heights of which to look up the block hashes. Accepts at most 1000 + /// heights per request. + /// + /// Output: + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `height` -- the current blockchain height of this node + /// - `""` -- the block hash of the block with the given height. Note that each height key is + /// the stringified integer value, e.g. "3456" rather than 3456. + /// + /// Example input: + /// + /// ```json + /// { "heights": [42, 123456] } + /// ``` + /// + /// Example output: + /// + /// ```json + /// { + /// "status": "OK", + /// "height": 123456, + /// "42": "b269367374fa87ec517405bf120f831e9b13b12c0ee6721dcca69d2c0fe73a0f", + /// "123456": "aa1f3b566aba42e522f8097403a3c513069206286ff08c2ff2871757dbc3e436" + /// } + /// ``` + struct GET_BLOCK_HASH : PUBLIC + { + static constexpr auto names() { return NAMES("get_block_hash", "on_get_block_hash", "on_getblockhash"); } + static constexpr size_t MAX_HEIGHTS = 1000; + struct request_parameters { + std::vector heights; + } request; // Block hash (string). }; - BELDEX_RPC_DOC_INTROSPECT - // Get all blocks info. Binary request. - struct GET_BLOCKS_FAST : PUBLIC, BINARY + // FIXME: This struct should go; it's just a bit of indirection (in _commands_defs.cpp) that isn't + // solve anything (because we can just set the fields directly in the output json values rather + // than use `fill_block_header_response`). + struct block_header_response { - static constexpr auto names() { return NAMES("get_blocks.bin", "getblocks.bin"); } + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + std::string prev_hash; + uint32_t nonce; + bool orphan_status; + uint64_t height; + uint64_t depth; + std::string hash; + difficulty_type difficulty; + difficulty_type cumulative_difficulty; + uint64_t reward; + uint64_t coinbase_payouts; + uint64_t block_size; + uint64_t block_weight; + uint64_t num_txes; + std::optional pow_hash; + uint64_t long_term_weight; + std::string miner_tx_hash; + std::vector tx_hashes; + std::string master_node_winner; + }; + + void to_json(nlohmann::json& j, const block_header_response& h); + void from_json(const nlohmann::json& j, block_header_response& h); + + /// RPC: blockchain/get_last_block_header + /// + /// Block header information for the most recent block is easily retrieved with this method. No + /// inputs are needed. + /// + /// Inputs: + /// - `fill_pow_hash` -- Tell the daemon if it should fill out pow_hash field. + /// - `get_tx_hashes` -- If true (default false) then include the hashes of non-coinbase transactions + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `block_header` -- A structure containing block header information. + /// - `major_version` -- The major version of the beldex protocol at this block height. + /// - `minor_version` -- The minor version of the beldex protocol at this block height. + /// - `timestamp` -- The unix time at which the block was recorded into the blockchain. + /// - `prev_hash` -- The hash of the block immediately preceding this block in the chain. + /// - `nonce` -- A cryptographic random one-time number used in mining a Beldex block. + /// - `orphan_status` -- Usually `false`. If `true`, this block is not part of the longest + /// chain. + /// - `height` -- The number of blocks preceding this block on the blockchain. + /// - `depth` -- The number of blocks succeeding this block on the blockchain. A larger number + /// means an older block. + /// - `hash` -- The hash of this block. + /// - `difficulty` -- The strength of the Beldex network based on mining power. + /// - `cumulative_difficulty` -- The cumulative strength of the Beldex network based on mining + /// power. + /// - `reward` -- The amount of new BELDEX (in atomic units) generated in this block and allocated + /// to master nodes and governance. As of BELDEX 10 (HF 19) this is the *earned* amount, but + /// not the *paid* amount which occurs in batches. + /// - `coinbase_payouts` -- The amount of BELDEX paid out in this block. As of Beldex 10 (HF 19), + /// this reflects the current batched amounts being paid from earnings batched over previous + /// blocks, not the amounts *earned* in the current block. + /// - `block_size` -- The block size in bytes. + /// - `block_weight` -- The block weight in bytes. + /// - `num_txes` -- Number of transactions in the block, not counting the coinbase tx. + /// - `pow_hash` -- The hash of the block's proof of work (requires `fill_pow_hash`) + /// - `long_term_weight` -- Long term weight of the block. + /// - `miner_tx_hash` -- The TX hash of the miner transaction + /// - `tx_hashes` -- The TX hashes of all non-coinbase transactions (requires `get_tx_hashes`) + /// - `master_node_winner` -- Master node that received a reward for this block + /// + /// Example input: + /// ```json + /// {} + /// ``` + /// + /// Example-JSON-Fetch + struct GET_LAST_BLOCK_HEADER : PUBLIC + { + static constexpr auto names() { return NAMES("get_last_block_header", "getlastblockheader"); } - static constexpr size_t MAX_COUNT = 1000; + struct request_parameters + { + bool fill_pow_hash; + bool get_tx_hashes; + } request; + }; + + /// RPC: blockchain/get_block_header_by_hash + /// + /// Block header information can be retrieved using either a block's hash or height. This method + /// includes a block's hash as an input parameter to retrieve basic information about the block. + /// + /// Inputs: + /// - `hash` -- The block's SHA256 hash. + /// - `hashes` -- Request multiple blocks via an array of hashes + /// - `fill_pow_hash` -- Tell the daemon if it should fill out pow_hash field. + /// - `get_tx_hashes` -- If true (default false) then include the hashes of non-coinbase transactions + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `block_header` -- Block header information for the requested `hash` block + /// - `block_headers` -- Block header information for the requested `hashes` blocks + struct GET_BLOCK_HEADER_BY_HASH : PUBLIC + { + static constexpr auto names() { return NAMES("get_block_header_by_hash", "getblockheaderbyhash"); } - struct request - { - std::list block_ids; // First 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block - uint64_t start_height; // The starting block's height. - bool prune; // Prunes the blockchain, drops off 7/8 off the block iirc. - bool no_miner_tx; // Optional (false by default). + struct request_parameters + { + std::string hash; + std::vector hashes; + bool fill_pow_hash; + bool get_tx_hashes; + } request; + }; + + /// RPC: blockchain/get_block_header_by_height + /// + /// Similar to get_block_header_by_hash above, this method includes a block's height as an input + /// parameter to retrieve basic information about the block. + /// + /// Inputs: + /// + /// - `height` -- A block height to look up; returned in `block_header` + /// - `heights` -- Block heights to retrieve; returned in `block_headers` + /// - `fill_pow_hash` -- Tell the daemon if it should fill out pow_hash field. + /// - `get_tx_hashes` -- If true (default false) then include the hashes of non-coinbase transactions + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `block_header` -- Block header information for the requested `height` block + /// - `block_headers` -- Block header information for the requested `heights` blocks + struct GET_BLOCK_HEADER_BY_HEIGHT : PUBLIC + { + static constexpr auto names() { return NAMES("get_block_header_by_height", "getblockheaderbyheight"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters + { + std::optional height; + std::vector heights; + bool fill_pow_hash; + bool get_tx_hashes; + } request; + }; + + /// RPC: blockchain/get_block + /// + /// Full block information can be retrieved by either block height or hash, like with the above + /// block header calls. For full block information, both lookups use the same method, but with + /// different input parameters. + /// + /// Inputs: + /// + /// - `hash` -- The block's hash. + /// - `height` -- A block height to look up; returned in `block_header` + /// - `fill_pow_hash` -- Tell the daemon if it should fill out pow_hash field. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `block_header` -- Block header information for the requested `height` block + /// - `tx_hashes` -- List of hashes of non-coinbase transactions in the block. If there are no other transactions, this will be an empty list. + /// - `blob` -- Hexadecimal blob of block information. + /// - `json` -- JSON formatted block details. + /// + /// Example input: + /// + /// ```json + /// { "height": 42 } + /// ``` + /// + /// Example-JSON-Fetch + struct GET_BLOCK : PUBLIC + { + static constexpr auto names() { return NAMES("get_block", "getblock"); } - struct tx_output_indices + struct request_parameters { - std::vector indices; // Array of unsigned int. - - KV_MAP_SERIALIZABLE - }; + std::string hash; + uint64_t height; + bool fill_pow_hash; + } request; + }; + + /// RPC: daemon/get_peer_list + /// + /// Get the list of current network peers known to this node. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `white_list` -- list of "whitelist" peers (see below), that is, peers that were recorded + /// reachable the last time this node connected to them. Peers that are unreachable or not + /// synchronized with the network are moved to the graylist. + /// - `gray_list` -- list of peers (see below) that this node knows of but has not (recently) tried + /// to connect to. + /// + /// Each peer list is an array of dicts containing the following fields: + /// - `id` -- a unique integer locally identifying the peer + /// - `host` -- the peer's IP address (as a string) + /// - `port` -- the port on which the peer is reachable + /// - `last_seen` -- unix timestamp when this node last connected to the peer. Will be omitted if + /// never connected (e.g. for a peer we received from another node but haven't yet tried). + struct GET_PEER_LIST : LEGACY + { + static constexpr auto names() { return NAMES("get_peer_list"); } - struct block_output_indices + struct request_parameters { - std::vector indices; // Array of TX output indices: - - KV_MAP_SERIALIZABLE - }; + bool public_only = false; // Hidden option: can be set to false to also include non-public-zone peers (Tor, I2P), but since Beldex currently only really exists in public zones, we don't put this in the RPC docs. + } request; - struct response - { - std::vector blocks; // Array of block complete entries - uint64_t start_height; // The starting block's height. - uint64_t current_height; // The current block height. - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector output_indices; // Array of indices. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; }; - BELDEX_RPC_DOC_INTROSPECT - // Get blocks by height. Binary request. - struct GET_BLOCKS_BY_HEIGHT : PUBLIC, BINARY + /// RPC: daemon/set_log_level + /// + /// Set the daemon log level. By default, log level is set to `0`. For more fine-tuned logging + /// control set the set_log_categories command instead. + /// + /// Inputs: + /// - `level` -- Daemon log level to set from `0` (less verbose) to `4` (most verbose) + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct SET_LOG_LEVEL : LEGACY { - static constexpr auto names() { return NAMES("get_blocks_by_height.bin", "getblocks_by_height.bin"); } - - struct request - { - std::vector heights; // List of block heights + static constexpr auto names() { return NAMES("set_log_level"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters + { + int8_t level; + } request; + + }; + + /// RPC: daemon/set_log_categories + /// + /// Set the daemon log categories for debugging purposes. + /// + /// Categories are represented as a comma separated list of `:`, where + /// `` is is one of the various internal debugging categories defined in the beldex source + /// code, or `*` to refer to all logging categories. + /// + /// Level is one of the following: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE. + /// + /// You can append to the current the log level for updating just one or more categories while + /// leaving other log levels unchanged by specifying one or more ":" pairs + /// preceded by a "+", for example "+difficulty:DEBUG,net:WARNING". + /// + /// Inputs: + /// - `categories` -- Optional, daemon log categores to enable + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `categories` -- Daemon log enabled categories + struct SET_LOG_CATEGORIES : LEGACY + { + static constexpr auto names() { return NAMES("set_log_categories"); } - struct response + struct request_parameters { - std::vector blocks; // Array of block complete entries - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + std::string categories; + } request; - KV_MAP_SERIALIZABLE - }; }; - BELDEX_RPC_DOC_INTROSPECT - // Get the known blocks hashes which are not on the main chain. - struct GET_ALT_BLOCKS_HASHES : PUBLIC, BINARY + /// RPC: blockchain/get_transaction_pool_hashes + /// + /// Get hashes from transaction pool. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `tx_hashes` -- List of transaction hashes, + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore not + struct GET_TRANSACTION_POOL_HASHES : PUBLIC, LEGACY, NO_ARGS { - static constexpr auto names() { return NAMES("get_alt_blocks_hashes.bin"); } - - struct request : EMPTY {}; - struct response - { - std::vector blks_hashes; // List of alternative blocks hashes to main chain. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; + static constexpr auto names() { return NAMES("get_transaction_pool_hashes"); } }; BELDEX_RPC_DOC_INTROSPECT - // Get hashes. Binary request. - struct GET_HASHES_FAST : PUBLIC, BINARY + struct txpool_histo { - static constexpr auto names() { return NAMES("get_hashes.bin", "gethashes.bin"); } - - struct request - { - std::list block_ids; // First 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ - uint64_t start_height; // The starting block's height. + uint32_t txs; // Number of transactions. + uint64_t bytes; // Size in bytes. - KV_MAP_SERIALIZABLE - }; + KV_MAP_SERIALIZABLE + }; - struct response - { - std::vector m_block_ids; // Binary array of hashes, See block_ids above. - uint64_t start_height; // The starting block's height. - uint64_t current_height; // The current block height. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + /// RPC: blockchain/get_transaction_pool_stats + /// + /// Get the transaction pool statistics. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `pool_stats` -- Dict of pool statistics: + /// - `bytes_total` -- the total size (in bytes) of the transactions in the transaction pool. + /// - `bytes_min` -- the size of the smallest transaction in the tx pool. + /// - `bytes_max` -- the size of the largest transaction in the pool. + /// - `bytes_med` -- the median transaction size in the pool. + /// - `fee_total` -- the total fees of all transactions in the transaction pool. + /// - `txs_total` -- the total number of transactions in the transaction pool + /// - `num_failing` -- the number of failing transactions: that is, transactions that are in the + /// mempool but are not currently eligible to be added to the blockchain. + /// - `num_10m` -- the number of transactions received within the last ten minutes + /// - `num_not_relayed` -- the number of transactions which are not being relayed to the + /// network. Only included when the `include_unrelayed` request parameter is set to true. + /// - `num_double_spends` -- the number of transactions in the mempool that are marked as + /// double-spends of existing blockchain transactions. + /// - `oldest` -- the unix timestamp of the oldest transaction in the pool. + /// - `histo` -- pairs of [# txes, size of bytes] that form a histogram of transactions in the + /// mempool, if there are at least two transactions in the mempool (and omitted entirely + /// otherwise). When present, this field will contain 10 pairs: + /// - When `histo_max` is given then `histo` consists of 10 equally-spaced bins from + /// newest to oldest where the newest bin begins at age 0 and the oldest bin ends at age + /// `histo_max`. For example, bin `[3]` contains statistics for transactions with ages + /// between `3*histo_max/10` and `4*histo_max/10`. + /// - Otherwise `histo_98pc` will be present in which case `histo` contains 9 equally spaced + /// bins from newest to oldest where the newest bin begins at age 0 and the oldest bin ends + /// at age `histo_98pc`, and at least 98% of the mempool transactions will fall in these 9 + /// bins. The 10th bin contains statistics for all transactions with ages greater than + /// `histo_98pc`. + /// - `histo_98pc` -- See `histo` for details. + /// - `histo_max` -- See `histo` for details. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore not + struct GET_TRANSACTION_POOL_STATS : PUBLIC, LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("get_transaction_pool_stats"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters { + /// Whether to include transactions marked "do not relay" in the returned statistics. False + /// by default: since they are not relayed, they do not form part of the global network + /// transaction pool. + bool include_unrelayed = false; + } request; + }; + + /// RPC: daemon/get_connections + /// + /// Retrieve information about incoming and outgoing P2P connections to your node. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `connections` -- List of all connections and their info; each element is a dict containing: + /// - `incoming` -- bool of whether this connection was established by the remote to us (true) or + /// by us to the remove (false). + /// - `ip` -- address of the remote peer + /// - `port` -- the remote port of the peer connection + /// - `address_type` -- - 1/2/3/4 for ipv4/ipv6/i2p/tor, respectively. + /// - `peer_id` -- a string that uniquely identifies a peer node + /// - `recv_count` -- number of bytes of data received from this peer + /// - `recv_idle_ms` -- number of milliseconds since we last received data from this peer + /// - `send_count` -- number of bytes of data send to this peer + /// - `send_idle_ms` -- number of milliseconds since we last sent data to this peer + /// - `state` -- returns the current state of the connection with this peer as a string, one of: + /// - `before_handshake` -- the connection is still being established/negotiated + /// - `synchronizing` -- we are synchronizing the blockchain with this peer + /// - `standby` -- the peer is available for synchronizing but we are not currently using it + /// - `normal` -- this is a regular, synchronized peer + /// - `live_ms` -- number of milliseconds since this connection was initiated + /// - `avg_download` -- the average download speed from this peer in bytes per second + /// - `current_download` -- the current (i.e. average over a very recent period) download speed + /// from this peer in bytes per second. + /// - `avg_upload` -- the average upload speed to this peer in bytes per second + /// - `current_upload` -- the current upload speed to this peer in bytes per second + /// - `connection_id` -- a unique random string identifying this connection + /// - `height` -- the height of the peer + /// - `host` -- the hostname for this peer; only included if not the same as `ip` + /// - `localhost` -- set to true if the peer is a localhost connection; omitted otherwise. + /// - `local_ip` -- set to true if the peer is a non-public, local network connection; omitted + /// otherwise. + /// + /// Example output: + /// ```json + /// { + /// "connections": [ + /// { + /// "address": "1.2.3.4:51890", + /// "address_type": 1, + /// "avg_download": 0, + /// "avg_upload": 2, + /// "connection_id": "abcdef0123456789abcdef0123456789", + /// "current_download": 0, + /// "current_upload": 0, + /// "height": 1088107, + /// "host": "1.2.3.4", + /// "incoming": true, + /// "ip": "1.2.3.4", + /// "live_time": 33, + /// "local_ip": false, + /// "localhost": false, + /// "peer_id": "fedcba9876543210", + /// "port": "51890", + /// "pruning_seed": 0, + /// "recv_count": 20628, + /// "recv_idle_time": 0, + /// "rpc_port": 0, + /// "send_count": 83253, + /// "send_idle_time": 0, + /// "state": "normal", + /// "support_flags": 1 + /// }, + /// { + /// "address": "5.6.7.8:22022", + /// "address_type": 1, + /// "avg_download": 1, + /// "avg_upload": 1, + /// "connection_id": "00112233445566778899aabbccddeeff", + /// "current_download": 0, + /// "current_upload": 0, + /// "height": 1088107, + /// "host": "5.6.7.8", + /// "incoming": false, + /// "ip": "5.6.7.8", + /// "live_time": 66, + /// "local_ip": false, + /// "localhost": false, + /// "peer_id": "ffddbb9977553311", + /// "port": "22022", + /// "pruning_seed": 0, + /// "recv_count": 95687, + /// "recv_idle_time": 0, + /// "rpc_port": 0, + /// "send_count": 85542, + /// "send_idle_time": 0, + /// "state": "normal", + /// "support_flags": 1 + /// } + /// ], + /// "status": "OK" + /// } + /// ``` + struct GET_CONNECTIONS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_connections"); } }; - BELDEX_RPC_DOC_INTROSPECT - // Look up one or more transactions by hash. - struct GET_TRANSACTIONS : PUBLIC, LEGACY + /// RPC: blockchain/get_block_headers_range + /// + /// Similar to get_block_header_by_height above, but for a range of blocks. + /// This method includes a starting block height and an ending block height as + /// parameters to retrieve basic information about the range of blocks. + /// + /// Inputs: + /// + /// - `start_height` -- The starting block's height. + /// - `end_height` -- The ending block's height. + /// - `fill_pow_hash` -- Tell the daemon if it should fill out pow_hash field. + /// - `get_tx_hashes` -- If true (default false) then include the hashes of non-coinbase transactions + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `headers` -- Array of block_header (a structure containing block header information. See get_last_block_header). + /// Example input: + /// ```json + /// { "start_height": 1087845, "end_height": 1087847, "get_tx_hashes": true } + /// ``` + /// + /// Example-JSON-Fetch + struct GET_BLOCK_HEADERS_RANGE : PUBLIC { - static constexpr auto names() { return NAMES("get_transactions", "gettransactions"); } - - // Information from a transactions tx-extra fields. Fields within this will only be populated - // when actually found in the transaction. (Requires tx_extra=true in the request). - struct extra_entry - { - struct mn_reg_info - { - struct contribution - { - std::string wallet; // Contributor wallet - uint32_t portion; // Reserved portion, as the rounded nearest value out of 1'000'000 (i.e. 234567 == 23.4567%). - KV_MAP_SERIALIZABLE - }; - - std::vector contributors; // Operator contribution plus any reserved contributions - uint32_t fee; // Operator fee, as the rounded nearest value out of 1'000'000 - uint64_t expiry; // unix timestamp at which the registration expires - KV_MAP_SERIALIZABLE - }; - struct state_change - { - std::optional old_dereg; // Will be present and set to true iff this record is an old (pre-HF12) deregistration field - std::string type; // "dereg", "decom", "recom", or "ip" indicating the state change type - uint64_t height; // The voting block height for the changing service node and validators - uint32_t index; // The index of all tested nodes at the given height for which this state change applies - std::vector voters; // The position of validators in the testing quorum who validated and voted for this state change. This typically contains just 7 required voter slots (of 10 eligible voters). - std::optional> reasons; // Reasons for the decommissioning/deregistration as reported by the voting quorum. This contains any reasons that all voters agreed on, one or more of: "uptime" (missing uptime proofs), "checkpoints" (missed checkpoint votes), "POS" (missing POS votes), "storage" (storage server pings failed), "belnet" (belnet router unreachable), "timecheck" (time sync pings failed), "timesync" (time was out of sync) - std::optional> reasons_maybe; // If present, this contains any decomm/dereg reasons that were given by some but not all quorum voters - KV_MAP_SERIALIZABLE - }; - struct bns_details - { - uint8_t version; // The version which is given when the registrations version = 1 after the hf18 - std::optional buy; // Provided and true iff this is an BNS buy record - std::optional update; // Provided and true iff this is an BNS record update - std::optional renew; // Provided and true iff this is an BNS record renewal - std::optional type; // The BNS request type. For registrations: "belnet", "bchat", "wallet"; for a record update: "update" - std::optional blocks; // The registration length in blocks (only applies to belnet registrations; bchat/wallet registrations do not expire) - std::string name_hash; // The hashed name of the record being purchased/updated, in hex (the actual name is not provided on the blockchain). - std::optional prev_txid; // For an update, this points at the txid of the previous bns update transaction. - std::optional value_bchat; // The encrypted value of the record, in hex for the bchat. Note that this is encrypted using the actual name itself (*not* the hashed name). - std::optional value_wallet; // The encrypted value of the record, in hex for the wallet. Note that this is encrypted using the actual name itself (*not* the hashed name). - std::optional value_belnet; // The encrypted value of the record, in hex for the belnet. Note that this is encrypted using the actual name itself (*not* the hashed name). - std::optional value_eth_addr;// The encrypted value of the record, in hex for the belnet. Note that this is encrypted using the actual name itself (*not* the hashed name). - std::optional owner; // The owner of this record; this can be a main wallet, wallet subaddress, or a plain public key. - std::optional backup_owner; // Backup owner wallet/pubkey of the record, if provided. - KV_MAP_SERIALIZABLE - }; - - std::optional pubkey; // The tx extra public key - std::optional burn_amount; // The amount of BELDEX that this transaction burns - std::optional extra_nonce; // Optional extra nonce value (in hex); will be empty if nonce is recognized as a payment id - std::optional payment_id; // The payment ID, if present. This is either a 16 hex character (8-byte) encrypted payment id, or a 64 hex character (32-byte) deprecated, unencrypted payment ID - std::optional mm_depth; // (Merge-mining) the merge-mined depth - std::optional mm_root; // (Merge-mining) the merge mining merkle root hash - std::vector additional_pubkeys; // Additional public keys - std::optional mn_winner; // Master node block reward winner public key - std::optional mn_pubkey; // Master node public key (e.g. for registrations, stakes, unlocks) - std::optional security_sig; // Security Signature - std::optional mn_registration; // Master node registration details - std::optional mn_contributor; // Master node contributor wallet address (for stakes) - std::optional mn_state_change; // A state change transaction (deregistration, decommission, recommission, ip change) - std::optional tx_secret_key; // The transaction secret key, included in registrations/stakes to decrypt transaction amounts and recipients - std::vector locked_key_images; // Key image(s) locked by the transaction (for registrations, stakes) - std::optional key_image_unlock; // A key image being unlocked in a stake unlock request (an unlock will be started for *all* key images locked in the same MN contributions). - std::optional bns; // an BNS registration or update - KV_MAP_SERIALIZABLE - }; + static constexpr auto names() { return NAMES("get_block_headers_range", "getblockheadersrange"); } - struct entry - { - std::string tx_hash; // Transaction hash. - std::optional as_hex; // Full transaction information as a hex string. Always omitted if any of `decode_as_json`, `split`, or `prune` is requested; or if the transaction has been pruned in the database. - std::optional pruned_as_hex; // The non-prunable part of the transaction. Always included if `split` or `prune` and specified; without those options it will be included instead of `as_hex` if the transaction has been pruned. - std::optional prunable_as_hex; // The prunable part of the transaction. Only included when `split` is specified, the transaction is prunable, and the tx has not been pruned from the database. - std::optional prunable_hash; // The hash of the prunable part of the transaction. Will be provided if either: the tx has been pruned; or the tx is prunable and either of `prune` or `split` are specified. - std::optional as_json; // Transaction information parsed into json. Requires decode_as_json in request. - uint32_t size; // Size of the transaction, in bytes. Note that if the transaction has been pruned this is the post-pruning size, not the original size. - bool in_pool; // States if the transaction is in pool (`true`) or included in a block (`false`). - bool double_spend_seen; // States if the transaction is a double-spend (`true`) or not (`false`). - uint64_t block_height; // Block height including the transaction. - uint64_t block_timestamp; // Unix time at which the block has been added to the blockchain. - std::vector output_indices; // List of transaction indexes. - uint64_t received_timestamp; // Timestamp transaction was received in the pool. - bool relayed; - bool flash; // True if this is an approved, flash transaction (only available for in_pool transactions or txes in recent blocks) - std::optional extra; // Parsed tx_extra information (only if requested) - std::optional stake_amount; // Calculated transaction stake amount, if a staking/registration transaction and `stake_info=true` is requested. - - KV_MAP_SERIALIZABLE - }; + // Used for this endpoint as well as the by_hash/by_height versions. + static constexpr int64_t MAX_COUNT = 1000; - struct request + struct request_parameters { - std::vector txs_hashes; // List of transaction hashes to look up. - bool decode_as_json; // Optional (`false` by default). If set true, the returned transaction information will be decoded. - bool tx_extra; // Parse tx-extra information - bool split; // Always split transactions into non-prunable and prunable parts in the response. `False` by default. - bool prune; // Like `split`, but also omits the prunable part (or details, for decode_as_json) of transactions from the response. `False` by default. - bool stake_info; // If true, calculate staking amount for staking/registration transactions - - KV_MAP_SERIALIZABLE - }; - + uint64_t start_height; + uint64_t end_height; + bool fill_pow_hash; + bool get_tx_hashes; + } request; + }; - struct response + // Set the bootstrap daemon to use for data on the blockchain whilst syncing the chain. + struct SET_BOOTSTRAP_DAEMON : RPC_COMMAND + { + static constexpr auto names() { return NAMES("set_bootstrap_daemon"); } + struct request_parameters { - std::vector missed_tx; // (Optional - returned if not empty) Transaction hashes that could not be found. - std::vector txs; // Array of tx data - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; + std::string address; + std::string username; + std::string password; + } request; }; - BELDEX_RPC_DOC_INTROSPECT - // Check if outputs have been spent using the key image associated with the output. - struct IS_KEY_IMAGE_SPENT : PUBLIC, LEGACY + /// RPC: daemon/stop_daemon + /// + /// Stop the daemon. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct STOP_DAEMON : LEGACY, NO_ARGS { - static constexpr auto names() { return NAMES("is_key_image_spent"); } + static constexpr auto names() { return NAMES("stop_daemon"); } + }; - enum STATUS - { - UNSPENT = 0, - SPENT_IN_BLOCKCHAIN = 1, - SPENT_IN_POOL = 2, - }; + /// RPC: daemon/get_limit + /// + /// Get daemon p2p bandwidth limits. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `limit_up` -- Upload limit in kiB/s + /// - `limit_down` -- Download limit in kiB/s + struct GET_LIMIT : LEGACY, NO_ARGS + { + static constexpr auto names() { return NAMES("get_limit"); } + }; - struct request - { - std::vector key_images; // List of key image hex strings to check. + /// RPC: daemon/set_limit + /// + /// Set daemon p2p bandwidth limits. + /// + /// Inputs: + /// + /// - `limit_down` -- Download limit in kBytes per second. -1 means reset to default; 0 (or + /// omitted) means don't change the current limit + /// - `limit_up` -- Upload limit in kBytes per second. -1 means reset to default; 0 (or omitted) + /// means don't change the current limit + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `limit_up` -- The new (or existing, if unchanged) upload limit in kiB/s + /// - `limit_down` -- The new (or existing, if unchanged) download limit in kiB/s + struct SET_LIMIT : LEGACY + { + static constexpr auto names() { return NAMES("set_limit"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters { + int64_t limit_down = 0; + int64_t limit_up = 0; + } request; + }; + + /// RPC: daemon/out_peers + /// + /// Limit number of Outgoing peers. + /// + /// Inputs: + /// + /// - `set` -- If true, set the number of outgoing peers, otherwise the response returns the current + /// limit of outgoing peers. (Defaults to true) + /// - `out_peers` -- Max number of outgoing peers + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `out_peers` -- The current limit set for outgoing peers. + struct OUT_PEERS : LEGACY + { + static constexpr auto names() { return NAMES("out_peers"); } + struct request_parameters + { + bool set; + uint32_t out_peers; + } request; + }; + + /// RPC: daemon/in_peers + /// + /// Limit number of Incoming peers. + /// + /// Inputs: + /// + /// - `set` -- If true, set the number of incoming peers, otherwise the response returns the current + /// limit of incoming peers. (Defaults to true) + /// - `in_peers` -- Max number of incoming peers + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `in_peers` -- The current limit set for incoming peers. + struct IN_PEERS : LEGACY + { + static constexpr auto names() { return NAMES("in_peers"); } - struct response - { - std::vector spent_status; // List of statuses for each image checked. Statuses are follows: 0 = unspent, 1 = spent in blockchain, 2 = spent in transaction pool - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + struct request_parameters + { + bool set; + uint32_t in_peers; + } request; + }; + + /// RPC: network/hard_fork_info + /// + /// Retrieves information about the current or a specific hard fork network rules. + /// + /// Inputs: + /// + /// - `version` -- If specified, this is the hard fork (i.e. major block) version for the fork. + /// Only one of `version` and `height` may be given; returns the current hard fork info if + /// neither is given. + /// - `height` -- Request hard fork info by querying a particular height. Only one of `version` + /// and `height` may be given. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore + /// - `version` -- The major block version for the fork. + /// - `enabled` -- Indicates whether the hard fork is enforced on the blockchain (that is, whether + /// the blockchain height is at or above the requested hardfork). + /// - `earliest_height` -- Block height at which the hard fork will become enabled. + /// - `last_height` -- The last block height at which this hard fork will be active; will be omitted + /// if this beldexd is not aware of any following hard fork. + /// + /// Example input: + /// + /// ```json + /// { "version": 19 } + /// ``` + /// + /// Example-JSON-Fetch + struct HARD_FORK_INFO : PUBLIC + { + static constexpr auto names() { return NAMES("hard_fork_info"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters { + uint8_t version = 0; + uint64_t height = 0; + } request; + }; + + /// RPC: daemon/get_bans + /// + /// Get list of banned IPs. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `bans` -- List of banned nodes. Each element is a dict containing: + /// - `host` -- Banned host (IP in A.B.C.D form). + /// - `seconds` -- Unix timestamp when the ban expires + /// + /// Example output: + /// ```json + /// { + /// "bans": [ + /// { + /// "host": "1.2.3.4", + /// "ip": 67305985, + /// "seconds": 5504 + /// }, + /// { + /// "host": "8.8.8.8", + /// "ip": 134744072, + /// "seconds": 679104 + /// } + /// ], + /// "status": "OK" + /// } + /// ``` + struct GET_BANS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_bans"); } }; + struct ban + { + std::string host; + uint32_t seconds; + }; + inline void to_json(nlohmann::json& j, const ban& b) { j = nlohmann::json{{"host", b.host}, {"seconds", b.seconds} }; }; + + /// RPC: daemon/set_bans + /// + /// Ban another node by IP. + /// + /// Inputs: + /// - `host` -- Banned host (IP in A.B.C.D form). + /// - `ip` -- Banned IP address, in Int format. + /// - `seconds` -- Local Unix time that IP is banned until, or Number of seconds to ban node + /// - `ban` -- Set true to ban. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct SET_BANS : RPC_COMMAND + { + static constexpr auto names() { return NAMES("set_bans"); } - BELDEX_RPC_DOC_INTROSPECT - // Get global outputs of transactions. Binary request. - struct GET_TX_GLOBAL_OUTPUTS_INDEXES : PUBLIC, BINARY + struct request_parameters + { + std::string host; + uint32_t seconds; + bool ban; + } request; + }; + + /// RPC: daemon/banned + /// + /// Determine whether a given IP address is banned + /// + /// Inputs: + /// - `address` -- The IP address to check. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `banned` -- True if the given address is banned, false otherwise. + /// - `seconds` -- The number of seconds remaining in the ban. + /// + /// Example input: + /// ```json + /// { "address": "1.2.3.4" } + /// ``` + /// + /// Example output: + /// ```json + /// { + /// "banned": true, + /// "seconds": 5710, + /// "status": "OK" + /// } + /// + /// Example input: + /// ```json + /// { "addess": "4.3.2.1" } + /// ``` + /// + /// Example output: + /// ```json + /// { + /// "banned": false, + /// "seconds": 0, + /// "status": "OK" + /// } + /// ``` + struct BANNED : RPC_COMMAND { - static constexpr auto names() { return NAMES("get_o_indexes.bin"); } + static constexpr auto names() { return NAMES("banned"); } - struct request + struct request_parameters { - crypto::hash txid; // Binary txid. - - KV_MAP_SERIALIZABLE - }; + std::string address; // The IP address to check + } request; + }; + + /// RPC: daemon/flush_txpool + /// + /// Flush tx ids from transaction pool.. + /// + /// Inputs: + /// - `txids` -- Optional, list of transactions IDs to flosh from pool (all tx ids flushed if empty) + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct FLUSH_TRANSACTION_POOL : RPC_COMMAND + { + static constexpr auto names() { return NAMES("flush_txpool"); } + struct request_parameters + { + std::vector txids; + } request; + }; + + /// RPC: blockchain/get_output_histogram + /// + /// Get a histogram of output amounts. For all amounts (possibly filtered by parameters), + /// gives the number of outputs on the chain for that amount. RingCT outputs counts as 0 amount. + /// + /// Inputs: + /// + /// - `amounts` -- list of amounts in Atomic Units. + /// - `min_count` -- The minimum amounts you are requesting. + /// - `max_count` -- The maximum amounts you are requesting. + /// - `unlocked` -- Look for locked only. + /// - `recent_cutoff` + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `histogram` -- List of histogram entries. Each element is structured as follows: + /// - `uint64_t` -- amount Output amount in atomic units. + /// - `uint64_t` -- total_instances + /// - `uint64_t` -- unlocked_instances + /// - `uint64_t` -- recent_instances + struct GET_OUTPUT_HISTOGRAM : PUBLIC + { + static constexpr auto names() { return NAMES("get_output_histogram"); } - struct response + struct request_parameters { - std::vector o_indexes; // List of output indexes - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + std::vector amounts; + uint64_t min_count; + uint64_t max_count; + bool unlocked; + uint64_t recent_cutoff; + } request; - KV_MAP_SERIALIZABLE + struct entry + { + uint64_t amount; + uint64_t total_instances; + uint64_t unlocked_instances; + uint64_t recent_instances; }; }; + void to_json(nlohmann::json& j, const GET_OUTPUT_HISTOGRAM::entry& c); + void from_json(const nlohmann::json& j, GET_OUTPUT_HISTOGRAM::entry& c); - BELDEX_RPC_DOC_INTROSPECT - struct get_outputs_out + /// RPC: daemon/get_version + /// + /// Get current RPC protocol version. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `version` -- RPC current version. + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore not + struct GET_VERSION : PUBLIC, NO_ARGS { - uint64_t amount; // Amount of Beldex in TXID. - uint64_t index; - - KV_MAP_SERIALIZABLE + static constexpr auto names() { return NAMES("get_version"); } }; - BELDEX_RPC_DOC_INTROSPECT - // Get outputs. Binary request. - struct GET_OUTPUTS_BIN : PUBLIC, BINARY + /// RPC: blockchain/get_coinbase_tx_sum + /// + /// Get the coinbase amount and the fees amount for n last blocks starting at particular height. + /// + /// Note that this call can be extremely slow the first time it is called, particularly when + /// requesting the values for the entire chain (by specifying `height=0`), as it has to scan the + /// full blockchain to calculate the result. As such this call is restricted to admin + /// connections. Future versions may lift this restriction. + /// + /// Inputs: + /// + /// - `height` -- Block height from which getting the amounts. + /// - `count` -- Number of blocks to include in the sum. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `emission_amount` -- Amount of coinbase reward in atomic units. + /// - `fee_amount` -- Amount of fees in atomic units. + /// - `burn_amount` -- Amount of burnt beldex. + /// + /// Example input: + /// ```json + /// {"height": 0, "count":1000000000} + /// ``` + /// + /// Example output: + /// ```json + /// { + /// "burn_amount": 720279959424405, + /// "emission_amount": 59537648307402880, + /// "fee_amount": 80671056941541, + /// "status": "OK" + /// } + /// ``` + struct GET_COINBASE_TX_SUM : RPC_COMMAND { - static constexpr auto names() { return NAMES("get_outs.bin"); } - - /// Maximum outputs that may be requested in a single request (unless admin) - static constexpr size_t MAX_COUNT = 5000; + static constexpr auto names() { return NAMES("get_coinbase_tx_sum"); } - struct request + struct request_parameters { - std::vector outputs; // Array of structure `get_outputs_out`. - bool get_txid; // TXID - - KV_MAP_SERIALIZABLE - }; + uint64_t height; + uint64_t count; + } request; + + }; + + /// RPC: network/get_fee_estimate + /// + /// Gives an estimation of per-output + per-byte fees + /// + /// Inputs: + /// + /// - `grace_blocks` -- If specified, make sure that the fee is high enough to cover any fee + /// increases in the next `grace_blocks` blocks. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `emission_amount` -- Amount of coinbase reward in atomic units. + /// - `fee_amount` -- Amount of fees in atomic units. + /// - `burn_amount` -- Amount of burnt beldex. + /// - `fee_per_byte` -- Amount of fees estimated per byte in atomic units + /// - `fee_per_output` -- Amount of fees per output generated by the tx (adds to the `fee_per_byte` per-byte value) + /// - `flash_fee_per_byte` -- Value for sending a flash. The portion of the overall flash fee above the overall base fee is burned. + /// - `flash_fee_per_output` -- Value for sending a flash. The portion of the overall flash fee above the overall base fee is burned. + /// - `flash_fee_fixed` -- Fixed flash fee in addition to the per-output and per-byte amounts. The portion of the overall flash fee above the overall base fee is burned. + /// - `quantization_mask` + /// - `untrusted` -- States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + /// + /// Example input: + /// + /// ```json + /// {} + /// ``` + /// + /// Example-JSON-Fetch + struct GET_FEE_ESTIMATE : PUBLIC + { + static constexpr auto names() { return NAMES("get_fee_estimate"); } - struct outkey + struct request_parameters { - crypto::public_key key; // The public key of the output. - rct::key mask; - bool unlocked; // States if output is locked (`false`) or not (`true`). - uint64_t height; // Block height of the output. - crypto::hash txid; // Transaction id. + uint64_t grace_blocks; + } request; - KV_MAP_SERIALIZABLE - }; + }; - struct response - { - std::vector outs; // List of outkey information. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). + /// RPC: blockchain/get_alternative_chains + /// + /// Display alternative chains seen by the node. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `chains` -- Array of Chains. Each element is contains the following keys: + /// - `block_hash` -- The block hash of the first diverging block of this alternative chain. + /// - `height` -- The block height of the first diverging block of this alternative chain. + /// - `length` -- The length in blocks of this alternative chain, after divergence. + /// - `difficulty` -- The cumulative difficulty of all blocks in the alternative chain. + /// - `block_hashes` -- List containing hex block hashes + /// - `main_chain_parent_block` + struct GET_ALTERNATE_CHAINS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_alternative_chains"); } - KV_MAP_SERIALIZABLE + struct chain_info + { + std::string block_hash; + uint64_t height; + uint64_t length; + uint64_t difficulty; + std::vector block_hashes; + std::string main_chain_parent_block; }; }; + void to_json(nlohmann::json& j, const GET_ALTERNATE_CHAINS::chain_info& c); + void from_json(const nlohmann::json& j, GET_ALTERNATE_CHAINS::chain_info& c); + + /// RPC: daemon/relay_tx + /// + /// Relay a list of transaction IDs. + /// + /// Inputs: + /// + /// - `txids` -- List of transactions IDs to relay from pool. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + struct RELAY_TX : RPC_COMMAND + { + static constexpr auto names() { return NAMES("relay_tx"); } - BELDEX_RPC_DOC_INTROSPECT - struct GET_OUTPUTS : PUBLIC, LEGACY + struct request_parameters + { + std::vector txids; + } request; + + }; + + /// RPC: daemon/sync_info + /// + /// Get node synchronisation information. This returns information on the node's syncing "spans" + /// which are block segments being downloaded from peers while syncing; spans are generally + /// downloaded out of order from multiple peers, and so these spans reflect in-progress downloaded + /// blocks that have not yet been added to the block chain: typically because the spans is not yet + /// complete, or because the span is for future blocks that are dependent on intermediate blocks + /// (likely in another span) being added to the chain first. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `height` -- Current block height + /// - `target_height` -- If the node is currently syncing then this is the target height the node + /// wants to reach. If fully synced then this field is omitted. + /// - `peers` -- dict of connection information about peers. The key is the peer connection_id; the + /// value is identical to the values of the `connections` field of the + /// [`get_connections`](#get_connections) endpoint. + /// - `span` -- array of span information of current in progress synchronization. Element element + /// contains: + /// - `start_block_height` -- Block height of the first block in the span + /// - `nblocks` -- the number of blocks in the span + /// - `connection_id` -- the connection_id of the connection from which we are downloading the span + /// - `rate` -- the most recent connection speed measurement + /// - `speed` -- the average connection speed over recent downloaded blocks + /// - `size` -- total number of block and transaction data stored in the span + /// - `overview` -- a string containing a one-line ascii-art depiction of the current sync status + struct SYNC_INFO : NO_ARGS { - static constexpr auto names() { return NAMES("get_outs"); } + static constexpr auto names() { return NAMES("sync_info"); } + }; - /// Maximum outputs that may be requested in a single request (unless admin) - static constexpr size_t MAX_COUNT = 5000; + struct output_distribution_data + { + std::vector distribution; + std::uint64_t start_height; + std::uint64_t base; + }; - struct request - { - std::vector outputs; // Array of structure `get_outputs_out`. - bool get_txid; // Request the TXID/hash of the transaction as well. - KV_MAP_SERIALIZABLE - }; + /// RPC: blockchain/get_output_distribution + /// + /// This endpoint returns the output distribution of the blockchain. Its primary use is for the + /// wallet to obtain the distribution of outputs on the blockchain so as to construct a feasible + /// decoy set. + /// + /// Inputs: + /// + /// - `amounts` -- Amounts to look for in atomic units. + /// - `from_height` -- starting height to check from. Optional (default is 0). + /// - `to_height` -- ending height to check up to; 0 (or omitted) means the current chain height. + /// - `cumulative` -- if true then results are cumulative output counts up to the given block; if + /// false (or omitted) then results are the number of outputs added in the given block. + /// - `binary` -- request the result in binary format (ignored for JSON requests) + /// - `compressed` -- (required `binary`) -- use varint encoding of the binary result + /// + /// Outputs: + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `distributions` -- array of distribution data; each element is an object containing keys: + /// - `amount` -- the requested amount + /// - `start_height` -- the requested start height + /// - `base` -- the number of outputs at the start_height + /// - `binary` -- true if the result is binary. This will always be false for JSON-encoded + /// responses. + /// - `compressed` -- true if the result is binary and using varint encoding. Will always be + /// false for JSON-encoded responses. + /// - `distribution` -- the distribution of rct outputs in blocks beginning after + /// `start_height`. If `binary` and `compressed` are true then this is a binary value + /// consisting of varint-encoded values; if just `binary` is set then this is a raw dump of + /// unsigned 64-bit integer binary data of the values. When `binary` is unset (i.e. for a + /// JSON response) this is an array of integer values. + /// + /// Example input: + /// ```json + /// { "amounts": [0], "from_height": 100002, "to_height": 100008, "cumulative": false } + /// ``` + /// + /// Example-JSON-Fetch + struct GET_OUTPUT_DISTRIBUTION : PUBLIC + { + static constexpr auto names() { return NAMES("get_output_distribution"); } - struct outkey + struct request_parameters { - std::string key; // The public key of the output. - std::string mask; - bool unlocked; // States if output is locked (`false`) or not (`true`). - uint64_t height; // Block height of the output. - std::string txid; // Transaction id. - - KV_MAP_SERIALIZABLE - }; + std::vector amounts; // Amounts to look for in atomic units. + uint64_t from_height; // (optional, default is 0) starting height to check from. + uint64_t to_height; // (optional, default is 0) ending height to check up to. + bool cumulative; // (optional, default is false) States if the result should be cumulative (true) or not (false). + }request; - struct response + struct distribution { - std::vector outs; // List of outkey information. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE + rpc::output_distribution_data data; + uint64_t amount; }; }; + void to_json(nlohmann::json& j, const GET_OUTPUT_DISTRIBUTION::distribution& y); + + /// RPC: daemon/pop_blocks + /// + /// Pop blocks off the main chain + /// + /// Inputs: + /// + /// - `nblocks` -- Number of blocks in that span. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `height` -- Height of the blockchain after blocks have been popped. + struct POP_BLOCKS : LEGACY + { + static constexpr auto names() { return NAMES("pop_blocks"); } - BELDEX_RPC_DOC_INTROSPECT - // Broadcast a raw transaction to the network. - struct SEND_RAW_TX : PUBLIC, LEGACY + struct request_parameters + { + uint64_t nblocks; + } request; + }; + + /// RPC: daemon/prune_blockchain + /// + /// Pruning is the process of removing non-critical blockchain information from local storage. + /// Full nodes keep an entire copy of everything that is stored on the blockchain, including data + /// that is not very useful anymore. Pruned nodes remove much of this less relevant information + /// to have a lighter footprint. Of course, running a full node is always better; however, pruned + /// nodes have most of the important information and can still support the network. + /// + /// Inputs: + /// + /// - `check` -- Instead of running check if the blockchain has already been pruned. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `pruned` -- Bool returning whether the blockchain was pruned or not. + /// - `pruning_seed` -- The seed that determined how the blockchain was to be pruned. + struct PRUNE_BLOCKCHAIN : RPC_COMMAND { - static constexpr auto names() { return NAMES("send_raw_transaction", "sendrawtransaction"); } + static constexpr auto names() { return NAMES("prune_blockchain"); } - struct request - { - std::string tx_as_hex; // Full transaction information as hexidecimal string. - bool do_not_relay; // (Optional: Default false) Stop relaying transaction to other nodes. Ignored if `flash` is true. - bool flash; // (Optional: Default false) Submit this as a flash tx rather than into the mempool. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - std::string reason; // Additional information. Currently empty, "Not relayed" if transaction was accepted but not relayed, or some descriptive message of why the tx failed. - bool not_relayed; // Transaction was not relayed (true) or relayed (false). - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - tx_verification_context tvc; - bool sanity_check_failed; - flash_result flash_status; // 0 for a non-flash tx. For a flash tx: 1 means rejected, 2 means accepted, 3 means timeout. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Start mining on the daemon. - struct START_MINING : LEGACY - { - static constexpr auto names() { return NAMES("start_mining"); } - - struct request - { - std::string miner_address; // Account address to mine to. - uint64_t threads_count; // Number of mining thread to run. - uint64_t num_blocks; // Mine until the blockchain has this many new blocks, then stop (no limit if 0, the default) - bool slow_mining; // Do slow mining (i.e. don't allocate RandomX cache); primarily intended for testing - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Stop mining on the daemon. - struct STOP_MINING : LEGACY - { - static constexpr auto names() { return NAMES("stop_mining"); } - - struct request : EMPTY {}; - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the mining status of the daemon. - struct MINING_STATUS : LEGACY - { - static constexpr auto names() { return NAMES("mining_status"); } - - struct request : EMPTY {}; - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - bool active; // States if mining is enabled (`true`) or disabled (`false`). - uint64_t speed; // Mining power in hashes per seconds. - uint32_t threads_count; // Number of running mining threads. - std::string address; // Account address daemon is mining to. Empty if not mining. - std::string pow_algorithm; // Current hashing algorithm name - uint32_t block_target; // The expected time to solve per block, i.e. TARGET_BLOCK_TIME_OLD - uint64_t block_reward; // Block reward for the current block being mined. - uint64_t difficulty; // The difficulty for the current block being mined. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Retrieve general information about the state of your node and the network. - // Note that all of the std::optional<> fields here are not included if the request is a public - // (restricted) RPC request. - struct GET_INFO : PUBLIC, LEGACY - { - static constexpr auto names() { return NAMES("get_info", "getinfo"); } - - struct request : EMPTY {}; - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint64_t height; // Current length of longest chain known to daemon. - uint64_t target_height; // The height of the next block in the chain. - uint64_t immutable_height; // The latest height in the blockchain that can not be reorganized from (backed by atleast 2 Master Node, or 1 hardcoded checkpoint, 0 if N/A). - uint64_t POS_ideal_timestamp; // For POS blocks this is the ideal timestamp of the next block, that is, the timestamp if the network was operating with perfect 2-minute blocks since the POS hard fork. - uint64_t POS_target_timestamp; // For POS blocks this is the target timestamp of the next block, which targets 2 minutes after the previous block but will be slightly faster/slower if the previous block is behind/ahead of the ideal timestamp. - uint64_t difficulty; // Network difficulty (analogous to the strength of the network). - uint64_t target; // Current target for next proof of work. - uint64_t tx_count; // Total number of non-coinbase transaction in the chain. - uint64_t tx_pool_size; // Number of transactions that have been broadcast but not included in a block. - std::optional alt_blocks_count; // Number of alternative blocks to main chain. - std::optional outgoing_connections_count; // Number of peers that you are connected to and getting information from. - std::optional incoming_connections_count; // Number of peers connected to and pulling from your node. - std::optional white_peerlist_size; // White Peerlist Size - std::optional grey_peerlist_size; // Grey Peerlist Size - bool mainnet; // States if the node is on the mainnet (`true`) or not (`false`). - bool testnet; // States if the node is on the testnet (`true`) or not (`false`). - bool devnet; // States if the node is on the devnet (`true`) or not (`false`). - std::string nettype; // Nettype value used. - std::string top_block_hash; // Hash of the highest block in the chain. - std::string immutable_block_hash; // Hash of the highest block in the chain that can not be reorganized. - uint64_t cumulative_difficulty; // Cumulative difficulty of all blocks in the blockchain. - uint64_t block_size_limit; // Maximum allowed block size. - uint64_t block_weight_limit; // Maximum allowed block weight. - uint64_t block_size_median; // Median block size of latest 100 blocks. - uint64_t block_weight_median; // Median block weight of latest 100 blocks. - int bns_counts; // BNS registration counts. - std::optional master_node; // Will be true if the node is running in --service-node mode. - std::optional start_time; // Start time of the daemon, as UNIX time. - std::optional last_storage_server_ping; // Last ping time of the storage server (0 if never or not running as a service node) - std::optional last_belnet_ping; // Last ping time of belnet (0 if never or not running as a service node) - std::optional free_space; // Available disk space on the node. - bool offline; // States if the node is offline (`true`) or online (`false`). - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - std::optional bootstrap_daemon_address; // Bootstrap node to give immediate usability to wallets while syncing by proxying RPC to it. (Note: the replies may be untrustworthy). - std::optional height_without_bootstrap; // Current length of the local chain of the daemon. - std::optional was_bootstrap_ever_used; // States if a bootstrap node has ever been used since the daemon started. - uint64_t database_size; // Current size of Blockchain data. Over public RPC this is rounded up to the next-largest GB value. - std::string version; // Current version of software running. - std::string status_line; // A short one-line summary status of the node (requires an admin/unrestricted connection for most details) - - KV_MAP_SERIALIZABLE - }; - }; - - //----------------------------------------------- - BELDEX_RPC_DOC_INTROSPECT - struct GET_NET_STATS : LEGACY - { - static constexpr auto names() { return NAMES("get_net_stats"); } - - struct request : EMPTY {}; - struct response - { - std::string status; - uint64_t start_time; - uint64_t total_packets_in; - uint64_t total_bytes_in; - uint64_t total_packets_out; - uint64_t total_bytes_out; - - KV_MAP_SERIALIZABLE - }; - }; - - - BELDEX_RPC_DOC_INTROSPECT - // Save the blockchain. The blockchain does not need saving and is always saved when modified, - // however it does a sync to flush the filesystem cache onto the disk for safety purposes against Operating System or Hardware crashes. - struct SAVE_BC : LEGACY - { - static constexpr auto names() { return NAMES("save_bc"); } - - struct request : EMPTY {}; - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Look up how many blocks are in the longest chain known to the node. - struct GETBLOCKCOUNT : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_count", "getblockcount"); } - - struct request : EMPTY {}; - struct response - { - uint64_t count; // Number of blocks in longest chain seen by the node. - std::string status; // General RPC error code. "OK" means everything looks good. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Look up a block's hash by its height. - struct GETBLOCKHASH : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_hash", "on_get_block_hash", "on_getblockhash"); } - - struct request { - std::vector height; // Block height (int array of length 1). - - // epee serialization; this is a bit hacky because epee serialization makes things hacky. - bool load(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section = nullptr); - bool store(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section = nullptr); - }; - - using response = std::string; // Block hash (string). - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get a block template on which mining a new block. - struct GETBLOCKTEMPLATE : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_template", "getblocktemplate"); } - - struct request - { - uint64_t reserve_size; // Max 255 bytes - std::string wallet_address; // Address of wallet to receive coinbase transactions if block is successfully mined. - std::string prev_block; - std::string extra_nonce; - - KV_MAP_SERIALIZABLE - }; - - struct response - { - uint64_t difficulty; // Difficulty of next block. - uint64_t height; // Height on which to mine. - uint64_t reserved_offset; // Reserved offset. - uint64_t expected_reward; // Coinbase reward expected to be received if block is successfully mined. - std::string prev_hash; // Hash of the most recent block on which to mine the next block. - std::string seed_hash; // RandomX current seed hash - std::string next_seed_hash; // RandomX upcoming seed hash - blobdata blocktemplate_blob; // Blob on which to try to mine a new block. - blobdata blockhashing_blob; // Blob on which to try to find a valid nonce. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Submit a mined block to the network. - struct SUBMITBLOCK : PUBLIC - { - static constexpr auto names() { return NAMES("submit_block", "submitblock"); } - - struct request { - std::vector blob; // Block blob data - array containing exactly one block blob string which has been mined. See get_block_template to get a blob on which to mine. - - // epee serialization; this is a bit hacky because epee serialization makes things hacky. - bool load(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section = nullptr); - bool store(epee::serialization::portable_storage& ps, epee::serialization::section* hparent_section = nullptr); - }; - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Developer only. - struct GENERATEBLOCKS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("generateblocks"); } - - struct request - { - uint64_t amount_of_blocks; - std::string wallet_address; - std::string prev_block; - uint32_t starting_nonce; - - KV_MAP_SERIALIZABLE - }; - - struct response - { - uint64_t height; - std::vector blocks; - std::string status; // General RPC error code. "OK" means everything looks good. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct block_header_response - { - uint8_t major_version; // The major version of the beldex protocol at this block height. - uint8_t minor_version; // The minor version of the beldex protocol at this block height. - uint64_t timestamp; // The unix time at which the block was recorded into the blockchain. - std::string prev_hash; // The hash of the block immediately preceding this block in the chain. - uint32_t nonce; // A cryptographic random one-time number used in mining a Beldex block. - bool orphan_status; // Usually `false`. If `true`, this block is not part of the longest chain. - uint64_t height; // The number of blocks preceding this block on the blockchain. - uint64_t depth; // The number of blocks succeeding this block on the blockchain. A larger number means an older block. - std::string hash; // The hash of this block. - difficulty_type difficulty; // The strength of the Beldex network based on mining power. - difficulty_type cumulative_difficulty; // The cumulative strength of the Beldex network based on mining power. - uint64_t reward; // The amount of new generated in this block and rewarded to the miner, foundation and service Nodes. Note: 1 BELDEX = 1e9 atomic units. - uint64_t miner_reward; // The amount of new generated in this block and rewarded to the miner. Note: 1 BELDEX = 1e9 atomic units. - uint64_t block_size; // The block size in bytes. - uint64_t block_weight; // The block weight in bytes. - uint64_t num_txes; // Number of transactions in the block, not counting the coinbase tx. - std::optional pow_hash; // The hash of the block's proof of work (requires `fill_pow_hash`) - uint64_t long_term_weight; // Long term weight of the block. - std::string miner_tx_hash; // The TX hash of the miner transaction - std::vector tx_hashes; // The TX hashes of all non-coinbase transactions (requires `get_tx_hashes`) - std::string master_node_winner; // Master node that received a reward for this block - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - // Block header information for the most recent block is easily retrieved with this method. No inputs are needed. - struct GET_LAST_BLOCK_HEADER : PUBLIC - { - static constexpr auto names() { return NAMES("get_last_block_header", "getlastblockheader"); } - - struct request - { - bool fill_pow_hash; // Tell the daemon if it should fill out pow_hash field. - bool get_tx_hashes; // If true (default false) then include the hashes of non-coinbase transactions - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - block_header_response block_header; // A structure containing block header information. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Block header information can be retrieved using either a block's hash or height. This method includes a block's hash as an input parameter to retrieve basic information about the block. - struct GET_BLOCK_HEADER_BY_HASH : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_header_by_hash", "getblockheaderbyhash"); } - - struct request - { - std::string hash; // The block's SHA256 hash. - std::vector hashes; // Request multiple blocks via an array of hashes - bool fill_pow_hash; // Tell the daemon if it should fill out pow_hash field. - bool get_tx_hashes; // If true (default false) then include the hashes of non-coinbase transactions - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::optional block_header; // Block header information for the requested `hash` block - std::vector block_headers; // Block header information for the requested `hashes` blocks - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Similar to get_block_header_by_hash above, this method includes a block's height as an input parameter to retrieve basic information about the block. - struct GET_BLOCK_HEADER_BY_HEIGHT : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_header_by_height", "getblockheaderbyheight"); } - - struct request - { - std::optional height; // A block height to look up; returned in `block_header` - std::vector heights; // Block heights to retrieve; returned in `block_headers` - bool fill_pow_hash; // Tell the daemon if it should fill out pow_hash field. - bool get_tx_hashes; // If true (default false) then include the hashes of non-coinbase transactions - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::optional block_header; // Block header information for the requested `height` block - std::vector block_headers; // Block header information for the requested `heights` blocks - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Full block information can be retrieved by either block height or hash, like with the above block header calls. - // For full block information, both lookups use the same method, but with different input parameters. - struct GET_BLOCK : PUBLIC - { - static constexpr auto names() { return NAMES("get_block", "getblock"); } - - struct request - { - std::string hash; // The block's hash. - uint64_t height; // The block's height. - bool fill_pow_hash; // Tell the daemon if it should fill out pow_hash field. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - block_header_response block_header; // A structure containing block header information. See get_last_block_header. - std::vector tx_hashes; // List of hashes of non-coinbase transactions in the block. If there are no other transactions, this will be an empty list. - std::string blob; // Hexadecimal blob of block information. - std::string json; // JSON formatted block details. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the known peers list. - struct GET_PEER_LIST : LEGACY - { - static constexpr auto names() { return NAMES("get_peer_list"); } - - struct request - { - bool public_only; - KV_MAP_SERIALIZABLE - }; - - struct peer - { - uint64_t id; // Peer id. - std::string host; // IP address in string format. - uint32_t ip; // IP address in integer format. - uint16_t port; // TCP port the peer is using to connect to beldex network. - uint16_t rpc_port; // RPC port the peer is using - uint64_t last_seen; // Unix time at which the peer has been seen for the last time - uint32_t pruning_seed; // - - peer() = default; - - peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) - {} - peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) - {} - peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) - {} - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - std::vector white_list; // Array of online peer structure. - std::vector gray_list; // Array of offline peer structure. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct public_node - { - std::string host; - uint64_t last_seen; - uint16_t rpc_port; - - public_node() = default; - public_node(const GET_PEER_LIST::peer &peer) : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port) {} - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - // Query the daemon's peerlist and retrieve peers who have set their public rpc port. - struct GET_PUBLIC_NODES : PUBLIC - { - static constexpr auto names() { return NAMES("get_public_nodes"); } - - struct request - { - bool gray; // Get peers that have recently gone offline. - bool white; // Get peers that are online - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - std::vector gray; // Graylist peers - std::vector white; // Whitelist peers - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Set the log hash rate display mode. - struct SET_LOG_HASH_RATE : LEGACY - { - static constexpr auto names() { return NAMES("set_log_hash_rate"); } - - struct request - { - bool visible; // States if hash rate logs should be visible (true) or hidden (false) - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Set the daemon log level. By default, log level is set to `0`. For more fine-tuned logging - // control set the set_log_categories command instead. - struct SET_LOG_LEVEL : LEGACY - { - static constexpr auto names() { return NAMES("set_log_level"); } - - struct request - { - int8_t level; // Daemon log level to set from `0` (less verbose) to `4` (most verbose) - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Set the daemon log categories. Categories are represented as a comma separated list of `:` (similarly to syslog standard `:`), where: - // Category is one of the following: * (all facilities), default, net, net.http, net.p2p, logging, net.trottle, blockchain.db, blockchain.db.lmdb, bcutil, checkpoints, net.dns, net.dl, - // i18n, perf,stacktrace, updates, account, cn ,difficulty, hardfork, miner, blockchain, txpool, cn.block_queue, net.cn, daemon, debugtools.deserialize, debugtools.objectsizes, device.ledger, - // wallet.gen_multisig, multisig, bulletproofs, ringct, daemon.rpc, wallet.simplewallet, WalletAPI, wallet.ringdb, wallet.wallet2, wallet.rpc, tests.core. - // - // Level is one of the following: FATAL - higher level, ERROR, WARNING, INFO, DEBUG, TRACE. - // Lower level A level automatically includes higher level. By default, categories are set to: - // `*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO` - // Setting the categories to "" prevent any logs to be outputed. - // - // You can append to the current the log level for updating just one or more categories while - // leaving other log levels unchanged by specifying one or more ":" pairs - // preceded by a "+", for example "+difficulty:DEBUG,net:WARNING". - struct SET_LOG_CATEGORIES : LEGACY - { - static constexpr auto names() { return NAMES("set_log_categories"); } - - struct request - { - std::string categories; // Optional, daemon log categories to enable - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - std::string categories; // Daemon log enabled categories - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct tx_info - { - std::string id_hash; // The transaction ID hash. - std::string tx_json; // JSON structure of all information in the transaction - uint64_t blob_size; // The size of the full transaction blob. - uint64_t weight; // The weight of the transaction. - uint64_t fee; // The amount of the mining fee included in the transaction, in atomic units. - std::string max_used_block_id_hash; // Tells the hash of the most recent block with an output used in this transaction. - uint64_t max_used_block_height; // Tells the height of the most recent block with an output used in this transaction. - bool kept_by_block; // States if the tx was included in a block at least once (`true`) or not (`false`). - uint64_t last_failed_height; // If the transaction validation has previously failed, this tells at what height that occured. - std::string last_failed_id_hash; // Like the previous, this tells the previous transaction ID hash. - uint64_t receive_time; // The Unix time that the transaction was first seen on the network by the node. - bool relayed; // States if this transaction has been relayed - uint64_t last_relayed_time; // Last unix time at which the transaction has been relayed. - bool do_not_relay; // States if this transaction should not be relayed. - bool double_spend_seen; // States if this transaction has been seen as double spend. - std::string tx_blob; // Hexadecimal blob represnting the transaction. - bool flash; // True if this is a signed flash transaction - std::optional extra; // Parsed tx_extra information (only if requested) - std::optional stake_amount; // Will be set to the staked amount if the transaction is a staking transaction *and* stake amounts were requested. - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - struct spent_key_image_info - { - std::string id_hash; // Key image. - std::vector txs_hashes; // List of tx hashes of the txes (usually one) spending that key image. - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - // Show information about valid transactions seen by the node but not yet mined into a block, - // as well as spent key image information for the txpool in the node's memory. - struct GET_TRANSACTION_POOL : PUBLIC, LEGACY - { - static constexpr auto names() { return NAMES("get_transaction_pool"); } - - struct request - { - bool tx_extra; // Parse tx-extra information and adds it to the `extra` field. - bool stake_info; // Calculate and include staking contribution amount for registration/staking transactions - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector transactions; // List of transactions in the mempool are not in a block on the main chain at the moment: - std::vector spent_key_images; // List of spent output key images: - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get hashes from transaction pool. Binary request. - struct GET_TRANSACTION_POOL_HASHES_BIN : PUBLIC, BINARY - { - static constexpr auto names() { return NAMES("get_transaction_pool_hashes.bin"); } - - static constexpr std::chrono::seconds long_poll_timeout{15}; - - struct request - { - bool flashed_txs_only; // Optional: If true only transactions that were sent via flash and approved are queried. - bool long_poll; // Optional: If true, this call is blocking until timeout OR tx pool has changed since the last query. TX pool change is detected by comparing the hash of all the hashes in the tx pool. Ignored when using LMQ RPC. - crypto::hash tx_pool_checksum; // Optional: If `long_poll` is true the caller must pass the hashes of all their known tx pool hashes, XOR'ed together. Ignored when using LMQ RPC. - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector tx_hashes; // List of transaction hashes, - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get hashes from transaction pool. - struct GET_TRANSACTION_POOL_HASHES : PUBLIC, LEGACY - { - static constexpr auto names() { return NAMES("get_transaction_pool_hashes"); } - - struct request : EMPTY {}; - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector tx_hashes; // List of transaction hashes, - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct tx_backlog_entry - { - uint64_t weight; // - uint64_t fee; // Fee in Beldex measured in atomic units. - uint64_t time_in_pool; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get all transaction pool backlog. - struct GET_TRANSACTION_POOL_BACKLOG : PUBLIC - { - static constexpr auto names() { return NAMES("get_txpool_backlog"); } - - struct request : EMPTY {}; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector backlog; // Array of structures tx_backlog_entry (in binary form): - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct txpool_histo - { - uint32_t txs; // Number of transactions. - uint64_t bytes; // Size in bytes. - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - struct txpool_stats - { - uint64_t bytes_total; // Total size of all transactions in pool. - uint32_t bytes_min; // Min transaction size in pool. - uint32_t bytes_max; // Max transaction size in pool. - uint32_t bytes_med; // Median transaction size in pool. - uint64_t fee_total; // Total fee's in pool in atomic units. - uint64_t oldest; // Unix time of the oldest transaction in the pool. - uint32_t txs_total; // Total number of transactions. - uint32_t num_failing; // Bumber of failing transactions. - uint32_t num_10m; // Number of transactions in pool for more than 10 minutes. - uint32_t num_not_relayed; // Number of non-relayed transactions. - uint64_t histo_98pc; // the time 98% of txes are "younger" than. - std::vector histo; // List of txpool histo. - uint32_t num_double_spends; // Number of double spend transactions. - - txpool_stats(): bytes_total(0), bytes_min(0), bytes_max(0), bytes_med(0), fee_total(0), oldest(0), txs_total(0), num_failing(0), num_10m(0), num_not_relayed(0), histo_98pc(0), num_double_spends(0) {} - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the transaction pool statistics. - struct GET_TRANSACTION_POOL_STATS : PUBLIC, LEGACY - { - static constexpr auto names() { return NAMES("get_transaction_pool_stats"); } - - struct request : EMPTY {}; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - txpool_stats pool_stats; // List of pool stats: - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Retrieve information about incoming and outgoing connections to your node. - struct GET_CONNECTIONS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_connections"); } - - struct request : EMPTY {}; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::list connections; // List of all connections and their info: - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Similar to get_block_header_by_height above, but for a range of blocks. - // This method includes a starting block height and an ending block height as - // parameters to retrieve basic information about the range of blocks. - struct GET_BLOCK_HEADERS_RANGE : PUBLIC - { - static constexpr auto names() { return NAMES("get_block_headers_range", "getblockheadersrange"); } - - struct request - { - uint64_t start_height; // The starting block's height. - uint64_t end_height; // The ending block's height. - bool fill_pow_hash; // Tell the daemon if it should fill out pow_hash field. - bool get_tx_hashes; // If true (default false) then include the hashes or txes in the block details - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector headers; // Array of block_header (a structure containing block header information. See get_last_block_header). - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Set the bootstrap daemon to use for data on the blockchain whilst syncing the chain. - struct SET_BOOTSTRAP_DAEMON : RPC_COMMAND - { - static constexpr auto names() { return NAMES("set_bootstrap_daemon"); } - struct request - { - - std::string address; - std::string username; - std::string password; - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Send a command to the daemon to safely disconnect and shut down. - struct STOP_DAEMON : LEGACY - { - static constexpr auto names() { return NAMES("stop_daemon"); } - - struct request : EMPTY {}; - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get daemon bandwidth limits. - struct GET_LIMIT : LEGACY - { - static constexpr auto names() { return NAMES("get_limit"); } - - struct request : EMPTY {}; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint64_t limit_up; // Upload limit in kBytes per second. - uint64_t limit_down; // Download limit in kBytes per second. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Set daemon bandwidth limits. - struct SET_LIMIT : LEGACY - { - static constexpr auto names() { return NAMES("set_limit"); } - - struct request - { - int64_t limit_down; // Download limit in kBytes per second (-1 reset to default, 0 don't change the current limit) - int64_t limit_up; // Upload limit in kBytes per second (-1 reset to default, 0 don't change the current limit) - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - int64_t limit_up; // Upload limit in kBytes per second. - int64_t limit_down; // Download limit in kBytes per second. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Limit number of Outgoing peers. - struct OUT_PEERS : LEGACY - { - static constexpr auto names() { return NAMES("out_peers"); } - - struct request - { - bool set; // If true, set the number of outgoing peers, otherwise the response returns the current limit of outgoing peers. (Defaults to true) - uint32_t out_peers; // Max number of outgoing peers - KV_MAP_SERIALIZABLE - }; - - struct response { - uint32_t out_peers; // The current limit set for outgoing peers - std::string status; // General RPC error code. "OK" means everything looks good. - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Limit number of Incoming peers. - struct IN_PEERS : LEGACY - { - static constexpr auto names() { return NAMES("in_peers"); } - - struct request - { - bool set; // If true, set the number of incoming peers, otherwise the response returns the current limit of incoming peers. (Defaults to true) - uint32_t in_peers; // Max number of incoming peers - KV_MAP_SERIALIZABLE - }; - - struct response { - uint32_t in_peers; // The current limit set for outgoing peers - std::string status; // General RPC error code. "OK" means everything looks good. - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Look up information regarding hard fork voting and readiness. - struct HARD_FORK_INFO : PUBLIC - { - static constexpr auto names() { return NAMES("hard_fork_info"); } - - struct request - { - uint8_t version; // The major block version for the fork (only one of `version` and `height` may be given). - uint64_t height; // Request hard fork info about this height (only one of `version` and `height` may be given). - - KV_MAP_SERIALIZABLE - }; - - struct response - { - uint8_t version; // The major block version for the fork. - bool enabled; // Indicates whether hard fork is enforced (that is, at or above the requested hardfork) - std::optional earliest_height; // Block height at which hard fork will be enabled. - std::optional last_height; // The last block height at which this hard fork will be active; will be omitted if this beldexd is not aware of any future hard fork. - std::string status; // General RPC error code. "OK" means everything looks good. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get list of banned IPs. - struct GETBANS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_bans"); } - - struct request : EMPTY {}; - - struct ban - { - std::string host; // Banned host (IP in A.B.C.D form). - uint32_t ip; // Banned IP address, in Int format. - uint32_t seconds; // Local Unix time that IP is banned until. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector bans; // List of banned nodes: - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Ban another node by IP. - struct SETBANS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("set_bans"); } - - struct ban - { - std::string host; // Host to ban (IP in A.B.C.D form - will support I2P address in the future). - uint32_t ip; // IP address to ban, in Int format. - bool ban; // Set true to ban. - uint32_t seconds; // Number of seconds to ban node. - - KV_MAP_SERIALIZABLE - }; - - struct request - { - std::vector bans; // List of nodes to ban. - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Determine whether a given IP address is banned - struct BANNED : RPC_COMMAND - { - static constexpr auto names() { return NAMES("banned"); } - - struct request - { - std::string address; // The IP address to check - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - bool banned; // True if the given address is banned, false otherwise. - uint32_t seconds; // The number of seconds remaining in the ban. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Flush tx ids from transaction pool.. - struct FLUSH_TRANSACTION_POOL : RPC_COMMAND - { - static constexpr auto names() { return NAMES("flush_txpool"); } - - struct request - { - std::vector txids; // Optional, list of transactions IDs to flush from pool (all tx ids flushed if empty). - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get a histogram of output amounts. For all amounts (possibly filtered by parameters), - // gives the number of outputs on the chain for that amount. RingCT outputs counts as 0 amount. - struct GET_OUTPUT_HISTOGRAM : PUBLIC - { - static constexpr auto names() { return NAMES("get_output_histogram"); } - - struct request - { - std::vector amounts; // list of amounts in Atomic Units. - uint64_t min_count; // The minimum amounts you are requesting. - uint64_t max_count; // The maximum amounts you are requesting. - bool unlocked; // Look for locked only. - uint64_t recent_cutoff; - - KV_MAP_SERIALIZABLE - }; - - struct entry - { - uint64_t amount; // Output amount in atomic units. - uint64_t total_instances; - uint64_t unlocked_instances; - uint64_t recent_instances; - - KV_MAP_SERIALIZABLE - - entry(uint64_t amount, uint64_t total_instances, uint64_t unlocked_instances, uint64_t recent_instances): - amount(amount), total_instances(total_instances), unlocked_instances(unlocked_instances), recent_instances(recent_instances) {} - entry() = default; - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector histogram; // List of histogram entries: - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get current RPC protocol version. - struct GET_VERSION : PUBLIC - { - static constexpr auto names() { return NAMES("get_version"); } - - struct request : EMPTY {}; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint32_t version; // RPC current version. - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the coinbase amount and the fees amount for n last blocks starting at particular height. - struct GET_COINBASE_TX_SUM : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_coinbase_tx_sum"); } - - struct request - { - uint64_t height; // Block height from which getting the amounts. - uint64_t count; // Number of blocks to include in the sum. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint64_t emission_amount; // Amount of coinbase reward in atomic units. - int64_t fee_amount; // Amount of fees in atomic units. - int64_t burn_amount; // Amount of burnt beldex. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Gives an estimation of per-output + per-byte fees - struct GET_BASE_FEE_ESTIMATE : PUBLIC - { - static constexpr auto names() { return NAMES("get_fee_estimate"); } - - struct request - { - uint64_t grace_blocks; // Optional - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint64_t fee_per_byte; // Amount of fees estimated per byte in atomic units - uint64_t fee_per_output; // Amount of fees per output generated by the tx (adds to the `fee_per_byte` per-byte value) - uint64_t flash_fee_per_byte; // `fee_per_byte` value for sending a flash. The portion of the overall flash fee above the overall base fee is burned. - uint64_t flash_fee_per_output; // `fee_per_output` value for sending a flash. The portion of the overall flash fee above the overall base fee is burned. - uint64_t flash_fee_fixed; // Fixed flash fee in addition to the per-output and per-byte amounts. The portion of the overall flash fee above the overall base fee is burned. - uint64_t quantization_mask; - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Display alternative chains seen by the node. - struct GET_ALTERNATE_CHAINS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_alternative_chains"); } - - struct request : EMPTY {}; - - struct chain_info - { - std::string block_hash; // The block hash of the first diverging block of this alternative chain. - uint64_t height; // The block height of the first diverging block of this alternative chain. - uint64_t length; // The length in blocks of this alternative chain, after divergence. - uint64_t difficulty; // The cumulative difficulty of all blocks in the alternative chain. - std::vector block_hashes; - std::string main_chain_parent_block; - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector chains; // Array of Chains. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Relay a list of transaction IDs. - struct RELAY_TX : RPC_COMMAND - { - static constexpr auto names() { return NAMES("relay_tx"); } - - struct request - { - std::vector txids; // List of transactions IDs to relay from pool. - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get synchronisation information. - struct SYNC_INFO : RPC_COMMAND - { - static constexpr auto names() { return NAMES("sync_info"); } - - struct request : EMPTY {}; - - struct peer - { - connection_info info; // Structure of connection info, as defined in get_connections. - - KV_MAP_SERIALIZABLE - }; - - struct span - { - uint64_t start_block_height; // Block height of the first block in that span. - uint64_t nblocks; // Number of blocks in that span. - std::string connection_id; // Id of connection. - uint32_t rate; // Connection rate. - uint32_t speed; // Connection speed. - uint64_t size; // Total number of bytes in that span's blocks (including txes). - std::string remote_address; // Peer address the node is downloading (or has downloaded) than span from. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. Any other value means that something went wrong. - uint64_t height; // Block height. - uint64_t target_height; // Target height the node is syncing from (optional, absent if node is fully synced). - uint32_t next_needed_pruning_seed; - std::list peers; // Array of Peer structure - std::list spans; // Array of Span Structure. - std::string overview; - - KV_MAP_SERIALIZABLE - }; - }; - - struct output_distribution_data - { - std::vector distribution; - std::uint64_t start_height; - std::uint64_t base; - }; - - - BELDEX_RPC_DOC_INTROSPECT - struct GET_OUTPUT_DISTRIBUTION : PUBLIC - { - static constexpr auto names() { return NAMES("get_output_distribution"); } - - struct request - { - std::vector amounts; // Amounts to look for in atomic units. - uint64_t from_height; // (optional, default is 0) starting height to check from. - uint64_t to_height; // (optional, default is 0) ending height to check up to. - bool cumulative; // (optional, default is false) States if the result should be cumulative (true) or not (false). - bool binary; - bool compress; - - KV_MAP_SERIALIZABLE - }; - - struct distribution - { - rpc::output_distribution_data data; - uint64_t amount; - std::string compressed_data; - bool binary; - bool compress; - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - std::vector distributions; // - bool untrusted; // States if the result is obtained using the bootstrap mode, and is therefore not trusted (`true`), or when the daemon is fully synced (`false`). - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Exactly like GET_OUTPUT_DISTRIBUTION, but does a binary RPC transfer instead of JSON - struct GET_OUTPUT_DISTRIBUTION_BIN : PUBLIC, BINARY - { - static constexpr auto names() { return NAMES("get_output_distribution.bin"); } - - struct request : GET_OUTPUT_DISTRIBUTION::request {}; - using response = GET_OUTPUT_DISTRIBUTION::response; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct POP_BLOCKS : LEGACY - { - static constexpr auto names() { return NAMES("pop_blocks"); } - - struct request - { - uint64_t nblocks; // Number of blocks in that span. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // General RPC error code. "OK" means everything looks good. - uint64_t height; - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct PRUNE_BLOCKCHAIN : RPC_COMMAND - { - static constexpr auto names() { return NAMES("prune_blockchain"); } - - struct request + struct request_parameters { bool check; - - KV_MAP_SERIALIZABLE - }; - - struct response - { - bool pruned; - uint32_t pruning_seed; - std::string status; - - KV_MAP_SERIALIZABLE - }; - }; - - - BELDEX_RPC_DOC_INTROSPECT - // Accesses the list of public keys of the nodes who are participating or being tested in a quorum. + } request; + }; + + /// RPC: network/get_quorum_state + /// + /// Accesses the list of public keys of the nodes who are participating or being tested in a quorum. + /// + /// Inputs: + /// + /// - `start_height` -- (Optional): Start height, omit both start and end height to request the latest quorum. Note that "latest" means different heights for different types of quorums as not all quorums exist at every block heights. + /// - `end_height` -- (Optional): End height, omit both start and end height to request the latest quorum + /// - `quorum_type` -- (Optional): Set value to request a specific quorum, 0 = Obligation, 1 = Checkpointing, 2 = Flash, 3 = POS, 255 = all quorums, default is all quorums. For POS quorums, requesting the blockchain height (or latest) returns the primary POS quorum responsible for the next block; for heights with blocks this returns the actual quorum, which may be a backup quorum if the primary quorum did not produce in time. + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `quorums` -- An array of quorums associated with the requested height. Each element is structured with the following keys: + /// - `master_node_pubkey` -- The public key of the Master Node, in hex (json) or binary (bt). + /// - `height` -- The height the quorums are relevant for + /// - `quorum_type` -- The quorum type + /// - `quorum` -- Quorum of Master Nodes. Each element is structured with the following keys: + /// - `validators` -- List of master node public keys in the quorum. For obligations quorums these are the testing nodes; for checkpoint and Flash these are the participating nodes (there are no workers); for POS Flash quorums these are the block signers. This is hex encoded, even for bt-encoded requests. + /// - `workers` -- Public key of the quorum workers. For obligations quorums these are the nodes being tested; for POS quorums this is the block producer. Checkpoint and Flash quorums do not populate this field. This is hex encoded, even for bt-encoded requests. + /// + /// Example input: + /// + /// ```json + /// { "quorum_type": 3 } + /// ``` + /// + /// Example-JSON-Fetch struct GET_QUORUM_STATE : PUBLIC { static constexpr auto names() { return NAMES("get_quorum_state"); } static constexpr size_t MAX_COUNT = 256; - static constexpr uint64_t HEIGHT_SENTINEL_VALUE = UINT64_MAX; - static constexpr uint8_t ALL_QUORUMS_SENTINEL_VALUE = 255; - struct request + struct request_parameters { - uint64_t start_height; // (Optional): Start height, omit both start and end height to request the latest quorum. Note that "latest" means different heights for different types of quorums as not all quorums exist at every block heights. - uint64_t end_height; // (Optional): End height, omit both start and end height to request the latest quorum - uint8_t quorum_type; // (Optional): Set value to request a specific quorum, 0 = Obligation, 1 = Checkpointing, 2 = Flash, 3 = POS, 255 = all quorums, default is all quorums. For POS quorums, requesting the blockchain height (or latest) returns the primary POS quorum responsible for the next block; for heights with blocks this returns the actual quorum, which may be a backup quorum if the primary quorum did not produce in time. - - KV_MAP_SERIALIZABLE - }; + std::optional start_height; + std::optional end_height; + std::optional quorum_type; + } request; struct quorum_t { - std::vector validators; // List of service node public keys in the quorum. For obligations quorums these are the testing nodes; for checkpoint and flash these are the participating nodes (there are no workers); for POS flash quorums these are the block signers. - std::vector workers; // Public key of the quorum workers. For obligations quorums these are the nodes being tested; for POS quorums this is the block producer. Checkpoint and Flash quorums do not populate this field. - - KV_MAP_SERIALIZABLE - - BEGIN_SERIALIZE() // NOTE: For store_t_to_json - FIELD(validators) - FIELD(workers) - END_SERIALIZE() - }; - - struct quorum_for_height - { - uint64_t height; // The height the quorums are relevant for - uint8_t quorum_type; // The quorum type - quorum_t quorum; // Quorum of Master Nodes - - KV_MAP_SERIALIZABLE - - BEGIN_SERIALIZE() // NOTE: For store_t_to_json - FIELD(height) - FIELD(quorum_type) - FIELD(quorum) - END_SERIALIZE() - }; - - struct response - { - std::string status; // Generic RPC error code. "OK" is the success value. - std::vector quorums; // An array of quorums associated with the requested height - bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct GET_MASTER_NODE_REGISTRATION_CMD_RAW : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_master_node_registration_cmd_raw"); } - - struct request - { - std::vector args; // (Developer) The arguments used in raw registration, i.e. portions - bool make_friendly; // Provide information about how to use the command in the result. - uint64_t staking_requirement; // The staking requirement to become a Master Node the registration command will be generated upon - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string status; // Generic RPC error code. "OK" is the success value. - std::string registration_cmd; // The command to execute in the wallet CLI to register the queried daemon as a Master Node. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct GET_MASTER_NODE_REGISTRATION_CMD : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_master_node_registration_cmd"); } - - struct contribution_t - { - std::string address; // The wallet address for the contributor - uint64_t amount; // The amount that the contributor will reserve in Beldex atomic units towards the staking requirement - - KV_MAP_SERIALIZABLE - }; - - struct request - { - std::string operator_cut; // The percentage of cut per reward the operator receives expressed as a string, i.e. "1.1%" - std::vector contributions; // Array of contributors for this Master Node - uint64_t staking_requirement; // The staking requirement to become a Master Node the registration command will be generated upon - - KV_MAP_SERIALIZABLE - }; - - using response = GET_MASTER_NODE_REGISTRATION_CMD_RAW::response; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the service public keys of the queried daemon, encoded in hex. All three keys are used - // when running as a service node; when running as a regular node only the x25519 key is regularly - // used for some RPC and and node-to-MN communication requests. - struct GET_MASTER_KEYS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_master_keys", "get_master_node_key"); } - - struct request : EMPTY {}; - - struct response - { - std::string master_node_pubkey; // The queried daemon's service node public key. Will be empty if not running as a service node. - std::string master_node_ed25519_pubkey; // The daemon's ed25519 auxiliary public key. - std::string master_node_x25519_pubkey; // The daemon's x25519 auxiliary public key. - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the service private keys of the queried daemon, encoded in hex. Do not ever share - // these keys: they would allow someone to impersonate your service node. All three keys are used - // when running as a service node; when running as a regular node only the x25519 key is regularly - // used for some RPC and and node-to-MN communication requests. - struct GET_MASTER_PRIVKEYS : RPC_COMMAND - { - static constexpr auto names() { return NAMES("get_master_privkeys", "get_master_node_privkey"); } - - struct request : EMPTY {}; - - struct response - { - std::string master_node_privkey; // The queried daemon's service node private key. Will be empty if not running as a service node. - std::string master_node_ed25519_privkey; // The daemon's ed25519 private key (note that this is in sodium's format, which consists of the private and public keys concatenated together) - std::string master_node_x25519_privkey; // The daemon's x25519 private key. - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - struct master_node_contribution - { - std::string key_image; // The contribution's key image that is locked on the network. - std::string key_image_pub_key; // The contribution's key image, public key component - uint64_t amount; // The amount that is locked in this contribution. - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - struct master_node_contributor - { - uint64_t amount; // The total amount of locked Beldex in atomic units for this contributor. - uint64_t reserved; // The amount of Beldex in atomic units reserved by this contributor for this Master Node. - std::string address; // The wallet address for this contributor rewards are sent to and contributions came from. - std::vector locked_contributions; // Array of contributions from this contributor. - - KV_MAP_SERIALIZABLE - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get information on some, all, or a random subset of Master Nodes. - struct GET_MASTER_NODES : PUBLIC - { - static constexpr auto names() { return NAMES("get_master_nodes", "get_n_master_nodes", "get_all_master_nodes"); } - - // Boolean values indicate whether corresponding fields should be included in the response - struct requested_fields_t { - bool all = false; // If set, overrides any individual requested fields. Defaults to *true* if "fields" is entirely omitted - bool master_node_pubkey; - bool registration_height; - bool registration_hf_version; - bool requested_unlock_height; - bool last_reward_block_height; - bool last_reward_transaction_index; - bool active; - bool funded; - bool state_height; - bool decommission_count; - bool last_decommission_reason_consensus_all; - bool last_decommission_reason_consensus_any; - bool earned_downtime_blocks; - - bool master_node_version; - bool belnet_version; - bool storage_server_version; - bool contributors; - bool total_contributed; - bool total_reserved; - bool staking_requirement; - bool portions_for_operator; - bool swarm_id; - bool operator_address; - bool public_ip; - bool storage_port; - bool storage_lmq_port; - bool quorumnet_port; - bool pubkey_ed25519; - bool pubkey_x25519; - - bool last_uptime_proof; - bool storage_server_reachable; - bool storage_server_last_reachable; - bool storage_server_last_unreachable; - bool storage_server_first_unreachable; - bool belnet_reachable; - bool belnet_last_reachable; - bool belnet_last_unreachable; - bool belnet_first_unreachable; - bool checkpoint_participation; - bool POS_participation; - bool timestamp_participation; - bool timesync_status; - - bool block_hash; - bool height; - bool target_height; - bool hardfork; - bool mnode_revision; - KV_MAP_SERIALIZABLE - }; - - struct request - { - std::vector master_node_pubkeys; // Array of public keys of registered Master Nodes to get information about. Omit to query all Master Nodes. - bool include_json; // When set, the response's as_json member is filled out. - uint32_t limit; // If non-zero, select a random sample (in random order) of the given number of service nodes to return from the full list. - bool active_only; // If true, only include results for active (fully staked, not decommissioned) service nodes. - std::optional fields; // If omitted return all fields; otherwise return only the specified fields - - std::string poll_block_hash; // If specified this changes the behaviour to only return service node records if the block hash is *not* equal to the given hash; otherwise it omits the records and instead sets `"unchanged": true` in the response. This is primarily used to poll for new results where the requested results only change with new blocks. - - KV_MAP_SERIALIZABLE + std::vector validators; + std::vector workers; }; - struct response + struct quorum_for_height { + uint64_t height; + uint8_t quorum_type; + quorum_t quorum; + }; + }; + void to_json(nlohmann::json& j, const GET_QUORUM_STATE::quorum_t& q); + void to_json(nlohmann::json& j, const GET_QUORUM_STATE::quorum_for_height& q); + + /// RPC: master_node/get_master_node_registration_cmd_raw + /// + /// Generates a signed master node registration command for use in the operator's Beldex wallet. + /// This endpoint is primarily for internal use by the `beldexd prepare_registration` command. + /// + /// Inputs: + /// + /// - `args` -- (Developer) The list of arguments used in raw registration, i.e. portions + /// - `make_friendly` -- Provide information about how to use the command in the result. + /// - `staking_requirement` -- The staking requirement to become a Master Node the registration command will be generated upon + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `registration_cmd` -- The command to execute in the wallet CLI to register the queried daemon as a Master Node. + struct GET_MASTER_NODE_REGISTRATION_CMD_RAW : RPC_COMMAND + { + static constexpr auto names() { return NAMES("get_master_node_registration_cmd_raw"); } - struct entry { - std::string master_node_pubkey; // The public key of the Master Node. - uint64_t registration_height; // The height at which the registration for the Master Node arrived on the blockchain. - uint16_t registration_hf_version; // The hard fork at which the registration for the Master Node arrived on the blockchain. - uint64_t requested_unlock_height; // The height at which contributions will be released and the Master Node expires. 0 if not requested yet. - uint64_t last_reward_block_height; // The height that determines when this service node will next receive a reward. This field is updated when receiving a reward, but is also updated when a MN is activated, recommissioned, or has an IP change position reset. - uint32_t last_reward_transaction_index; // When multiple Master Nodes register (or become active/reactivated) at the same height (i.e. have the same last_reward_block_height), this field contains the activating transaction position in the block which is used to break ties in determining which MN is next in the reward list. - bool active; // True if fully funded and not currently decommissioned (and so `active && !funded` implicitly defines decommissioned) - bool funded; // True if the required stakes have been submitted to activate this Master Node - uint64_t state_height; // If active: the state at which the service node became active (i.e. fully staked height, or last recommissioning); if decommissioned: the decommissioning height; if awaiting: the last contribution (or registration) height - uint32_t decommission_count; // The number of times the Master Node has been decommissioned since registration - uint16_t last_decommission_reason_consensus_all; // The reason for the last decommission as voted by all MNs - uint16_t last_decommission_reason_consensus_any; // The reason for the last decommission as voted by any MNs - int64_t earned_downtime_blocks; // The number of blocks earned towards decommissioning, or the number of blocks remaining until deregistration if currently decommissioned - std::array master_node_version; // The major, minor, patch version of the Master Node respectively. - std::array belnet_version; // The major, minor, patch version of the Master Node's belnet router. - std::array storage_server_version; // The major, minor, patch version of the Master Node's storage server. - std::vector contributors; // Array of contributors, contributing to this Master Node. - uint64_t total_contributed; // The total amount of Beldex in atomic units contributed to this Master Node. - uint64_t total_reserved; // The total amount of Beldex in atomic units reserved in this Master Node. - uint64_t staking_requirement; // The staking requirement in atomic units that is required to be contributed to become a Master Node. - uint64_t portions_for_operator; // The operator percentage cut to take from each reward expressed in portions, see cryptonote_config.h's STAKING_PORTIONS. - uint64_t swarm_id; // The identifier of the Master Node's current swarm. - std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to. - std::string public_ip; // The public ip address of the service node - uint16_t storage_port; // The port number associated with the storage server - uint16_t storage_lmq_port; // The port number associated with the storage server (oxenmq interface) - uint16_t quorumnet_port; // The port for direct MN-to-MN communication - std::string pubkey_ed25519; // The service node's ed25519 public key for auxiliary services - std::string pubkey_x25519; // The service node's x25519 public key for auxiliary services - - // Master Node Testing - uint64_t last_uptime_proof; // The last time this Master Node's uptime proof was relayed by at least 1 Master Node other than itself in unix epoch time. - bool storage_server_reachable; // True if this storage server is currently passing tests for the purposes of MN node testing: true if the last test passed, or if it has been unreachable for less than an hour; false if it has been failing tests for more than an hour (and thus is considered unreachable). - uint64_t storage_server_first_unreachable; // If the last test we received was a failure, this field contains the timestamp when failures started. Will be 0 if the last result was a success or the node has not yet been tested. (To disinguish between these cases check storage_server_last_reachable). - uint64_t storage_server_last_unreachable; // The last time this service node's storage server failed a ping test (regardless of whether or not it is currently failing); 0 if it never failed a test since startup. - uint64_t storage_server_last_reachable; // The last time we received a successful ping response for this storage server (whether or not it is currently failing); 0 if we have never received a success since startup. - bool belnet_reachable; // True if this belnet is currently passing tests for the purposes of MN node testing: true if the last test passed, or if it has been unreachable for less than an hour; false if it has been failing tests for more than an hour (and thus is considered unreachable). - uint64_t belnet_first_unreachable; // If the last test we received was a failure, this field contains the timestamp when failures started. Will be 0 if the last result was a success or the node has not yet been tested. (To disinguish between these cases check belnet_last_reachable). - uint64_t belnet_last_unreachable; // The last time this service node's belnet failed a reachable test (regardless of whether or not it is currently failing); 0 if it never failed a test since startup. - uint64_t belnet_last_reachable; // The last time we received a successful test response for this service node's belnet router (whether or not it is currently failing); 0 if we have never received a success since startup. - - std::vector checkpoint_participation; // Of the last N checkpoints the Master Node is in a checkpointing quorum, record whether or not the Master Node voted to checkpoint a block - std::vector POS_participation; // Of the last N POS blocks the Master Node is in a POS quorum, record whether or not the Master Node voted (participated) in that block - std::vector timestamp_participation; // Of the last N timestamp messages, record whether or not the Master Node was in sync with the network - std::vector timesync_status; // Of the last N timestamp messages, record whether or not the Master Node responded - - KV_MAP_SERIALIZABLE - }; - - requested_fields_t fields; // @NoBeldexRPCDocGen Internal use only, not serialized - bool polling_mode; // @NoBeldexRPCDocGen Internal use only, not serialized - - std::vector master_node_states; // Array of service node registration information - uint64_t height; // Current block's height. - uint64_t target_height; // Blockchain's target height. - std::string block_hash; // Current block's hash. - bool unchanged; // Will be true (and `master_node_states` omitted) if you gave the current block hash to poll_block_hash - uint8_t hardfork; // Current hardfork version. - uint8_t mnode_revision; // mnode revision for non-hardfork but mandatory mnode updates - std::string status; // Generic RPC error code. "OK" is the success value. - std::string as_json; // If `include_json` is set in the request, this contains the json representation of the `entry` data structure - - KV_MAP_SERIALIZABLE - - }; + struct request_parameters + { + std::vector args; + bool make_friendly; + uint64_t staking_requirement; + } request; }; - BELDEX_RPC_DOC_INTROSPECT - // Get information on the queried daemon's Master Node state. - struct GET_MASTER_NODE_STATUS : RPC_COMMAND + struct GET_MASTER_NODE_REGISTRATION_CMD : RPC_COMMAND { - static constexpr auto names() { return NAMES("get_master_node_status"); } + static constexpr auto names() { return NAMES("get_master_node_registration_cmd"); } - struct request + struct request_parameters { - bool include_json; // When set, the response's as_json member is filled out. + std::string operator_cut; // The percentage of cut per reward the operator receives expressed as a string, i.e. "1.1%" + std::vector contributor_addresses; + std::vector contributor_amounts; + uint64_t staking_requirement; // The staking requirement to become a Master Node the registration command will be generated upon + } request; + }; + + /// RPC: master_node/get_master_keys + /// + /// Get the master public keys of the queried daemon, encoded in hex. All three keys are used + /// when running as a master node; when running as a regular node only the x25519 key is regularly + /// used for some RPC and and node-to-MN communication requests. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `master_node_pubkey` -- The queried daemon's master node public key. Will be empty if not running as a master node. + /// - `master_node_ed25519_pubkey` -- The daemon's ed25519 auxiliary public key. + /// - `master_node_x25519_pubkey` -- The daemon's x25519 auxiliary public key. + struct GET_MASTER_KEYS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_master_keys", "get_master_node_key"); } + }; - KV_MAP_SERIALIZABLE - }; + /// RPC: master_node/get_master_privkeys + /// + /// Get the master private keys of the queried daemon, encoded in hex. Do not ever share + /// these keys: they would allow someone to impersonate your master node. All three keys are used + /// when running as a master node; when running as a regular node only the x25519 key is regularly + /// used for some RPC and and node-to-MN communication requests. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- General RPC status string. `"OK"` means everything looks good. + /// - `master_node_privkey` -- The queried daemon's master node private key. Will be empty if not running as a master node. + /// - `master_node_ed25519_privkey` -- The daemon's ed25519 private key (note that this is in sodium's format, which consists of the private and public keys concatenated together) + /// - `master_node_x25519_privkey` -- The daemon's x25519 private key. + struct GET_MASTER_PRIVKEYS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_master_privkeys", "get_master_node_privkey"); } + }; - struct response - { - GET_MASTER_NODES::response::entry master_node_state; // Master node registration information - uint64_t height; // Current block's height. - std::string block_hash; // Current block's hash. - std::string status; // Generic RPC error code. "OK" is the success value. - std::string as_json; // If `include_json` is set in the request, this contains the json representation of the `entry` data structure + /// RPC: master_node/get_master_nodes + /// + /// Get information on some, all, or a random subset of Master Nodes. + /// + /// Inputs: + /// - `fields` -- Set of fields to return; listed fields apply to both the top level (such as + /// `"height"` or `"block_hash"`) and to keys inside `master_node_states`. Fields should be + /// provided as a list of field names to include. For backwards compatibility when making a + /// json request field names can also be provided as a dictionary of `{"field_name": true}` + /// pairs, but this usage is deprecated (and not supported for bt-encoded requests). + /// + /// The special field name `"all"` can be used to request all available fields; this is the + /// default when no fields key are provided at all. Be careful when requesting all fields: + /// the response can be very large. + /// + /// When providing a list you may prefix a field name with a `-` (e.g. `"-funded"`) to remove + /// the field from the list; this is mainly useful when following `"all"` to remove some + /// fields from the returned results. (There is no equivalent mode when using the deprecated + /// dict value). + /// + /// - `master_node_pubkeys` -- Array of public keys of registered master nodes to request + /// information about. Omit to query all master nodes. For a JSON request pubkeys must be + /// specified in hex; for a bt-encoded request pubkeys can be hex or bytes. + /// + /// - `active_only` -- If true then only return active master nodes. + /// + /// - `limit` -- If specified and non-zero then only return a random selection of this number of + /// master nodes (in random order) from the result. If negative then no limiting is performed + /// but the returned result is still shuffled. + /// + /// - `poll_block_hash` -- If specified then only return results if the current top block hash is + /// different than the hash given here. This is intended to allow quick polling of results + /// without needing to do anything if the block (and thus SN registrations) have not changed + /// since the last request. + /// + /// Outputs: + /// + /// Output variables available are as follows (you can request which parameters are returned; see + /// the request parameters description). Note that BDX values are returned in atomic BDX units, + /// which are nano-BDX (i.e. 1.000000000 BDX will be returned as 1000000000). + /// + /// - `height` -- the height of the current top block. (Note that this is one less than the + /// "blockchain height" as would be returned by the [`get_info`](#get_info) endpoint). + /// - `target_height` -- the target height of the blockchain; will be greater than height+1 if this + /// node is still syncing the chain. + /// - `block_hash` -- the hash of the most recent block + /// - `hardfork` -- the current hardfork version of the daemon + /// - `mnode_revision` -- the current mnode revision for non-hardfork, but mandatory, master node + /// updates. + /// - `status` -- generic RPC error code; "OK" means the request was successful. + /// - `unchanged` -- when using poll_block_hash, this value is set to true and results are omitted + /// if the current block hash has not changed from the requested polling block hash. If block + /// hash has changed this is set to false (and results included). When not polling this value + /// is omitted entirely. + /// - `master_node_states` -- list of information about all known master nodes; each element is a + /// dict containing the following keys (which fields are included/omitted can be controlled via + /// the "fields" input parameter): + /// - `master_node_pubkey` -- The public key of the Master Node, in hex (json) or binary (bt). + /// - `registration_height` -- The height at which the registration for the Master Node arrived + /// on the blockchain. + /// - `registration_hf_version` -- The current hard fork at which the registration for the Master + /// Node arrived on the blockchain. + /// - `requested_unlock_height` -- If an unlock has been requested for this MN, this field + /// contains the height at which the Master Node registration expires and contributions will + /// be released. + /// - `last_reward_block_height` -- The height that determines when this master node will next + /// receive a reward. This field is somewhat misnamed for historic reasons: it is updated + /// when receiving a reward, but is also updated when a MN is activated, recommissioned, or + /// has an IP change position reset, and so does not strictly indicate when a reward was + /// received. + /// - `last_reward_transaction_index` -- When multiple Master Nodes register (or become + /// active/reactivated) at the same height (i.e. have the same last_reward_block_height), this + /// field contains the activating transaction position in the block which is used to break + /// ties in determining which MN is next in the reward list. + /// - `active` -- True if fully funded and not currently decommissioned (and so `funded and not + /// active` implicitly defines decommissioned). + /// - `funded` -- True if the required stakes have been submitted to activate this Master Node. + /// - `state_height` -- Indicates the height at which the master node entered its current state: + /// - If `active` is true: this is the height at which the master node last became active + /// (i.e. became fully staked, or was last recommissioned); + /// - If decommissioned (i.e. `funded and not active`): the decommissioning height; + /// - If awaiting contributions (i.e. `not funded`): the height at which the last contribution + /// (or registration) was processed. + /// - `decommission_count` -- The number of times the Master Node has been decommissioned since + /// registration + /// - `last_decommission_reason_consensus_all` -- The reason for the last decommission as voted by + /// the testing quorum MNs that decommissioned the node. This is a numeric bitfield made up + /// of the sum of given reasons (multiple reasons may be given for a decommission). Values + /// are included here if *all* quorum members agreed on the reasons: + /// - `0x01` - Missing uptime proofs + /// - `0x02` - Missed too many checkpoint votes + /// - `0x04` - Missed too many POS blocks + /// - `0x08` - Storage server unreachable + /// - `0x10` - Beldexd quorumnet unreachable for timesync checks + /// - `0x20` - Beldexd system clock is too far off + /// - `0x40` - Belnet unreachable + /// - `0x50` - Multi_mn_accept_range_not_met + /// - other bit values are reserved for future use. + /// - `last_decommission_reason_consensus_any` -- The reason for the last decommission as voted + /// by *any* MNs. Reasons are set here if *any* quorum member gave a reason, even if not all + /// quorum members agreed. Bit values are the same as + /// `last_decommission_reason_consensus_all`. + /// - `decomm_reasons` -- a gentler version of the last_decommission_reason_consensus_all/_any + /// values: this is returned as a dict with two keys, `"all"` and `"some"`, containing a list + /// of short string identifiers of the reasons. `"all"` contains reasons that are agreed upon + /// by all voting nodes; `"some"` contains reasons that were provided by some but not all + /// nodes (and is included only if there are at least one such value). Note that, + /// unlike `last_decommission_reason_consensus_any`, the `"some"` field only includes reasons + /// reasons that are *not* included in `"all"`. Returned values in the lists are: + /// - `"uptime"` + /// - `"checkpoints"` + /// - `"POS"` + /// - `"storage"` + /// - `"timecheck"` + /// - `"timesync"` + /// - `"belnet"` + /// - `"multi-mn"` + /// - other values are reserved for future use. + /// - `earned_downtime_blocks` -- The number of blocks earned towards decommissioning (if + /// currently active), or the number of blocks remaining until the master node is eligible + /// for deregistration (if currently decommissioned). + /// - `master_node_version` -- The three-element numeric version of the Master Node (as received + /// in the last uptime proof). Omitted if we have never received a proof. + /// - `belnet_version` -- The major, minor, patch version of the Master Node's belnet router + /// (as received in the last uptime proof). Omitted if we have never received a proof. + /// - `storage_server_version` -- The major, minor, patch version of the Master Node's storage + /// server (as received in the last uptime proof). Omitted if we have never received a proof. + /// - `contributors` -- Array of contributors, contributing to this Master Node. Each element is + /// a dict containing: + /// - `amount` -- The total amount of BDX staked by this contributor into + /// this Master Node. + /// - `reserved` -- The amount of BDX reserved by this contributor for this Master Node; this + /// field will be included only if the contributor has unfilled, reserved space in the + /// master node. + /// - `address` -- The wallet address of this contributor to which rewards are sent and from + /// which contributions were made. + /// - `locked_contributions` -- Array of contributions from this contributor; this field + /// (unlike the other fields inside `contributors`) is controlled by the "fields" input parameter. + /// Each element contains: + /// - `key_image` -- The contribution's key image which is locked on the network. + /// - `key_image_pub_key` -- The contribution's key image, public key component. + /// - `amount` -- The amount of BDX that is locked in this contribution. + /// - `total_contributed` -- The total amount of BDX contributed to this Master Node. + /// - `total_reserved` -- The total amount of BDX contributed or reserved for this Master Node. + /// Only included in the response if there are still unfilled reservations (i.e. if it is + /// greater than total_contributed). + /// - `staking_requirement` -- The total BDX staking requirement in that is/was required to be + /// contributed for this Master Node. + /// - `portions_for_operator` -- The operator fee to take from the master node reward, as a + /// fraction of 18446744073709551612 (2^64 - 4) (that is, this number corresponds to 100%). + /// Note that some JSON parsers may silently change this value while parsing as typical values + /// do not fit into a double without loss of precision. + /// - `operator_fee` -- The operator fee expressed as thousandths of a percent (and rounded to the + /// nearest integer value). That is, 100000 corresponds to a 100% fee, 5456 corresponds to a + /// 5.456% fee. Note that this number is for human consumption; the actual value that matters + /// for the blockchain is the precise `portions_for_operator` value. + /// - `swarm_id` -- The numeric identifier of the Master Node's current swarm. Note that + /// returned values can exceed the precision available in a double value, which can result in + /// (changed) incorrect values by some JSON parsers. Consider using `swarm` instead if you + /// are not sure your JSON parser supports 64-bit values. + /// - `swarm` -- The swarm id, expressed in hexadecimal, such as `"f4ffffffffffffff"`. + /// - `operator_address` -- The wallet address of the Master Node operator. + /// - `public_ip` -- The public ip address of the master node; omitted if we have not yet + /// received a network proof containing this information from the master node. + /// - `storage_port` -- The port number associated with the storage server; omitted if we have no + /// uptime proof yet. + /// - `storage_lmq_port` -- The port number associated with the storage server (oxenmq interface); + /// omitted if we have no uptime proof yet. + /// - `quorumnet_port` -- The port for direct MN-to-MN beldexd communication (oxenmq interface). + /// Omitted if we have no uptime proof yet. + /// - `pubkey_ed25519` -- The master node's ed25519 public key for auxiliary services. Omitted if + /// we have no uptime proof yet. Note that for newer registrations this will be the same as + /// the `master_node_pubkey`. + /// - `pubkey_x25519` -- The master node's x25519 public key for auxiliary services (mainly + /// used for `quorumnet_port` and the `storage_lmq_port` OxenMQ encrypted connections). + /// - `last_uptime_proof` -- The last time we received an uptime proof for this master node from + /// the network, in unix epoch time. 0 if we have never received one. + /// - `storage_server_reachable` -- True if this storage server is currently passing tests for the + /// purposes of MN node testing: true if the last test passed, or if it has been unreachable + /// for less than an hour; false if it has been failing tests for more than an hour (and thus + /// is considered unreachable). This field is omitted if the queried beldexd is not a master + /// node. + /// - `storage_server_first_unreachable` -- If the last test we received was a failure, this field + /// contains the timestamp when failures started. Will be 0 if the last result was a success, + /// and will be omitted if the node has not yet been tested since this beldexd last restarted. + /// - `storage_server_last_unreachable` -- The last time this master node's storage server failed + /// a ping test (regardless of whether or not it is currently failing). Will be omitted if it + /// has never failed a test since startup. + /// - `storage_server_last_reachable` -- The last time we received a successful ping response for + /// this storage server (whether or not it is currently failing). Will be omitted if we have + /// never received a successful ping response since startup. + /// - `belnet_reachable` -- Same as `storage_server_reachable`, but for belnet router testing. + /// - `belnet_first_unreachable` -- Same as `storage_server_first_unreachable`, but for belnet + /// router testing. + /// - `belnet_last_unreachable` -- Same as `storage_server_last_unreachable`, but for belnet + /// router testing. + /// - `belnet_last_reachable` -- Same as `storage_server_last_reachable`, but for belnet router + /// testing. + /// - `checkpoint_votes` -- dict containing recent received checkpoint voting information for this + /// master node. Master node tests will fail if too many recent POS blocks are missed. + /// Contains keys: + /// - `voted` -- list of blocks heights at which a required vote was received from this + /// master node + /// - `missed` -- list of block heights at which a vote from this master node was required + /// but not received. + /// - `POS_votes` -- dict containing recent POS blocks in which this master node was supposed + /// to have participated. Master node testing will fail if too many recent POS blocks are + /// missed. Contains keys: + /// - `voted` -- list of [,] pairs in which an expected POS participation + /// was recorded for this node. starts at 0 and increments for backup POS + /// quorums if a previous round does not broadcast a POS block for the given height in time. + /// - `missed` -- list of [,] pairs in which POS participation by this master node + /// was expected but did not occur. + /// - `quorumnet_tests` -- array containing the results of recent attempts to connect to the + /// remote node's quorumnet port (while conducting timesync checks). The array contains two + /// values: [,], where is the number of recent + /// successful connections and FAILURES is the number of recent connection and/or request + /// timeouts. If there are two many failures then the master node will fail testing. + /// - `timesync_tests` -- array containing the results of recent time synchronization checks of + /// this master node. Contains [,] counts where is the + /// number of recent checks where the system clock was relatively close and is the + /// number of recent checks where we received a significantly out-of-sync timestamp response + /// from the master node. A master node fails tests if there are too many recent + /// out-of-sync responses. + /// + /// Example input: + /// ```json + /// { "limit": 1 } + /// ``` + /// + /// Example-JSON-Fetch + struct GET_MASTER_NODES : PUBLIC + { + static constexpr auto names() { return NAMES("get_master_nodes", "get_n_master_nodes", "get_all_master_nodes"); } - KV_MAP_SERIALIZABLE - }; + struct request_parameters { + std::unordered_set fields; + std::vector master_node_pubkeys; + bool active_only = false; + int limit = 0; + crypto::hash poll_block_hash = crypto::hash::null(); + } request; + + }; + + /// RPC: master_node/get_master_node_status + /// + /// Retrieves information on the current daemon's Master Node state. The returned information is + /// the same as what would be returned by "get_master_nodes" when passed this master node's + /// public key. + /// + /// Inputs: none. + /// + /// Outputs: + /// - `master_node_state` -- if this is a registered master node then all available fields for + /// this master node. See [`get_master_nodes`](#get_master_nodes) for the list of fields. + /// Note that some fields (such as remote testing results) will not be available (through this + /// call or [`get_master_nodes`](#get_master_nodes)) because a master node is incapable of + /// testing itself for remote connectivity. If this daemon is running in master node mode but + /// not registered then only MN pubkey, ip, and port fields are returned. + /// - `height` -- current top block height at the time of the request (note that this is generally + /// one less than the "blockchain height"). + /// - `block_hash` -- current top block hash at the time of the request + /// - `status` -- generic RPC error code; "OK" means the request was successful. + struct GET_MASTER_NODE_STATUS : NO_ARGS + { + static constexpr auto names() { return NAMES("get_master_node_status"); } }; - BELDEX_RPC_DOC_INTROSPECT + /// Dev-RPC: master_node/storage_server_ping + /// + /// Endpoint to receive an uptime ping from the connected storage server. This is used + /// to record whether the storage server is ready before the master node starts + /// sending uptime proofs. This is usually called internally from the storage server + /// and this endpoint is mostly available for testing purposes. + /// + /// Inputs: + /// + /// - `version` -- Storage server version + /// - `https_port` -- Storage server https port to include in uptime proofs. + /// - `omq_port` -- Storage server oxenmq port to include in uptime proofs. + /// - `pubkey_ed25519` -- Master node Ed25519 pubkey for verifying that storage server is running + /// with the correct master node keys. + /// - `error` -- If given and non-empty then this is an error message telling beldexd to *not* + /// submit an uptime proof and to report this error in the logs instead. Beldexd won't send + /// proofs until it gets another ping (without an error). + /// + /// Output: + /// + /// - `status` -- generic RPC error code; "OK" means the request was successful. struct STORAGE_SERVER_PING : RPC_COMMAND { static constexpr auto names() { return NAMES("storage_server_ping"); } - struct request - { - std::array version; // Storage server version - uint16_t https_port; // Storage server https port to include in uptime proofs - uint16_t omq_port; // Storage Server oxenmq port to include in uptime proofs - std::string pubkey_ed25519; // Master node Ed25519 pubkey for verifying that storage server is using the right one - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT + struct request_parameters + { + std::array version; + uint16_t https_port; + uint16_t omq_port; + std::string pubkey_ed25519; + std::string error; + } request; + }; + + /// Dev-RPC: master_node/belnet_ping + /// + /// Endpoint to receive an uptime ping from the connected belnet server. This is used + /// to record whether belnet is ready before the master node starts sending uptime proofs. + /// This is usually called internally from belnet and this endpoint is mostly + /// available for testing purposes. + /// + /// Inputs: + /// + /// - `version` -- Belnet version + /// - `pubkey_ed25519` -- Master node Ed25519 pubkey for verifying that belnet is running with + /// the correct master node keys. + /// - `error` -- If given and non-empty then this is an error message telling beldexd to *not* + /// submit an uptime proof and to report this error in the logs instead. Beldexd won't send + /// proofs until it gets another ping (without an error). + /// + /// Output: + /// + /// - `status` -- generic RPC error code; "OK" means the request was successful. struct BELNET_PING : RPC_COMMAND { static constexpr auto names() { return NAMES("belnet_ping"); } - struct request - { - std::array version; // Belnet version - std::string pubkey_ed25519; // Master node Ed25519 pubkey for verifying that belnet is using the right one - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the required amount of Beldex to become a Master Node at the queried height. - // For devnet and testnet values, ensure the daemon is started with the - // `--devnet` or `--testnet` flags respectively. + struct request_parameters + { + std::array version; + std::string pubkey_ed25519; + std::string error; + } request; + + }; + + /// RPC: master_node/get_staking_requirement + /// + /// Get the required amount of BDX to become a Master Node at the queried height. + /// For devnet and testnet values, ensure the daemon is started with the + /// `--devnet` or `--testnet` flags respectively. + /// + /// Inputs: + /// + /// - `height` -- The height to query the staking requirement for. 0 (or omitting) means current height. + /// + /// Output: + /// + /// - `status` -- generic RPC error code; "OK" means the request was successful. + /// - `staking_requirement` -- The staking requirement in Beldex, in atomic units. + /// - `height` -- The height requested (or current height if 0 was requested) struct GET_STAKING_REQUIREMENT : PUBLIC { static constexpr auto names() { return NAMES("get_staking_requirement"); } - struct request - { - uint64_t height; // The height to query the staking requirement for. 0 (or omitting) means current height. - - KV_MAP_SERIALIZABLE - }; - - struct response + struct request_parameters { - uint64_t staking_requirement; // The staking requirement in Beldex, in atomic units. - uint64_t height; // The height requested (or current height if 0 was requested) - std::string status; // Generic RPC error code. "OK" is the success value. + uint64_t height; + } request; - KV_MAP_SERIALIZABLE - }; }; - BELDEX_RPC_DOC_INTROSPECT - // Get information on blacklisted Master Node key images. - struct GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES : PUBLIC + /// RPC: blockchain/get_master_node_blacklisted_key_images + /// + /// Get information on blacklisted Master Node key images. + /// + /// Inputs: None + /// + /// Output: + /// + /// - `status` -- generic RPC error code; "OK" means the request was successful. + /// - `blacklist` -- Array of blacklisted key images, i.e. unspendable transactions. Each entry contains + /// - `key_image` -- The key image of the transaction that is blacklisted on the network. + /// - `unlock_height` -- The height at which the key image is removed from the blacklist and becomes spendable. + /// - `amount` -- The total amount of locked Beldex in atomic units in this blacklisted stake. + struct GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES : NO_ARGS { static constexpr auto names() { return NAMES("get_master_node_blacklisted_key_images"); } - - struct request : EMPTY {}; - - struct entry - { - std::string key_image; // The key image of the transaction that is blacklisted on the network. - uint64_t unlock_height; // The height at which the key image is removed from the blacklist and becomes spendable. - uint64_t amount; // The total amount of locked Beldex in atomic units in this blacklisted stake. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::vector blacklist; // Array of blacklisted key images, i.e. unspendable transactions - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get information on output blacklist. - struct GET_OUTPUT_BLACKLIST : PUBLIC, BINARY - { - static constexpr auto names() { return NAMES("get_output_blacklist.bin"); } - struct request : EMPTY {}; - - struct response - { - std::vector blacklist; // (Developer): Array of indexes from the global output list, corresponding to blacklisted key images. - std::string status; // Generic RPC error code. "OK" is the success value. - bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. - - KV_MAP_SERIALIZABLE - }; }; - BELDEX_RPC_DOC_INTROSPECT - // Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints. + /// RPC: blockchain/get_checkpoints + /// + /// Query hardcoded/master node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints. + /// + /// Inputs: + /// + /// - `start_height` -- Optional: Get the first count checkpoints starting from this height. Specify both start and end to get the checkpoints inbetween. + /// - `end_height` -- Optional: Get the first count checkpoints before end height. Specify both start and end to get the checkpoints inbetween. + /// - `count` -- Optional: Number of checkpoints to query. + /// + /// Output: + /// + /// - `status` -- generic RPC error code; "OK" means the request was successful. + /// - `checkpoints` -- Array of requested checkpoints + /// + /// Example input: + /// ```json + /// { "count": 2 } + /// ``` + /// + /// Example-JSON-Fetch struct GET_CHECKPOINTS : PUBLIC { static constexpr auto names() { return NAMES("get_checkpoints"); } - static constexpr size_t MAX_COUNT = 256; + static constexpr uint32_t MAX_COUNT = 256; static constexpr uint32_t NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT = 60; - static constexpr uint64_t HEIGHT_SENTINEL_VALUE = std::numeric_limits::max() - 1; - struct request - { - uint64_t start_height; // Optional: Get the first count checkpoints starting from this height. Specify both start and end to get the checkpoints inbetween. - uint64_t end_height; // Optional: Get the first count checkpoints before end height. Specify both start and end to get the checkpoints inbetween. - uint32_t count; // Optional: Number of checkpoints to query. - - KV_MAP_SERIALIZABLE - }; - - struct quorum_signature_serialized - { - uint16_t voter_index; // Index of the voter in the relevant quorum - std::string signature; // The signature generated by the voter in the quorum - - quorum_signature_serialized() = default; - quorum_signature_serialized(master_nodes::quorum_signature const &entry) - : voter_index(entry.voter_index) - , signature(tools::type_to_hex(entry.signature)) { } - - KV_MAP_SERIALIZABLE - - BEGIN_SERIALIZE() // NOTE: For store_t_to_json - FIELD(voter_index) - FIELD(signature) - END_SERIALIZE() - }; - - struct checkpoint_serialized - { - uint8_t version; - std::string type; // Either "Hardcoded" or "MasterNode" for checkpoints generated by Master Nodes or declared in the code - uint64_t height; // The height the checkpoint is relevant for - std::string block_hash; // The block hash the checkpoint is specifying - std::vector signatures; // Signatures from Master Nodes who agree on the block hash - uint64_t prev_height; // The previous height the checkpoint is based off - - checkpoint_serialized() = default; - checkpoint_serialized(checkpoint_t const &checkpoint) - : version(checkpoint.version) - , type(checkpoint_t::type_to_string(checkpoint.type)) - , height(checkpoint.height) - , block_hash(tools::type_to_hex(checkpoint.block_hash)) - , prev_height(checkpoint.prev_height) - { - signatures.reserve(checkpoint.signatures.size()); - for (master_nodes::quorum_signature const &entry : checkpoint.signatures) - signatures.push_back(entry); - } - - KV_MAP_SERIALIZABLE - - BEGIN_SERIALIZE() // NOTE: For store_t_to_json - FIELD(version) - FIELD(type) - FIELD(height) - FIELD(block_hash) - FIELD(signatures) - FIELD(prev_height) - END_SERIALIZE() - }; - - struct response - { - std::vector checkpoints; // Array of requested checkpoints - std::string status; // Generic RPC error code. "OK" is the success value. - bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints. - struct GET_MN_STATE_CHANGES : PUBLIC + struct request_parameters + { + std::optional start_height; + std::optional end_height; + std::optional count; + } request; + }; + + /// RPC: blockchain/get_master_nodes_state_changes + /// + /// Query the number of master node state change transactions contained in a range of blocks. + /// + /// Inputs: + /// + /// - `start_height` -- The starting block's height. + /// - `end_height` -- The ending block's height. + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `untrusted` -- If the result is obtained using bootstrap mode then this will be set to true, otherwise will be omitted. + /// - `total_deregister` -- the total number of master node deregistrations + /// - `total_ip_change_penalty` -- the total number of IP change penalties + /// - `total_decommission` -- the total number of master node decommissions + /// - `total_recommission` -- the total number of master node recommissions + /// - `total_unlock` -- the total number of master node unlock requests + /// - `start_height` -- the start height of the given statistics + /// - `end_height` -- the end height of the given statistics + /// + /// Example input: + /// ```json + /// { "start_height": 1085000, "end_height": 1085191 } + /// ``` + /// + /// Example-JSON-Fetch + struct GET_MN_STATE_CHANGES : RPC_COMMAND { static constexpr auto names() { return NAMES("get_master_nodes_state_changes"); } - static constexpr uint64_t HEIGHT_SENTINEL_VALUE = std::numeric_limits::max() - 1; - struct request - { - uint64_t start_height; - uint64_t end_height; // Optional: If omitted, the tally runs until the current block - - KV_MAP_SERIALIZABLE - }; - - struct response + struct request_parameters { - std::string status; // Generic RPC error code. "OK" is the success value. - bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`. - - uint32_t total_deregister; - uint32_t total_ip_change_penalty; - uint32_t total_decommission; - uint32_t total_recommission; - uint32_t total_unlock; uint64_t start_height; - uint64_t end_height; - - KV_MAP_SERIALIZABLE - }; - }; - - - BELDEX_RPC_DOC_INTROSPECT - // Reports service node peer status (success/fail) from belnet and storage server. + std::optional end_height; + } request; + }; + + /// Dev-RPC: master_node/report_peer_status + /// + /// Reports master node peer status (success/fail) from belnet and storage server. + /// + /// Inputs: + /// + /// - `type` -- test type; currently supported are: "storage" and "belnet" for storage server and belnet tests, respectively. + /// - `pubkey` -- master node pubkey + /// - `passed` -- whether node is passing the test + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. struct REPORT_PEER_STATUS : RPC_COMMAND { // TODO: remove the `report_peer_storage_server_status` once we require a storage server version // that stops using the old name. static constexpr auto names() { return NAMES("report_peer_status", "report_peer_storage_server_status"); } - struct request + struct request_parameters { - std::string type; // test type; currently supported are: "storage" and "belnet" for storage server and belnet tests, respectively. - std::string pubkey; // service node pubkey - bool passed; // whether the node is passing the test - - KV_MAP_SERIALIZABLE - }; - - struct response : STATUS {}; + std::string type; + std::string pubkey; + bool passed; + } request; }; - // Deliberately undocumented; this RPC call is really only useful for testing purposes to reset - // the resync idle timer (which normally fires every 60s) for the test suite. - struct TEST_TRIGGER_P2P_RESYNC : RPC_COMMAND + /// Dev-RPC: daemon/test_trigger_p2p_resync + /// + /// Deliberately undocumented; this RPC call is really only useful for testing purposes to reset + /// the resync idle timer (which normally fires every 60s) for the test suite. + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + struct TEST_TRIGGER_P2P_RESYNC : NO_ARGS { static constexpr auto names() { return NAMES("test_trigger_p2p_resync"); } - - struct request : EMPTY {}; - struct response : STATUS {}; }; - struct TEST_TRIGGER_UPTIME_PROOF : RPC_COMMAND + /// Dev-RPC: master_node/test_trigger_uptime_proof + /// + /// Deliberately undocumented; this RPC call is really only useful for testing purposes to + /// force send an uptime proof. NOT available on mainnet + /// + /// Inputs: none + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + struct TEST_TRIGGER_UPTIME_PROOF : NO_ARGS { static constexpr auto names() { return NAMES("test_trigger_uptime_proof"); } - struct request : EMPTY {}; - struct response : STATUS {}; }; - BELDEX_RPC_DOC_INTROSPECT // Get the name mapping for a Beldex Name Service entry. Beldex currently supports mappings - // for Bchat and Belnet. + // for Bchat,Wallet,eth_address and Belnet. struct BNS_NAMES_TO_OWNERS : PUBLIC { static constexpr auto names() { return NAMES("bns_names_to_owners", "lns_names_to_owners"); } static constexpr size_t MAX_REQUEST_ENTRIES = 256; - struct request - { - std::vector entries; // Entries to look up - bool include_expired; // Optional: if provided and true, include entries in the results even if they are expired - - KV_MAP_SERIALIZABLE - }; - - struct response_entry - { - uint64_t entry_index; // The index in request_entry's `entries` array that was resolved via Beldex Name Service. - std::string name_hash; // The hash of the name that was queried, in base64 - std::string owner; // The public key that purchased the Beldex Name Service entry. - std::optional backup_owner; // The backup public key that the owner specified when purchasing the Beldex Name Service entry. Omitted if no backup owner. - std::string encrypted_bchat_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::string encrypted_wallet_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::string encrypted_belnet_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::string encrypted_eth_addr_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - uint64_t update_height; // The last height that this Beldex Name Service entry was updated on the Blockchain. - std::optional expiration_height;// For records that expire, this will be set to the expiration block height. - std::string txid; // The txid of the mapping's most recent update or purchase. - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::vector entries; - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get the name mapping for a Beldex Name Service entry. Beldex currently supports mappings - // for Bchat and Belnet and wallet. - struct BNS_LOOKUP : PUBLIC - { - static constexpr auto names() { return NAMES("bns_lookup"); } - - static constexpr size_t MAX_REQUEST_ENTRIES = 256; - struct request - { - std::string name; // Entries to look up - KV_MAP_SERIALIZABLE - }; - struct response + struct request_parameters { - std::string name_hash; // The hash of the name that was queried, in base64 - std::string owner; // The public key that purchased the Beldex Name Service entry. - std::optional backup_owner; // The backup public key that the owner specified when purchasing the Beldex Name Service entry. Omitted if no backup owner. - std::optional bchat_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::optional wallet_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::optional belnet_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - std::optional eth_addr_value; // The encrypted value that the name maps to. See the `BNS_RESOLVE` description for information on how this value can be decrypted. - uint64_t update_height; // The last height that this Beldex Name Service entry was updated on the Blockchain. - std::optional expiration_height;// For records that expire, this will be set to the expiration block height. - std::string txid; // The txid of the mapping's most recent update or purchase. - - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Get all the name mappings for the queried owner. The owner can be either a ed25519 public key or Monero style - // public key; by default purchases are owned by the spend public key of the purchasing wallet. + std::vector name_hash; // The 32-byte BLAKE2b hash of the name to resolve to a public key via Beldex Name Service. The value must be provided either in hex (64 hex digits) or base64 (44 characters with padding, or 43 characters without). + bool include_expired; // Optional: if provided and true, include entries in the results even if they are expired + } request; + }; + + /// RPC: bns/bns_owners_to_names + /// + /// Get all the name mappings for the queried owner. The owner can be either a ed25519 public key or Monero style + /// public key; by default purchases are owned by the spend public key of the purchasing wallet. + /// + /// Inputs: + /// + /// - `entries` -- List of owner's public keys to find all Beldex Name Service entries for. + /// - `include_expired` -- Optional: if provided and true, include entries in the results even if they are expired + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `entries` -- List of BNS names. Each element is structured as follows: + /// - `request_index` -- (Deprecated) The index in request's `entries` array that was resolved via Beldex Name Service. + /// - `type` -- The category the Beldex Name Service entry belongs to; currently 0 for Bchat, 1 for Wallet, 2 for Belnet and 3 Eth Address . + /// - `name_hash` -- The hash of the name that the owner purchased via Beldex Name Service in base64 + /// - `owner` -- The backup public key specified by the owner that purchased the Beldex Name Service entry. + /// - `backup_owner` -- The backup public key specified by the owner that purchased the Beldex Name Service entry. Omitted if no backup owner. + /// - `encrypted_bchat_value` -- The encrypted Bchat value that the name maps to, in hex. This value is encrypted using the name (not the hash) as the secret. + /// - `encrypted_wallet_value` -- The encrypted Wallet value that the name maps to, in hex. This value is encrypted using the name (not the hash) as the secret. + /// - `encrypted_belnet_value` -- The encrypted Belnet value that the name maps to, in hex. This value is encrypted using the name (not the hash) as the secret. + /// - `encrypted_eth_addr_value` -- The encrypted Eth Address value that the name maps to, in hex. This value is encrypted using the name (not the hash) as the secret. + /// - `update_height` -- The last height that this Beldex Name Service entry was updated on the Blockchain. + /// - `expiration_height` -- For records that expire, this will be set to the expiration block height. + /// - `txid` -- The txid of the mapping's most recent update or purchase. struct BNS_OWNERS_TO_NAMES : PUBLIC { static constexpr auto names() { return NAMES("bns_owners_to_names", "lns_owners_to_names"); } static constexpr size_t MAX_REQUEST_ENTRIES = 256; - struct request + struct request_parameters { - std::vector entries; // The owner's public key to find all Beldex Name Service entries for. - bool include_expired; // Optional: if provided and true, include entries in the results even if they are expired - - KV_MAP_SERIALIZABLE - }; + std::vector entries; + bool include_expired; + } request; struct response_entry { - uint64_t request_index; // (Deprecated) The index in request's `entries` array that was resolved via Beldex Name Service. - std::string name_hash; // The hash of the name that the owner purchased via Beldex Name Service in base64 - std::string owner; // The backup public key specified by the owner that purchased the Beldex Name Service entry. - std::optional backup_owner; // The backup public key specified by the owner that purchased the Beldex Name Service entry. Omitted if no backup owner. - std::string encrypted_bchat_value; // The bchat encrypted value that the name maps to, in hex. This value of bchat is encrypted using the name (not the hash) as the secret. - std::string encrypted_wallet_value; // The wallet encrypted value that the name maps to, in hex. This value of wallet is encrypted using the name (not the hash) as the secret. - std::string encrypted_belnet_value; // The belnet encrypted value that the name maps to, in hex. This value of belnet is encrypted using the name (not the hash) as the secret. - std::string encrypted_eth_addr_value; // The eth_address encrypted value that the name maps to, in hex. This value of eth_address is encrypted using the name (not the hash) as the secret. - uint64_t update_height; // The last height that this Beldex Name Service entry was updated on the Blockchain. - std::optional expiration_height;// For records that expire, this will be set to the expiration block height. - std::string txid; // The txid of the mapping's most recent update or purchase. - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::vector entries; - std::string status; // Generic RPC error code. "OK" is the success value. - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Performs a simple BNS lookup of a BLAKE2b-hashed name. This RPC method is meant for simple, - // single-value resolutions that do not care about registration details, etc.; if you need more - // information use BNS_NAMES_TO_OWNERS instead. - // - // Technical details: the returned value is encrypted using the name itself so that neither this - // beldexd responding to the RPC request nor any other blockchain observers can (easily) obtain the - // name of registered addresses or the registration details. Thus, from a client's point of view, - // resolving an BNS record involves: - // - // - Lower-case the name. - // - Calculate the name hash as a null-key, 32-byte BLAKE2b hash of the lower-case name. - // - Obtain the encrypted value and the nonce from this RPC call (or BNS_NAMES_TO_OWNERS); (encode - // the name hash using either hex or base64.). - // - Calculate the decryption key as a 32-byte BLAKE2b keyed hash of the name using the - // (unkeyed) name hash calculated above as the hash key. - // - Decrypt (and verify) using XChaCha20-Poly1305 (for example libsodium's - // crypto_aead_xchacha20poly1305_ietf_decrypt) using the above decryption key and using the - // first 24 bytes of the name hash as the public nonce. + uint64_t request_index; + std::string name_hash; + std::string owner; + std::optional backup_owner; + std::string encrypted_bchat_value; + std::string encrypted_wallet_value; + std::string encrypted_belnet_value; + std::string encrypted_eth_addr_value; + uint64_t update_height; + std::optional expiration_height; + std::string txid; + }; + }; + void to_json(nlohmann::json& j, const BNS_OWNERS_TO_NAMES::response_entry& r); + + /// RPC: bns/bns_resolve + /// + /// Performs a simple BNS lookup of a BLAKE2b-hashed name. This RPC method is meant for simple, + /// single-value resolutions that do not care about registration details, etc.; if you need more + /// information use BNS_NAMES_TO_OWNERS instead. + /// + /// Inputs: + /// + /// - `type` -- The BNS type (mandatory); currently supported values are: 0 = bchat, 1 = wallet, 2 = belnet, 6 = eth_addr. + /// - `name_hash` -- The 32-byte BLAKE2b hash of the name to look up, encoded as 64 hex digits or + /// 44/43 base64 characters (with/without padding). For bt-encoded requests this can also be + /// the raw 32 bytes. + /// + /// Outputs: + /// + /// - `encrypted_value` -- The encrypted BNS value, in hex. Will be omitted from the response if + /// the given name_hash is not registered. + /// - `nonce` -- The nonce value used for encryption, in hex. Will be omitted if the given name is + /// not registered. + /// + /// Technical details: the returned value is encrypted using the name itself so that neither this + /// beldexd responding to the RPC request nor any other blockchain observers can (easily) obtain the + /// name of registered addresses or the registration details. Thus, from a client's point of view, + /// resolving an BNS record involves: + /// + /// 1. Lower-case the name. + /// 2. Calculate the name hash as a null-key, 32-byte BLAKE2b hash of the lower-case name. + /// 3. Obtain the encrypted value and the nonce from this RPC call (or BNS_NAMES_TO_OWNERS); when + /// using json encode the name hash using either hex or base64. + /// 4. Calculate the decryption key as a 32-byte BLAKE2b *keyed* hash of the name using the + /// (unkeyed) name hash calculated above (in step 2) as the hash key. + /// 5. Decrypt (and verify) using XChaCha20-Poly1305 (for example libsodium's + /// crypto_aead_xchacha20poly1305_ietf_decrypt) using the above decryption key and using the + /// first 24 bytes of the name hash as the public nonce. struct BNS_RESOLVE : PUBLIC { static constexpr auto names() { return NAMES("bns_resolve", "lns_resolve"); } - struct request - { - uint16_t type; // The BNS type (mandatory); currently supported values are: 0 = bchat, 1 = wallet, 2 = belnet. 6=eth_addr - std::string name_hash; // The 32-byte BLAKE2b hash of the name to look up, encoded as 64 hex digits or 44/43 base64 characters (with/without padding). - - KV_MAP_SERIALIZABLE - }; + struct request_parameters { + int type = -1; ///< The BNS type (mandatory); currently supported values are: 0 = bchat, 1 = wallet, 2 = belnet, 6=eth_addr. + std::string name_hash; ///< The 32-byte BLAKE2b hash of the name to look up, encoded as 64 hex digits or 44/43 base64 characters (with/without padding). For bt-encoded requests this can also be the raw 32 bytes. + } request; + }; + + /// Get the name mapping for a Beldex Name Service entry. Beldex currently supports mappings + /// for Bchat, Belnet, eth-address and wallet. + /// Inputs: + /// + /// - `name` -- bns-name for the lookup + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `name_hash` -- The hash of the name that the owner purchased via Beldex Name Service in base64 + /// - `owner` -- The backup public key specified by the owner that purchased the Beldex Name Service entry. + /// - `backup_owner` -- The backup public key specified by the owner that purchased the Beldex Name Service entry. Omitted if no backup owner. + /// - `bchat_value` -- The decrypted Bchat value that the name maps to, in string. + /// - `wallet_value` -- The decrypted Wallet value that the name maps to, in string. + /// - `belnet_value` -- The decrypted Belnet value that the name maps to, in string. + /// - `eth_addr_value` -- The decrypted Eth Address value that the name maps to, in string. + /// - `update_height` -- The last height that this Beldex Name Service entry was updated on the Blockchain. + /// - `expiration_height` -- For records that expire, this will be set to the expiration block height. + /// - `txid` -- The txid of the mapping's most recent update or purchase. + struct BNS_LOOKUP : PUBLIC + { + static constexpr auto names() { return NAMES("bns_lookup"); } - struct response + struct request_parameters { - std::optional encrypted_value; // The encrypted BNS value, in hex. Will be omitted from the response if the given name_hash is not registered. - std::optional nonce; // The nonce value used for encryption, in hex. + std::string name; // Entries to look up + }request; - KV_MAP_SERIALIZABLE - }; }; - BELDEX_RPC_DOC_INTROSPECT - // Takes a BNS encrypted value and decrypts the mapping value using the BNS name. + /// Takes a BNS encrypted value and decrypts the mapping value using the BNS name. + /// Inputs: + /// + /// - `name` -- The BNS name of the given encrypted value. + /// - `type` -- The mapping type: "bchat" or "belnet" or "wallet" or "eth_addr". + /// - `encrypted_value` -- // The encrypted value represented in hex. + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. + /// - `value` -- The decrypted value that the name maps to, in string. + struct BNS_VALUE_DECRYPT : PUBLIC { static constexpr auto names() { return NAMES("bns_value_decrypt"); } - struct request + struct request_parameters { std::string name; // The BNS name of the given encrypted value. std::string type; // The mapping type: "bchat" or "belnet" or "wallet". std::string encrypted_value; // The encrypted value represented in hex. - - KV_MAP_SERIALIZABLE - }; - - struct response - { - std::string value; // The value decrypted - - KV_MAP_SERIALIZABLE - }; - }; - - BELDEX_RPC_DOC_INTROSPECT - // Clear TXs from the daemon cache, currently only the cache storing TX hashes that were previously verified bad by the daemon. + }request; + }; + + /// RPC: daemon/flush_cache + /// + /// Clear TXs from the daemon cache, currently only the cache storing TX hashes that were previously verified bad by the daemon. + /// + /// Inputs: + /// + /// - `bad_txs` -- Clear the cache storing TXs that failed verification. + /// - `bad_blocks` -- Clear the cache storing blocks that failed verfication. + /// + /// Output: + /// + /// - `status` -- Generic RPC error code. "OK" is the success value. struct FLUSH_CACHE : RPC_COMMAND { static constexpr auto names() { return NAMES("flush_cache"); } - struct request + struct request_parameter { - bool bad_txs; // Clear the cache storing TXs that failed verification. - bool bad_blocks; // Clear the cache storing blocks that failed verfication. - KV_MAP_SERIALIZABLE; - }; - - struct response : STATUS { }; + bool bad_txs; + bool bad_blocks; + } request; }; - /// List of all supported rpc command structs to allow compile-time enumeration of all supported - /// RPC types. Every type added above that has an RPC endpoint needs to be added here, and needs - /// a core_rpc_server::invoke() overload that takes a ::request and returns a - /// ::response. The ::request has to be unique (for overload resolution); - /// ::response does not. + // List of all supported rpc command structs to allow compile-time enumeration of all supported + // RPC types. Every type added above that has an RPC endpoint needs to be added here, and needs + // a core_rpc_server::invoke() overload that takes a ::request and returns a + // ::response. The ::request has to be unique (for overload resolution); + // ::response does not. using core_rpc_types = tools::type_list< + BANNED, + FLUSH_CACHE, + FLUSH_TRANSACTION_POOL, + GET_ALTERNATE_CHAINS, + GET_BANS, + GET_FEE_ESTIMATE, + GET_BLOCK, + GET_BLOCK_COUNT, + GET_BLOCK_HASH, + GET_BLOCK_HEADERS_RANGE, + GET_BLOCK_HEADER_BY_HASH, + GET_BLOCK_HEADER_BY_HEIGHT, + GET_CHECKPOINTS, + GET_COINBASE_TX_SUM, + GET_CONNECTIONS, GET_HEIGHT, - GET_BLOCKS_FAST, - GET_BLOCKS_BY_HEIGHT, - GET_ALT_BLOCKS_HASHES, - GET_HASHES_FAST, - GET_TRANSACTIONS, - IS_KEY_IMAGE_SPENT, - GET_TX_GLOBAL_OUTPUTS_INDEXES, - GET_OUTPUTS_BIN, - GET_OUTPUTS, - SEND_RAW_TX, - START_MINING, - STOP_MINING, - MINING_STATUS, GET_INFO, - GET_NET_STATS, - SAVE_BC, - GETBLOCKCOUNT, - GETBLOCKHASH, - GETBLOCKTEMPLATE, - SUBMITBLOCK, - GENERATEBLOCKS, GET_LAST_BLOCK_HEADER, - GET_BLOCK_HEADER_BY_HASH, - GET_BLOCK_HEADER_BY_HEIGHT, - GET_BLOCK, + GET_LIMIT, + GET_NET_STATS, + GET_OUTPUTS, + GET_OUTPUT_HISTOGRAM, GET_PEER_LIST, - GET_PUBLIC_NODES, - SET_LOG_HASH_RATE, - SET_LOG_LEVEL, - SET_LOG_CATEGORIES, + GET_QUORUM_STATE, + GET_MASTER_KEYS, + GET_MASTER_NODES, + GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES, + GET_MASTER_NODE_REGISTRATION_CMD, + GET_MASTER_NODE_REGISTRATION_CMD_RAW, + GET_MASTER_NODE_STATUS, + GET_MASTER_PRIVKEYS, + GET_MN_STATE_CHANGES, + GET_STAKING_REQUIREMENT, + GET_TRANSACTIONS, GET_TRANSACTION_POOL, - GET_TRANSACTION_POOL_HASHES_BIN, GET_TRANSACTION_POOL_HASHES, - GET_TRANSACTION_POOL_BACKLOG, GET_TRANSACTION_POOL_STATS, - GET_CONNECTIONS, - GET_BLOCK_HEADERS_RANGE, - SET_BOOTSTRAP_DAEMON, - STOP_DAEMON, - GET_LIMIT, - SET_LIMIT, - OUT_PEERS, - IN_PEERS, - HARD_FORK_INFO, - GETBANS, - SETBANS, - BANNED, - FLUSH_TRANSACTION_POOL, - GET_OUTPUT_HISTOGRAM, GET_VERSION, - GET_COINBASE_TX_SUM, - GET_BASE_FEE_ESTIMATE, - GET_ALTERNATE_CHAINS, - RELAY_TX, - SYNC_INFO, + HARD_FORK_INFO, + IN_PEERS, + IS_KEY_IMAGE_SPENT, + BELNET_PING, + MINING_STATUS, + BNS_OWNERS_TO_NAMES, + BNS_NAMES_TO_OWNERS, + BNS_RESOLVE, + BNS_LOOKUP, + BNS_VALUE_DECRYPT, + OUT_PEERS, GET_OUTPUT_DISTRIBUTION, - GET_OUTPUT_DISTRIBUTION_BIN, POP_BLOCKS, PRUNE_BLOCKCHAIN, - GET_QUORUM_STATE, - GET_MASTER_NODE_REGISTRATION_CMD_RAW, - GET_MASTER_NODE_REGISTRATION_CMD, - GET_MASTER_KEYS, - GET_MASTER_PRIVKEYS, - GET_MASTER_NODES, - GET_MASTER_NODE_STATUS, - STORAGE_SERVER_PING, - BELNET_PING, - GET_STAKING_REQUIREMENT, - GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES, - GET_OUTPUT_BLACKLIST, - GET_CHECKPOINTS, - GET_MN_STATE_CHANGES, REPORT_PEER_STATUS, + SAVE_BC, + SET_BANS, + SET_LIMIT, + SET_LOG_CATEGORIES, + SET_LOG_LEVEL, + START_MINING, + STOP_DAEMON, + STOP_MINING, + STORAGE_SERVER_PING, + SUBMIT_TRANSACTION, + SYNC_INFO, TEST_TRIGGER_P2P_RESYNC, TEST_TRIGGER_UPTIME_PROOF, - BNS_NAMES_TO_OWNERS, - BNS_LOOKUP, - BNS_OWNERS_TO_NAMES, - BNS_RESOLVE, - BNS_VALUE_DECRYPT, - FLUSH_CACHE + SET_BOOTSTRAP_DAEMON, + RELAY_TX >; - -} } // namespace cryptonote::rpc +} // namespace cryptonote::rpc diff --git a/src/rpc/http_client.cpp b/src/rpc/http_client.cpp index ac9e9c7ae78..d65b34a4baa 100755 --- a/src/rpc/http_client.cpp +++ b/src/rpc/http_client.cpp @@ -1,9 +1,10 @@ #include "http_client.h" #include #include -#include #include "common/string_util.h" -#include "cpr/ssl_options.h" +#include +#include +#include #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "rpc.http_client" @@ -130,6 +131,36 @@ void http_client::copy_params_from(const http_client& other) { auth = other.auth; } +nlohmann::json http_client::json_rpc(std::string_view method, std::optional params) { + nlohmann::json shell{ + {"id", json_rpc_id++}, + {"jsonrpc", "2.0"}, + {"method", method} + }; + if (params) + shell["params"] = std::move(*params); + + // std::cout << "shell.dump() : " << shell.dump() << std::endl; //TODO will remove + cpr::Response res = post("json_rpc", shell.dump(), {{"Content-Type", "application/json; charset=utf-8"}}); + + nlohmann::json result; + try { + auto response = nlohmann::json::parse(res.text); + + if (auto err = response.find("error"); err != response.end()) + throw http_client_response_error{false, (*err)["code"].get(), + "JSON RPC returned an error response: " + (*err)["message"].get()}; + + // std::cout << "response : " << response << std::endl; //TODO will remove + if (auto res = response.find("result"); res != response.end()) + result = std::move(*res); + + } catch (const nlohmann::json::parse_error& e) { + throw http_client_serialization_error{"Failed to deserialize response for json_rpc request for " + std::string{method} + ": " + e.what()}; + } + + return result; +} cpr::Response http_client::post(const std::string& uri, cpr::Body body, cpr::Header header) { if (base_url.str().empty()) diff --git a/src/rpc/http_client.h b/src/rpc/http_client.h index ffa92172d4f..588b1d7857f 100755 --- a/src/rpc/http_client.h +++ b/src/rpc/http_client.h @@ -17,6 +17,8 @@ #include #include +#include + namespace cryptonote::rpc { using namespace std::literals; @@ -143,6 +145,24 @@ class http_client /// Copies parameters (base url, timeout, authentication) from another http_client. void copy_params_from(const http_client& other); + /// Makes a JSON-RPC request; that is, a POST request to /json_rpc with a proper JSON-RPC wrapper + /// around some json data as the body. On a successful response the returned json body is + /// returned. + /// + /// \param method - the end-point to be passed as the "method" parameter of the JSON-RPC request. + /// \param params - the JSON to be sent as the JSON-RPC "params" value. Omit (or pass nullopt) to + /// omit the params value entirely. + /// + /// \returns nlohmann::json of the inner "result" field of the response, on success. + /// + /// \throws rpc::http_client_error on connection-related failure + /// \throws rpc::http_client_serialization_error on a serialization failure + /// \throws rpc::http_client_response_error on a successful HTTP request that returns a json_rpc + /// error, or on an HTTP request that returns an HTTP error code. + nlohmann::json json_rpc(std::string_view method, std::optional params = std::nullopt); + + /// FIXME: drop this. + /// /// Makes a JSON-RPC request; that is, a POST request to /json_rpc with a proper JSON-RPC wrapper /// around the serialized json data as the body. On a successful response the response is /// deserialized into a RPC::response which is returned. diff --git a/src/rpc/http_server.cpp b/src/rpc/http_server.cpp index 4ad5d6d67c1..8abe7d24816 100755 --- a/src/rpc/http_server.cpp +++ b/src/rpc/http_server.cpp @@ -2,8 +2,8 @@ #include "http_server.h" #include #include +#include #include -#include #include #include "common/command_line.h" #include "common/string_util.h" @@ -11,7 +11,7 @@ #include "cryptonote_core/cryptonote_core.h" #include "epee/net/jsonrpc_structs.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "rpc/rpc_args.h" +#include "rpc/common/rpc_args.h" #include "version.h" #undef BELDEX_DEFAULT_LOG_CATEGORY @@ -90,7 +90,7 @@ namespace cryptonote::rpc { : m_server{server}, m_restricted{restricted} { // uWS is designed to work from a single thread, which is good (we pull off the requests and - // then stick them into the LMQ job queue to be scheduled along with other jobs). But as a + // then stick them into the OMQ job queue to be scheduled along with other jobs). But as a // consequence, we need to create everything inside that thread. We *also* need to get the // (thread local) event loop pointer back from the thread so that we can shut it down later // (injecting a callback into it is one of the few thread-safe things we can do across threads). @@ -170,6 +170,7 @@ namespace cryptonote::rpc { error_response(*res, HTTP_FORBIDDEN); }; + //note: rpc_commands is a pseudo-global in core_rpc_server.h for (auto& [name, call] : rpc_commands) { if (call->is_legacy || call->is_binary) { if (!call->is_public && m_restricted) @@ -209,7 +210,7 @@ namespace cryptonote::rpc { bool aborted{false}; bool replied{false}; bool jsonrpc{false}; - std::string jsonrpc_id; // pre-formatted json value + nlohmann::json jsonrpc_id{nullptr}; std::vector> extra_headers; // Extra headers to send // If we have to drop the request because we are overloaded we want to reply with an error (so @@ -219,7 +220,7 @@ namespace cryptonote::rpc { if (replied || aborted) return; http.loop_defer([&http=http, &res=res, jsonrpc=jsonrpc] { if (jsonrpc) - http.jsonrpc_error_response(res, -32003, "Server busy, try again later"); + http.jsonrpc_error_response(res, -32003, "Server busy, try again later", nullptr); else http.error_response(res, http_server::HTTP_SERVICE_UNAVAILABLE, "Server busy, try again later"); }); @@ -247,9 +248,8 @@ namespace cryptonote::rpc { } }; - // Queues a response for the HTTP thread to handle; the response can be in multiple string pieces - // to be concatenated together. - void queue_response(std::shared_ptr data, std::vector body) + // Queues a response for the HTTP thread to handle + void queue_response(std::shared_ptr data, std::string body) { auto& http = data->http; data->replied = true; @@ -263,27 +263,15 @@ namespace cryptonote::rpc { if (data->http.closing()) res.writeHeader("Connection", "close"); for (const auto& [name, value] : data->extra_headers) res.writeHeader(name, value); - - for (const auto& piece : body) - res.write(piece); - - res.end(); + res.end(body); if (data->http.closing()) res.close(); }); }); } - // Wrapper around the above that takes a single string - void queue_response(std::shared_ptr data, std::string body) - { - std::vector b; - b.push_back(std::move(body)); - queue_response(std::move(data), std::move(b)); - } - void invoke_txpool_hashes_bin(std::shared_ptr data); - // Invokes the actual RPC request; this is called (via lokimq) from some random LMQ worker thread, + // Invokes the actual RPC request; this is called (via lokimq) from some random OMQ worker thread, // which means we can't just write our reply; instead we have to post it to the uWS loop. void invoke_rpc(std::shared_ptr dataptr) { @@ -302,21 +290,20 @@ namespace cryptonote::rpc { if (time_logging) start = std::chrono::steady_clock::now(); - std::vector result; - result.reserve(data.jsonrpc ? 3 : 1); - if (data.jsonrpc) - { - result.emplace_back(R"({"jsonrpc":"2.0","id":)"); - result.back() += data.jsonrpc_id; - result.back() += R"(,"result":)"; - } - int json_error = -32603; std::string json_message = "Internal error"; std::string http_message; + std::string result; try { - result.push_back(data.call->invoke(std::move(data.request), data.core_rpc)); + auto r = data.call->invoke(std::move(data.request), data.core_rpc); + if (data.jsonrpc) + result = nlohmann::json{{"jsonrpc", "2.0"}, {"id", data.jsonrpc_id}, {"result", var::get(std::move(r))}}.dump(); + else if (auto* json = std::get_if(&r)) + result = json->dump(); + else + result = var::get(std::move(r)); + // And throw if we get back a bt_value because we don't accept that at all json_error = 0; } catch (const parse_error& e) { // This isn't really WARNable as it's the client fault; log at info level instead. @@ -338,24 +325,18 @@ namespace cryptonote::rpc { if (json_error != 0) { data.http.loop_defer([data=std::move(dataptr), json_error, msg=std::move(data.jsonrpc ? json_message : http_message)] { if (data->jsonrpc) - data->jsonrpc_error_response(data->res, json_error, msg); + data->jsonrpc_error_response(data->res, json_error, msg, data->jsonrpc_id); else data->error_response(data->res, http_server::HTTP_ERROR, msg.empty() ? std::nullopt : std::make_optional(msg)); }); return; } - if (data.jsonrpc) - result.emplace_back("}\n"); - std::string call_duration; if (time_logging) call_duration = " in " + tools::friendly_duration(std::chrono::steady_clock::now() - start); - if (LOG_ENABLED(Info)) { - size_t bytes = 0; - for (const auto& r : result) bytes += r.size(); - MINFO("HTTP RPC " << data.uri << " [" << data.request.context.remote << "] OK (" << bytes << " bytes)" << call_duration); - } + if (LOG_ENABLED(Info)) + MINFO("HTTP RPC " << data.uri << " [" << data.request.context.remote << "] OK (" << result.size() << " bytes)" << call_duration); queue_response(std::move(dataptr), std::move(result)); } @@ -499,7 +480,7 @@ namespace cryptonote::rpc { { std::shared_ptr data{new call_data{*this, m_server, res, std::string{req.getUrl()}, &call}}; auto& request = data->request; - request.body = ""s; + request.body = std::monostate{}; request.context.admin = !m_restricted; request.context.source = rpc_source::http; request.context.remote = get_remote_address(res); @@ -508,7 +489,12 @@ namespace cryptonote::rpc { res.onAborted([data] { data->aborted = true; }); res.onData([data=std::move(data)](std::string_view d, bool done) mutable { - var::get(data->request.body) += d; + if (!d.empty()) { + if (std::holds_alternative(data->request.body)) + data->request.body = std::string{d}; + else + var::get(data->request.body) += d; + } if (!done) return; @@ -543,51 +529,44 @@ namespace cryptonote::rpc { else body = (buffer += d); - auto& [ps, st_entry] = var::get(data->request.body = jsonrpc_params{}); - if(!ps.load_from_json(body)) - return data->jsonrpc_error_response(data->res, -32700, "Parse error"); - - epee::serialization::storage_entry id{std::string{}}; - ps.get_value("id", id, nullptr); + nlohmann::json jsonrpc; + try { + jsonrpc = nlohmann::json::parse(body); + } catch (const std::exception& e) { + return data->jsonrpc_error_response(data->res, -32700, "Parse error", nullptr); + } - std::string method; - if(!ps.get_value("method", method, nullptr)) - { + data->jsonrpc_id = std::move(jsonrpc["id"]); + const std::string* method; + try { + method = &jsonrpc["method"].get_ref(); + } catch (const std::exception& e) { MINFO("Invalid JSON RPC request from " << data->request.context.remote << ": no 'method' in request"); - return data->jsonrpc_error_response(data->res, -32600, "Invalid Request", id); + return data->jsonrpc_error_response(data->res, -32600, "Invalid Request", data->jsonrpc_id); } - auto it = rpc_commands.find(method); - if (it == rpc_commands.end() || it->second->is_binary) - { - MINFO("Invalid JSON RPC request from " << data->request.context.remote << ": method '" << method << "' is invalid"); - return data->jsonrpc_error_response(data->res, -32601, "Method not found", id); + if (auto it = rpc_commands.find(*method); + it != rpc_commands.end() && !it->second->is_binary) + data->call = it->second.get(); + else { + MINFO("Invalid JSON RPC request from " << data->request.context.remote << ": method '" << *method << "' is invalid"); + return data->jsonrpc_error_response(data->res, -32601, "Method not found", data->jsonrpc_id); } - data->call = it->second.get(); if (restricted && !data->call->is_public) { - MWARNING("Invalid JSON RPC request from " << data->request.context.remote << ": method '" << method << "' is restricted"); - return data->jsonrpc_error_response(data->res, 403, "Forbidden; this command is not available over public RPC", id); + MWARNING("Invalid JSON RPC request from " << data->request.context.remote << ": method '" << *method << "' is restricted"); + return data->jsonrpc_error_response(data->res, 403, "Forbidden; this command is not available over public RPC", data->jsonrpc_id); } - MDEBUG("Incoming JSON RPC request for " << method << " from " << data->request.context.remote); - - { - std::ostringstream o; - epee::serialization::dump_as_json(o, id, 0 /*indent*/, false /*newlines*/); - data->jsonrpc_id = o.str(); - } + MDEBUG("Incoming JSON RPC request for " << *method << " from " << data->request.context.remote); - // Try to load "params" into a generic epee value; if it fails (because there is no "params") - // then we replace request.body with an empty string (instead of the epee jsonrpc_params - // alternative) to signal that no params were provided at all. - if (!ps.get_value("params", st_entry, nullptr)) - data->request.body = ""sv; + if (auto it = jsonrpc.find("params"); it != jsonrpc.end()) + data->request.body = *it; auto& omq = data->core_rpc.get_core().get_omq(); std::string cat{data->call->is_public ? "rpc" : "admin"}; - std::string cmd{"jsonrpc:" + method}; // Used for LMQ job logging; prefixed with jsonrpc: so we can distinguish it + std::string cmd{"jsonrpc:" + *method}; // Used for OMQ job logging; prefixed with jsonrpc: so we can distinguish it std::string remote{data->request.context.remote}; omq.inject_task(std::move(cat), std::move(cmd), std::move(remote), [data=std::move(data)] { invoke_rpc(std::move(data)); }); }); @@ -602,7 +581,7 @@ namespace cryptonote::rpc { auto net = m_server.nettype(); m_server_header = "beldexd/"s + (m_restricted ? std::to_string(BELDEX_VERSION[0]) : std::string{BELDEX_VERSION_FULL}) - + (net == MAINNET ? " mainnet" : net == TESTNET ? " testnet" : net == DEVNET ? " devnet" : net == FAKECHAIN ? " fakenet" : " unknown net"); + + (net == network_type::MAINNET ? " mainnet" : net == network_type::TESTNET ? " testnet" : net == network_type::DEVNET ? " devnet" : net == network_type::FAKECHAIN ? " fakenet" : " unknown net"); m_startup_promise.set_value(true); m_sent_startup = true; diff --git a/src/rpc/http_server.h b/src/rpc/http_server.h index 23753a268ff..9fed69d81d0 100755 --- a/src/rpc/http_server.h +++ b/src/rpc/http_server.h @@ -34,8 +34,8 @@ #include "common/command_line.h" #include "common/password.h" #include "core_rpc_server.h" -#include "http_server_base.h" -#include "rpc/rpc_args.h" +#include "rpc/common/http_server_base.h" +#include "rpc/common/rpc_args.h" namespace cryptonote::rpc { diff --git a/src/rpc/lmq_server.cpp b/src/rpc/omq_server.cpp similarity index 66% rename from src/rpc/lmq_server.cpp rename to src/rpc/omq_server.cpp index aadd1f63793..2e84dcdfb39 100755 --- a/src/rpc/lmq_server.cpp +++ b/src/rpc/omq_server.cpp @@ -1,6 +1,9 @@ -#include "lmq_server.h" +#include "omq_server.h" +#include "rpc/common/param_parser.hpp" +#include "cryptonote_config.h" #include "oxenmq/oxenmq.h" +#include "oxenc/bt.h" #include #undef BELDEX_DEFAULT_LOG_CATEGORY @@ -48,7 +51,7 @@ const command_line::arg_descriptor> arg_omq_local_contr #ifndef _WIN32 const command_line::arg_descriptor arg_omq_umask{ "lmq-umask", - "Sets the umask to apply to any listening ipc:///path/to/sock LMQ sockets, in octal.", + "Sets the umask to apply to any listening ipc:///path/to/sock OMQ sockets, in octal.", "0007"}; #endif @@ -66,21 +69,20 @@ auto as_x_pubkeys(const std::vector& pk_strings) { pks.reserve(pk_strings.size()); for (const auto& pkstr : pk_strings) { if (pkstr.size() != 64 || !oxenc::is_hex(pkstr)) - throw std::runtime_error("Invalid LMQ login pubkey: '" + pkstr + "'; expected 64-char hex pubkey"); + throw std::runtime_error("Invalid OMQ login pubkey: '" + pkstr + "'; expected 64-char hex pubkey"); pks.emplace_back(); oxenc::to_hex(pkstr.begin(), pkstr.end(), reinterpret_cast(&pks.back())); } return pks; } -// LMQ RPC responses consist of [CODE, DATA] for code we (partially) mimic HTTP error codes: 200 +// OMQ RPC responses consist of [CODE, DATA] for code we (partially) mimic HTTP error codes: 200 // means success, anything else means failure. (We don't have codes for Forbidden or Not Found -// because those happen at the LMQ protocol layer). +// because those happen at the OMQ protocol layer). constexpr std::string_view - LMQ_OK{"200"sv}, - LMQ_BAD_REQUEST{"400"sv}, - LMQ_ERROR{"500"sv}; - + OMQ_OK{"200"sv}, + OMQ_BAD_REQUEST{"400"sv}, + OMQ_ERROR{"500"sv}; } // end anonymous namespace @@ -107,21 +109,21 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog // the quorumnet listener set up in cryptonote_core). for (const auto &addr : command_line::get_arg(vm, arg_omq_public)) { check_omq_listen_addr(addr); - MGINFO("LMQ listening on " << addr << " (public unencrypted)"); + MGINFO("OMQ listening on " << addr << " (public unencrypted)"); omq.listen_plain(addr, [&core](std::string_view ip, std::string_view pk, bool /*mn*/) { return core.omq_allow(ip, pk, AuthLevel::basic); }); } for (const auto &addr : command_line::get_arg(vm, arg_omq_curve_public)) { check_omq_listen_addr(addr); - MGINFO("LMQ listening on " << addr << " (public curve)"); + MGINFO("OMQ listening on " << addr << " (public curve)"); omq.listen_curve(addr, [&core](std::string_view ip, std::string_view pk, bool /*mn*/) { return core.omq_allow(ip, pk, AuthLevel::basic); }); } for (const auto &addr : command_line::get_arg(vm, arg_omq_curve)) { check_omq_listen_addr(addr); - MGINFO("LMQ listening on " << addr << " (curve restricted)"); + MGINFO("OMQ listening on " << addr << " (curve restricted)"); omq.listen_curve(addr, [&core](std::string_view ip, std::string_view pk, bool /*mn*/) { return core.omq_allow(ip, pk, AuthLevel::denied); }); } @@ -133,10 +135,10 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog // enough to support unix domain sockets, but for now the Windows default is just "don't listen" #ifndef _WIN32 // Push default .beldex/beldexd.sock - locals.push_back("ipc://" + core.get_config_directory().u8string() + "/" + CRYPTONOTE_NAME + "d.sock"); + locals.push_back("ipc://" + core.get_config_directory().u8string() + "/" + std::string{cryptonote::SOCKET_FILENAME}); // Pushing old default beldexd.sock onto the list. A symlink from .beldex -> .beldex so the user should be able // to communicate via the old .beldex/beldexd.sock - locals.push_back("ipc://" + core.get_config_directory().u8string() + "/beldexd.sock"); + locals.push_back("ipc://" + core.get_config_directory().u8string() + "/" + std::string{cryptonote::SOCKET_FILENAME}); #endif } else if (locals.size() == 1 && locals[0] == "none") { locals.clear(); @@ -191,17 +193,29 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog omq.add_request_command(cmd.second->is_public ? "rpc" : "admin", cmd.first, [name=std::string_view{cmd.first}, &call=*cmd.second, this](oxenmq::Message& m) { if (m.data.size() > 1) - m.send_reply(LMQ_BAD_REQUEST, "Bad request: RPC commands must have at most one data part " + m.send_reply(OMQ_BAD_REQUEST, "Bad request: RPC commands must have at most one data part " "(received " + std::to_string(m.data.size()) + ")"); rpc_request request{}; request.context.admin = m.access.auth >= AuthLevel::admin; request.context.source = rpc_source::omq; request.context.remote = m.remote; - request.body = m.data.empty() ? ""sv : m.data[0]; + if (!m.data.empty()) + request.body = m.data[0]; try { - m.send_reply(LMQ_OK, call.invoke(std::move(request), rpc_)); + auto result = std::visit([](auto&& v) -> std::string { + using T = decltype(v); + if constexpr (std::is_same_v) + return bt_serialize(std::move(v)); + else if constexpr (std::is_same_v) + return v.dump(); + else { + static_assert(std::is_same_v); + return std::move(v); + } + }, call.invoke(std::move(request), rpc_)); + m.send_reply(OMQ_OK, std::move(result)); return; } catch (const parse_error& e) { // This isn't really WARNable as it's the client fault; log at info level instead. @@ -210,27 +224,32 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog // warnings that get generated deep inside epee, for example when passing a string or // number instead of a JSON object. If you want to find some, `grep number2 epee` (for // real). - MINFO("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' called with invalid/unparseable data: " << e.what()); - m.send_reply(LMQ_BAD_REQUEST, "Unable to parse request: "s + e.what()); + MINFO("OMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' called with invalid/unparseable data: " << e.what()); + MDEBUG("Bad request body:" << m.data.empty() ? "(empty)" : m.data[0]); + m.send_reply(OMQ_BAD_REQUEST, "Unable to parse request: "s + e.what()); return; } catch (const rpc_error& e) { - MWARNING("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' failed with: " << e.what()); - m.send_reply(LMQ_ERROR, e.what()); + MWARNING("OMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' failed with: " << e.what()); + m.send_reply(OMQ_ERROR, e.what()); return; } catch (const std::exception& e) { - MWARNING("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' " + MWARNING("OMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' " "raised an exception: " << e.what()); } catch (...) { - MWARNING("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' " + MWARNING("OMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' " "raised an unknown exception"); } // Don't include the exception message in case it contains something that we don't want go // back to the user. If we want to support it eventually we could add some sort of // `rpc::user_visible_exception` that carries a message to send back to the user. - m.send_reply(LMQ_ERROR, "An exception occured while processing your request"); + m.send_reply(OMQ_ERROR, "An exception occured while processing your request"); }); } + omq.add_request_command("rpc", "get_blocks", [this](oxenmq::Message& m) { + on_get_blocks(m); + }); + // Subscription commands // The "subscribe" category is for public subscriptions; i.e. anyone on a public RPC node, or @@ -255,38 +274,7 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog // txblob are binary: in particular, txhash is *not* hex-encoded. // omq.add_request_command("sub", "mempool", [this](oxenmq::Message& m) { - - if (m.data.size() != 1) { - m.send_reply("Invalid subscription request: no subscription type given"); - return; - } - - mempool_sub_type sub_type; - if (m.data[0] == "flash"sv) - sub_type = mempool_sub_type::flash; - else if (m.data[0] == "all"sv) - sub_type = mempool_sub_type::all; - else { - m.send_reply("Invalid mempool subscription type '" + std::string{m.data[0]} + "'"); - return; - } - - { - std::unique_lock lock{subs_mutex_}; - auto expiry = std::chrono::steady_clock::now() + 30min; - auto result = mempool_subs_.emplace(m.conn, mempool_sub{expiry, sub_type}); - if (!result.second) { - result.first->second.expiry = expiry; - if (result.first->second.type == sub_type) { - MTRACE("Renewed mempool subscription request from conn id " << m.conn << " @ " << m.remote); - m.send_reply("ALREADY"); - return; - } - result.first->second.type = sub_type; - } - MDEBUG("New " << (sub_type == mempool_sub_type::flash ? "flash" : "all") << " mempool subscription request from conn " << m.conn << " @ " << m.remote); - m.send_reply("OK"); - } + on_mempool_sub_request(m); }); // New block subscriptions: [sub.block]. This sends a notification every time a new block is @@ -301,17 +289,7 @@ omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog // containing the latest height/hash. (Note that blockhash is the hash in bytes, *not* the hex // encoded block hash). omq.add_request_command("sub", "block", [this](oxenmq::Message& m) { - std::unique_lock lock{subs_mutex_}; - auto expiry = std::chrono::steady_clock::now() + 30min; - auto result = block_subs_.emplace(m.conn, block_sub{expiry}); - if (!result.second) { - result.first->second.expiry = expiry; - MTRACE("Renewed block subscription request from conn id " << m.conn << " @ " << m.remote); - m.send_reply("ALREADY"); - } else { - MDEBUG("New block subscription request from conn " << m.conn << " @ " << m.remote); - m.send_reply("OK"); - } + on_block_sub_request(m); }); core_.get_blockchain_storage().hook_block_post_add([this] (const auto& info) { send_block_notifications(info.block); return true; }); @@ -374,5 +352,196 @@ void omq_rpc::send_mempool_notifications(const crypto::hash& id, const transacti }); } +void omq_rpc::on_get_blocks(oxenmq::Message& m) +{ + if (m.data.size() == 0) + { + m.send_reply("Invalid rpc.get_blocks request: no parameters given."); + return; + } + + if (m.data[0].front() != 'd') + { + m.send_reply("Invalid rpc.get_blocks request: parameters must be bt-encoded."); + return; + } + + uint64_t start_height; + uint64_t max_count; + uint64_t size_limit; + try + { + get_values(m.data[0], + "max_count", required{max_count}, + "size_limit", required{size_limit}, + "start_height", required{start_height}); + } + catch (const std::exception& e) + { + m.send_reply(std::string("Invalid rpc.get_blocks request: ") + e.what()); + } + + size_limit = std::min(size_limit, 2000000); + + auto chain_height = core_.get_current_blockchain_height(); + if (start_height > chain_height) + { + m.send_reply("Invalid rpc.get_blocks request: start_height given is above current chain height."); + return; + } + + size_t message_size = 128; // initial size conservative overhead assumption + + auto end = chain_height; + if (max_count != 0) + end = std::min(start_height + max_count, chain_height); + + using bt_list = oxenc::bt_list; + using bt_dict = oxenc::bt_dict; + + std::vector bt_blocks; + + uint64_t i; + for (i = start_height; i < end; i++) + { + bt_dict block_bt; + + auto hash = core_.get_block_id_by_height(i); + block b; + if (!core_.get_block_by_height(i, b)) + { + m.send_reply("Unknown error fetching blocks."); + return; + } + + block_bt["hash"] = std::string_view{hash.data, sizeof(hash.data)}; + block_bt["height"] = i; + block_bt["timestamp"] = b.timestamp; + + std::vector txs; + core_.get_transactions(b.tx_hashes, txs); + if (txs.size() != b.tx_hashes.size()) + { + m.send_reply("Unknown error fetching transactions."); + return; + } + + bt_list tx_list_bt; + + std::vector indices; + + { + bt_dict tx_bt; + + crypto::hash miner_tx_hash; + cryptonote::get_transaction_hash(b.miner_tx, miner_tx_hash, nullptr); + + if (not core_.get_tx_outputs_gindexs(miner_tx_hash, indices)) + { + m.send_reply("Unknown error fetching output info."); + return; + } + + tx_bt["global_indices"] = bt_list(indices.begin(), indices.end()); + tx_bt["hash"] = std::string{miner_tx_hash.data, sizeof(miner_tx_hash.data)}; + tx_bt["tx"] = tx_to_blob(b.miner_tx); + + tx_list_bt.push_back(std::move(tx_bt)); + } + + for (size_t tx_index = 0; tx_index < txs.size(); tx_index++) + { + bt_dict tx_bt; + + indices.clear(); + + const auto& txhash = b.tx_hashes[tx_index]; + + if (not core_.get_tx_outputs_gindexs(txhash, indices)) + { + m.send_reply("Unknown error fetching output info."); + return; + } + + tx_bt["global_indices"] = bt_list(indices.begin(), indices.end()); + tx_bt["hash"] = std::string{txhash.data, sizeof(txhash.data)}; + tx_bt["tx"] = std::move(txs[tx_index]); + + tx_list_bt.push_back(std::move(tx_bt)); + } + + block_bt["transactions"] = std::move(tx_list_bt); + + auto block_str = oxenc::bt_serialize(block_bt); + size_t sz = block_str.size() + 16; // conservative estimate of 16 bytes wire overhead per block + + if (message_size + sz > size_limit) + { + // i is checked after loop to signal "end of chain", so decrement if we don't add the block + i--; + break; + } + + bt_blocks.push_back(std::move(block_str)); + } + + std::string status = "OK"; + if (i == chain_height) + status = "END"; + else if (bt_blocks.empty()) + status = "TOO BIG"; + + m.send_reply(status, oxenmq::send_option::data_parts(bt_blocks)); +} + +void omq_rpc::on_mempool_sub_request(oxenmq::Message& m) +{ + if (m.data.size() != 1) { + m.send_reply("Invalid subscription request: no subscription type given"); + return; + } + + mempool_sub_type sub_type; + if (m.data[0] == "flash"sv) + sub_type = mempool_sub_type::flash; + else if (m.data[0] == "all"sv) + sub_type = mempool_sub_type::all; + else { + m.send_reply("Invalid mempool subscription type '" + std::string{m.data[0]} + "'"); + return; + } + + { + std::unique_lock lock{subs_mutex_}; + auto expiry = std::chrono::steady_clock::now() + 30min; + auto result = mempool_subs_.emplace(m.conn, mempool_sub{expiry, sub_type}); + if (!result.second) { + result.first->second.expiry = expiry; + if (result.first->second.type == sub_type) { + MTRACE("Renewed mempool subscription request from conn id " << m.conn << " @ " << m.remote); + m.send_reply("ALREADY"); + return; + } + result.first->second.type = sub_type; + } + MDEBUG("New " << (sub_type == mempool_sub_type::flash ? "flash" : "all") << " mempool subscription request from conn " << m.conn << " @ " << m.remote); + m.send_reply("OK"); + } +} + +void omq_rpc::on_block_sub_request(oxenmq::Message& m) +{ + std::unique_lock lock{subs_mutex_}; + auto expiry = std::chrono::steady_clock::now() + 30min; + auto result = block_subs_.emplace(m.conn, block_sub{expiry}); + if (!result.second) { + result.first->second.expiry = expiry; + MTRACE("Renewed block subscription request from conn id " << m.conn << " @ " << m.remote); + m.send_reply("ALREADY"); + } else { + MDEBUG("New block subscription request from conn " << m.conn << " @ " << m.remote); + m.send_reply("OK"); + } +} }} // namespace cryptonote::rpc diff --git a/src/rpc/lmq_server.h b/src/rpc/omq_server.h similarity index 92% rename from src/rpc/lmq_server.h rename to src/rpc/omq_server.h index aa3ebfde4e3..f21838fe835 100755 --- a/src/rpc/lmq_server.h +++ b/src/rpc/omq_server.h @@ -40,7 +40,7 @@ namespace cryptonote::rpc { void init_omq_options(boost::program_options::options_description& desc); /** - * LMQ RPC server class. This doesn't actually hold the OxenMQ instance--that's in + * OMQ RPC server class. This doesn't actually hold the OxenMQ instance--that's in * cryptonote_core--but it works with it to add RPC endpoints, make it listen on RPC ports, and * handles RPC requests. */ @@ -68,6 +68,13 @@ class omq_rpc final{ void send_block_notifications(const block& block); void send_mempool_notifications(const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts); + +private: + void on_get_blocks(oxenmq::Message& m); + + void on_mempool_sub_request(oxenmq::Message& m); + + void on_block_sub_request(oxenmq::Message& m); }; } // namespace cryptonote::rpc diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 4ecc6181126..726fc2799ef 100755 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include "base.h" @@ -57,27 +57,6 @@ static_assert(-1 == ~0, "Non 2s-complement architecture not supported!"); using binary_variant_tag_type = uint8_t; -// RAII class for `begin_array()`. This particular implementation is a no-op. -template -struct binary_archive_nested_array { - Archive& ar; - - // Call before writing an element to add a delimiter. (For binary_archive this is a no-op). - // Returns the archive itself, allowing you to write: - // - // auto arr = ar.begin_array(); - // for (auto& val : whatever) - // value(arr.element(), val); - // - Archive& element() { return ar; } - ~binary_archive_nested_array() {} // Explicitly empty constructor to silent unused variable warnings -}; - -// Do-nothing object for the RAII `begin_object` interface. -struct binary_archive_nested_object { - ~binary_archive_nested_object() {} // As above. -}; - /* \struct binary_unarchiver * * \brief the deserializer class for a binary archive @@ -108,7 +87,7 @@ class binary_unarchiver : public deserializer { stream_.read(reinterpret_cast(&v), sizeof(T)); if constexpr (sizeof(T) > 1) - boost::endian::little_to_native_inplace(v); + oxenc::little_to_host_inplace(v); } /// Serializes binary data of a given size by reading it directly into the given buffer @@ -132,23 +111,28 @@ class binary_unarchiver : public deserializer throw std::runtime_error{"deserialization of varint failed"}; } + // RAII class for `begin_array()`/`begin_object()`. This particular implementation is a no-op. + struct nested { + ~nested() {}; // Avoids unused variable warnings + }; + // Reads array size into s and returns an RAII object to help delimit and end it. - [[nodiscard]] binary_archive_nested_array begin_array(size_t& s) + [[nodiscard]] nested begin_array(size_t& s) { serialize_varint(s); - return {*this}; + return {}; } // Begins a sizeless array (this requires that the size is provided by some other means). - [[nodiscard]] binary_archive_nested_array begin_array() + [[nodiscard]] nested begin_array() { - return {*this}; + return {}; } // Does nothing. (This is used for tag annotations for archivers such as json) void tag(std::string_view) { } - [[nodiscard]] binary_archive_nested_object begin_object() { return {}; } + [[nodiscard]] nested begin_object() { return {}; } void read_variant_tag(binary_variant_tag_type &t) { serialize_int(t); @@ -211,7 +195,7 @@ class binary_archiver : public serializer void serialize_int(T v) { if constexpr (sizeof(T) > 1) - boost::endian::native_to_little_inplace(v); + oxenc::host_to_little_inplace(v); stream_.write(reinterpret_cast(&v), sizeof(T)); } @@ -232,24 +216,29 @@ class binary_archiver : public serializer tools::write_varint(std::ostreambuf_iterator{stream_}, v); } + // RAII class for `begin_array()`/`begin_object()`. This particular implementation is a no-op. + struct nested { + ~nested() {}; // Avoids unused variable warnings + }; + // Begins an array and returns an RAII object that is used to delimit array elements. For // binary_archiver the size is written when the array begins, and the RAII is a no-op. - [[nodiscard]] binary_archive_nested_array begin_array(size_t& s) + [[nodiscard]] nested begin_array(size_t& s) { serialize_varint(s); - return {*this}; + return {}; } // Begins a sizeless array. (Typically requires that size be stored some other way). - [[nodiscard]] binary_archive_nested_array begin_array() + [[nodiscard]] nested begin_array() { - return {*this}; + return {}; } // Does nothing. (This is used for tag annotations for archivers such as json) void tag(std::string_view) { } - [[nodiscard]] binary_archive_nested_object begin_object() { return {}; } + [[nodiscard]] nested begin_object() { return {}; } void write_variant_tag(binary_variant_tag_type t) { serialize_int(t); } diff --git a/src/serialization/boost_std_variant.h b/src/serialization/boost_std_variant.h index 13b95fbddd5..072cea03bb3 100755 --- a/src/serialization/boost_std_variant.h +++ b/src/serialization/boost_std_variant.h @@ -1,5 +1,8 @@ #pragma once +#include + +#if BOOST_VERSION < 108400 // Adapts boost::serialization to support std::variant, serializing just as a boost::variant would // be serialized (so that the serialized boost::variant and std::variant values are // interchangeable). @@ -50,3 +53,5 @@ inline void serialize(Archive& ar, std::variant& v, const unsigned int fil } } // namespace boost::serialization + +#endif diff --git a/src/serialization/container.h b/src/serialization/container.h index 4d32f903c7b..68abf394595 100755 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -101,7 +101,6 @@ void serialize_container(Archive& ar, C& v) static_assert(detail::has_emplace_back || detail::has_value_insert, "Unsupported container type"); for (size_t i = 0; i < cnt; i++) { - arr.element(); if constexpr (detail::has_emplace_back) detail::serialize_container_element(ar, v.emplace_back()); else { @@ -119,7 +118,7 @@ void serialize_container(Archive& ar, C& v) size_t cnt = v.size(); auto arr = ar.begin_array(cnt); for (auto& e : v) - serialize_container_element(arr.element(), e); + serialize_container_element(ar, e); } } // namespace detail diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index ae05435878d..b51d57ff505 100755 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,5 +1,5 @@ // Copyright (c) 2018-2020, The Beldex Project -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2022, The Monero Project // // All rights reserved. // @@ -38,19 +38,17 @@ #include "serialization.h" #include "base.h" +#include "common/json_binary_proxy.h" #include -#include -#include #include -#include +#include +#include namespace serialization { -using json_variant_tag_type = std::string_view; - /*! \struct json_archiver * - * \brief a archive using the JSON standard + * \brief serialize data to JSON via nlohmann::json * * \detailed there is no deserializing counterpart; we only support JSON serializing here. */ @@ -58,151 +56,112 @@ struct json_archiver : public serializer { using variant_tag_type = std::string_view; - json_archiver(std::ostream& s, bool indent = false) - : stream_{s}, indent_{indent} - { - exc_restore_ = stream_.exceptions(); - stream_.exceptions(std::istream::badbit | std::istream::failbit | std::istream::eofbit); - } + explicit json_archiver(tools::json_binary_proxy::fmt bin_format = tools::json_binary_proxy::fmt::hex) + : bin_format_{bin_format} {} - ~json_archiver() { stream_.exceptions(exc_restore_); } + /// Returns the current nlohmann::json. + const nlohmann::json& json() const& { return top_; } + nlohmann::json&& json() && { return std::move(top_); } - void tag(std::string_view tag) { - if (!object_begin) - stream_ << (indent_ ? ", "sv : ","sv); - make_indent(); - stream_ << '"' << tag << (indent_ ? "\": "sv : "\":"); + /// Dumps the current nlohmann::json; arguments are forwarded to nlohmann::json::dump() + template + auto dump(T&&... args) const { + return top_.dump(std::forward(args)...); + } - object_begin = false; + // Sets the tag for the next object value we will write. + void tag(std::string_view tag) { + tag_ = tag; } - struct nested_object { + struct nested_value { json_archiver& ar; - ~nested_object() { - --ar.depth_; - ar.make_indent(); - ar.stream_ << '}'; + ~nested_value() { + assert(ar.stack_.size() >= 2); + ar.stack_.pop_back(); } - nested_object(const nested_object&) = delete; - nested_object& operator=(const nested_object&) = delete; - nested_object(nested_object&&) = delete; - nested_object& operator=(nested_object&&) = delete; + nested_value(const nested_value&) = delete; + nested_value& operator=(const nested_value&) = delete; + nested_value(nested_value&&) = delete; + nested_value& operator=(nested_value&&) = delete; }; - [[nodiscard]] nested_object begin_object() + [[nodiscard]] nested_value begin_object() { - stream_ << '{'; - ++depth_; - object_begin = true; - return nested_object{*this}; + stack_.emplace_back(set(nlohmann::json::object())); + return {*this}; } - template - static auto promote_to_printable_integer_type(T v) + [[nodiscard]] nested_value begin_array(size_t s=0) { - // Unary operator '+' performs integral promotion on type T [expr.unary.op]. - // If T is signed or unsigned char, it's promoted to int and printed as number. - return +v; + stack_.emplace_back(set(nlohmann::json::array())); + return {*this}; } template void serialize_int(T v) { - stream_ << std::dec << promote_to_printable_integer_type(v); - } - - void serialize_blob(void *buf, size_t len, std::string_view delimiter="\""sv) { - stream_ << delimiter; - auto* begin = static_cast(buf); - oxenc::to_hex(begin, begin + len, std::ostreambuf_iterator{stream_}); - stream_ << delimiter; - } - - template - void serialize_blobs(const std::vector& blobs, std::string_view delimiter="\""sv) { - serialize_blob(blobs.data(), blobs.size()*sizeof(T), delimiter); + set(v); } template void serialize_varint(T &v) { - stream_ << std::dec << promote_to_printable_integer_type(v); + serialize_int(v); } - struct nested_array { - json_archiver& ar; - int exc_count = std::uncaught_exceptions(); - bool first = true; - - // Call before writing an element to add a delimiter. The first element() call adds no - // delimiter. Returns the archive itself, allowing you to write: - // - // auto arr = ar.begin_array(); - // for (auto& val : whatever) - // value(arr.element(), val); - // - json_archiver& element() { - if (first) first = false; - else ar.delimit_array(); - return ar; - } - - ~nested_array() noexcept(false) { - if (std::uncaught_exceptions() == exc_count) { // Normal destruction - --ar.depth_; - if (ar.inner_array_contents_) - ar.make_indent(); - ar.stream_ << ']'; - } - // else we're destructing during a stack unwind so some other serialization failed, thus don't - // try terminating the array (since it might *also* throw if an IO error occurs). - } - - // Non-copyable, non-moveable - nested_array(const nested_array&) = delete; - nested_array& operator=(const nested_array&) = delete; - nested_array(nested_array&&) = delete; - nested_array& operator=(nested_array&&) = delete; - }; - - // Begins an array and returns an RAII object that is used to delimit array elements and - // terminates the array on destruction. - [[nodiscard]] nested_array begin_array(size_t s=0) - { - inner_array_contents_ = s > 0; - ++depth_; - stream_ << '['; - return {*this}; + void serialize_blob(const void *buf, size_t len) { + nlohmann::json val; + tools::json_binary_proxy{val, bin_format_} = std::string_view{static_cast(buf), len}; + set(std::move(val)); } - void delimit_array() { stream_ << (indent_ ? ", "sv : ","sv); } + template + void serialize_blobs(const std::vector& blobs) { + serialize_blob(blobs.data(), blobs.size()*sizeof(T)); + } void write_variant_tag(std::string_view t) { tag(t); } - // Returns the current position (i.e. stream.tellp()) of the output stream. - unsigned int streampos() { return static_cast(stream_.tellp()); } - private: - static constexpr std::string_view indents{" "}; - void make_indent() - { - if (indent_) - { - stream_ << '\n'; - auto in = 2 * depth_; - for (; in > indents.size(); in -= indents.size()) - stream_ << indents; - stream_ << indents.substr(0, in); + nlohmann::json& curr() { + if (stack_.empty()) + return top_; + else + return stack_.back(); + } + + template + nlohmann::json& set(T&& val) { + auto& c = curr(); + if (stack_.empty()) { + c = std::forward(val); + return c; + } + if (c.is_array()) { + c.push_back(std::forward(val)); + return c.back(); } + return (c[tag_] = std::forward(val)); } - std::ostream& stream_; - std::ios_base::iostate exc_restore_; - bool indent_ = false; - bool object_begin = false; - bool inner_array_contents_ = false; - size_t depth_ = 0; + nlohmann::json top_; + std::vector> stack_{}; + tools::json_binary_proxy::fmt bin_format_; + std::string tag_; }; + +/*! serializes the data in v to a string. Throws on error. +*/ +template +std::string dump_json(T& v, int indent = -1) +{ + json_archiver oar; + serialize(oar, v); + return oar.dump(indent); +} + + } // namespace serialization diff --git a/src/serialization/pair.h b/src/serialization/pair.h index 8f02e97a0e4..9c63c70110e 100755 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -58,10 +58,9 @@ void serialize_value(Archive& ar, std::pair& p) if (!Archive::is_serializer && cnt != 2) throw std::runtime_error("Serialization failed: expected pair, found " + std::to_string(cnt) + " values"); - detail::serialize_pair_element(arr.element(), p.first); - detail::serialize_pair_element(arr.element(), p.second); + detail::serialize_pair_element(ar, p.first); + detail::serialize_pair_element(ar, p.second); - ar.end_array(); } } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ccc9997e339..28f8a2faae3 100755 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -37,6 +37,7 @@ #include "common/string_util.h" #include "beldex_economy.h" +#include #include #ifdef _WIN32 #define __STDC_FORMAT_MACROS // NOTE(beldex): Explicitly define the PRIu64 macro on Mingw @@ -156,7 +157,7 @@ namespace const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=[,[,...]]]"); const char* USAGE_PAYMENTS("payments [ ... ]"); const char* USAGE_PAYMENT_ID("payment_id"); - const char* USAGE_TRANSFER("transfer [index=[,,...]] [flash|unimportant] ( |
) []"); + const char* USAGE_TRANSFER("transfer [index=[,,...]] [flash|unimportant] ( |
) [subtractfeefrom=[,,all,...]] []"); const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=[,,...]] [] ( | ) []"); const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=[,,...] | index=all] [] [
] []"); const char* USAGE_SWEEP_ALL("sweep_all [index=[,,...] | index=all] [flash|unimportant] [outputs=] [
[]]"); @@ -461,6 +462,52 @@ namespace return r; } + + static constexpr std::string_view SFFD_ARG_NAME{"subtractfeefrom="}; + + bool parse_subtract_fee_from_outputs + ( + const std::string& arg, + tools::wallet2::unique_index_container& subtract_fee_from_outputs, + bool& subtract_fee_from_all, + bool& matches + ) + { + matches = false; + if (std::string_view{arg}.rfind(SFFD_ARG_NAME, 0) != 0) // if arg doesn't match + return true; + matches = true; + + const char* arg_end = arg.c_str() + arg.size(); + for (const char* p = arg.c_str() + SFFD_ARG_NAME.size(); p < arg_end;) + { + const char* new_p = nullptr; + const unsigned long dest_index = strtoul(p, const_cast(&new_p), 10); + if (dest_index == 0 && new_p == p) // numerical conversion failed + { + if (0 != strncmp(p, "all", 3)) + { + fail_msg_writer() << tr("Failed to parse subtractfeefrom list"); + return false; + } + subtract_fee_from_all = true; + break; + } + else if (dest_index > std::numeric_limits::max()) + { + fail_msg_writer() << tr("Destination index is too large") << ": " << dest_index; + return false; + } + else + { + subtract_fee_from_outputs.insert(dest_index); + p = new_p + 1; // skip the comma + } + } + + return true; + } + void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon) { bool warn_of_possible_attack = !trusted_daemon; @@ -867,10 +914,10 @@ bool simple_wallet::print_fee_info(const std::vector &args/* = std: typical_fees << print_money(typical_fee) << " (" << tools::allowed_priority_strings[1] << ")"; auto hf_version = m_wallet->get_hard_fork_version(); - if (hf_version && *hf_version >= HF_VERSION_FLASH) + if (hf_version && *hf_version >= feature::FLASH) { uint64_t pct = m_wallet->get_fee_percent(tools::tx_priority_flash, txtype::standard); - uint64_t fixed = FLASH_BURN_FIXED; + uint64_t fixed = beldex::FLASH_BURN_FIXED; uint64_t typical_flash_fee = (base_fee.first * typical_size + base_fee.second * typical_outs) * pct / 100 + fixed; @@ -2439,7 +2486,7 @@ bool simple_wallet::set_ignore_outputs_above(const std::vector &arg return true; } if (amount == 0) - amount = MONEY_SUPPLY; + amount = beldex::MONEY_SUPPLY; m_wallet->ignore_outputs_above(amount); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } @@ -2604,7 +2651,7 @@ simple_wallet::simple_wallet() tr("Show the blockchain height.")); m_cmd_binder.set_handler("transfer", [this](const auto& x) { return transfer(x); }, tr(USAGE_TRANSFER), - tr("Transfer to
. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction, or \"flash\" for an instant transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding et cetera (before the payment ID, if it's included)")); + tr("Transfer to
. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer 3 0.5 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 BDX including fees, and addr1 & addr3 would receive 2.925 & 0.975 BDX, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations.")); m_cmd_binder.set_handler("locked_transfer", [this](const auto& x) { return locked_transfer(x); }, tr(USAGE_LOCKED_TRANSFER), @@ -3442,7 +3489,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("Can't specify more than one of --testnet and --devnet"); return false; } - network_type const nettype = testnet ? TESTNET : devnet ? DEVNET : MAINNET; + network_type const nettype = testnet ? network_type::TESTNET : devnet ? network_type::DEVNET : network_type::MAINNET; epee::wipeable_string multisig_keys; epee::wipeable_string password; @@ -4019,11 +4066,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) message_writer(epee::console_color_yellow, true) << tr("If you or someone you trust are operating this daemon, you can use --trusted-daemon"); message_writer(); - cryptonote::rpc::GET_INFO::request req; - cryptonote::rpc::GET_INFO::response res; - bool r = m_wallet->invoke_http(req, res); - std::string err = interpret_rpc_response(r, res.status); - if (r && err.empty() && res.untrusted) + nlohmann::json res; + try { + res = m_wallet->json_rpc("get_info", {}); + } catch (const std::exception& e) { + fail_msg_writer() << tr("wallet failed to connect to daemon when calling get_info at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n"; + } + std::string err = interpret_rpc_response(true, res["status"]); + if (err.empty() && res["untrusted"].get()) message_writer(epee::console_color_yellow, true) << tr("Moreover, a daemon is also less secure when running in bootstrap mode"); } @@ -4628,21 +4678,19 @@ bool simple_wallet::start_mining(const std::vector& args) fail_msg_writer() << tr("wallet is null"); return true; } - rpc::START_MINING::request req{}; - req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); bool ok = true; size_t arg_size = args.size(); + nlohmann::json req_params{ + {"miner_address", m_wallet->get_account().get_public_address_str(m_wallet->nettype())}, + {"threads_count", 1}, + }; if(arg_size >= 1) { uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); ok = ok && 1 <= num; - req.threads_count = num; - } - else - { - req.threads_count = 1; + req_params["threads_count"] = num; } if (!ok) @@ -4651,9 +4699,13 @@ bool simple_wallet::start_mining(const std::vector& args) return true; } - rpc::START_MINING::response res{}; - bool r = m_wallet->invoke_http(req, res); - std::string err = interpret_rpc_response(r, res.status); + nlohmann::json res; + try { + res = m_wallet->json_rpc("start_mining", req_params); + } catch (const std::exception& e) { + fail_msg_writer() << tr("wallet failed to communicate with daemon when calling start_mining at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n"; + } + std::string err = interpret_rpc_response(true, res["status"].get()); if (err.empty()) success_msg_writer() << tr("Mining started in daemon"); else @@ -4672,9 +4724,13 @@ bool simple_wallet::stop_mining(const std::vector& args) return true; } - rpc::STOP_MINING::response res{}; - bool r = m_wallet->invoke_http({}, res); - std::string err = interpret_rpc_response(r, res.status); + nlohmann::json res; + try { + res = m_wallet->json_rpc("stop_mining", {}); + } catch (const std::exception& e) { + fail_msg_writer() << tr("wallet failed to communicate with daemon when calling stop_mining at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n"; + } + std::string err = interpret_rpc_response(true, res["status"].get()); if (err.empty()) success_msg_writer() << tr("Mining stopped in daemon"); else @@ -4740,10 +4796,13 @@ bool simple_wallet::save_bc(const std::vector& args) fail_msg_writer() << tr("wallet is null"); return true; } - rpc::SAVE_BC::request req{}; - rpc::SAVE_BC::response res{}; - bool r = m_wallet->invoke_http(req, res); - std::string err = interpret_rpc_response(r, res.status); + nlohmann::json res; + try { + res = m_wallet->json_rpc("save_bc", {}); + } catch (const std::exception& e) { + fail_msg_writer() << tr("wallet failed to connect to daemon when calling save_bc at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n"; + } + std::string err = interpret_rpc_response(true, res["status"]); if (err.empty()) success_msg_writer() << tr("Blockchain saved"); else @@ -4775,7 +4834,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tr("idx ") << subaddr_index; } - const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == DEVNET ? 0 : 1650000; + const uint64_t warn_height = m_wallet->nettype() == network_type::TESTNET ? 1000000 : m_wallet->nettype() == network_type::DEVNET ? 0 : 1650000; if (height >= warn_height) { std::vector tx_extra_fields; @@ -5071,6 +5130,22 @@ bool simple_wallet::show_balance_unlocked(bool detailed) << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; std::map balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account, false); std::map>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, false); + + if (m_current_subaddress_account == 0) { // Only the primary account can stake and earn rewards, currently + if (auto stakes = m_wallet->get_staked_master_nodes(); !stakes.empty()) { + auto my_addr = m_wallet->get_address_as_str(); + uint64_t total_staked = 0, stakes_unlocking = 0; + for (auto& stake : stakes) + for (auto& contr : stake["contributors"]) + if (contr["address"].get() == my_addr) + { + total_staked += contr["amount"].get(); + if (stake["requested_unlock_height"].get() > 0) + stakes_unlocking += contr["amount"].get(); + } + success_msg_writer() << fmt::format(tr("Total staked: {}, {} unlocking"), print_money(total_staked), print_money(stakes_unlocking)); + } + } if (!detailed || balance_per_subaddress.empty()) return true; success_msg_writer() << tr("Balance per address:"); @@ -5700,7 +5775,7 @@ bool simple_wallet::confirm_and_send_tx(std::vector 0) { - float days = lock_time_in_blocks / (double) BLOCKS_PER_DAY; + double days = lock_time_in_blocks / (double) BLOCKS_PER_DAY; prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days; } @@ -5714,7 +5789,7 @@ bool simple_wallet::confirm_and_send_tx(std::vector(&vin)) { - if (in_to_key->key_offsets.size() != CRYPTONOTE_DEFAULT_TX_MIXIN + 1) + if (in_to_key->key_offsets.size() != cryptonote::TX_OUTPUT_DECOYS + 1) default_ring_size = false; } } @@ -5865,6 +5940,28 @@ bool simple_wallet::transfer_main(Transfer transfer_type, const std::vector dsts_info; std::vector dsts; @@ -5956,6 +6053,13 @@ bool simple_wallet::transfer_main(Transfer transfer_type, const std::vector hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) { fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED; @@ -5983,7 +6087,7 @@ bool simple_wallet::transfer_main(Transfer transfer_type, const std::vectorcreate_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, tx_params); + ptx_vector = m_wallet->create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices, tx_params, subtract_fee_from_outputs); if (ptx_vector.empty()) { @@ -6251,135 +6355,271 @@ bool simple_wallet::request_stake_unlock(const std::vector &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::query_locked_stakes(bool print_result, bool print_key_images) +bool simple_wallet::query_locked_stakes(bool print_details, bool print_key_images) { if (!try_connect_to_daemon()) return false; bool has_locked_stakes = false; - std::string msg_buf; - { - using namespace cryptonote; - auto response = m_wallet->list_current_stakes(); + std::string msg; + auto my_addr = m_wallet->get_address_as_str(); - for (rpc::GET_MASTER_NODES::response::entry const &node_info : response) + auto response = m_wallet->get_staked_master_nodes(); + + // From the old RPC GET_MASTER_NODES::response::entry, but only the + // fields used below. + struct master_node_contribution + { + std::string key_image; + uint64_t amount; + }; + struct master_node_contributor + { + uint64_t amount; // total locked contributions in atomic BDX + std::string address; + std::vector locked_contributions; + }; + struct mn_entry + { + std::string master_node_pubkey; + uint64_t requested_unlock_height; + std::vector contributors; + uint64_t staking_requirement; + }; + std::vector mns; + for (const auto& node_info : response) + { + mn_entry entry; + for (const auto& contributor : node_info["contributors"]) { - bool only_once = true; - for (const auto& contributor : node_info.contributors) + master_node_contributor a; + for (const auto& contribution : contributor["locked_contributions"]) { - std::unordered_set printed_addresses; - for (size_t i = 0; i < contributor.locked_contributions.size(); ++i) - { - const auto& contribution = contributor.locked_contributions[i]; - has_locked_stakes = true; - - if (!print_result) - continue; - auto required = cryptonote::print_money(node_info.staking_requirement); - msg_buf.reserve(512); - std::string walletaddress = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); - if (only_once) - { - if((node_info.contributors.size() - 1)==0) - msg_buf.append(fmt::format(fg(fmt::color::sky_blue) | fmt::emphasis::bold,"Master Node :{}\n",node_info.master_node_pubkey)); - else - msg_buf.append(fmt::format(fg(fmt::color::sky_blue) | fmt::emphasis::bold,"Master Node :{} ({} {})\n",node_info.master_node_pubkey,(node_info.contributors.size() - 1),(node_info.contributors.size()-1)==0 ? "": ((node_info.contributors.size()-1) > 1 ? "Contributions" : "Contribution"))); - - if (node_info.requested_unlock_height != master_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT) - { - msg_buf.append(fmt::format("Unlock Height :{}\n",std::to_string(node_info.requested_unlock_height))); - } - if(walletaddress == contributor.address) - { - msg_buf.append(fmt::format("Operator's Contribution :{} of {} BDX required\n",cryptonote::print_money(contributor.amount),required)); - } - else - { - msg_buf.append(fmt::format("Operator's Contribution :{} of {} BDX required ({})\n",cryptonote::print_money(contributor.amount),required,contributor.address)); - } - printed_addresses.insert(contributor.address); - } - if(!only_once && printed_addresses.find(contributor.address) == printed_addresses.end()) - { - msg_buf.append(fmt::format(" Total_Contributions:{} ({})\n",cryptonote::print_money(contributor.amount),(walletaddress == contributor.address)? "YOURS" : contributor.address)); - printed_addresses.insert(contributor.address); - } - only_once = false; - msg_buf.append(fmt::format(" ● BDX :{}\n",cryptonote::print_money(contribution.amount))); - if(print_key_images) - { - msg_buf.append(fmt::format(" Key Image :{}\n",contribution.key_image)); - } - msg_buf.append("\n"); - } + master_node_contribution b; + b.key_image = contribution["key_image"].get(); + b.amount = contribution["amount"].get(); + a.locked_contributions.push_back(std::move(b)); } + a.address = contributor["address"].get(); + a.amount = contributor["amount"].get(); + entry.contributors.push_back(std::move(a)); } + entry.master_node_pubkey = node_info["master_node_pubkey"].get(); + entry.requested_unlock_height = node_info["requested_unlock_height"].get(); + entry.staking_requirement = node_info["staking_requirement"].get(); + mns.push_back(std::move(entry)); } + // Sort the list by pubkey, and partition into unlocking and not-unlocking: + std::stable_sort(mns.begin(), mns.end(), [](const auto& a, const auto& b) { + return a.master_node_pubkey < b.master_node_pubkey; }); + std::stable_partition(mns.begin(), mns.end(), [](const auto& a) { + return a.requested_unlock_height != master_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT; }); + + for (auto& node_info : mns) { - auto [success, response] = m_wallet->get_master_node_blacklisted_key_images(); - if (!success) - { - fail_msg_writer() << "Connection to daemon failed when retrieving blacklisted key images"; - return has_locked_stakes; - } + auto& contributors = node_info.contributors; + // Filter out any contributor rows without any actual contributions (i.e. from unfilled reserved + // contributions): + contributors.erase( + std::remove_if(contributors.begin(), contributors.end(), + [](const auto& c) { return c.amount == 0; }), + contributors.end()); - bool once_only = true; - bool first = true; - crypto::key_image key_image; - for (const auto& entry : response) - { - if (!tools::hex_to_type(entry.key_image, key_image)) - { - fail_msg_writer() << tr("Failed to parse hex representation of key image: ") << entry.key_image; - continue; - } + // Reorder contributors to put this wallet's contribution(s) first: + std::stable_partition(contributors.begin(), contributors.end(), + [&my_addr](const auto& x) { return x.address == my_addr; }); - if (!m_wallet->contains_key_image(key_image)) - continue; + if (contributors.empty() || contributors[0].address != my_addr) + continue; // We filtered out ourself + auto& me = contributors.front(); - if (first) - first = false; - else - msg_buf += "\n"; + has_locked_stakes = true; + if (!print_details) + continue; - has_locked_stakes = true; - if (!print_result) - continue; + uint64_t total = 0; + for (const auto& c : contributors) + total += c.amount; + + // Formatting: first 1-2 lines of general info: + // + // Master Node: abcdef123456... + // Unlock Height: 1234567 (omitted if not unlocking) + // + // If there are other contributors then we print a total line such as: + // + // Total Contributions: 10000 BDX of 10000 BDX required + // + // For our own contribution, when we have a single contribution, we use one of: + // + // Your Contribution: 5000 BDX (Key image: abcdef123...) + // Your Contribution: 5000 BDX of 10000 BDX required (Key image: abcdef123...) + // + // (the second one applies if we are the only contributor so far). + // + // If we made multiple contributions then: + // + // Your Contributions: 5000 BDX in 2 contributions: + // Your Contributions: 5000 BDX of 10000 BDX required in 2 contributions: + // + // (the second one if we are the only contributor so far). + // + // This is followed by the individual contributions: + // + // ‣ 4000.5 BDX (Key image: abcdef123...) + // ‣ 999.5 BDX (Key image: 789cba456...) + // + // If there are other contributors then we also print: + // + // Other contributions: 10000 BDX from 2 contributors: + // • 1234.565 BDX (bxU7YGUcPJffbaF5p8NLC3VidwJyHSdMaGmSxTBV645v33CmLq2ZvMqBdY9AVB2z8uhbHPCZSuZbv68hE6NBXBc51Gg9MGUGr) + // Key image 123456789... + // • 8765.435 BDX (bxTpop5RZdwE39iBvoP5xpJVoMpYPUwQpef9zS2tLL8yVgbppBbtGnzZxzkSp53Coi88wbsTHiokr7k8MQU94mGF1zzERqELK) + // ‣ 7530 BDX (Key image: 23456789a...) + // ‣ 1235.435 BDX (Key image: 3456789ab...) + // + // If we aren't showing key images then all the key image details get omitted. + + msg += fmt::format("Master Node: {}\n", node_info.master_node_pubkey); + if (node_info.requested_unlock_height != master_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT) + msg += fmt::format("Unlock height: {}\n", node_info.requested_unlock_height); + + bool just_me = contributors.size() == 1; + + auto required = fmt::format(" of {} required", cryptonote::format_money(node_info.staking_requirement)); + if (!just_me) { + msg += fmt::format("Total Contributions: {}{}\n", cryptonote::format_money(total), required); + required.clear(); + } + + auto my_total = me.amount; + if (me.locked_contributions.size() == 1) + msg += "Your Contribution: "; + else + { + msg += fmt::format("Your Contributions: {}{} in {} contributions:\n ‣ ", + cryptonote::format_money(my_total), + required, + me.locked_contributions.size()); + required.clear(); + } - msg_buf.reserve(512); - if (once_only) - { - msg_buf.append("Blacklisted Stakes\n"); - once_only = false; - } - msg_buf.append(fmt::format(" Unlock Height : {}\n", std::to_string(entry.unlock_height))); - if(print_key_images) + for (size_t i = 0; i < me.locked_contributions.size(); i++) + { + auto& c = me.locked_contributions[i]; + if (i > 0) msg += " ‣ "; + msg += cryptonote::format_money(c.amount); + if (!required.empty()) { - msg_buf.append(fmt::format(" Key Image : {}\n", entry.key_image)); + msg += required; + required.clear(); } - if (entry.amount > 0) + if (print_key_images) + msg += fmt::format(" (Key image: {})", c.key_image); + msg += '\n'; + } + + if (contributors.size() > 1) + { + msg += fmt::format("Other Contributions: {} from {} contributor{}:\n", + cryptonote::format_money(total - my_total), + contributors.size() - 1, + contributors.size() == 2 ? "" : "s"); + for (size_t i = 1; i < contributors.size(); i++) { - // version >= master_nodes::key_image_blacklist_entry::version_1_serialize_amount - msg_buf.append(fmt::format(" Total Locked : {}\n", cryptonote::print_money(entry.amount))); + const auto& contributor = contributors[i]; + const auto& locked = contributor.locked_contributions; + msg += fmt::format(" • {} ({})\n", + cryptonote::format_money(contributor.amount), contributor.address); + if (locked.size() == 1) + { + if (print_key_images) + msg += fmt::format(" Key image: {}\n", locked[0].key_image); + } + else + { + for (auto& c : locked) + { + msg += " ‣ "; + msg += cryptonote::format_money(c.amount); + if (print_key_images) + msg += fmt::format(" (Key image: {})\n", c.key_image); + else + msg += '\n'; + } + } } - msg_buf.append("\n"); - } + msg += "\n"; } - if (print_result) + // Find blacklisted key images (i.e. locked contributions from deregistered SNs) that belong to + // this wallet. If there are any, output will be: + // + // Locked Stakes due to Master Node Deregistration: + // ‣ 234.567 BDX (Unlock height 1234567; Key image: abcfed999...) + // ‣ 5000 BDX (Unlock height 123333; Key image: cbcfef989...) + // + // where the "; Key image: ..." part is omitted if not printing key images. + + auto [success, bl] = m_wallet->get_master_node_blacklisted_key_images(); + if (!success) { - if (has_locked_stakes) - { - tools::msg_writer() << msg_buf; - } - else + fail_msg_writer() << "Connection to daemon failed when retrieving blacklisted key images"; + return has_locked_stakes; + } + struct blacklisted_images + { + std::string key_image; + uint64_t unlock_height; + uint64_t amount; + }; + std::vector blacklisted; + for (const auto& b : bl) + { + blacklisted.push_back({b["key_image"].get(), + b["unlock_height"].get(), + b["amount"].get()}); + } + + // Filter out key images that aren't ours: + blacklisted.erase(std::remove_if(blacklisted.begin(), blacklisted.end(), + [this](const auto& black) { + if (crypto::key_image ki; tools::hex_to_type(black.key_image, ki)) + return !m_wallet->contains_key_image(ki); + fail_msg_writer() << "Failed to parse key image hex: " << black.key_image; + return true; + }), + blacklisted.end()); + + if (!blacklisted.empty()) + { + has_locked_stakes = true; + if (print_details) { - tools::msg_writer() << "No locked stakes known for this wallet on the network"; + msg += "Locked Stakes due to Master Node Deregistration:\n"; + + // Sort by unlock time (earliest first): + std::stable_sort(blacklisted.begin(), blacklisted.end(), + [](const auto& a, const auto& b) { return a.unlock_height < b.unlock_height; }); + + for (const auto& black : blacklisted) + { + msg += fmt::format(" • {} (Unlock height {}", cryptonote::format_money(black.amount), black.unlock_height); + if (print_key_images) + msg += fmt::format("; Key image: {})\n", black.key_image); + else + msg += ")\n"; + } } } + if (msg.empty() && print_details) + msg = "No locked stakes known for this wallet on the network"; + if (!msg.empty()) + tools::msg_writer() << msg; + return has_locked_stakes; } //---------------------------------------------------------------------------------------------------- @@ -6611,7 +6851,7 @@ bool simple_wallet::bns_renew_mapping(std::vector args) SCOPED_WALLET_UNLOCK(); std::string reason; std::vector ptx_vector; - std::vector response; + nlohmann::json response; try { ptx_vector = m_wallet->bns_create_renewal_tx( @@ -6643,7 +6883,7 @@ bool simple_wallet::bns_renew_mapping(std::vector args) std::optional blocks = bns::expiry_blocks(m_wallet->nettype(), *mapping_years); fmt::print(fmt::format(tr("Renewal {} {} ({} blocks)\n"),(years > 1) ? "years :" : "year :", years, *blocks)); - fmt::print(fmt::format(tr("New expiry : Block {}\n"), (*response[0].expiration_height + *blocks))); + fmt::print(fmt::format(tr("New expiry : Block {}\n"), (response["expiration_height"].get() + *blocks))); std::cout << std::flush; if (!confirm_and_send_tx(dsts, ptx_vector, false /*flash*/)) @@ -6683,7 +6923,7 @@ bool simple_wallet::bns_update_mapping(std::vector args) SCOPED_WALLET_UNLOCK(); std::string reason; std::vector ptx_vector; - std::vector response; + nlohmann::json response; for(std::string check : {owner, backup_owner, value_wallet}) { @@ -6726,100 +6966,48 @@ bool simple_wallet::bns_update_mapping(std::vector args) return true; } - auto &enc_bchat_hex = response[0].encrypted_bchat_value; - if (!oxenc::is_hex(enc_bchat_hex) || enc_bchat_hex.size() > 2 * bns::mapping_value::BUFFER_SIZE) - { - LOG_ERROR("invalid BNS data returned from beldexd"); - fail_msg_writer() << tr("invalid BNS data returned from beldexd"); - return true; - } - - auto &enc_wallet_hex = response[0].encrypted_wallet_value; - if (!oxenc::is_hex(enc_wallet_hex) || enc_wallet_hex.size() > 2 * bns::mapping_value::BUFFER_SIZE) - { - LOG_ERROR("invalid BNS data returned from beldexd"); - fail_msg_writer() << tr("invalid BNS data returned from beldexd"); - return true; - } - - auto &enc_belnet_hex = response[0].encrypted_belnet_value; - if (!oxenc::is_hex(enc_belnet_hex) || enc_belnet_hex.size() > 2 * bns::mapping_value::BUFFER_SIZE) - { - LOG_ERROR("invalid BNS data returned from beldexd"); - fail_msg_writer() << tr("invalid BNS data returned from beldexd"); + auto validate_encrypted_hex = [&](const std::string& label, const std::string& hex) -> bool { + if (!oxenc::is_hex(hex) || hex.size() > 2 * bns::mapping_value::BUFFER_SIZE) { + LOG_ERROR("Invalid BNS data for " << label << " returned from beldexd"); + fail_msg_writer() << tr("Invalid BNS data returned from beldexd"); + return false; + } return true; - } + }; - auto &enc_eth_hex = response[0].encrypted_eth_addr_value; - if (!oxenc::is_hex(enc_eth_hex) || enc_eth_hex.size() > 2 * bns::mapping_value::BUFFER_SIZE) - { - LOG_ERROR("invalid BNS data returned from beldexd"); - fail_msg_writer() << tr("invalid BNS data returned from beldexd"); + auto enc_bchat_hex = response["encrypted_bchat_value"].get(); + auto enc_wallet_hex = response["encrypted_wallet_value"].get(); + auto enc_belnet_hex = response["encrypted_belnet_value"].get(); + auto enc_eth_hex = response["encrypted_eth_addr_value"].get(); + + if (!validate_encrypted_hex("bchat", enc_bchat_hex) || + !validate_encrypted_hex("wallet", enc_wallet_hex) || + !validate_encrypted_hex("belnet", enc_belnet_hex) || + !validate_encrypted_hex("eth_addr", enc_eth_hex)) { return true; } - //BCHAT - bns::mapping_value bchat{}; - { - if (!enc_bchat_hex.empty()) - { - bchat.len = enc_bchat_hex.size() / 2; - bchat.encrypted = true; - oxenc::from_hex(enc_bchat_hex.begin(), enc_bchat_hex.end(), bchat.buffer.begin()); - if (!bchat.decrypt(tools::lowercase_ascii_string(name), bns::mapping_type::bchat)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_bchat_hex; - return false; - } - } - } - - //WALLET - bns::mapping_value wallet{}; - { - if (!enc_wallet_hex.empty()) - { - wallet.len = enc_wallet_hex.size() / 2; - wallet.encrypted = true; - oxenc::from_hex(enc_wallet_hex.begin(), enc_wallet_hex.end(), wallet.buffer.begin()); - if (!wallet.decrypt(tools::lowercase_ascii_string(name), bns::mapping_type::wallet)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_wallet_hex; - return false; - } - } - } - - //BELNET - bns::mapping_value belnet{}; - { - if (!enc_belnet_hex.empty()) - { - belnet.len = enc_belnet_hex.size() / 2; - belnet.encrypted = true; - oxenc::from_hex(enc_belnet_hex.begin(), enc_belnet_hex.end(), belnet.buffer.begin()); - if (!belnet.decrypt(tools::lowercase_ascii_string(name), bns::mapping_type::belnet)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_belnet_hex; - return false; - } - } - } - - //ETH_ADDRESS - bns::mapping_value eth_addr{}; - { - if (!enc_eth_hex.empty()) - { - eth_addr.len = enc_eth_hex.size() / 2; - eth_addr.encrypted = true; - oxenc::from_hex(enc_eth_hex.begin(), enc_eth_hex.end(), eth_addr.buffer.begin()); - if (!eth_addr.decrypt(tools::lowercase_ascii_string(name), bns::mapping_type::eth_addr)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_eth_hex; - return false; - } + auto decrypt_mapping = [&](const std::string& hex, bns::mapping_type type, bns::mapping_value& out) -> bool { + if (hex.empty()) return true; + + out.len = hex.size() / 2; + out.encrypted = true; + oxenc::from_hex(hex.begin(), hex.end(), out.buffer.begin()); + + if (!out.decrypt(tools::lowercase_ascii_string(name), type)) { + fail_msg_writer() << "Failed to decrypt the mapping value=" << hex; + return false; } + + return true; + }; + + bns::mapping_value bchat{}, wallet{}, belnet{}, eth_addr{}; + if (!decrypt_mapping(enc_bchat_hex, bns::mapping_type::bchat, bchat) || + !decrypt_mapping(enc_wallet_hex, bns::mapping_type::wallet, wallet) || + !decrypt_mapping(enc_belnet_hex, bns::mapping_type::belnet, belnet) || + !decrypt_mapping(enc_eth_hex, bns::mapping_type::eth_addr, eth_addr)) { + return false; } std::vector dsts; @@ -6876,22 +7064,22 @@ bool simple_wallet::bns_update_mapping(std::vector args) if (owner.size()) { - fmt::print(fmt::fg(fmt::color::red),fmt::format(tr("Old Owner : {}\n"), response[0].owner)); + fmt::print(fmt::fg(fmt::color::red),fmt::format(tr("Old Owner : {}\n"), response["owner"].get())); fmt::print(fmt::fg(fmt::color::light_green),fmt::format(tr("New Owner : {}\n"), owner)); } else { - fmt::print(fmt::format(tr("Owner : {} (unchanged)\n"), response[0].owner)); + fmt::print(fmt::format(tr("Owner : {} (unchanged)\n"), response["owner"].get())); } if (backup_owner.size()) { - fmt::print(fmt::fg(fmt::color::red),fmt::format(tr("Old Backup Owner : {}\n"), response[0].backup_owner.value_or(NULL_STR))); + fmt::print(fmt::fg(fmt::color::red),fmt::format(tr("Old Backup Owner : {}\n"), response.value("backup_owner", ""))); fmt::print(fmt::fg(fmt::color::light_green),fmt::format(tr("New Backup Owner : {}\n"), backup_owner)); } else { - fmt::print(fmt::format(tr("Backup Owner : {} (unchanged)\n"), response[0].backup_owner.value_or(NULL_STR))); + fmt::print(fmt::format(tr("Backup Owner : {} (unchanged)\n"), response.value("backup_owner", ""))); } if (value_bchat.size() && (value_bchat == bchat.to_readable_value(m_wallet->nettype(), bns::mapping_type::bchat))) @@ -6960,14 +7148,14 @@ bool simple_wallet::bns_encrypt(std::vector args) return false; } - std::string reason; - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) { tools::fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED; return false; } + std::string reason; if (!bns::validate_bns_name(name, &reason)) { tools::fail_msg_writer() << "Invalid BNS name '" << name << "': " << reason; @@ -6981,7 +7169,7 @@ bool simple_wallet::bns_encrypt(std::vector args) return false; } - bool old_argon2 = type == bns::mapping_type::bchat && *hf_version < cryptonote::network_version_17_POS; + bool old_argon2 = type == bns::mapping_type::bchat && *hf_version < hf::hf17_POS; if (!mval.encrypt(name, nullptr, old_argon2)) { tools::fail_msg_writer() << "Value encryption failed"; @@ -7056,14 +7244,14 @@ bool simple_wallet::bns_lookup(std::vector args) return true; } - rpc::BNS_NAMES_TO_OWNERS::request request = {}; + std::vector name_hash{}; for (auto& name : args) { name = tools::lowercase_ascii_string(std::move(name)); - request.entries.push_back(bns::name_to_base64_hash(name)); + name_hash.push_back(bns::name_to_base64_hash(name)); } - auto [success, response] = m_wallet->bns_names_to_owners(request); + auto [success, response] = m_wallet->bns_names_to_owners({{"name_hash", name_hash}}); if (!success) { fail_msg_writer() << "Connection to daemon failed when requesting BNS owners"; @@ -7071,147 +7259,65 @@ bool simple_wallet::bns_lookup(std::vector args) } int last_index = -1; - for (auto const &mapping : response) - { - auto& enc_bchat_hex = mapping.encrypted_bchat_value; - if (mapping.entry_index >= args.size() || !oxenc::is_hex(enc_bchat_hex) || enc_bchat_hex.size() > 2*bns::mapping_value::BUFFER_SIZE) - { - fail_msg_writer() << "Received invalid BNS mapping data from beldexd"; - return false; - } - - auto& enc_wallet_hex = mapping.encrypted_wallet_value; - if (mapping.entry_index >= args.size() || !oxenc::is_hex(enc_wallet_hex) || enc_wallet_hex.size() > 2*bns::mapping_value::BUFFER_SIZE) - { - fail_msg_writer() << "Received invalid BNS mapping data from beldexd"; - return false; - } - - auto& enc_belnet_hex = mapping.encrypted_belnet_value; - if (mapping.entry_index >= args.size() || !oxenc::is_hex(enc_belnet_hex) || enc_belnet_hex.size() > 2*bns::mapping_value::BUFFER_SIZE) - { - fail_msg_writer() << "Received invalid BNS mapping data from beldexd"; - return false; - } - - auto& enc_eth_hex = mapping.encrypted_eth_addr_value; - if (mapping.entry_index >= args.size() || !oxenc::is_hex(enc_eth_hex) || enc_eth_hex.size() > 2*bns::mapping_value::BUFFER_SIZE) - { - fail_msg_writer() << "Received invalid BNS mapping data from beldexd"; - return false; - } - - // Print any skipped (i.e. not registered) results: - for (size_t i = last_index + 1; i < mapping.entry_index; i++) - fail_msg_writer() << args[i] << " not found\n"; - last_index = mapping.entry_index; - - const auto& name = args[mapping.entry_index]; - - //BCHAT - bns::mapping_value value_bchat{}; - { - if (!enc_bchat_hex.empty()) - { - value_bchat.len = enc_bchat_hex.size() / 2; - value_bchat.encrypted = true; - oxenc::from_hex(enc_bchat_hex.begin(), enc_bchat_hex.end(), value_bchat.buffer.begin()); - - if (!value_bchat.decrypt(name, bns::mapping_type::bchat)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_bchat_hex; - return false; - } - } - } - - //WALLET - bns::mapping_value value_wallet{}; - { - if (!enc_wallet_hex.empty()) - { - value_wallet.len = enc_wallet_hex.size() / 2; - value_wallet.encrypted = true; - oxenc::from_hex(enc_wallet_hex.begin(), enc_wallet_hex.end(), value_wallet.buffer.begin()); - - if (!value_wallet.decrypt(name, bns::mapping_type::wallet)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_wallet_hex; - return false; - } - } - } - - //BELNET - bns::mapping_value value_belnet{}; - { - if (!enc_belnet_hex.empty()) - { - value_belnet.len = enc_belnet_hex.size() / 2; - value_belnet.encrypted = true; - oxenc::from_hex(enc_belnet_hex.begin(), enc_belnet_hex.end(), value_belnet.buffer.begin()); - - if (!value_belnet.decrypt(name, bns::mapping_type::belnet)) - { - fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_belnet_hex; + for (const auto& mapping : response) { + // Print any skipped (i.e. not registered) results: + for (size_t i = last_index + 1; i < mapping["entry_index"]; i++) + fail_msg_writer() << args[i] << " not found\n"; + last_index = mapping["entry_index"]; + + const std::string name = args[mapping["entry_index"]]; + std::string name_hash_b64 = mapping["name_hash"].get(); + std::string owner = mapping["owner"]; + std::string enc_bchat_hex = mapping["encrypted_bchat_value"]; + std::string enc_wallet_hex = mapping["encrypted_wallet_value"]; + std::string enc_belnet_hex = mapping["encrypted_belnet_value"]; + std::string enc_eth_hex = mapping["encrypted_eth_addr_value"]; + + auto validate = [&](const std::string& hex) { + return oxenc::is_hex(hex) && hex.size() <= 2 * bns::mapping_value::BUFFER_SIZE; + }; + + if ((!enc_bchat_hex.empty() && !validate(enc_bchat_hex)) || + (!enc_wallet_hex.empty() && !validate(enc_wallet_hex)) || + (!enc_belnet_hex.empty() && !validate(enc_belnet_hex)) || + (!enc_eth_hex.empty() && !validate(enc_eth_hex))) { + fail_msg_writer() << "Received invalid BNS mapping data from beldexd"; return false; - } } - } - //ETH_ADDRESS - bns::mapping_value value_eth{}; - { - if (!enc_eth_hex.empty()) - { - value_eth.len = enc_eth_hex.size() / 2; - value_eth.encrypted = true; - oxenc::from_hex(enc_eth_hex.begin(), enc_eth_hex.end(), value_eth.buffer.begin()); - - if (!value_eth.decrypt(name, bns::mapping_type::eth_addr)) - { - fail_msg_writer() << "Failed to decrypt the mapping eth_value=" << enc_eth_hex; + auto decrypt = [&](const std::string& hex, bns::mapping_type type, bns::mapping_value& out_val) -> bool { + if (hex.empty()) return true; + out_val.len = hex.size() / 2; + out_val.encrypted = true; + oxenc::from_hex(hex.begin(), hex.end(), out_val.buffer.begin()); + return out_val.decrypt(name, type); + }; + + bns::mapping_value val_bchat, val_wallet, val_belnet, val_eth; + if (!decrypt(enc_bchat_hex, bns::mapping_type::bchat, val_bchat) || + !decrypt(enc_wallet_hex, bns::mapping_type::wallet, val_wallet) || + !decrypt(enc_belnet_hex, bns::mapping_type::belnet, val_belnet) || + !decrypt(enc_eth_hex, bns::mapping_type::eth_addr, val_eth)) { + fail_msg_writer() << "Decryption failed for one of the BNS mapping values."; return false; - } } - } - auto writer = tools::msg_writer(); - writer - << fmt::format(fg(fmt::color::sky_blue), " Name : {}", name); - if(!enc_bchat_hex.empty()) writer - << "\n Value bchat : " << value_bchat.to_readable_value(m_wallet->nettype(), bns::mapping_type::bchat); - if(!enc_wallet_hex.empty()) writer - << "\n Value wallet : " << value_wallet.to_readable_value(m_wallet->nettype(), bns::mapping_type::wallet); - if(!enc_belnet_hex.empty()) writer - << "\n Value belnet : " << value_belnet.to_readable_value(m_wallet->nettype(), bns::mapping_type::belnet); - if(!enc_eth_hex.empty()) writer - << "\n Value ethAddress : " << value_eth.to_readable_value(m_wallet->nettype(), bns::mapping_type::eth_addr); - writer - << "\n Owner : " << mapping.owner; - if (mapping.backup_owner) writer - << "\n Backup owner : " << *mapping.backup_owner; - writer - << "\n Last updated height : " << mapping.update_height; - if (mapping.expiration_height) writer - << "\n Expiration height : " << *mapping.expiration_height; - writer - << "\n Encrypted bchat value : " << (enc_bchat_hex.empty() ? "(none)" :enc_bchat_hex); - writer - << "\n Encrypted wallet value : " << (enc_wallet_hex.empty() ? "(none)" :enc_wallet_hex); - writer - << "\n Encrypted belnet value : " << (enc_belnet_hex.empty() ? "(none)" :enc_belnet_hex); - writer - << "\n Encrypted Eth value : " << (enc_eth_hex.empty() ? "(none)" :enc_eth_hex); - writer - << "\n"; - - tools::wallet2::bns_detail detail = - { - name, - mapping.name_hash}; - m_wallet->set_bns_cache_record(detail); + auto writer = tools::msg_writer(); + writer << fmt::format(fg(fmt::color::sky_blue), " Name Hash : {}", name_hash_b64); + if (!enc_bchat_hex.empty()) writer << "\n Value bchat : " << val_bchat.to_readable_value(m_wallet->nettype(), bns::mapping_type::bchat); + if (!enc_wallet_hex.empty()) writer << "\n Value wallet : " << val_wallet.to_readable_value(m_wallet->nettype(), bns::mapping_type::wallet); + if (!enc_belnet_hex.empty()) writer << "\n Value belnet : " << val_belnet.to_readable_value(m_wallet->nettype(), bns::mapping_type::belnet); + if (!enc_eth_hex.empty()) writer << "\n Value ethAddress : " << val_eth.to_readable_value(m_wallet->nettype(), bns::mapping_type::eth_addr); + writer << "\n Owner : " << owner; + if (mapping.contains("backup_owner")) writer << "\n Backup owner : " << mapping["backup_owner"]; + writer << "\n Last updated height : " << mapping["update_height"]; + if (mapping.contains("expiration_height")) writer << "\n Expiration height : " << mapping["expiration_height"]; + writer << "\n"; + + tools::wallet2::bns_detail detail = {name, name_hash_b64}; + m_wallet->set_bns_cache_record(detail); } + for (size_t i = last_index + 1; i < args.size(); i++) fail_msg_writer() << args[i] << " not found\n"; @@ -7223,8 +7329,9 @@ bool simple_wallet::bns_by_owner(const std::vector& args) if (!try_connect_to_daemon()) return false; - std::vector> rpc_results; - std::vector requests(1); + nlohmann::json req_params{ + {"entries", {}} + }; std::unordered_map cache = m_wallet->get_bns_cache(); @@ -7232,10 +7339,7 @@ bool simple_wallet::bns_by_owner(const std::vector& args) { for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index) { - - if (requests.back().entries.size() >= cryptonote::rpc::BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES) - requests.emplace_back(); - requests.back().entries.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account, index})); + req_params["entries"].push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account, index})); } } else @@ -7255,104 +7359,81 @@ bool simple_wallet::bns_by_owner(const std::vector& args) return false; } - if (requests.back().entries.size() >= cryptonote::rpc::BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES) - requests.emplace_back(); - requests.back().entries.push_back(arg); + req_params["entries"].push_back(arg); } } - rpc_results.reserve(requests.size()); - for (auto const &request : requests) + auto [success, result] = m_wallet->bns_owners_to_names(req_params); + if (!success) { - auto [success, result] = m_wallet->bns_owners_to_names(request); - if (!success) - { - fail_msg_writer() << "Connection to daemon failed when requesting BNS names"; - return false; - } - rpc_results.emplace_back(std::move(result)); + fail_msg_writer() << "Connection to daemon failed when requesting BNS names"; + return false; } - auto nettype = m_wallet->nettype(); - for (size_t i = 0; i < rpc_results.size(); i++) + for (const auto& entry : result["entries"]) { - auto const &rpc = rpc_results[i]; - for (auto const &entry : rpc) + std::string_view name; + std::string value_bchat, value_wallet, value_belnet, value_eth; + + if (auto got = cache.find(entry["name_hash"]); got != cache.end()) { - std::string_view name; - std::string value_bchat, value_wallet, value_belnet, value_eth; - if (auto got = cache.find(entry.name_hash); got != cache.end()) - { - name = got->second.name; - //BCHAT - { - bns::mapping_value mv; - const auto type = bns::mapping_type::bchat; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_bchat_value), &mv) - && mv.decrypt(name, type)) - value_bchat = mv.to_readable_value(nettype, type); - } - //WALLET - { - bns::mapping_value mv; - const auto type = bns::mapping_type::wallet; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_wallet_value), &mv) - && mv.decrypt(name, type)) - value_wallet = mv.to_readable_value(nettype,type); - } - //BELNET - { - bns::mapping_value mv; - const auto type = bns::mapping_type::belnet; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_belnet_value), &mv) - && mv.decrypt(name, type)) - value_belnet = mv.to_readable_value(nettype, type); - } - - //ETH_ADDRESS + name = got->second.name; + + auto decrypt_value = [&](std::string_view key, bns::mapping_type type, std::string& out) { + auto it = entry.find(key); + if (it != entry.end() && !it->empty()) { bns::mapping_value mv; - const auto type = bns::mapping_type::eth_addr; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_eth_addr_value), &mv) - && mv.decrypt(name, type)) - value_eth = mv.to_readable_value(nettype, type); + const auto& hex_str = it->get_ref(); + if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(hex_str), &mv) && + mv.decrypt(name, type)) + { + out = mv.to_readable_value(nettype, type); + } } - } + }; - auto writer = tools::msg_writer(); - writer - << fmt::format(fg(fmt::color::sky_blue) , " Name (hashed) : {}", entry.name_hash); - if (!name.empty()) writer - << fmt::format(fg(fmt::color::sky_blue) , "\n Name : {}", name); - if (!value_bchat.empty()) writer - << "\n Value bchat : " << value_bchat; - if (!value_wallet.empty()) writer - << "\n Value wallet : " << value_wallet; - if (!value_belnet.empty()) writer - << "\n Value belnet : " << value_belnet; - if (!value_eth.empty()) writer - << "\n Value ethAddress : " << value_eth; - writer - << "\n Owner : " << entry.owner; - if (entry.backup_owner) writer - << "\n Backup owner : " << *entry.backup_owner; - writer - << "\n Last updated height : " << entry.update_height; - if (entry.expiration_height) writer - << "\n Expiration height : " << *entry.expiration_height; - writer - << "\n Encrypted bchat value : " << (entry.encrypted_bchat_value.empty() ? "(none)" : entry.encrypted_bchat_value); - writer - << "\n Encrypted wallet value : " << (entry.encrypted_wallet_value.empty() ? "(none)" : entry.encrypted_wallet_value); - writer - << "\n Encrypted belnet value : " << (entry.encrypted_belnet_value.empty() ? "(none)" : entry.encrypted_belnet_value); - writer - << "\n Encrypted Eth value : " << (entry.encrypted_eth_addr_value.empty() ? "(none)" : entry.encrypted_eth_addr_value); - writer - << "\n"; + decrypt_value("encrypted_bchat_value", bns::mapping_type::bchat, value_bchat); + decrypt_value("encrypted_wallet_value", bns::mapping_type::wallet, value_wallet); + decrypt_value("encrypted_belnet_value", bns::mapping_type::belnet, value_belnet); + decrypt_value("encrypted_eth_addr_value", bns::mapping_type::eth_addr, value_eth); } + + auto writer = tools::msg_writer(); + writer << fmt::format(fg(fmt::color::sky_blue), " Name (hashed) : {}", entry["name_hash"]); + if (!name.empty()) writer << fmt::format(fg(fmt::color::sky_blue), "\n Name : {}", name); + + writer << "\n Owner : " << entry["owner"].get(); + + if (entry.contains("backup_owner") && !entry["backup_owner"].is_null()) + writer << "\n Backup owner : " << entry["backup_owner"].get(); + + if (!value_bchat.empty()) writer << "\n Value bchat : " << value_bchat; + if (!value_wallet.empty()) writer << "\n Value wallet : " << value_wallet; + if (!value_belnet.empty()) writer << "\n Value belnet : " << value_belnet; + if (!value_eth.empty()) writer << "\n Value ethAddress : " << value_eth; + + writer << "\n Last updated height : " << entry["update_height"]; + + if (auto it = entry.find("expiration_height"); it != entry.end()) + writer << "\n Expiration height : " << *it; + + // Print encrypted values only if they are present and non-empty + auto safe_print_enc = [&](std::string_view label, std::string_view key) { + auto it = entry.find(key); + if (it != entry.end() && !it->get_ref().empty()) + writer << "\n " << label << " : " << it->get(); + }; + + safe_print_enc("Encrypted bchat value", "encrypted_bchat_value"); + safe_print_enc("Encrypted wallet value", "encrypted_wallet_value"); + safe_print_enc("Encrypted belnet value", "encrypted_belnet_value"); + safe_print_enc("Encrypted Eth value", "encrypted_eth_addr_value"); + + writer << "\n"; } + return true; } //---------------------------------------------------------------------------------------------------- @@ -7397,7 +7478,7 @@ bool simple_wallet::coin_burn(std::vector args) { std::vector extra; std::vector ptx_vector; - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) { fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED; @@ -7407,7 +7488,7 @@ bool simple_wallet::coin_burn(std::vector args) beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, txtype::coin_burn, priority, burn_amount); // transaction process called if(burn_amount){ - ptx_vector = m_wallet->create_transactions_2({}, CRYPTONOTE_DEFAULT_TX_MIXIN, 0, priority, extra, m_current_subaddress_account, subaddr_indices, tx_params); + ptx_vector = m_wallet->create_transactions_2({}, cryptonote::TX_OUTPUT_DECOYS, 0, priority, extra, m_current_subaddress_account, subaddr_indices, tx_params); }else{ tools::wallet2::transfer_container transfers; bool available = false; @@ -7436,7 +7517,7 @@ bool simple_wallet::coin_burn(std::vector args) return false; } - ptx_vector = m_wallet->create_transactions_burn(ki, outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, 0, priority, extra); + ptx_vector = m_wallet->create_transactions_burn(ki, outputs, cryptonote::TX_OUTPUT_DECOYS, 0, priority, extra); } if (ptx_vector.empty()) @@ -7850,7 +7931,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, Transfer transf SCOPED_WALLET_UNLOCK(); try { - auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, cryptonote::TX_OUTPUT_DECOYS, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices); sweep_main_internal(sweep_type_t::all_or_below, ptx_vector, info, priority == tools::tx_priority_flash); } catch (const std::exception &e) @@ -7946,7 +8027,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */, priority, extra); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, cryptonote::TX_OUTPUT_DECOYS, 0 /* unlock_time */, priority, extra); sweep_main_internal(sweep_type_t::single, ptx_vector, info, priority == tools::tx_priority_flash); } catch (const std::exception& e) @@ -8534,7 +8615,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) try { uint64_t received; - bool in_pool; + bool in_pool = false; uint64_t confirmations; if (m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, args.size() == 4 ? args[3] : "", sig_str, received, in_pool, confirmations)) { @@ -9569,17 +9650,18 @@ void simple_wallet::print_accounts(const std::string& tag) success_msg_writer() << tr("Accounts with tag: ") << tag; success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second; } - success_msg_writer() << fmt::format(tr(" {:>15} {:>21} {:>21} {:>21}"), "Account", "Balance", "Unlocked balance", "Label"); + success_msg_writer() << fmt::format(tr(" {:>15} {:>21} {:>21} {:>21}"), "Address", "Balance", "Unlocked balance", "Label"); uint64_t total_balance = 0, total_unlocked_balance = 0; for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) { + std::string address_str = m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6); if (account_tags.second[account_index] != tag) continue; success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s")) % (m_current_subaddress_account == account_index ? '*' : ' ') % account_index - % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6) + % address_str % print_money(m_wallet->balance(account_index, false)) % print_money(m_wallet->unlocked_balance(account_index, false,NULL,NULL)) % m_wallet->get_subaddress_label({account_index, 0}); @@ -9974,8 +10056,8 @@ bool simple_wallet::wallet_info(const std::vector &args) type = tr("Normal"); message_writer() << tr("Type: ") << type; message_writer() << tr("Network type: ") << ( - m_wallet->nettype() == cryptonote::TESTNET ? tr("Testnet") : - m_wallet->nettype() == cryptonote::DEVNET ? tr("Devnet") : tr("Mainnet")); + m_wallet->nettype() == cryptonote::network_type::TESTNET ? tr("Testnet") : + m_wallet->nettype() == cryptonote::network_type::DEVNET ? tr("Devnet") : tr("Mainnet")); return true; } @@ -10389,9 +10471,9 @@ bool simple_wallet::show_transfer(const std::vector &args) success_msg_writer() << "Timestamp: " << tools::get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(pd.m_amount); success_msg_writer() << "Payment ID: " << payment_id; - if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + if (pd.m_unlock_time < MAX_BLOCK_NUMBER) { - uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17); + uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + DEFAULT_TX_SPENDABLE_AGE_V17); uint64_t suggested_threshold = 0; if (!pd.m_unmined_flash) { @@ -10410,7 +10492,7 @@ bool simple_wallet::show_transfer(const std::vector &args) uint64_t current_time = static_cast(time(NULL)); - uint64_t threshold = current_time + tools::to_seconds(CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V3); + uint64_t threshold = current_time + tools::to_seconds(LOCKED_TX_ALLOWED_DELTA_BLOCKS * TARGET_BLOCK_TIME); if (threshold >= pd.m_unlock_time) success_msg_writer() << "unlocked for " << tools::get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time)); else @@ -10449,9 +10531,9 @@ bool simple_wallet::show_transfer(const std::vector &args) success_msg_writer() << "Change: " << print_money(change); success_msg_writer() << "Fee: " << print_money(fee); success_msg_writer() << "Destinations: " << dests; - if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + if (pd.m_unlock_time < MAX_BLOCK_NUMBER) { - uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17); + uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + DEFAULT_TX_SPENDABLE_AGE_V17); if (bh >= last_block_height) success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock"; else @@ -10460,7 +10542,7 @@ bool simple_wallet::show_transfer(const std::vector &args) else { uint64_t current_time = static_cast(time(NULL)); - uint64_t threshold = current_time + tools::to_seconds(CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V3); + uint64_t threshold = current_time + tools::to_seconds(LOCKED_TX_ALLOWED_DELTA_BLOCKS * TARGET_BLOCK_TIME); if (threshold >= pd.m_unlock_time) success_msg_writer() << "unlocked for " << tools::get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time)); else diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 3965355ffad..4b54a8c6e4a 100755 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -373,7 +373,7 @@ namespace cryptonote { auto current_time = std::chrono::system_clock::now(); auto hf_version = cryptonote::get_network_version(nettype, height); - const auto node_update_threshold = (hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD) / 2; + const auto node_update_threshold = (hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12) / 2; if (node_update_threshold < current_time - m_blockchain_height_update_time || m_blockchain_height <= height) { update_blockchain_height(); diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 8f1352d7e95..1460dc3fa33 100755 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -41,10 +41,9 @@ target_link_libraries(wallet PUBLIC multisig rpc_commands - rpc_server_base + rpc_common cryptonote_core mnemonics - device_trezor net lmdb rpc_http_client @@ -54,6 +53,10 @@ target_link_libraries(wallet PRIVATE extra) +if(TARGET device_trezor) + target_link_libraries(wallet PUBLIC device_trezor) +endif() + beldex_add_executable(wallet_rpc_server "beldex-wallet-rpc" wallet_rpc_server.cpp wallet_rpc_server_commands_defs.cpp @@ -61,7 +64,7 @@ beldex_add_executable(wallet_rpc_server "beldex-wallet-rpc" target_link_libraries(wallet_rpc_server PRIVATE - rpc_server_base + rpc_common wallet daemonizer Boost::program_options diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt index acf1983dea7..df4e4267301 100755 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -95,9 +95,12 @@ function(combine_archives output_archive) endfunction(combine_archives) if (STATIC AND BUILD_STATIC_DEPS) - set(merged_protobuf) + set(optional_deps) if(TARGET protobuf_lite) - set(merged_protobuf protobuf_lite) + list(APPEND optional_deps protobuf_lite) + endif() + if(TARGET device_trezor) + list(APPEND optional_deps device_trezor) endif() combine_archives(wallet_merged @@ -116,17 +119,16 @@ if (STATIC AND BUILD_STATIC_DEPS) checkpoints version net - device_trezor epee blockchain_db rpc_http_client rpc_commands + ${optional_deps} # Static deps: Boost::program_options Boost::serialization Boost::system Boost::thread zlib sqlite3 - ${merged_protobuf} sodium libzmq CURL::libcurl diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 831c3cfb371..0ab68c7e0b5 100755 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -167,7 +167,8 @@ uint64_t PendingTransactionImpl::amount() const std::optional hf_version = m_wallet.hardForkVersion(); if (hf_version) { - if (master_nodes::tx_get_staking_components_and_amounts(static_cast(w->nettype()), *hf_version, ptx.tx, height, &sc) + auto hf = static_cast(*hf_version); + if (master_nodes::tx_get_staking_components_and_amounts(static_cast(w->nettype()), hf, ptx.tx, height, &sc) && sc.transferred > 0) result = sc.transferred; } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1789589d8da..fd84ffffd32 100755 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -74,9 +74,9 @@ namespace { auto dir = tools::get_default_data_dir(); // remove .beldex, replace with .shared-ringdb dir.replace_filename(".shared-ringdb"); - if (nettype == cryptonote::TESTNET) + if (nettype == cryptonote::network_type::TESTNET) dir /= "testnet"; - else if (nettype == cryptonote::DEVNET) + else if (nettype == cryptonote::network_type::DEVNET) dir /= "devnet"; return dir; } @@ -324,7 +324,7 @@ EXPORT uint64_t Wallet::amountFromDouble(double amount) { std::stringstream ss; - ss << std::fixed << std::setprecision(CRYPTONOTE_DISPLAY_DECIMAL_POINT) << amount; + ss << std::fixed << std::setprecision(beldex::DISPLAY_DECIMAL_POINT) << amount; return amountFromString(ss.str()); } @@ -1066,28 +1066,24 @@ int WalletImpl::countBns() { clearStatus(); auto w = wallet(); - std::vector requests(1); - int count=0; + + nlohmann::json req_params{ + {"entries", nlohmann::json::array()} + }; for (uint32_t index = 0; index < w->get_num_subaddresses(0); ++index) { - if (requests.back().entries.size() >= cryptonote::rpc::BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES) - requests.emplace_back(); - requests.back().entries.push_back(w->get_subaddress_as_str({0, index})); + req_params["entries"].push_back(w->get_subaddress_as_str({0, index})); } - for (auto const &request : requests) + auto [success, result] = w->bns_owners_to_names(req_params); + if (!success) { - auto [success, result] = w->bns_owners_to_names(request); - if (!success) - { - LOG_PRINT_L1(__FUNCTION__ << "Connection to daemon failed when requesting BNS names"); - setStatusError(tr("Connection to daemon failed when requesting BNS names")); - break; - } - count += result.size(); - } - return count; + LOG_PRINT_L1(__FUNCTION__ << "Connection to daemon failed when requesting BNS names"); + setStatusError(tr("Connection to daemon failed when requesting BNS names")); + } + + return result["entries"].size(); } EXPORT @@ -1095,21 +1091,21 @@ std::vector* WalletImpl::listCurrentStakes() const { std::vector* stakes = new std::vector; - auto response = wallet()->list_current_stakes(); + auto response = wallet()->get_staked_master_nodes(); auto address = mainAddress(); - for (rpc::GET_MASTER_NODES::response::entry const &node_info : response) + for (const auto& node_info : response) { - for (const auto& contributor : node_info.contributors) + for (const auto& contributor : node_info["contributors"]) { - if(contributor.address == address){ + if(contributor["address"] == address){ auto &info = stakes->emplace_back(); - info.mn_pubkey = node_info.master_node_pubkey; - info.stake = contributor.amount; - if(node_info.requested_unlock_height !=0) - info.unlock_height = node_info.requested_unlock_height; - info.decommissioned = !node_info.active && node_info.funded; - info.awaiting = !node_info.funded; + info.mn_pubkey = node_info["master_node_pubkey"]; + info.stake = contributor["amount"]; + if(node_info["requested_unlock_height"] !=0) + info.unlock_height = node_info["requested_unlock_height"]; + info.decommissioned = !node_info["active"] && node_info["funded"]; + info.awaiting = !node_info["funded"]; } } @@ -1645,7 +1641,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector hf_version = w->get_hard_fork_version(); + auto hf_version = w->get_hard_fork_version(); if (!hf_version) { setStatusError(tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED); @@ -1653,11 +1649,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = w->create_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */, + transaction->m_pending_tx = w->create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, 0 /* unlock_time */, priority, extra, subaddr_account, subaddr_indices, tx_params); } else { - transaction->m_pending_tx = w->create_transactions_all(0, info.address, info.is_subaddress, 1, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */, + transaction->m_pending_tx = w->create_transactions_all(0, info.address, info.is_subaddress, 1, cryptonote::TX_OUTPUT_DECOYS, 0 /* unlock_time */, priority, extra, subaddr_account, subaddr_indices); } @@ -1779,7 +1775,7 @@ PendingTransaction *WalletImpl::createSweepAllTransaction(uint32_t priority, uin break; } try { - transaction->m_pending_tx = w->create_transactions_all(0, info.address, info.is_subaddress, 1, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */, + transaction->m_pending_tx = w->create_transactions_all(0, info.address, info.is_subaddress, 1, cryptonote::TX_OUTPUT_DECOYS, 0 /* unlock_time */, priority, extra, subaddr_account, subaddr_indices); pendingTxPostProcess(transaction); @@ -2285,89 +2281,71 @@ std::vector* WalletImpl::MyBns() const auto w = wallet(); - std::vector> rpc_results; - std::vector requests(1); + nlohmann::json req_params{ + {"entries", nlohmann::json::array()} + }; std::unordered_map cache = w->get_bns_cache(); for (uint32_t index = 0; index < w->get_num_subaddresses(0); ++index) { - if (requests.back().entries.size() >= cryptonote::rpc::BNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES) - requests.emplace_back(); - requests.back().entries.push_back(w->get_subaddress_as_str({0, index})); + req_params["entries"].push_back(w->get_subaddress_as_str({0, index})); } - rpc_results.reserve(requests.size()); - for (auto const &request : requests) + auto [success, result] = w->bns_owners_to_names(req_params); + if (!success) { - auto [success, result] = w->bns_owners_to_names(request); - if (!success) - { - setStatusError(tr("Connection to daemon failed when requesting BNS names")); - } - rpc_results.emplace_back(std::move(result)); + setStatusError(tr("Connection to daemon failed when requesting BNS names")); } auto nettype = w->nettype(); - for (size_t i = 0; i < rpc_results.size(); i++) + + for (auto const &entry : result["entries"]) { - auto const &rpc = rpc_results[i]; - for (auto const &entry : rpc) + std::string_view name; + std::string value_bchat, value_wallet, value_belnet, value_eth; + if (auto got = cache.find(entry["name_hash"]); got != cache.end()) { - std::string_view name; - std::string value_bchat, value_wallet, value_belnet, value_eth_addr; - if (auto got = cache.find(entry.name_hash); got != cache.end()) - { - name = got->second.name; - //BCHAT - { - bns::mapping_value mv; - const auto type = bns::mapping_type::bchat; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_bchat_value), &mv) - && mv.decrypt(name, type)) - value_bchat = mv.to_readable_value(nettype, type); - } - //ETH_ADDRESS - { - bns::mapping_value mv; - const auto type = bns::mapping_type::eth_addr; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_eth_addr_value), &mv) - && mv.decrypt(name, type)) - value_eth_addr = mv.to_readable_value(nettype, type); - } - //WALLET + name = got->second.name; + auto decrypt_value = [&](std::string_view key, bns::mapping_type type, std::string& out) { + auto it = entry.find(key); + if (it != entry.end() && !it->empty()) { - bns::mapping_value mv; - const auto type = bns::mapping_type::wallet; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_wallet_value), &mv) - && mv.decrypt(name, type)) - value_wallet = mv.to_readable_value(nettype,type); + bns::mapping_value mv; + const auto& hex_str = it->get_ref(); + if (!hex_str.empty() && bns::mapping_value::validate_encrypted(type, oxenc::from_hex(hex_str), &mv) && + mv.decrypt(name, type)) + { + out = mv.to_readable_value(nettype, type); + } } - //BELNET - { - bns::mapping_value mv; - const auto type = bns::mapping_type::belnet; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(entry.encrypted_belnet_value), &mv) - && mv.decrypt(name, type)) - value_belnet = mv.to_readable_value(nettype, type); - } - } - auto &info = my_bns->emplace_back(); - info.name_hash = entry.name_hash; - info.name = name.empty() ? "(none)" : std::string(name); - info.value_bchat = value_bchat.empty() ? "(none)" : value_bchat; - info.value_wallet = value_wallet.empty() ? "(none)" : value_wallet; - info.value_belnet = value_belnet.empty() ? "(none)" : value_belnet; - info.value_eth_addr = value_eth_addr.empty() ? "(none)" : value_eth_addr; - info.owner = entry.owner; - info.backup_owner = entry.backup_owner? *entry.backup_owner : "(none)"; - info.update_height = entry.update_height; - info.expiration_height = entry.expiration_height ? *entry.expiration_height : 0; - info.encrypted_bchat_value = entry.encrypted_bchat_value.empty() ? "(none)" : entry.encrypted_bchat_value; - info.encrypted_wallet_value = entry.encrypted_wallet_value.empty() ? "(none)" : entry.encrypted_wallet_value; - info.encrypted_belnet_value = entry.encrypted_belnet_value.empty() ? "(none)" : entry.encrypted_belnet_value; - info.encrypted_eth_addr_value = entry.encrypted_eth_addr_value.empty() ? "(none)" : entry.encrypted_eth_addr_value; + }; + + decrypt_value("encrypted_bchat_value", bns::mapping_type::bchat, value_bchat); + decrypt_value("encrypted_wallet_value", bns::mapping_type::wallet, value_wallet); + decrypt_value("encrypted_belnet_value", bns::mapping_type::belnet, value_belnet); + decrypt_value("encrypted_eth_addr_value", bns::mapping_type::eth_addr, value_eth); } + + auto &info = my_bns->emplace_back(); + info.name_hash = entry["name_hash"]; + info.name = name.empty() ? "(none)" : std::string(name); + info.value_bchat = value_bchat.empty() ? "(none)" : value_bchat; + info.value_wallet = value_wallet.empty() ? "(none)" : value_wallet; + info.value_belnet = value_belnet.empty() ? "(none)" : value_belnet; + info.value_eth_addr = value_eth.empty() ? "(none)" : value_eth; + info.owner = entry["owner"]; + if (entry.contains("backup_owner") && !entry["backup_owner"].is_null()) + info.backup_owner = entry["backup_owner"]; + else + info.backup_owner = "(none)"; + info.update_height = entry["update_height"]; + info.expiration_height = entry["expiration_height"]; + + info.encrypted_bchat_value = entry["encrypted_bchat_value"].get().empty() ? "(none)" : entry["encrypted_bchat_value"]; + info.encrypted_wallet_value = entry["encrypted_wallet_value"].get().empty() ? "(none)" : entry["encrypted_wallet_value"]; + info.encrypted_belnet_value = entry["encrypted_belnet_value"].get().empty() ? "(none)" : entry["encrypted_belnet_value"]; + info.encrypted_eth_addr_value = entry["encrypted_eth_addr_value"].get().empty() ? "(none)" : entry["encrypted_eth_addr_value"]; } return my_bns; } @@ -3102,13 +3080,16 @@ void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const EXPORT std::optional WalletImpl::hardForkVersion() const { - return m_wallet_ptr->get_hard_fork_version(); + auto v = m_wallet_ptr->get_hard_fork_version(); + if (!v) + return std::nullopt; + return static_cast(*v); } EXPORT bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const { - return wallet()->use_fork_rules(version = 17,early_blocks); + return wallet()->use_fork_rules(static_cast(version), early_blocks); // have to update } EXPORT diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index af95cc32369..24a26a2ed84 100755 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -635,7 +635,7 @@ struct Wallet virtual int countBns() = 0; /** - * @brief listCurrentStakes - returns a list of the wallets locked stakes, provides both service node address and the staked amount + * @brief listCurrentStakes - returns a list of the wallets locked stakes, provides both master node address and the staked amount * @return */ virtual std::vector* listCurrentStakes() const = 0; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 010782eff98..66b623d3c49 100755 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -220,30 +220,21 @@ void WalletManagerImpl::setDaemonAddress(std::string address) } EXPORT -bool WalletManagerImpl::connected(uint32_t *version) -{ +bool WalletManagerImpl::connected(uint32_t* version) { using namespace cryptonote::rpc; try { - auto res = m_http_client.json_rpc(GET_VERSION::names()[0], {}); - if (version) *version = res.version; + auto res = m_http_client.json_rpc("get_version"); + if (version) + *version = res["version"]; return true; - } catch (...) {} + } catch (...) { + } return false; } -template -static std::optional json_rpc(cryptonote::rpc::http_client& http, const typename RPC::request& req = {}) -{ - using namespace cryptonote::rpc; - try { return http.json_rpc(RPC::names()[0], req); } - catch (...) {} - return std::nullopt; -} - -static std::optional get_info(cryptonote::rpc::http_client& http) -{ - return json_rpc(http); +static nlohmann::json get_info(cryptonote::rpc::http_client& http) { + return http.json_rpc("get_info"); } @@ -251,7 +242,7 @@ EXPORT uint64_t WalletManagerImpl::blockchainHeight() { auto res = get_info(m_http_client); - return res ? res->height : 0; + return res ? res["height"].get() : 0; } EXPORT @@ -260,14 +251,14 @@ uint64_t WalletManagerImpl::blockchainTargetHeight() auto res = get_info(m_http_client); if (!res) return 0; - return std::max(res->target_height, res->height); + return std::max(res["target_height"].get(), res["height"].get()); } EXPORT uint64_t WalletManagerImpl::blockTarget() { auto res = get_info(m_http_client); - return res ? res->target : 0; + return res ? res["target"].get() : 0; } ///////////////////// WalletManagerFactory implementation ////////////////////// diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index ddf268b16ba..35d74f1d78f 100755 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -82,8 +82,8 @@ bool NodeRPCProxy::get_rpc_version(rpc::version_t &rpc_version) const if (m_rpc_version == rpc::version_t{0, 0}) { try { - auto res = invoke_json_rpc({}); - m_rpc_version = rpc::make_version(res.version); + auto res = m_http_client.json_rpc("get_version", {}); + m_rpc_version = rpc::make_version(res.at("version").get()); } catch (...) { return false; } } rpc_version = m_rpc_version; @@ -105,14 +105,21 @@ bool NodeRPCProxy::get_info() const if (now >= m_get_info_time + 30s) // re-cache every 30 seconds { try { - auto resp_t = invoke_json_rpc({}); - m_height = resp_t.height; - m_target_height = resp_t.target_height; - m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit; - m_immutable_height = resp_t.immutable_height; - m_get_info_time = now; - m_height_time = now; - } catch (...) { + auto res = m_http_client.json_rpc("get_info", {}); + m_height = res.at("height").get(); + m_target_height = res.at("target_height").get(); + auto it_block_weight_limit = res.find("block_weight_limit"); + if (it_block_weight_limit != res.end()) + m_block_weight_limit = res.at("block_weight_limit"); + else + m_block_weight_limit = res.at("block_size_limit"); + auto it_immutable_height = res.find("immutable_height"); + if (it_immutable_height != res.end()) + m_immutable_height = res.at("immutable_height").get(); + m_get_info_time = now; + m_height_time = now; + } catch (const std::exception& e) { + // log::error(logcat, "Failed to get info message: {}", e.what()); //TODO return false; } } return true; @@ -122,8 +129,10 @@ bool NodeRPCProxy::get_height(uint64_t &height) const { auto now = std::chrono::steady_clock::now(); if (now >= m_height_time + 30s) // re-cache every 30 seconds + { if (!get_info()) return false; + } height = m_height; return true; } @@ -159,28 +168,29 @@ bool NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_heigh if (m_earliest_height[version] == 0) { - rpc::HARD_FORK_INFO::request req_t{}; - req_t.version = version; + nlohmann::json req_params{ + {"version", version} + }; try { - auto resp_t = invoke_json_rpc(req_t); - - if (!resp_t.earliest_height) - return false; - - m_earliest_height[version] = *resp_t.earliest_height; - } catch (...) { return false; } + auto res = m_http_client.json_rpc("hard_fork_info", req_params); + m_earliest_height[version] = res.at("earliest_height").get(); + } catch (const std::exception& e) { + // log::error(logcat, "Failed to get earliest height: {}", e.what()); //TODO + return false; + } } earliest_height = m_earliest_height[version]; return true; } -std::optional NodeRPCProxy::get_hardfork_version() const +std::optional NodeRPCProxy::get_hardfork_version() const { if (m_offline) return std::nullopt; try { - return invoke_json_rpc({}).version; + auto res = m_http_client.json_rpc("hard_fork_info", {}); + return res.at("version").get(); }catch (...) {} return std::nullopt; @@ -194,14 +204,15 @@ bool NodeRPCProxy::refresh_dynamic_base_fee_cache(uint64_t grace_blocks) const if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks) { - rpc::GET_BASE_FEE_ESTIMATE::request req_t{}; - req_t.grace_blocks = grace_blocks; + nlohmann::json req_params{ + {"grace_blocks", grace_blocks} + }; try { - auto resp_t = invoke_json_rpc(req_t); - m_dynamic_base_fee_estimate = {resp_t.fee_per_byte, resp_t.fee_per_output}; + auto res = m_http_client.json_rpc("get_fee_estimate", req_params); + m_dynamic_base_fee_estimate = {res.at("fee_per_byte").get(), res.at("fee_per_output").get()}; m_dynamic_base_fee_estimate_cached_height = height; m_dynamic_base_fee_estimate_grace_blocks = grace_blocks; - m_fee_quantization_mask = resp_t.quantization_mask; + m_fee_quantization_mask = res.at("quantization_mask").get(); } catch (...) { return false; } } return true; @@ -229,11 +240,22 @@ bool NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) co return true; } -std::pair> NodeRPCProxy::get_master_nodes(std::vector pubkeys) const +std::pair NodeRPCProxy::get_master_nodes(std::vector pubkeys) const { - rpc::GET_MASTER_NODES::request req{}; - req.master_node_pubkeys = std::move(pubkeys); - return get_result_pair(req, [](auto&& res) { return std::move(res.master_node_states); }); + std::pair result; + auto& [success, resolved] = result; + success = false; + nlohmann::json req_params{ + {"master_node_pubkeys", pubkeys} + }; + try { + auto res = m_http_client.json_rpc("get_master_nodes", req_params); + resolved = res.at("master_node_states"); + } catch (...) { + return result; + } + success = true; + return result; } // Updates the cache of all master nodes; the mutex lock must be already held @@ -241,19 +263,28 @@ bool NodeRPCProxy::update_all_master_nodes_cache(uint64_t height) const { if (m_offline) return false; + nlohmann::json req{}; + req["fields"] = nlohmann::json{}; + for (const auto& field : { + "active", "contributors", "funded", "locked_contributions", "registration_height", + "requested_unlock_height", "master_node_pubkey", "staking_requirement", "total_contributed", + "total_reserved", + }) + req["fields"][field] = true; + try { - auto res = invoke_json_rpc({}); + auto res = m_http_client.json_rpc("get_master_nodes", req); m_all_master_nodes_cached_height = height; - m_all_master_nodes = std::move(res.master_node_states); + m_all_master_nodes = std::move(res.at("master_node_states")); } catch (...) { return false; } return true; } -std::pair> NodeRPCProxy::get_all_master_nodes() const +std::pair NodeRPCProxy::get_all_master_nodes() const { - std::pair> result; + std::pair result; auto& [success, mns] = result; success = false; @@ -275,9 +306,9 @@ std::pair> // Filtered version of the above that caches the filtered result as long as used on the same // contributor at the same height (which is very common, for example, for wallet balance lookups). -std::pair> NodeRPCProxy::get_contributed_master_nodes(const std::string &contributor) const +std::pair NodeRPCProxy::get_contributed_master_nodes(const std::string &contributor) const { - std::pair> result; + std::pair result; auto& [success, mns] = result; success = false; @@ -295,8 +326,8 @@ std::pair> std::copy_if(m_all_master_nodes.begin(), m_all_master_nodes.end(), std::back_inserter(m_contributed_master_nodes), [&contributor](const auto& mn) { - return std::any_of(mn.contributors.begin(), mn.contributors.end(), - [&contributor](const auto& c) { return contributor == c.address; }); + return std::any_of(mn["contributors"].begin(), mn["contributors"].end(), + [&contributor](const nlohmann::json& c) { return contributor == c["address"].get(); }); } ); m_contributed_master_nodes_cached_height = height; @@ -310,9 +341,9 @@ std::pair> return result; } -std::pair> NodeRPCProxy::get_master_node_blacklisted_key_images() const +std::pair NodeRPCProxy::get_master_node_blacklisted_key_images() const { - std::pair> result; + std::pair result; auto& [success, mns] = result; success = false; @@ -325,9 +356,9 @@ std::pair({}); + auto res = m_http_client.json_rpc("get_master_node_blacklisted_key_images", {}); m_master_node_blacklisted_key_images_cached_height = height; - m_master_node_blacklisted_key_images = std::move(res.blacklist); + m_master_node_blacklisted_key_images = std::move(res.at("blacklist")); } catch (...) { return result; } @@ -340,28 +371,66 @@ std::pair> NodeRPCProxy::bns_owners_to_names(cryptonote::rpc::BNS_OWNERS_TO_NAMES::request const &request) const +std::pair NodeRPCProxy::bns_owners_to_names(nlohmann::json const &request) const { - return get_result_pair(request, [](auto&& res) { return std::move(res.entries); }); + std::pair result; + auto& [success, resolved] = result; + success = false; + + if (m_offline || !get_info()) + return result; + + try { + auto res = m_http_client.json_rpc("bns_owners_to_names", request); + resolved = res; + } catch (...) { + return result; + } + success = true; + return result; } -std::pair> NodeRPCProxy::bns_names_to_owners(cryptonote::rpc::BNS_NAMES_TO_OWNERS::request const &request) const +std::pair NodeRPCProxy::bns_names_to_owners(nlohmann::json const& request) const { - return get_result_pair(request, [](auto&& res) { return std::move(res.entries); }); + std::pair result; + auto& [success, resolved] = result; + success = false; + + if (m_offline || !get_info()) + return result; + + try { + auto res = m_http_client.json_rpc("bns_names_to_owners", request); + auto st_it = res.find("status"); + if (st_it == res.end() || !st_it->is_string()) { + // log::error(logcat, "Did not find expected result or status in:\n{}", res.dump()); + throw std::runtime_error{"Missing status"}; + } + if (auto status = st_it->get(); status != "OK") + throw std::runtime_error("Received error status"); + + resolved = res["result"]; + } catch (...) { + // log::error(logcat, "Failed to get ONS info: {}", e.what()); + return result; + } + + success = true; + return result; } -std::pair NodeRPCProxy::bns_resolve(cryptonote::rpc::BNS_RESOLVE::request const &request) const + +std::pair NodeRPCProxy::bns_resolve(nlohmann::json const& request) const { - std::pair result; + std::pair result; auto& [success, resolved] = result; success = false; - uint64_t height; - if (m_offline || !get_height(height)) + if (m_offline || !get_info()) return result; { try { - auto res = m_http_client.json_rpc(rpc::BNS_RESOLVE::names().front(), request); + auto res = m_http_client.json_rpc("bns_resolve", request); resolved = res; } catch (...) { return result; diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index bca24375507..40572762a82 100755 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -33,6 +33,7 @@ #include #include "rpc/http_client.h" #include "rpc/core_rpc_server_commands_defs.h" +#include namespace tools { @@ -54,16 +55,17 @@ class NodeRPCProxy bool get_earliest_height(uint8_t version, uint64_t &earliest_height) const; bool get_dynamic_base_fee_estimate(uint64_t grace_blocks, cryptonote::byte_and_output_fees &fees) const; bool get_fee_quantization_mask(uint64_t &fee_quantization_mask) const; - std::optional get_hardfork_version() const; - - std::pair> get_master_nodes(std::vector pubkeys) const; - std::pair> get_all_master_nodes() const; - std::pair> get_contributed_master_nodes(const std::string& contributor) const; - std::pair> get_master_node_blacklisted_key_images() const; - std::pair> bns_owners_to_names(cryptonote::rpc::BNS_OWNERS_TO_NAMES::request const &request) const; - std::pair> bns_names_to_owners(cryptonote::rpc::BNS_NAMES_TO_OWNERS::request const &request) const; - std::pair - bns_resolve(cryptonote::rpc::BNS_RESOLVE::request const &request) const; + std::optional get_hardfork_version() const; + + // Note that this master node responses only fills out fields that are used in wallet code, not + // the full master node records. (see node_rpc_proxy.cpp for the precise list). + std::pair get_master_nodes(std::vector pubkeys) const; + std::pair get_all_master_nodes() const; + std::pair get_contributed_master_nodes(const std::string& contributor) const; + std::pair get_master_node_blacklisted_key_images() const; + std::pair bns_owners_to_names(nlohmann::json const &request) const; + std::pair bns_names_to_owners(nlohmann::json const &request) const; + std::pair bns_resolve(nlohmann::json const &request) const; private: bool get_info() const; @@ -117,17 +119,17 @@ class NodeRPCProxy bool m_offline; mutable uint64_t m_master_node_blacklisted_key_images_cached_height; - mutable std::vector m_master_node_blacklisted_key_images; + mutable nlohmann::json m_master_node_blacklisted_key_images; bool update_all_master_nodes_cache(uint64_t height) const; mutable std::mutex m_mn_cache_mutex; mutable uint64_t m_all_master_nodes_cached_height; - mutable std::vector m_all_master_nodes; + mutable nlohmann::json m_all_master_nodes; mutable uint64_t m_contributed_master_nodes_cached_height; mutable std::string m_contributed_master_nodes_cached_address; - mutable std::vector m_contributed_master_nodes; + mutable nlohmann::json m_contributed_master_nodes; mutable uint64_t m_height; mutable uint64_t m_immutable_height; diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index 794d3a77d08..5cda407f1d7 100755 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -103,11 +103,11 @@ fs::path get_rings_filename(fs::path filename) static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - uint8_t buffer[sizeof(key_image) + sizeof(key) + config::HASH_KEY_RINGDB.size() + sizeof(field)]; + uint8_t buffer[sizeof(key_image) + sizeof(key) + cryptonote::hashkey::RINGDB.size() + sizeof(field)]; memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer + sizeof(key_image), &key, sizeof(key)); - memcpy(buffer + sizeof(key_image) + sizeof(key), config::HASH_KEY_RINGDB.data(), config::HASH_KEY_RINGDB.size()); - memcpy(buffer + sizeof(key_image) + sizeof(key) + config::HASH_KEY_RINGDB.size(), &field, sizeof(field)); + memcpy(buffer + sizeof(key_image) + sizeof(key), cryptonote::hashkey::RINGDB.data(), cryptonote::hashkey::RINGDB.size()); + memcpy(buffer + sizeof(key_image) + sizeof(key) + cryptonote::hashkey::RINGDB.size(), &field, sizeof(field)); crypto::hash hash; // if field is 0, backward compat mode: hash without the field crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data); diff --git a/src/wallet/tx_construction_data.h b/src/wallet/tx_construction_data.h index dc00e908df4..134d63d0164 100755 --- a/src/wallet/tx_construction_data.h +++ b/src/wallet/tx_construction_data.h @@ -47,7 +47,7 @@ struct tx_construction_data uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer std::set subaddr_indices; // set of address indices used as inputs in this transfer - uint8_t hf_version; + cryptonote::hf hf_version; cryptonote::txtype tx_type; }; @@ -106,7 +106,7 @@ void serialize(Archive &a, wallet::tx_construction_data &x, const unsigned int v if (ver < 6) { x.tx_type = cryptonote::txtype::standard; - x.hf_version = cryptonote::network_version_14_enforce_checkpoints; + x.hf_version = cryptonote::hf::hf15_flash; } } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6a7dbcb9ef1..3df14e83d79 100755 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "common/password.h" #include "common/string_util.h" #include "cryptonote_basic/tx_extra.h" @@ -55,7 +56,6 @@ #include "common/boost_serialization_helper.h" #include "common/command_line.h" #include "common/threadpool.h" -#include "epee/profile_tools.h" #include "crypto/crypto.h" #include "serialization/binary_utils.h" #include "serialization/string.h" @@ -79,7 +79,9 @@ #include "ringct/rctSigs.h" #include "ringdb.h" #include "device/device_cold.hpp" +#ifdef DEVICE_TREZOR_READY #include "device_trezor/device_trezor.hpp" +#endif #include "cryptonote_core/master_node_list.h" #include "cryptonote_core/master_node_rules.h" @@ -115,7 +117,7 @@ namespace { constexpr float RECENT_OUTPUT_RATIO = 0.5f; // 50% of outputs are from the recent zone constexpr float RECENT_OUTPUT_DAYS = 1.8f; // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) constexpr time_t RECENT_OUTPUT_ZONE = RECENT_OUTPUT_DAYS * 86400; - + constexpr uint64_t RECENT_OUTPUT_BLOCKS = RECENT_OUTPUT_DAYS * BLOCKS_PER_DAY; constexpr uint64_t FEE_ESTIMATE_GRACE_BLOCKS = 10; // estimate fee valid for that many blocks @@ -134,11 +136,11 @@ namespace { constexpr double GAMMA_SCALE = 1/1.61; constexpr uint32_t DEFAULT_MIN_OUTPUT_COUNT = 5; - constexpr uint64_t DEFAULT_MIN_OUTPUT_VALUE = 2*COIN; + constexpr uint64_t DEFAULT_MIN_OUTPUT_VALUE = 2 * beldex::COIN; constexpr auto DEFAULT_INACTIVITY_LOCK_TIMEOUT = 10min; - constexpr uint8_t IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION = 12; + constexpr hf IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION = hf::hf12_security_signature; constexpr std::string_view SIG_MAGIC = "SigV1"sv; constexpr std::string_view MULTISIG_MAGIC = "MultisigV1"sv; @@ -208,17 +210,51 @@ namespace { return false; } - std::string get_text_reason(const rpc::SEND_RAW_TX::response &res, cryptonote::transaction const *tx, bool flash) - { - if (flash) { - return res.reason; - } - else { - std::string reason = print_tx_verification_context (res.tvc, tx); - reason += print_vote_verification_context(res.tvc.m_vote_ctx); - return reason; - } - } + // std::string get_text_reason(const nlohmann::json& res, cryptonote::transaction const *tx, bool flash) + // { + // if (flash) { + // return res["reason"].get(); + // } + // else { + // std::ostringstream os; + + // const auto tvc = res["tvc"]; + // if (auto got = tvc.find("m_verbose_error"); got != tvc.end()) os << res["tvc"]["m_verbose_error"].get() << "\n"; + // if (auto got = tvc.find("m_verifivation_failed"); got != tvc.end()) os << "Verification failed, connection should be dropped, "; //bad tx, should drop connection + // if (auto got = tvc.find("m_verifivation_impossible"); got != tvc.end()) os << "Verification impossible, related to alt chain, "; //the transaction is related with an alternative blockchain + // if (auto got = tvc.find("m_should_be_relayed"); got == tvc.end()) os << "TX should NOT be relayed, "; + // if (auto got = tvc.find("m_added_to_pool"); got != tvc.end()) os << "TX added to pool, "; + // if (auto got = tvc.find("m_low_mixin"); got != tvc.end()) os << "Insufficient mixin, "; + // if (auto got = tvc.find("m_double_spend"); got != tvc.end()) os << "Double spend TX, "; + // if (auto got = tvc.find("m_invalid_input"); got != tvc.end()) os << "Invalid inputs, "; + // if (auto got = tvc.find("m_invalid_output"); got != tvc.end()) os << "Invalid outputs, "; + // if (auto got = tvc.find("m_too_few_outputs"); got != tvc.end()) os << "Need at least 2 outputs, "; + // if (auto got = tvc.find("m_too_big"); got != tvc.end()) os << "TX too big, "; + // if (auto got = tvc.find("m_overspend"); got != tvc.end()) os << "Overspend, "; + // if (auto got = tvc.find("m_fee_too_low"); got != tvc.end()) os << "Fee too low, "; + // if (auto got = tvc.find("m_invalid_version"); got != tvc.end()) os << "TX has invalid version, "; + // if (auto got = tvc.find("m_invalid_type"); got != tvc.end()) os << "TX has invalid type, "; + // if (auto got = tvc.find("m_key_image_locked_by_mnode"); got != tvc.end()) os << "Key image is locked by master node, "; + // if (auto got = tvc.find("m_key_image_blacklisted"); got != tvc.end()) os << "Key image is blacklisted on the master node network, "; + + // const auto m_vote_ctx = tvc["m_vote_ctx"]; + // if (auto got = m_vote_ctx.find("m_validator_index_out_of_bounds"); got != m_vote_ctx.end()) os << "Validator index out of bounds"; + // if (auto got = m_vote_ctx.find("m_signature_not_valid"); got != m_vote_ctx.end()) os << "Signature not valid, "; + // if (auto got = m_vote_ctx.find("m_added_to_pool"); got != m_vote_ctx.end()) os << "Added to pool, "; + // if (auto got = m_vote_ctx.find("m_not_enough_votes"); got != m_vote_ctx.end()) os << "Not enough votes, "; + // if (auto got = m_vote_ctx.find("m_incorrect_voting_group"); got != m_vote_ctx.end()) os << "Incorrect voting group specified,"; + // if (auto got = m_vote_ctx.find("m_votes_not_sorted"); got != m_vote_ctx.end()) os << "Votes are not stored in ascending order"; + + // if (tx) + // os << "TX Version: " << tx->version << ", Type: " << tx->type; + + // std::string buf = os.str(); + // if (buf.size() >= 2 && buf[buf.size() - 2] == ',') + // buf.resize(buf.size() - 2); + + // return buf; + // } + // } size_t get_num_outputs(const std::vector &dsts, const std::vector &transfers, const std::vector &selected_transfers, const beldex_construct_tx_params& tx_params) { @@ -323,7 +359,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl const bool testnet = command_line::get_arg(vm, opts.testnet); const bool devnet = command_line::get_arg(vm, opts.devnet); const bool fakenet = command_line::get_arg(vm, opts.regtest); - network_type nettype = testnet ? TESTNET : devnet ? DEVNET : fakenet ? FAKECHAIN : MAINNET; + network_type nettype = testnet ? network_type::TESTNET : devnet ? network_type::DEVNET : fakenet ? network_type::FAKECHAIN : network_type::MAINNET; THROW_WALLET_EXCEPTION_IF(testnet + devnet + fakenet > 1, tools::error::wallet_internal_error, "At most one of --testnet, --devnet, or --regtest may be specified"); @@ -475,7 +511,7 @@ std::pair, tools::password_container> generate_f { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool devnet = command_line::get_arg(vm, opts.devnet); - const network_type nettype = testnet ? TESTNET : devnet ? DEVNET : MAINNET; + const network_type nettype = testnet ? network_type::TESTNET : devnet ? network_type::DEVNET : network_type::MAINNET; /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly @@ -718,7 +754,7 @@ void drop_from_short_history(std::list &short_chain_history, size_ } } -size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool clsag) +size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool clsag, bool bulletproof_plus) { size_t size = 0; @@ -745,7 +781,7 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra size_t log_padded_outputs = 0; while ((1< 2) + size_t size = estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, clsag, bulletproof_plus); + if (n_outputs > 2) { - const uint64_t bp_base = 368; + const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2) size_t log_padded_outputs = 2; while ((1<get_tx_pub_key_from_received_outs = [wallet] (const auto& td) { return wallet->get_tx_pub_key_from_received_outs(td); }; } -bool get_pruned_tx(const rpc::GET_TRANSACTIONS::entry &entry, cryptonote::transaction &tx, crypto::hash &tx_hash) +bool get_pruned_tx(const nlohmann::json& entry, cryptonote::transaction &tx, crypto::hash &tx_hash) { cryptonote::blobdata bd; // easy case if we have the whole tx - if (entry.as_hex || (entry.prunable_as_hex && entry.pruned_as_hex)) + if (auto hex_it = entry.find("as_hex"); hex_it != entry.end() || (entry.contains("prunable") && entry.contains("pruned"))) { - if (entry.as_hex) { - CHECK_AND_ASSERT_MES(oxenc::is_hex(*entry.as_hex), false, "Failed to parse tx data"); - bd = oxenc::from_hex(*entry.as_hex); - } else { - CHECK_AND_ASSERT_MES(oxenc::is_hex(*entry.pruned_as_hex) && oxenc::is_hex(*entry.prunable_as_hex), false, "Failed to parse tx data"); - bd.reserve(oxenc::from_hex_size(entry.pruned_as_hex->size() + entry.prunable_as_hex->size())); - oxenc::from_hex(entry.pruned_as_hex->begin(), entry.pruned_as_hex->end(), std::back_inserter(bd)); - oxenc::from_hex(entry.prunable_as_hex->begin(), entry.prunable_as_hex->end(), std::back_inserter(bd)); - } + std::string hex_blob; + if (hex_it != entry.end()) + hex_blob = hex_it->get(); + else + hex_blob = entry["pruned"].get() + entry["prunable"].get(); + + CHECK_AND_ASSERT_MES(oxenc::is_hex(hex_blob), false, "Invalid tx data"); + bd = oxenc::from_hex(hex_blob); CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_tx_from_blob(bd, tx), false, "Invalid tx data"); tx_hash = cryptonote::get_transaction_hash(tx); // if the hash was given, check it matches - CHECK_AND_ASSERT_MES(entry.tx_hash.empty() || tools::type_to_hex(tx_hash) == entry.tx_hash, false, + CHECK_AND_ASSERT_MES(entry.value("tx_hash", ""sv).empty() || tools::type_to_hex(tx_hash) == entry["tx_hash"], false, "Response claims a different hash than the data yields"); return true; } // case of a pruned tx with its prunable data hash - if (entry.pruned_as_hex && entry.prunable_hash) + if (entry.contains("pruned") && entry.contains("prunable_hash")) { crypto::hash ph; - CHECK_AND_ASSERT_MES(tools::hex_to_type(*entry.prunable_hash, ph), false, "Failed to parse prunable hash"); - CHECK_AND_ASSERT_MES(oxenc::is_hex(*entry.pruned_as_hex), false, "Failed to parse pruned data"); - bd = oxenc::from_hex(*entry.pruned_as_hex); + CHECK_AND_ASSERT_MES(tools::hex_to_type(entry["prunable_hash"].get(), ph), false, "Failed to parse prunable hash"); + CHECK_AND_ASSERT_MES(oxenc::is_hex(entry["pruned"].get()), false, "Invalid pruned tx entry"); + bd = oxenc::from_hex(entry["pruned"].get()); CHECK_AND_ASSERT_MES(parse_and_validate_tx_base_from_blob(bd, tx), false, "Invalid base tx data"); // only v2 txes can calculate their txid after pruned if (bd[0] > 1) @@ -889,7 +924,7 @@ bool get_pruned_tx(const rpc::GET_TRANSACTIONS::entry &entry, cryptonote::transa else { // for v1, we trust the dameon - CHECK_AND_ASSERT_MES(tools::hex_to_type(entry.tx_hash, tx_hash), false, "Failed to parse tx hash"); + CHECK_AND_ASSERT_MES(tools::hex_to_type(entry["tx_hash"].get(), tx_hash), false, "Failed to parse tx hash"); } return true; } @@ -908,12 +943,12 @@ gamma_picker::gamma_picker(const std::vector &rct_offsets, double shap rct_offsets(rct_offsets) { gamma = std::gamma_distribution(shape, scale); - THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17, error::wallet_internal_error, "Bad offset calculation"); + THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= DEFAULT_TX_SPENDABLE_AGE_V17, error::wallet_internal_error, "Bad offset calculation"); const size_t blocks_in_a_year = BLOCKS_PER_DAY * 365; const size_t blocks_to_consider = std::min(rct_offsets.size(), blocks_in_a_year); const double outputs_to_consider = rct_offsets.back() - (blocks_to_consider < rct_offsets.size() ? rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0); begin = rct_offsets.data(); - end = rct_offsets.data() + rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17; + end = rct_offsets.data() + rct_offsets.size() - (std::max(1, DEFAULT_TX_SPENDABLE_AGE_V17) - 1); num_rct_outputs = *(end - 1); THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs"); average_output_time = tools::to_seconds(TARGET_BLOCK_TIME) * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range @@ -1059,10 +1094,10 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_segregate_pre_fork_outputs(true), m_key_reuse_mitigation2(true), m_segregation_height(0), - m_ignore_outputs_above(MONEY_SUPPLY), + m_ignore_outputs_above(beldex::MONEY_SUPPLY), m_ignore_outputs_below(0), m_track_uses(false), - m_inactivity_lock_timeout(m_nettype == MAINNET ? DEFAULT_INACTIVITY_LOCK_TIMEOUT : 0s), + m_inactivity_lock_timeout(m_nettype == network_type::MAINNET ? DEFAULT_INACTIVITY_LOCK_TIMEOUT : 0s), m_is_initialized(false), m_kdf_rounds(kdf_rounds), is_old_file_format(false), @@ -1701,6 +1736,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & case rct::RCTType::Bulletproof: case rct::RCTType::Bulletproof2: case rct::RCTType::CLSAG: + case rct::RCTType::BulletproofPlus: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTType::Full: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); @@ -1829,7 +1865,7 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, - uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool flash, bool double_spend_seen, + uint64_t height, hf block_version, uint64_t ts, bool miner_tx, bool pool, bool flash, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map, size_t> *output_tracker_cache) { if (!tx.is_transfer() || tx.version <= txversion::v1) @@ -2621,35 +2657,35 @@ bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) con //---------------------------------------------------------------------------------------------------- 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache) { - THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices["indices"].size(), error::wallet_internal_error, "block transactions=" + std::to_string(bche.txs.size()) + - " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size())); + " not match with daemon response size=" + std::to_string(parsed_block.o_indices["indices"].size())); //handle transactions from new block //optimization: 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 if (!should_skip_block(b, height)) { - TIME_MEASURE_START(miner_tx_handle_time); + auto miner_tx_handle_time_start = std::chrono::steady_clock::now(); if (m_refresh_type != RefreshNoCoinbase) - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.major_version, b.timestamp, true, false, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices["indices"][0]["indices"], height, b.major_version, b.timestamp, true, false, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache); ++tx_cache_data_offset; - TIME_MEASURE_FINISH(miner_tx_handle_time); + auto miner_tx_handle_time_duration = std::chrono::steady_clock::now() - miner_tx_handle_time_start; - TIME_MEASURE_START(txs_handle_time); + auto txs_handle_time_start = std::chrono::steady_clock::now(); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx) { - process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.major_version, b.timestamp, false, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache); + process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices["indices"][idx+1]["indices"], height, b.major_version, b.timestamp, false, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache); } - TIME_MEASURE_FINISH(txs_handle_time); + auto txs_handle_time_duration = std::chrono::steady_clock::now() - txs_handle_time_start; m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); if (height > 0 && ((height % 2000) == 0)) LOG_PRINT_L0("Blockchain sync progress: " << bl_id << ", height " << height); - LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); + LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << tools::friendly_duration(miner_tx_handle_time_duration + txs_handle_time_duration) << "(" << tools::friendly_duration(miner_tx_handle_time_duration) << "/" << tools::friendly_duration(txs_handle_time_duration) <<")ms"); }else { if (!(height % 128)) @@ -2699,10 +2735,10 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height) +void wallet2::pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list& short_chain_history, std::vector& blocks, std::vector& o_indices, uint64_t& current_height) { - cryptonote::rpc::GET_BLOCKS_FAST::request req{}; - cryptonote::rpc::GET_BLOCKS_FAST::response res{}; + cryptonote::rpc::GET_BLOCKS_BIN::request req{}; + cryptonote::rpc::GET_BLOCKS_BIN::response res{}; req.block_ids = short_chain_history; MDEBUG("Pulling blocks: start_height " << start_height); @@ -2710,7 +2746,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, req.prune = true; req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; - bool r = invoke_http(req, res); + bool r = invoke_http(req, res); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == rpc::STATUS_BUSY, error::daemon_busy, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status != rpc::STATUS_OK, error::get_blocks_error, get_rpc_status(res.status)); @@ -2729,12 +2765,12 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, //---------------------------------------------------------------------------------------------------- void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &hashes) { - cryptonote::rpc::GET_HASHES_FAST::request req{}; - cryptonote::rpc::GET_HASHES_FAST::response res{}; + cryptonote::rpc::GET_HASHES_BIN::request req{}; + cryptonote::rpc::GET_HASHES_BIN::response res{}; req.block_ids = short_chain_history; req.start_height = start_height; - bool r = invoke_http(req, res); + bool r = invoke_http(req, res); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == rpc::STATUS_BUSY, error::daemon_busy, "gethashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != rpc::STATUS_OK, error::get_hashes_error, get_rpc_status(res.status)); @@ -2922,7 +2958,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks } // pull the new blocks - std::vector o_indices; + std::vector o_indices; uint64_t current_height; pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices, current_height); THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices"); @@ -2942,6 +2978,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks error = true; break; } + // TODO sean -> parsed_blocks o_indices is now a nlohmann::json and o_indices is the struct from the binary parsed_blocks[i].o_indices = std::move(o_indices[i]); } @@ -3051,25 +3088,6 @@ void wallet2::cancel_long_poll() m_long_poll_client.cancel(); } -// Requests transactions transactions; throws a wallet exception on error. -rpc::GET_TRANSACTIONS::response wallet2::request_transactions(std::vector txids_hex) -{ - rpc::GET_TRANSACTIONS::request req{}; - req.txs_hashes = std::move(txids_hex); - req.decode_as_json = false; - req.prune = true; - - rpc::GET_TRANSACTIONS::response res{}; - bool ok = invoke_http(req, res); - - THROW_WALLET_EXCEPTION_IF(!ok, error::no_connection_to_daemon, "Failed to get transaction(s) from daemon: HTTP request failed"); - THROW_WALLET_EXCEPTION_IF(res.status == rpc::STATUS_BUSY, error::daemon_busy, "Failed to get transaction(s) from daemon: daemon busy"); - THROW_WALLET_EXCEPTION_IF(res.status != rpc::STATUS_OK, error::wallet_internal_error, "Failed to get transaction(s) from daemon: daemon returned " + get_rpc_status(res.status)); - THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, "Failed to get transaction(s) from daemon: expected " + std::to_string(req.txs_hashes.size()) + " txes, got " + std::to_string(res.txs.size())); - - return res; -} - template >, int> = 0> static std::vector hashes_to_hex(It begin, It end) { @@ -3081,12 +3099,6 @@ static std::vector hashes_to_hex(It begin, It end) return hexes; } -rpc::GET_TRANSACTIONS::response wallet2::request_transactions(const std::vector& txids) -{ - return request_transactions(hashes_to_hex(txids.begin(), txids.end())); -} - - //---------------------------------------------------------------------------------------------------- std::vector wallet2::get_pool_state(bool refreshed) { @@ -3246,21 +3258,26 @@ std::vector wallet2::get_pool_state(bool refreshed) // get those txes if (!txids.empty()) { - cryptonote::rpc::GET_TRANSACTIONS::response res; + nlohmann::json res; std::vector hex_hashes; hex_hashes.reserve(txids.size()); for (const auto &p: txids) hex_hashes.push_back(tools::type_to_hex(p.first)); try { - res = request_transactions(std::move(hex_hashes)); + nlohmann::json get_transactions_params{ + {"tx_hashes", hex_hashes}, + {"prune",true}, + {"split",true} + }; + res = m_http_client.json_rpc("get_transactions", get_transactions_params); } catch (const std::exception& e) { LOG_PRINT_L0("Failed to retrieve transactions: " << e.what()); return process_txs; } - for (const auto &tx_entry: res.txs) + for (const auto &tx_entry: res["txs"]) { - if (tx_entry.in_pool) + if (tx_entry["in_pool"]) { cryptonote::transaction tx; cryptonote::blobdata bd; @@ -3272,7 +3289,7 @@ std::vector wallet2::get_pool_state(bool refreshed) [tx_hash](const std::pair &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_txs.push_back({std::move(tx), tx_hash, tx_entry.double_spend_seen, tx_entry.flash}); + process_txs.push_back({std::move(tx), tx_hash, tx_entry["double_spend_seen"].get(), tx_entry["flash"].get()}); } else { @@ -3299,7 +3316,7 @@ void wallet2::process_pool_state(const std::vector &txs) const time_t now = time(NULL); for (const auto &e: txs) { - process_new_transaction(e.tx_hash, e.tx, std::vector(), 0, 0, now, false, true, e.flash, e.double_spend_seen, {}); + process_new_transaction(e.tx_hash, e.tx, std::vector(), 0, hf::none, now, false, true, e.flash, e.double_spend_seen, {}); m_scanned_pool_txs[0].insert(e.tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { @@ -3729,27 +3746,14 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector //---------------------------------------------------------------------------------------------------- bool wallet2::get_output_blacklist(std::vector &blacklist) { - rpc::version_t rpc_version; - if (!m_node_rpc_proxy.get_rpc_version(rpc_version)) - { - THROW_WALLET_EXCEPTION(tools::error::no_connection_to_daemon, "getversion"); - } - if (rpc_version < rpc::version_t{2, 3}) - { - MWARNING("Daemon is too old, not requesting output blacklist"); - return false; - } - MDEBUG("Daemon is recent enough, requesting output blacklist"); - - cryptonote::rpc::GET_OUTPUT_BLACKLIST::response res = {}; - bool r = invoke_http({}, res); + cryptonote::rpc::GET_OUTPUT_BLACKLIST_BIN::response res{}; + bool r = invoke_http({}, res); if (!r) { MWARNING("Failed to request output blacklist: no connection to daemon"); return false; } - blacklist = std::move(res.blacklist); return true; } @@ -4030,7 +4034,7 @@ std::optional wallet2::get_keys_file_data(const epee::w value2.SetUint64(m_min_output_value); json.AddMember("min_output_value", value2, json.GetAllocator()); - value2.SetInt(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + value2.SetInt(beldex::DISPLAY_DECIMAL_POINT); json.AddMember("default_decimal_point", value2, json.GetAllocator()); value2.SetInt(m_merge_destinations ? 1 :0); @@ -4045,7 +4049,7 @@ std::optional wallet2::get_keys_file_data(const epee::w value2.SetInt(m_confirm_export_overwrite ? 1 :0); json.AddMember("confirm_export_overwrite", value2, json.GetAllocator()); - value2.SetUint(m_nettype); + value2.SetUint(static_cast>(m_nettype)); json.AddMember("nettype", value2, json.GetAllocator()); value2.SetInt(m_segregate_pre_fork_outputs ? 1 : 0); @@ -4128,7 +4132,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password) static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); epee::mlocked> cache_key_data; memcpy(cache_key_data.data(), &key, HASH_SIZE); - cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; + cache_key_data[HASH_SIZE] = hashkey::WALLET_CACHE; cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key); get_ringdb_key(); } @@ -4227,7 +4231,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_segregate_pre_fork_outputs = true; m_key_reuse_mitigation2 = true; m_segregation_height = 0; - m_ignore_outputs_above = MONEY_SUPPLY; + m_ignore_outputs_above = beldex::MONEY_SUPPLY; m_ignore_outputs_below = 0; m_track_uses = false; m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; @@ -4375,21 +4379,21 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st THROW_WALLET_EXCEPTION_IF(static_cast(m_nettype) != field_nettype, error::wallet_internal_error, (boost::format("%s wallet cannot be opened as %s wallet") % (field_nettype == 0 ? "Mainnet" : field_nettype == 1 ? "Testnet" : "Devnet") - % (m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : "devnet")).str()); + % (m_nettype == network_type::MAINNET ? "mainnet" : m_nettype == network_type::TESTNET ? "testnet" : "devnet")).str()); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregate_pre_fork_outputs, int, Int, false, true); m_segregate_pre_fork_outputs = field_segregate_pre_fork_outputs; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_reuse_mitigation2, int, Int, false, true); m_key_reuse_mitigation2 = field_key_reuse_mitigation2; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0); m_segregation_height = field_segregation_height; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_outputs_above, uint64_t, Uint64, false, MONEY_SUPPLY); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_outputs_above, uint64_t, Uint64, false, beldex::MONEY_SUPPLY); m_ignore_outputs_above = field_ignore_outputs_above; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_outputs_below, uint64_t, Uint64, false, 0); m_ignore_outputs_below = field_ignore_outputs_below; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false); m_track_uses = field_track_uses; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, inactivity_lock_timeout, uint32_t, Uint, false, - m_nettype == MAINNET ? std::chrono::seconds{DEFAULT_INACTIVITY_LOCK_TIMEOUT}.count() : 0); + m_nettype == network_type::MAINNET ? std::chrono::seconds{DEFAULT_INACTIVITY_LOCK_TIMEOUT}.count() : 0); m_inactivity_lock_timeout = std::chrono::seconds{field_inactivity_lock_timeout}; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR); m_subaddress_lookahead_major = field_subaddress_lookahead_major; @@ -4787,7 +4791,7 @@ void wallet2::generate(const fs::path& wallet_, const epee::wipeable_string& pas m_multisig_signers = multisig_signers; setup_keys(password); - create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); + create_keys_file(wallet_, false, password, m_nettype != network_type::MAINNET || create_address_file); setup_new_blockchain(); if (!wallet_.empty()) @@ -4827,7 +4831,7 @@ crypto::secret_key wallet2::generate(const fs::path& wallet_, const epee::wipeab m_refresh_from_block_height = estimate_blockchain_height(); } - create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); + create_keys_file(wallet_, false, password, m_nettype != network_type::MAINNET || create_address_file); setup_new_blockchain(); @@ -4903,7 +4907,7 @@ void wallet2::generate(const fs::path& wallet_, const epee::wipeable_string& pas m_account_public_address = account_public_address; setup_keys(password); - create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file); + create_keys_file(wallet_, true, password, m_nettype != network_type::MAINNET || create_address_file); setup_new_blockchain(); @@ -4972,7 +4976,7 @@ void wallet2::restore_from_device(const fs::path& wallet_, const epee::wipeable_ progress_callback(tr("Retrieved wallet address from device: ") + m_account.get_public_address_str(m_nettype)); m_device_name = device_name; - create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); + create_keys_file(wallet_, false, password, m_nettype != network_type::MAINNET || create_address_file); if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) { // the default lookahead setting (50:200) is clearly too much for hardware wallet @@ -5631,10 +5635,13 @@ bool wallet2::check_connection(rpc::version_t *version, bool *ssl, bool throw_on if (!m_rpc_version) { - cryptonote::rpc::GET_VERSION::response resp_t{}; - bool r = invoke_http({}, resp_t, throw_on_http_error); - if(!r || resp_t.status != rpc::STATUS_OK) return false; - m_rpc_version = resp_t.version; + try { + auto res = m_http_client.json_rpc("get_version", {}); + if(res["status"] != rpc::STATUS_OK) return false; + m_rpc_version = res["version"]; + } catch(...) { + return false; + } } if (version) *version = rpc::make_version(m_rpc_version); @@ -5844,21 +5851,28 @@ void wallet2::trim_hashchain() if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset()) { MINFO("Fixing empty hashchain"); - cryptonote::rpc::GET_BLOCK_HEADER_BY_HEIGHT::request req{}; - cryptonote::rpc::GET_BLOCK_HEADER_BY_HEIGHT::response res{}; - req.height = m_blockchain.size() - 1; - bool r = invoke_http(req, res); - if (r && res.status == rpc::STATUS_OK) - { - crypto::hash hash; - tools::hex_to_type(res.block_header->hash, hash); - m_blockchain.refill(hash); + nlohmann::json req_params{ + {"height", m_blockchain.size() - 1} + }; + try { + auto res = m_http_client.json_rpc("get_block_header_by_height", req_params); + if (res["status"] == rpc::STATUS_OK) + { + crypto::hash hash; + tools::hex_to_type(res["block_header"]["hash"].get(), hash); + m_blockchain.refill(hash); + } + else + { + MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon"); + } } - else + catch (const std::exception &e) { - MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon"); + MERROR("Failed to request block header from daemon when requesting get_block_header_by_height, hash chain may be unable to sync till the wallet is loaded with a usable daemon"); } } + if (height > 0 && m_blockchain.size() > height) { --height; @@ -6082,10 +6096,10 @@ std::map>> wallet2:: else { uint64_t unlock_height = td.m_unmined_flash && td.m_block_height == 0 ? blockchain_height : td.m_block_height; - unlock_height += std::max(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); - if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height) + unlock_height += std::max(DEFAULT_TX_SPENDABLE_AGE_V17, LOCKED_TX_ALLOWED_DELTA_BLOCKS); + if (td.m_tx.unlock_time < MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height) unlock_height = td.m_tx.unlock_time; - uint64_t unlock_time = td.m_tx.unlock_time >= CRYPTONOTE_MAX_BLOCK_NUMBER ? td.m_tx.unlock_time : 0; + uint64_t unlock_time = td.m_tx.unlock_time >= MAX_BLOCK_NUMBER ? td.m_tx.unlock_time : 0; blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0; time_to_unlock = unlock_time > now ? unlock_time - now : 0; amount = 0; @@ -6287,7 +6301,7 @@ void wallet2::get_transfers(get_transfers_args_t args, std::vector(args.max_height, args.min_height); - args.max_height = std::min(args.max_height, CRYPTONOTE_MAX_BLOCK_NUMBER); + args.max_height = std::min(args.max_height, MAX_BLOCK_NUMBER); } int args_count = args.in + args.out + args.stake + args.pending + args.failed + args.pool + args.coinbase; @@ -6490,11 +6504,14 @@ std::optional wallet2::resolve_address(std::string address, uint64_ if (bns::validate_bns_name(name, &reason)) { std::string b64_hashed_name = bns::name_to_base64_hash(name); - rpc::BNS_RESOLVE::request lookup_req{1, b64_hashed_name}; - auto [success, addr_response] = resolve(lookup_req); - if (success && addr_response.encrypted_value) + nlohmann::json req_params{ + {"type", 1}, + {"name_hash", b64_hashed_name} + }; + auto [success, addr_response] = resolve(req_params); + if (success && addr_response.contains("encrypted_value") && addr_response["encrypted_value"].is_string()) { - std::optional addr_info = bns::encrypted_wallet_value_to_info(name, *addr_response.encrypted_value, *addr_response.nonce); + std::optional addr_info = bns::encrypted_wallet_value_to_info(name, addr_response["encrypted_value"].get(), addr_response["nonce"].get()); if (addr_info) { info = std::move(*addr_info); @@ -6534,18 +6551,21 @@ void wallet2::rescan_spent() { const size_t n_outputs = std::min(chunk_size, m_transfers.size() - start_offset); MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size()); - rpc::IS_KEY_IMAGE_SPENT::request req{}; - rpc::IS_KEY_IMAGE_SPENT::response daemon_resp{}; + std::vector key_images; + key_images.reserve(n_outputs); for (size_t n = start_offset; n < start_offset + n_outputs; ++n) - req.key_images.push_back(tools::type_to_hex(m_transfers[n].m_key_image)); - bool r = invoke_http(req, daemon_resp); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == rpc::STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != rpc::STATUS_OK, error::is_key_image_spent_error, get_rpc_status(daemon_resp.status)); - THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, + key_images.push_back(tools::type_to_hex(m_transfers[n].m_key_image)); + + nlohmann::json req_params{ + {"key_images", key_images} + }; + auto kispent_res = m_http_client.json_rpc("is_key_image_spent", req_params); + THROW_WALLET_EXCEPTION_IF(kispent_res["status"] == rpc::STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(kispent_res["status"] != rpc::STATUS_OK, error::is_key_image_spent_error, get_rpc_status(kispent_res["status"])); + THROW_WALLET_EXCEPTION_IF(kispent_res["spent_status"].size() != n_outputs, error::wallet_internal_error, "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + - std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs)); - std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status)); + std::to_string(kispent_res["spent_status"].size()) + ", expected " + std::to_string(n_outputs)); + std::copy(kispent_res["spent_status"].begin(), kispent_res["spent_status"].end(), std::back_inserter(spent_status)); } // update spent status @@ -6555,7 +6575,7 @@ void wallet2::rescan_spent() // a view wallet may not know about key images if (!td.m_key_image_known || td.m_key_image_partial) continue; - if (td.m_spent != (spent_status[i] != rpc::IS_KEY_IMAGE_SPENT::UNSPENT)) + if (td.m_spent != (static_cast(spent_status[i]) != rpc::IS_KEY_IMAGE_SPENT::SPENT::UNSPENT)) { if (td.m_spent) { @@ -6620,7 +6640,7 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height, if(!is_tx_spendtime_unlocked(unlock_time, block_height)) return false; - if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17 > blockchain_height) + if(block_height + DEFAULT_TX_SPENDABLE_AGE_V17 > blockchain_height) return false; if (m_offline) @@ -6630,7 +6650,6 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height, return true; { - std::optional failed; // FIXME: can just check one here by adding a is_key_image_blacklisted auto [success, blacklist] = m_node_rpc_proxy.get_master_node_blacklisted_key_images(); if (!success) @@ -6640,12 +6659,12 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height, return true; } - for (cryptonote::rpc::GET_MASTER_NODE_BLACKLISTED_KEY_IMAGES::entry const &entry : blacklist) + for (auto const& entry : blacklist) { crypto::key_image check_image; - if(!tools::hex_to_type(entry.key_image, check_image)) + if(!tools::hex_to_type(entry["key_image"].get(), check_image)) { - MERROR("Failed to parse hex representation of key image: " << entry.key_image); + MERROR("Failed to parse hex representation of key image: " << entry["key_image"]); break; } @@ -6656,7 +6675,6 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height, { const std::string primary_address = get_address_as_str(); - std::optional failed; auto [success, master_nodes_states] = m_node_rpc_proxy.get_contributed_master_nodes(primary_address); if (!success) { @@ -6664,19 +6682,20 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height, return true; } - for (cryptonote::rpc::GET_MASTER_NODES::response::entry const &entry : master_nodes_states) + for (auto const& entry : master_nodes_states) { - for (auto const& contributor : entry.contributors) + for (auto const& contributor : entry.at("contributors")) { - if (primary_address != contributor.address) + if (primary_address != contributor.at("address")) continue; - for (auto const &contribution : contributor.locked_contributions) + for (auto const &contribution : contributor.at("locked_contributions")) { + auto input_ki = contribution.at("key_image").get(); crypto::key_image check_image; - if(!tools::hex_to_type(contribution.key_image, check_image)) + if(!tools::hex_to_type(input_ki, check_image)) { - MERROR("Failed to parse hex representation of key image: " << contribution.key_image); + MERROR("Failed to parse hex representation of key image: " << input_ki); break; } @@ -6927,18 +6946,18 @@ void wallet2::commit_tx(pending_tx& ptx, bool flash) else { // Normal submit - rpc::SEND_RAW_TX::request req{}; - req.tx_as_hex = oxenc::to_hex(tx_to_blob(ptx.tx)); - req.do_not_relay = false; - req.flash = flash; - rpc::SEND_RAW_TX::response daemon_send_resp{}; - bool r = invoke_http(req, daemon_send_resp); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == rpc::STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); + nlohmann::json send_transaction_params{ + {"tx_as_hex", oxenc::to_hex(tx_to_blob(ptx.tx))}, + {"do_not_relay", false}, + {"flash", flash}, + + }; + auto daemon_send_resp = m_http_client.json_rpc("send_raw_transaction", send_transaction_params); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp["status"] == rpc::STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); if (flash) - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != rpc::STATUS_OK, error::tx_flash_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp, &ptx.tx, flash)); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp["status"] != rpc::STATUS_OK, error::tx_flash_rejected, ptx.tx, get_rpc_status(daemon_send_resp["status"]), daemon_send_resp["reason"].is_string() ? daemon_send_resp["reason"].get() : "Daemon provided no reason"); else - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != rpc::STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp, &ptx.tx, flash)); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp["status"] != rpc::STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp["status"]), daemon_send_resp["reason"].is_string() ? daemon_send_resp["reason"].get() : "Daemon provided no reason"); // sanity checks for (size_t idx: ptx.selected_transfers) { @@ -7128,7 +7147,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector percents.size()) @@ -7761,12 +7780,12 @@ byte_and_output_fees wallet2::get_dynamic_base_fee_estimate() const if (m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fees)) return fees; - if (use_fork_rules(cryptonote::network_version_17_POS)) + if (use_fork_rules(hf::hf17_POS)) fees = {FEE_PER_BYTE, FEE_PER_OUTPUT_V17}; - if (use_fork_rules(HF_VERSION_PER_OUTPUT_FEE)) - fees = {FEE_PER_BYTE, FEE_PER_OUTPUT}; // v13 switches back from v12 per-byte fees, add per-output + if (use_fork_rules(feature::PER_OUTPUT_FEE)) + fees = {FEE_PER_BYTE, old::FEE_PER_OUTPUT}; // v13 switches back from v12 per-byte fees, add per-output else - fees = {FEE_PER_BYTE_V12, 0}; + fees = {old::FEE_PER_BYTE_V12, 0}; LOG_PRINT_L1("Failed to query base fee, using " << print_money(fees.first) << "/byte + " << print_money(fees.second) << "/output"); return fees; @@ -7790,7 +7809,7 @@ uint64_t wallet2::get_fee_quantization_mask() const return 1; } -beldex_construct_tx_params wallet2::construct_params(uint8_t hf_version, txtype tx_type, uint32_t priority, uint64_t extra_burn, bns::mapping_years map_years) +beldex_construct_tx_params wallet2::construct_params(hf hf_version, txtype tx_type, uint32_t priority, uint64_t extra_burn, bns::mapping_years map_years) { beldex_construct_tx_params tx_params; tx_params.hf_version = hf_version; @@ -7803,8 +7822,8 @@ beldex_construct_tx_params wallet2::construct_params(uint8_t hf_version, txtype } else if (priority == tools::tx_priority_flash) { - tx_params.burn_fixed = FLASH_BURN_FIXED; - tx_params.burn_percent = FLASH_BURN_TX_FEE_PERCENT_OLD; + tx_params.burn_fixed = beldex::FLASH_BURN_FIXED; + tx_params.burn_percent = beldex::FLASH_BURN_TX_FEE_PERCENT_OLD; } if (extra_burn) tx_params.burn_fixed += extra_burn; @@ -7850,7 +7869,9 @@ crypto::chacha_key wallet2::get_ringdb_key() } void wallet2::register_devices(){ - hw::trezor::register_all(); + #ifdef DEVICE_TREZOR_READY + hw::trezor::register_all(); + #endif } hw::device& wallet2::lookup_device(const std::string & device_descriptor){ @@ -7944,13 +7965,17 @@ bool wallet2::unset_ring(const crypto::hash &txid) if (!m_ringdb) return false; - auto res = request_transaction(txid); - + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; cryptonote::transaction tx; - crypto::hash tx_hash; - if (!get_pruned_tx(res.txs.front(), tx, tx_hash)) - return false; - THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); + try { + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); + crypto::hash tx_hash; + if (!get_pruned_tx(res["txs"].front(), tx, tx_hash)) + return false; + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); + } catch (const std::exception &e) { return false; } try { return m_ringdb->remove_rings(get_ringdb_key(), tx); } catch (const std::exception &e) { return false; } @@ -7980,12 +8005,15 @@ bool wallet2::find_and_save_rings(bool force) for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE) { size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE; - auto res = request_transactions(hashes_to_hex(txs_hashes.begin() + slice, txs_hashes.begin() + ntxes)); + nlohmann::json get_transactions_params{ + {"tx_hashes", hashes_to_hex(txs_hashes.begin() + slice, txs_hashes.begin() + ntxes)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); - MDEBUG("Scanning " << res.txs.size() << " transactions"); - for (size_t i = 0; i < res.txs.size(); ++i, ++it) + MDEBUG("Scanning " << res["txs"].size() << " transactions"); + for (size_t i = 0; i < res["txs"].size(); ++i, ++it) { - const auto &tx_info = res.txs[i]; + const auto &tx_info = res["txs"][i]; cryptonote::transaction tx; crypto::hash tx_hash; THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error, @@ -8072,7 +8100,7 @@ wallet2::stake_result wallet2::check_stake_allowed(const crypto::public_key& mn_ if (!success) { result.status = stake_result_status::master_node_list_query_failed; - result.msg = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; + result.msg = ERR_MSG_MASTER_NODE_LIST_QUERY_FAILED; return result; } @@ -8083,8 +8111,8 @@ wallet2::stake_result wallet2::check_stake_allowed(const crypto::public_key& mn_ return result; } - const std::optional res = m_node_rpc_proxy.get_hardfork_version(); - if (!res) + const auto hf_version = m_node_rpc_proxy.get_hardfork_version(); + if (!hf_version) { result.status = stake_result_status::network_version_query_failed; result.msg = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; @@ -8092,30 +8120,38 @@ wallet2::stake_result wallet2::check_stake_allowed(const crypto::public_key& mn_ } const auto& mnode_info = response.front(); - if (amount == 0) amount = mnode_info.staking_requirement * fraction; + const auto staking_req = mnode_info.at("staking_requirement").get(); + const auto total_res = mnode_info.value("total_reserved", + mnode_info.at("total_contributed").get()); + if (amount == 0) + amount = staking_req * fraction; size_t total_existing_contributions = 0; // Count both contributions and reserved spots - for (auto const &contributor : mnode_info.contributors) + const auto& contributors = mnode_info.at("contributors"); + for (auto const &contributor : contributors) { - total_existing_contributions += contributor.locked_contributions.size(); // contribution - if (contributor.reserved > contributor.amount) + total_existing_contributions += contributor.at("locked_contributions").size(); // contribution + if (auto it = contributor.find("reserved"); it != contributor.end() && it->get() > contributor.at("amount").get()) total_existing_contributions++; // reserved contributor spot } - uint8_t const hf_version = *res; - uint64_t max_contrib_total = mnode_info.staking_requirement - mnode_info.total_reserved; - uint64_t min_contrib_total = master_nodes::get_min_node_contribution(hf_version, mnode_info.staking_requirement, mnode_info.total_reserved, total_existing_contributions); + uint64_t max_contrib_total = staking_req - total_res; + uint64_t min_contrib_total = master_nodes::get_min_node_contribution(*hf_version, staking_req, total_res, total_existing_contributions); bool is_preexisting_contributor = false; - for (const auto& contributor : mnode_info.contributors) + for (const auto& contributor : contributors) { address_parse_info info; - if (!cryptonote::get_account_address_from_str(info, m_nettype, contributor.address)) + if (!cryptonote::get_account_address_from_str(info, m_nettype, contributor.at("address").get())) continue; if (info.address == addr_info.address) { - uint64_t const reserved_amount_not_contributed_yet = contributor.reserved - contributor.amount; + const auto amount = contributor.at("amount").get(); + + uint64_t reserved_amount_not_contributed_yet = 0; + if (auto it = contributor.find("reserved"); it != contributor.end()) + reserved_amount_not_contributed_yet = it->get() - contributor.at("amount").get(); max_contrib_total += reserved_amount_not_contributed_yet; is_preexisting_contributor = true; @@ -8132,7 +8168,7 @@ wallet2::stake_result wallet2::check_stake_allowed(const crypto::public_key& mn_ return result; } - const bool full = mnode_info.contributors.size() >= MAX_NUMBER_OF_CONTRIBUTORS; + const bool full = contributors.size() >= beldex::MAX_NUMBER_OF_CONTRIBUTORS; if (full && !is_preexisting_contributor) { result.status = stake_result_status::master_node_contributors_maxed; @@ -8142,7 +8178,7 @@ wallet2::stake_result wallet2::check_stake_allowed(const crypto::public_key& mn_ if (amount < min_contrib_total) { - const uint64_t DUST = MAX_NUMBER_OF_CONTRIBUTORS; + const uint64_t DUST = beldex::MAX_NUMBER_OF_CONTRIBUTORS; if (min_contrib_total - amount <= DUST) { amount = min_contrib_total; @@ -8237,7 +8273,7 @@ wallet2::stake_result wallet2::create_stake_tx(const crypto::public_key& master_ return result; } - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { result.status = stake_result_status::network_version_query_failed; @@ -8246,7 +8282,7 @@ wallet2::stake_result wallet2::create_stake_tx(const crypto::public_key& master_ } beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, txtype::stake, priority); - auto ptx_vector = create_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, unlock_at_block, priority, extra, 0, subaddr_indices, tx_params); + auto ptx_vector = create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, unlock_at_block, priority, extra, 0, subaddr_indices, tx_params); if (ptx_vector.size() == 1) { result.status = stake_result_status::success; @@ -8316,7 +8352,7 @@ wallet2::register_master_node_result wallet2::create_register_master_node_tx(con // // Parse Registration Contributor Args // - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { result.status = register_master_node_result_status::network_version_query_failed; @@ -8446,7 +8482,7 @@ wallet2::register_master_node_result wallet2::create_register_master_node_tx(con if (!success) { result.status = register_master_node_result_status::master_node_list_query_failed; - result.msg = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; + result.msg = ERR_MSG_MASTER_NODE_LIST_QUERY_FAILED; return result; } @@ -8464,7 +8500,7 @@ wallet2::register_master_node_result wallet2::create_register_master_node_tx(con { uint64_t amount_payable_by_operator = 0; { - const uint64_t DUST = MAX_NUMBER_OF_CONTRIBUTORS; + const uint64_t DUST = beldex::MAX_NUMBER_OF_CONTRIBUTORS; uint64_t amount_left = staking_requirement; for (size_t i = 0; i < contributor_args.portions.size(); i++) { @@ -8491,7 +8527,7 @@ wallet2::register_master_node_result wallet2::create_register_master_node_tx(con dest.address = address; beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, txtype::stake, priority); - auto ptx_vector = create_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, 0 /* unlock_time */, priority, extra, subaddr_account, subaddr_indices, tx_params); + auto ptx_vector = create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, 0 /* unlock_time */, priority, extra, subaddr_account, subaddr_indices, tx_params); if (ptx_vector.size() == 1) { result.status = register_master_node_result_status::success; @@ -8538,41 +8574,31 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry } cryptonote::account_public_address const primary_address = get_address(); - std::vector const *contributions = nullptr; - rpc::GET_MASTER_NODES::response::entry const &node_info = response[0]; - rpc::master_node_contributor const *p_contributor = nullptr; - for (auto const &contributor : node_info.contributors) + nlohmann::json contributions{}; + bool found = false; + auto const& node_info = response[0]; + for (auto const &contributor : node_info["contributors"]) { address_parse_info address_info = {}; - cryptonote::get_account_address_from_str(address_info, nettype(), contributor.address); + cryptonote::get_account_address_from_str(address_info, nettype(), contributor["address"].get()); if (address_info.address != primary_address) continue; - contributions = &contributor.locked_contributions; - p_contributor = &contributor; + found = true; + contributions = contributor["locked_contributions"]; break; } - if (!contributions) + if (!found) { result.msg = tr("No contributions recognised by this wallet in master node: ") + mn_key_as_str; return result; } - auto version = m_node_rpc_proxy.get_hardfork_version(); std::string error_msg; uint64_t cur_height = get_daemon_blockchain_height(error_msg); - if(version >= cryptonote::network_version_18_bns) - { - if(((p_contributor->amount) < master_nodes::SMALL_CONTRIBUTOR_THRESHOLD * COIN ) && ((cur_height - node_info.registration_height) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER)) - { - result.msg = tr("you can't give the unlock command! you have to wait upto ") + std::to_string(node_info.registration_height + master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER - cur_height) + " Blocks or "+ std::to_string((node_info.registration_height + master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER - cur_height)/ (master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER / 30)) + " days approx"; - result.success = false; - return result; - } - } cryptonote::tx_extra_tx_key_image_unlock unlock = {}; { uint64_t curr_height = 0; @@ -8587,46 +8613,53 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry } result.msg.reserve(1024); - auto const &contribution = (*contributions)[0]; - if (node_info.requested_unlock_height != 0) + auto const &contribution = contributions[0]; + if (node_info["requested_unlock_height"].get() != 0) { result.msg.append("Key image: "); - result.msg.append(contribution.key_image); + result.msg.append(contribution["key_image"]); result.msg.append(" has already been requested to be unlocked, unlocking at height: "); - result.msg.append(std::to_string(node_info.requested_unlock_height)); + result.msg.append(node_info["requested_unlock_height"].get()); result.msg.append(" (about "); - result.msg.append(tools::get_human_readable_timespan(std::chrono::seconds((node_info.requested_unlock_height - curr_height) * TARGET_BLOCK_TIME))); + result.msg.append(tools::get_human_readable_timespan(std::chrono::seconds((node_info["requested_unlock_height"].get() - curr_height) * TARGET_BLOCK_TIME))); result.msg.append(")"); return result; } + if((contribution["amount"] < master_nodes::SMALL_CONTRIBUTOR_THRESHOLD * beldex::COIN ) && ((cur_height - node_info["registration_height"].get()) < master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER)) + { + result.msg = tr("you can't give the unlock command! you have to wait upto ") + std::to_string(node_info["registration_height"].get() + master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER - cur_height) + " Blocks or "+ std::to_string((node_info["registration_height"].get() + master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER - cur_height)/ (master_nodes::SMALL_CONTRIBUTOR_UNLOCK_TIMER / 30)) + " days approx"; + result.success = false; + return result; + } + result.msg.append("You are requesting to unlock a stake of: "); - result.msg.append(cryptonote::print_money(contribution.amount)); + result.msg.append(cryptonote::print_money(contribution["amount"])); result.msg.append(" Beldex from the master node network.\nThis will schedule the master node: "); - result.msg.append(node_info.master_node_pubkey); + result.msg.append(node_info["master_node_pubkey"]); result.msg.append(" for deactivation."); - if (node_info.contributors.size() > 1) { + if (node_info["contributors"].size() > 1) { result.msg.append(" The stakes of the master node's "); - result.msg.append(std::to_string(node_info.contributors.size() - 1)); + result.msg.append(std::to_string(node_info["contributors"].size() - 1)); result.msg.append(" other contributors will unlock at the same time."); } result.msg.append("\n\n"); - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { result.msg = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; return result; } - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(nettype(), curr_height,*hf_version); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(nettype(), curr_height, *hf_version); result.msg.append("You will continue receiving rewards until the master node expires at the estimated height: "); result.msg.append(std::to_string(unlock_height)); result.msg.append(" (about "); result.msg.append(tools::get_human_readable_timespan(std::chrono::seconds((unlock_height - curr_height) * TARGET_BLOCK_TIME))); result.msg.append(")"); - if(!tools::hex_to_type(contribution.key_image, unlock.key_image)) + if(!tools::hex_to_type(contribution["key_image"].get(), unlock.key_image)) { - result.msg = tr("Failed to parse hex representation of key image: ") + contribution.key_image; + result.msg = tr("Failed to parse hex representation of key image: ") + contribution["key_image"].get(); return result; } @@ -8640,7 +8673,7 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry try { if (!generate_signature_for_request_stake_unlock(unlock.key_image, unlock.signature)) { - result.msg = tr("Failed to generate signature to sign request. The key image: ") + contribution.key_image + tr(" doesn't belong to this wallet"); + result.msg = tr("Failed to generate signature to sign request. The key image: ") + contribution["key_image"].get() + tr(" doesn't belong to this wallet"); return result; } } catch (const std::exception& e) { @@ -8713,7 +8746,7 @@ static bns_prepared_args prepare_tx_extra_beldex_name_system_values(wallet2 cons bns::bns_tx_type txtype, uint32_t account_index, std::string *reason, - std::vector *response) + nlohmann::json* record = nullptr) { bns_prepared_args result = {}; if (priority == tools::tx_priority_flash) @@ -8782,34 +8815,47 @@ static bns_prepared_args prepare_tx_extra_beldex_name_system_values(wallet2 cons return {}; { - cryptonote::rpc::BNS_NAMES_TO_OWNERS::request request = {}; - { - request.entries.push_back(oxenc::to_base64(tools::view_guts(result.name_hash))); + std::vector name_hash{}; + name_hash.push_back(oxenc::to_base64(tools::view_guts(result.name_hash))); + auto [success, response] = wallet.bns_names_to_owners({{"name_hash", name_hash}}); + bool bad_resp = false; + nlohmann::json tmp; + if (!success) + bad_resp = true; + else if(response.is_null()) { + if (record) + *record = nullptr; + else + record = &tmp; } + else { + if (record) + *record = std::move(response.front()); + else + record = &response.front(); + } + - auto [success, response_] = wallet.bns_names_to_owners(request); - if (!response) - response = &response_; - else - *response = std::move(response_); - if (!success) - { - if (reason) *reason = "Failed to query previous owner for BNS entry: communication with daemon failed"; + if (bad_resp) { + if (reason) + *reason = "Failed to query previous owner for BNS entry: communication with daemon failed"; return result; } - - if (response->size()) - { - if (!tools::hex_to_type((*response)[0].txid, result.prev_txid)) - { - if (reason) *reason = "Failed to convert response txid=" + (*response)[0].txid + " from the daemon into a 32 byte hash, it must be a 64 char hex string"; - return result; - } + + const auto& rec = *record; + + if (!rec.is_null()) { + auto txid = rec["txid"].get(); + if (!tools::hex_to_type(txid, result.prev_txid)) { + if (reason) + *reason ="Failed to convert response txid=" + (std::string)txid + " from the daemon into a 32 byte hash, it must be a 64 char hex string"; + return result; + } } if ((txtype == bns::bns_tx_type::update && make_signature) || (txtype == bns::bns_tx_type::renew)) { - if (response->empty()) + if (rec.is_null()) { if (reason) *reason = "Signature requested when preparing BNS TX but record to update/renew does not exist"; return result; @@ -8817,18 +8863,18 @@ static bns_prepared_args prepare_tx_extra_beldex_name_system_values(wallet2 cons cryptonote::address_parse_info curr_owner_parsed = {}; cryptonote::address_parse_info curr_backup_owner_parsed = {}; - auto& rowner = response->front().owner; - auto& rbackup_owner = response->front().backup_owner; + auto rowner = rec["owner"].get(); + std::string rbackup_owner = rec.value("backup_owner", ""); bool curr_owner = cryptonote::get_account_address_from_str(curr_owner_parsed, wallet.nettype(), rowner); - bool curr_backup_owner = rbackup_owner && cryptonote::get_account_address_from_str(curr_backup_owner_parsed, wallet.nettype(), *rbackup_owner); + bool curr_backup_owner = rbackup_owner.size() && cryptonote::get_account_address_from_str(curr_backup_owner_parsed, wallet.nettype(), rbackup_owner); if (!try_generate_bns_signature(wallet, rowner, owner, backup_owner, result)) { - if (!rbackup_owner || !try_generate_bns_signature(wallet, *rbackup_owner, owner, backup_owner, result)) + if (rbackup_owner.empty() || !try_generate_bns_signature(wallet, rbackup_owner, owner, backup_owner, result)) { if (reason) { *reason = "Signature requested when preparing BNS TX, but this wallet is not the owner of the record owner=" + rowner; - if (rbackup_owner) *reason += ", backup_owner=" + *rbackup_owner; + if (rbackup_owner.empty()) *reason += ", backup_owner=" + rbackup_owner; } return result; } @@ -8853,7 +8899,7 @@ std::vector wallet2::bns_create_buy_mapping_tx(bns::mapping uint32_t account_index, std::set subaddr_indices) { - std::vector response; + nlohmann::json response; constexpr bool make_signature = false; bns_prepared_args prepared_args = prepare_tx_extra_beldex_name_system_values(*this, priority, name, value_bchat, value_wallet, value_belnet,value_eth_addr, owner, backup_owner, make_signature, bns::bns_tx_type::buy, account_index, reason, &response); if (!owner) @@ -8875,21 +8921,21 @@ std::vector wallet2::bns_create_buy_mapping_tx(bns::mapping prepared_args.prev_txid); add_beldex_name_system_to_tx_extra(extra, entry); - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; return {}; } - if (hf_version <= cryptonote::network_version_17_POS) + if (hf_version <= hf::hf17_POS) { if (reason) *reason = ERR_MSG_BNS_HF_VERSION; return {}; } beldex_construct_tx_params tx_params = wallet2::construct_params(*hf_version, txtype::beldex_name_system, priority, 0, mapping_years); auto result = create_transactions_2({} /*dests*/, - CRYPTONOTE_DEFAULT_TX_MIXIN, + cryptonote::TX_OUTPUT_DECOYS, 0 /*unlock_at_block*/, priority, extra, @@ -8927,7 +8973,7 @@ std::vector wallet2::bns_create_renewal_tx( uint32_t priority, uint32_t account_index, std::set subaddr_indices, - std::vector *response + nlohmann::json *response ) { constexpr bool make_signature = false; @@ -8944,7 +8990,7 @@ std::vector wallet2::bns_create_renewal_tx( prepared_args.prev_txid); add_beldex_name_system_to_tx_extra(extra, entry); - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; @@ -8953,7 +8999,7 @@ std::vector wallet2::bns_create_renewal_tx( beldex_construct_tx_params tx_params = wallet2::construct_params(*hf_version, txtype::beldex_name_system, priority, 0, map_years); auto result = create_transactions_2({} /*dests*/, - CRYPTONOTE_DEFAULT_TX_MIXIN, + cryptonote::TX_OUTPUT_DECOYS, 0 /*unlock_at_block*/, priority, extra, @@ -8976,7 +9022,7 @@ std::vector wallet2::bns_create_update_mapping_tx(std::stri uint32_t priority, uint32_t account_index, std::set subaddr_indices, - std::vector *response) + nlohmann::json *response) { if (!value_bchat && !value_wallet && !value_belnet && !value_eth_addr && !owner && !backup_owner) { @@ -9008,7 +9054,7 @@ std::vector wallet2::bns_create_update_mapping_tx(std::stri backup_owner ? &prepared_args.backup_owner : nullptr, prepared_args.prev_txid); add_beldex_name_system_to_tx_extra(extra, entry); - std::optional hf_version = get_hard_fork_version(); + auto hf_version = get_hard_fork_version(); if (!hf_version) { if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED; @@ -9017,7 +9063,7 @@ std::vector wallet2::bns_create_update_mapping_tx(std::stri beldex_construct_tx_params tx_params = wallet2::construct_params(*hf_version, txtype::beldex_name_system, priority, 0,(owner || backup_owner) ? bns::mapping_years::update_owner_record : bns::mapping_years::update_record_internal); auto result = create_transactions_2({} /*dests*/, - CRYPTONOTE_DEFAULT_TX_MIXIN, + cryptonote::TX_OUTPUT_DECOYS, 0 /*unlock_at_block*/, priority, extra, @@ -9063,7 +9109,7 @@ bool wallet2::bns_make_update_mapping_signature(std::string name, uint32_t account_index, std::string *reason) { - std::vector response; + nlohmann::json response; constexpr bool make_signature = true; bns_prepared_args prepared_args = prepare_tx_extra_beldex_name_system_values(*this, tx_priority_unimportant, name, value_bchat, value_wallet, value_belnet, value_eth_addr, owner, backup_owner, make_signature, bns::bns_tx_type::update, account_index, reason, &response); if (!prepared_args) return false; @@ -9117,7 +9163,7 @@ bool wallet2::tx_add_fake_output(std::vector> // if we have at least one rct out, get the distribution, or fall back to the previous system uint64_t rct_start_height; std::vector rct_offsets; + std::vector amounts; const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); // get histogram for the amounts we need - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::request req_t{}; - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::response resp_t{}; { uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) @@ -9316,14 +9361,14 @@ void wallet2::get_outs(std::vector> // request histogram for all outputs, except 0 if we have the rct distribution if (!m_transfers[idx].is_rct() || !has_rct_distribution) { - req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); + amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); } } if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below - THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17, + THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= DEFAULT_TX_SPENDABLE_AGE_V17, error::get_output_distribution, "Not enough rct outputs"); THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index, error::get_output_distribution, "Daemon reports suspicious number of rct outputs"); @@ -9342,17 +9387,20 @@ void wallet2::get_outs(std::vector> << "), please notify the Beldex developers"); } - if (!req_t.amounts.empty()) + nlohmann::json res; + if (!amounts.empty()) { - std::sort(req_t.amounts.begin(), req_t.amounts.end()); - auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end()); - req_t.amounts.resize(std::distance(req_t.amounts.begin(), end)); - req_t.unlocked = true; - req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; - bool r = invoke_http(req_t, resp_t); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != rpc::STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status)); + std::sort(amounts.begin(), amounts.end()); + auto end = std::unique(amounts.begin(), amounts.end()); + amounts.resize(std::distance(amounts.begin(), end)); + nlohmann::json req_params{ + {"amounts", amounts}, + {"unlocked", true}, + {"recent_cutoff", time(NULL) - RECENT_OUTPUT_ZONE} + }; + res = m_http_client.json_rpc("get_output_histogram", req_params); + THROW_WALLET_EXCEPTION_IF(res["status"] == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); + THROW_WALLET_EXCEPTION_IF(res["status"] != rpc::STATUS_OK, error::get_histogram_error, get_rpc_status(res["status"])); } // if we want to segregate fake outs pre or post fork, get distribution @@ -9422,7 +9470,7 @@ void wallet2::get_outs(std::vector> const uint64_t amount = td.is_rct() ? 0 : td.amount(); std::unordered_set seen_indices; // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer - size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17 : 0); + size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_MONEY_UNLOCK_WINDOW - DEFAULT_TX_SPENDABLE_AGE_V17 : 0); size_t start = get_outputs.size(); bool use_histogram = amount != 0 || !has_rct_distribution; @@ -9441,14 +9489,14 @@ void wallet2::get_outs(std::vector> { // if there are just enough outputs to mix with, use all of them. // Eventually this should become impossible. - for (const auto &he: resp_t.histogram) + for (const auto &he: res["histogram"]) { - if (he.amount == amount) + if (he["amount"].get() == amount) { - LOG_PRINT_L2("Found " << print_money(amount) << ": " << he.total_instances << " total, " - << he.unlocked_instances << " unlocked, " << he.recent_instances << " recent"); - num_outs = he.unlocked_instances; - num_recent_outs = he.recent_instances; + LOG_PRINT_L2("Found " << print_money(amount) << ": " << he["total_instances"] << " total, " + << he["unlocked_instances"] << " unlocked, " << he["recent_instances"] << " recent"); + num_outs = he["unlocked_instances"].get(); + num_recent_outs = he["recent_instances"].get(); break; } } @@ -9491,7 +9539,7 @@ void wallet2::get_outs(std::vector> else { // the base offset of the first rct output in the first unlocked block (or the one to be if there's none) - num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17]; + num_outs = gamma->get_num_rct_outs(); LOG_PRINT_L1("" << num_outs << " unlocked rct outputs"); THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, "histogram reports no unlocked rct outputs, not even ours"); @@ -9769,7 +9817,7 @@ void wallet2::get_outs(std::vector> for(size_t idx: selected_transfers) { const transfer_details &td = m_transfers[idx]; - size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17 : 0); + size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? MINED_MONEY_UNLOCK_WINDOW - DEFAULT_TX_SPENDABLE_AGE_V17 : 0); outs.push_back(std::vector()); outs.back().reserve(fake_outputs_count + 1); const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount()); @@ -9779,17 +9827,17 @@ void wallet2::get_outs(std::vector> const bool output_is_pre_fork = td.m_block_height < segregation_fork_height; if (is_after_segregation_fork && m_segregate_pre_fork_outputs && output_is_pre_fork) num_outs = segregation_limit[amount].first; - else for (const auto &he: resp_t.histogram) + else for (const auto &he: res["histogram"]) { - if (he.amount == amount) + if (he["amount"].get() == amount) { - num_outs = he.unlocked_instances; + num_outs = he["unlocked_instances"].get(); break; } } bool use_histogram = amount != 0 || !has_rct_distribution; if (!use_histogram) - num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17]; + num_outs = gamma->get_num_rct_outs(); // make sure the real outputs we asked for are really included, along // with the correct key and mask: this guards against an active attack @@ -10235,8 +10283,10 @@ void wallet2::transfer_selected_rct(std::vector= hf::hf20_bulletproof_plus) + ? (use_fork_rules(cryptonote::feature::BULLETPROOF_PLUS, 0) ? 4 : 3) + : (use_fork_rules(cryptonote::feature::CLSAG, 0) ? 3 : 2) }; ptx.construction_data.dests = dsts; // record which subaddress indices are being used as inputs @@ -10855,10 +10905,10 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, // This system allows for sending (almost) the entire balance, since it does // not generate spurious change in all txes, thus decreasing the instantaneous // usable balance. -std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra_base, uint32_t subaddr_account, std::set subaddr_indices, beldex_construct_tx_params &tx_params) +std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra_base, uint32_t subaddr_account, std::set subaddr_indices, beldex_construct_tx_params &tx_params, const unique_index_container& subtract_fee_from_outputs) { //ensure device is let in NONE mode in any case - LOG_PRINT_L0("create_transactions_2 get_device prio:" << priority); + LOG_PRINT_L0("create_transactions_2 get_device prio:" << priority); hw::device &hwdev = m_account.get_device(); std::unique_lock hwdev_lock{hwdev}; hw::mode_resetter rst{hwdev}; @@ -10890,12 +10940,13 @@ std::vector wallet2::create_transactions_2(std::vector>> unused_transfers_indices_per_subaddr; std::vector>> unused_dust_indices_per_subaddr; - uint64_t needed_money; + uint64_t needed_money, total_needed_money; // 'needed_money' is the sum of the destination amounts, while 'total_needed_money' includes 'needed_money' plus the fee if not 'subtract_fee_from_outputs' uint64_t accumulated_fee, accumulated_outputs, accumulated_change; struct TX { std::vector selected_transfers; std::vector dsts; + std::vector dsts_are_fee_subtractable; cryptonote::transaction tx; pending_tx ptx; size_t weight; @@ -10904,7 +10955,7 @@ std::vector wallet2::create_transactions_2(std::vector::iterator i; @@ -10912,6 +10963,7 @@ std::vector wallet2::create_transactions_2(std::vectoramount = 0; } @@ -10925,19 +10977,73 @@ std::vector wallet2::create_transactions_2(std::vector get_adjusted_dsts(uint64_t needed_fee) const + { + uint64_t dest_total = 0; + uint64_t subtractable_dest_total = 0; + std::vector subtractable_indices; + subtractable_indices.reserve(dsts.size()); + for (size_t i = 0; i < dsts.size(); ++i) + { + dest_total += dsts[i].amount; + if (dsts_are_fee_subtractable[i]) + { + subtractable_dest_total += dsts[i].amount; + subtractable_indices.push_back(i); + } + } + + if (subtractable_indices.empty()) // if subtract_fee_from_outputs is not enabled for this tx + return dsts; + + THROW_WALLET_EXCEPTION_IF(subtractable_dest_total < needed_fee, error::tx_not_possible, + subtractable_dest_total, dest_total, needed_fee); + + std::vector res = dsts; + + // subtract fees from destinations equally, rounded down, until dust is left where we subtract 1 + uint64_t subtractable_remaining = needed_fee; + auto si_it = subtractable_indices.cbegin(); + uint64_t amount_to_subtract = 0; + while (subtractable_remaining) + { + // Set the amount to subtract iterating at the beginning of the list so equal amounts are + // subtracted throughout the list of destinations. We use max(x, 1) so that we we still step + // forwards even when the amount remaining is less than the number of subtractable indices + if (si_it == subtractable_indices.cbegin()) + amount_to_subtract = std::max(subtractable_remaining / subtractable_indices.size(), 1); + + cryptonote::tx_destination_entry& d = res[*si_it]; + THROW_WALLET_EXCEPTION_IF(d.amount <= amount_to_subtract, error::zero_amount); + + subtractable_remaining -= amount_to_subtract; + d.amount -= amount_to_subtract; + ++si_it; + + // Wrap around to first subtractable index once we hit the end of the list + if (si_it == subtractable_indices.cend()) + si_it = subtractable_indices.cbegin(); + } + + return res; + } }; std::vector txes; bool adding_fee; // true if new outputs go towards fee, rather than destinations uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); - const bool clsag = use_fork_rules(HF_VERSION_CLSAG, 0); - const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, clsag ? 3 : 2}; + const bool clsag = use_fork_rules(feature::CLSAG, 0); + const bool bulletproof_plus = (tx_params.hf_version >= hf::hf20_bulletproof_plus) ? use_fork_rules(cryptonote::feature::BULLETPROOF_PLUS, 0) : false; + const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, bulletproof_plus ? 4 : 3}; const auto base_fee = get_base_fees(); const uint64_t fee_percent = get_fee_percent(priority, tx_params.tx_type); uint64_t fixed_fee = 0; @@ -10950,7 +11056,7 @@ std::vector wallet2::create_transactions_2(std::vector extra_plus; // Copy and modified from input if modification needed const std::vector &extra = burning ? extra_plus : extra_base; if (burning) @@ -10964,6 +11070,14 @@ std::vector wallet2::create_transactions_2(std::vector= dsts.size(), + error::subtract_fee_from_bad_index, *subtract_fee_from_outputs.crbegin()); + + // throw if subtract_fee_from_outputs is enabled and we have too many outputs to fit into one tx + THROW_WALLET_EXCEPTION_IF(subtract_fee_from_outputs.size() && dsts.size() > cryptonote::TX_BULLETPROOF_MAX_OUTPUTS - 1, + error::wallet_internal_error, "subtractfeefrom transfers cannot be split over multiple transactions yet"); + // calculate total amount being sent to all destinations // throw if total amount overflows uint64_t needed_money = 0; @@ -10994,10 +11108,11 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector balance_subtotal, error::not_enough_money, + THROW_WALLET_EXCEPTION_IF(total_needed_money > balance_subtotal || min_fee + fixed_fee > balance_subtotal, error::not_enough_money, balance_subtotal, needed_money, 0); // first check overall balance is enough, then unlocked one, so we throw distinct exceptions - THROW_WALLET_EXCEPTION_IF(needed_money + min_fee + fixed_fee > unlocked_balance_subtotal, error::not_enough_unlocked_money, + THROW_WALLET_EXCEPTION_IF(total_needed_money > unlocked_balance_subtotal || min_fee + fixed_fee > unlocked_balance_subtotal, error::not_enough_unlocked_money, unlocked_balance_subtotal, needed_money, 0); } @@ -11016,8 +11131,8 @@ std::vector wallet2::create_transactions_2(std::vector tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!"); const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring; const uint64_t fractional_threshold = base_fee.first * fee_percent / 100 * tx_weight_per_ring; @@ -11107,10 +11222,11 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second; std::vector* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second; @@ -11226,23 +11342,26 @@ std::vector wallet2::create_transactions_2(std::vector 0 && !dsts.empty() && estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), clsag) < tx_weight_target(upper_transaction_weight_limit)) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), clsag, bulletproof_plus) < tx_weight_target(upper_transaction_weight_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); - tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations); + const bool subtract_fee_from_this_dest = subtract_fee_from_outputs.count(destination_index); + tx.add(dsts[0], available_amount, original_output_index, m_merge_destinations,subtract_fee_from_this_dest); dsts[0].amount -= available_amount; available_amount = 0; } @@ -11262,7 +11381,7 @@ std::vector wallet2::create_transactions_2(std::vector= tx_weight_target(upper_transaction_weight_limit)); THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit); } @@ -11273,12 +11392,17 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector test_ptx.fee) { - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + size_t fee_tries; + for (fee_tries = 0; fee_tries < 10 && needed_fee > test_ptx.fee; ++fee_tries) { + tx_dsts = tx.get_adjusted_dsts(needed_fee); + transfer_selected_rct(tx_dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx, rct_config, tx_params); txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(test_ptx.tx, txBlob.size(), base_fee, fee_percent, fixed_fee, fee_quantization_mask); @@ -11335,6 +11478,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector dsts,*/ + transfer_selected_rct( tx_dsts, /* NOMOD std::vector dsts,*/ tx.selected_transfers, /* const std::list selected_transfers */ fake_outs_count, /* CONST size_t fake_outputs_count, */ tx.outs, /* MOD std::vector> &outs, */ @@ -11424,25 +11571,40 @@ std::vector wallet2::create_transactions_2(std::vector &ptx_vector, std::vector dsts) const +bool wallet2::sanity_check(const std::vector &ptx_vector, std::vector dsts, const unique_index_container& subtract_fee_from_outputs) const { - MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations"); + MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations, subtract_fee_from_outputs " << + (subtract_fee_from_outputs.size() ? "enabled" : "disabled")); hw::device &hwdev = m_account.get_device(); THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions"); + THROW_WALLET_EXCEPTION_IF(!subtract_fee_from_outputs.empty() && ptx_vector.size() != 1, + error::wallet_internal_error, "feature subtractfeefrom not supported for split transactions"); + + // For destinations from where the fee is subtracted, the required amount has to be at least + // target amount - (tx fee / num_subtractable + 1). +1 since fee might not be evenly divisble by + // the number of subtractble destinations. For non-subtractable destinations, we need at least + // the target amount. + const size_t num_subtractable_dests = subtract_fee_from_outputs.size(); + const uint64_t fee0 = ptx_vector[0].fee; + const uint64_t subtractable_fee_deduction = fee0 / std::max(num_subtractable_dests, 1) + 1; // check every party in there does receive at least the required amount std::unordered_map> required; - for (const auto &d: dsts) + for (size_t i = 0; i < dsts.size(); ++i) { - required[d.addr].first += d.amount; + const cryptonote::tx_destination_entry& d = dsts[i]; + const bool dest_is_subtractable = subtract_fee_from_outputs.count(i); + const uint64_t fee_deduction = dest_is_subtractable ? subtractable_fee_deduction : 0; + const uint64_t required_amount = d.amount - std::min(fee_deduction, d.amount); + required[d.addr].first += required_amount; required[d.addr].second = d.is_subaddress; } @@ -11615,16 +11777,17 @@ std::vector wallet2::create_transactions_burn(const std::ve uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); std::vector> outs; - const bool clsag = use_fork_rules(HF_VERSION_CLSAG, 0); - const rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, clsag ? 3 : 2 }; + auto hf_version = get_hard_fork_version(); + THROW_WALLET_EXCEPTION_IF(!hf_version, error::get_hard_fork_version_error, "Failed to query current hard fork version"); + + const bool clsag = use_fork_rules(feature::CLSAG, 0); + const bool bulletproof_plus = (*hf_version >= hf::hf20_bulletproof_plus) ? use_fork_rules(cryptonote::feature::BULLETPROOF_PLUS, 0) : false; + const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, bulletproof_plus ? 4 : 3}; const auto base_fee = get_base_fees(); const uint64_t fee_percent = get_fee_percent(priority, tx_type); const uint64_t fee_quantization_mask = get_fee_quantization_mask(); uint64_t fixed_fee = 0; - std::optional hf_version = get_hard_fork_version(); - THROW_WALLET_EXCEPTION_IF(!hf_version, error::get_hard_fork_version_error, "Failed to query current hard fork version"); - beldex_construct_tx_params beldex_tx_params = tools::wallet2::construct_params(*hf_version, tx_type, priority); uint64_t burn_fixed = 0, burn_percent = 0; @@ -11633,7 +11796,7 @@ std::vector wallet2::create_transactions_burn(const std::ve // real transactions. std::swap(burn_fixed, beldex_tx_params.burn_fixed); std::swap(burn_percent, beldex_tx_params.burn_percent); - THROW_WALLET_EXCEPTION_IF(beldex_tx_params.hf_version < HF_VERSION_FEE_BURNING, error::wallet_internal_error, "cannot construct transaction: cannot burn amounts under the current hard fork"); + THROW_WALLET_EXCEPTION_IF(beldex_tx_params.hf_version < feature::FEE_BURNING, error::wallet_internal_error, "cannot construct transaction: cannot burn amounts under the current hard fork"); { std::vector extra_plus; // Copy and modified from input if modification needed extra_plus = extra_base; @@ -11663,7 +11826,7 @@ std::vector wallet2::create_transactions_burn(const std::ve // get a tx that can't pay for itself uint64_t fee_dust_threshold; { - const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra_base.size(), clsag); + const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra_base.size(), clsag, bulletproof_plus); fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, outputs, fee_percent, fixed_fee, fee_quantization_mask); } @@ -11690,7 +11853,7 @@ std::vector wallet2::create_transactions_burn(const std::ve // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_weight_limit); - const size_t estimated_rct_tx_weight = estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra_base.size(), clsag); + const size_t estimated_rct_tx_weight = estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra_base.size(), clsag, bulletproof_plus); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= tx_weight_target(upper_transaction_weight_limit)); LOG_PRINT_L2("Accumulated_outputs : " << accumulated_outputs); if (try_tx) { @@ -11698,7 +11861,7 @@ std::vector wallet2::create_transactions_burn(const std::ve pending_tx test_ptx; const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers, beldex_tx_params); - needed_fee = estimate_fee(tx.selected_transfers.size(), fake_outs_count, num_outputs, extra_base.size(), clsag, base_fee, fee_percent, fixed_fee, fee_quantization_mask); + needed_fee = estimate_fee(tx.selected_transfers.size(), fake_outs_count, num_outputs, extra_base.size(), clsag, bulletproof_plus, base_fee, fee_percent, fixed_fee, fee_quantization_mask); // add N - 1 outputs for correct initial fee estimation for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i) @@ -11810,16 +11973,17 @@ std::vector wallet2::create_transactions_from(const crypton uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); std::vector> outs; - const bool clsag = use_fork_rules(HF_VERSION_CLSAG, 0); - const rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, clsag ? 3 : 2 }; + auto hf_version = get_hard_fork_version(); + THROW_WALLET_EXCEPTION_IF(!hf_version, error::get_hard_fork_version_error, "Failed to query current hard fork version"); + + const bool clsag = use_fork_rules(feature::CLSAG, 0); + const bool bulletproof_plus = (*hf_version >= hf::hf20_bulletproof_plus) ? use_fork_rules(cryptonote::feature::BULLETPROOF_PLUS, 0) : false; + const rct::RCTConfig rct_config{rct::RangeProofType::PaddedBulletproof, bulletproof_plus ? 4 : 3}; const auto base_fee = get_base_fees(); const uint64_t fee_percent = get_fee_percent(priority, tx_type); const uint64_t fee_quantization_mask = get_fee_quantization_mask(); uint64_t fixed_fee = 0; - std::optional hf_version = get_hard_fork_version(); - THROW_WALLET_EXCEPTION_IF(!hf_version, error::get_hard_fork_version_error, "Failed to query current hard fork version"); - beldex_construct_tx_params beldex_tx_params = tools::wallet2::construct_params(*hf_version, tx_type, priority); uint64_t burn_fixed = 0, burn_percent = 0; // Swap these out because we don't want them present for building intermediate temporary tx @@ -11828,7 +11992,7 @@ std::vector wallet2::create_transactions_from(const crypton std::swap(burn_fixed, beldex_tx_params.burn_fixed); std::swap(burn_percent, beldex_tx_params.burn_percent); bool burning = burn_fixed || burn_percent; - THROW_WALLET_EXCEPTION_IF(burning && beldex_tx_params.hf_version < HF_VERSION_FEE_BURNING, error::wallet_internal_error, "cannot construct transaction: cannot burn amounts under the current hard fork"); + THROW_WALLET_EXCEPTION_IF(burning && beldex_tx_params.hf_version < feature::FEE_BURNING, error::wallet_internal_error, "cannot construct transaction: cannot burn amounts under the current hard fork"); std::vector extra_plus; // Copy and modified from input if modification needed const std::vector &extra = burning ? extra_plus : extra_base; if (burning) @@ -11861,7 +12025,7 @@ std::vector wallet2::create_transactions_from(const crypton // get a tx that can't pay for itself uint64_t fee_dust_threshold; { - const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), clsag); + const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), clsag, bulletproof_plus); fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, outputs, fee_percent, fixed_fee, fee_quantization_mask); } @@ -11888,7 +12052,7 @@ std::vector wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_weight_limit); - const size_t estimated_rct_tx_weight = estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), clsag); + const size_t estimated_rct_tx_weight = estimate_tx_weight(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), clsag, bulletproof_plus); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= tx_weight_target(upper_transaction_weight_limit)); if (try_tx) { @@ -11896,7 +12060,7 @@ std::vector wallet2::create_transactions_from(const crypton pending_tx test_ptx; const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers, beldex_tx_params); - needed_fee = estimate_fee(tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), clsag, base_fee, fee_percent, fixed_fee, fee_quantization_mask); + needed_fee = estimate_fee(tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), clsag, bulletproof_plus, base_fee, fee_percent, fixed_fee, fee_quantization_mask); // add N - 1 outputs for correct initial fee estimation for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i) @@ -12041,14 +12205,18 @@ void wallet2::cold_sign_tx(const std::vector& ptx_vector, signed_tx_ auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + auto hf_version = get_hard_fork_version(); + CHECK_AND_ASSERT_THROW_MES(hf_version, "Failed to query hard fork"); + hw::tx_aux_data aux_data; hw::wallet_shim wallet_shim; setup_shim(&wallet_shim, this); aux_data.tx_recipients = dsts_info; - aux_data.bp_version = use_fork_rules(HF_VERSION_CLSAG, 0) ? 3 : 2; - std::optional hf_version = get_hard_fork_version(); - CHECK_AND_ASSERT_THROW_MES(hf_version, "Failed to query hard fork"); - aux_data.hard_fork = *hf_version; + aux_data.bp_version = (*hf_version >= hf::hf20_bulletproof_plus) + ? (use_fork_rules(cryptonote::feature::BULLETPROOF_PLUS, 0) ? 4 : 3) + : (use_fork_rules(cryptonote::feature::CLSAG, 0) ? 3 : 2); + + aux_data.hard_fork = static_cast(*hf_version); dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); tx_device_aux = aux_data.tx_device_aux; @@ -12093,7 +12261,7 @@ void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) con THROW_WALLET_EXCEPTION(tools::error::no_connection_to_daemon, __func__); } //---------------------------------------------------------------------------------------------------- -bool wallet2::use_fork_rules(uint8_t version, uint64_t early_blocks) const +bool wallet2::use_fork_rules(hf version, uint64_t early_blocks) const { // TODO: How to get fork rule info from light wallet node? if(m_light_wallet) @@ -12102,7 +12270,7 @@ bool wallet2::use_fork_rules(uint8_t version, uint64_t early_blocks) const if (!m_node_rpc_proxy.get_height(height)) THROW_WALLET_EXCEPTION(tools::error::no_connection_to_daemon, __func__); - if (!m_node_rpc_proxy.get_earliest_height(version, earliest_height)) + if (!m_node_rpc_proxy.get_earliest_height(static_cast(version), earliest_height)) THROW_WALLET_EXCEPTION(tools::error::no_connection_to_daemon, __func__); bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand @@ -12120,7 +12288,7 @@ uint64_t wallet2::get_upper_transaction_weight_limit() const { if (m_upper_transaction_weight_limit > 0) return m_upper_transaction_weight_limit; - return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + return BLOCK_GRANTED_FULL_REWARD_ZONE_V5 / 2 - COINBASE_BLOB_RESERVED_SIZE; } //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_outputs(const std::function &f) const @@ -12162,23 +12330,21 @@ std::vector wallet2::get_unspent_amounts_vector(bool strict) const //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct) { - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::request req_t{}; - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::response resp_t{}; - if (is_trusted_daemon()) - req_t.amounts = get_unspent_amounts_vector(false); - req_t.min_count = count; - req_t.max_count = 0; - req_t.unlocked = unlocked; - req_t.recent_cutoff = 0; - bool r = invoke_http(req_t, resp_t); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != rpc::STATUS_OK, error::get_histogram_error, resp_t.status); + nlohmann::json req_params{ + {"amounts", get_unspent_amounts_vector(false)}, + {"min_count", count}, + {"max_count", 0}, + {"unlocked", unlocked}, + {"recent_cutoff", 0} + }; + auto res = m_http_client.json_rpc("get_output_histogram", req_params); + THROW_WALLET_EXCEPTION_IF(res["status"] == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); + THROW_WALLET_EXCEPTION_IF(res["status"] != rpc::STATUS_OK, error::get_histogram_error, res["status"]); std::set mixable; - for (const auto &i: resp_t.histogram) + for (const auto &i: res["histogram"]) { - mixable.insert(i.amount); + mixable.insert(i["amount"].get()); } return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) { @@ -12199,21 +12365,20 @@ std::vector wallet2::select_available_outputs_from_histogram(uint64_t co //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_num_rct_outputs() { - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::request req_t{}; - cryptonote::rpc::GET_OUTPUT_HISTOGRAM::response resp_t{}; - req_t.amounts.push_back(0); - req_t.min_count = 0; - req_t.max_count = 0; - req_t.unlocked = true; - req_t.recent_cutoff = 0; - bool r = invoke_http(req_t, resp_t); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != rpc::STATUS_OK, error::get_histogram_error, resp_t.status); - THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response"); - THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount"); + nlohmann::json req_params{ + {"amounts", std::vector{0}}, + {"min_count", 0}, + {"max_count", 0}, + {"unlocked", true}, + {"recent_cutoff", 0} + }; + auto res = m_http_client.json_rpc("get_output_histogram", req_params); + THROW_WALLET_EXCEPTION_IF(res["status"] == rpc::STATUS_BUSY, error::daemon_busy, "get_output_histogram"); + THROW_WALLET_EXCEPTION_IF(res["status"] != rpc::STATUS_OK, error::get_histogram_error, res["status"]); + THROW_WALLET_EXCEPTION_IF(res["histogram"].size() != 1, error::get_histogram_error, "Expected exactly one response"); + THROW_WALLET_EXCEPTION_IF(res["histogram"][0]["amount"].get() != 0, error::get_histogram_error, "Expected 0 amount"); - return resp_t.histogram[0].total_instances; + return res["histogram"][0]["total_instances"].get(); } //---------------------------------------------------------------------------------------------------- const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const @@ -12325,19 +12490,22 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s // Load missing tx prefix hash if (tx_key_data.tx_prefix_hash.empty()) { - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash{}; + std::string tx_data; crypto::hash tx_prefix_hash{}; - const auto& res_tx = res.txs.front(); + const auto& res_tx = res["txs"].front(); + std::string tx_blob_hex = res_tx["pruned"].get(); + if (res_tx["prunable"]) + tx_blob_hex.append(res_tx["prunable"].get()); - THROW_WALLET_EXCEPTION_IF(!oxenc::is_hex(*res_tx.pruned_as_hex) || !oxenc::is_hex(res_tx.prunable_as_hex.value_or(""s)),error::wallet_internal_error, "Failed to parse transaction from daemon"); - std::string tx_data; - tx_data.reserve(oxenc::from_hex_size(res_tx.pruned_as_hex->size() + (res_tx.prunable_as_hex ? res_tx.prunable_as_hex->size() : 0))); - oxenc::from_hex(res_tx.pruned_as_hex->begin(), res_tx.pruned_as_hex->end(), std::back_inserter(tx_data)); - if (res_tx.prunable_as_hex) - oxenc::from_hex(res_tx.prunable_as_hex->begin(), res_tx.prunable_as_hex->end(), std::back_inserter(tx_data)); + THROW_WALLET_EXCEPTION_IF(not oxenc::is_hex(tx_blob_hex), error::wallet_internal_error, "Failed to parse transaction from daemon"); + tx_data = oxenc::from_hex(tx_blob_hex); THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "Failed to validate transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, @@ -12368,10 +12536,13 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys) { // fetch tx from daemon and check if secret keys agree with corresponding public keys - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res["txs"][0], tx, tx_hash), error::wallet_internal_error, "Failed to get transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch"); std::vector tx_extra_fields; @@ -12404,11 +12575,14 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, std::string_view "get_spend_proof requires spend secret key and is not available for a watch-only wallet"); // fetch tx from daemon - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon"); + THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res["txs"][0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon"); std::vector> signatures; @@ -12501,11 +12675,14 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, std::string_view messa sig_str.remove_prefix(SPEND_PROOF_MAGIC.size()); // fetch tx from daemon - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon"); + THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res["txs"][0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon"); // check signature size size_t num_sigs = 0; @@ -12636,7 +12813,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt crypto::secret_key scalar1; crypto::derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG)); + rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG, rct::RCTType::BulletproofPlus)); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); @@ -12654,10 +12831,13 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) { - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - bool ok = get_pruned_tx(res.txs.front(), tx, tx_hash); + bool ok = get_pruned_tx(res["txs"].front(), tx, tx_hash); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); @@ -12666,25 +12846,28 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de check_tx_key_helper(tx, derivation, additional_derivations, address, received); - in_pool = res.txs.front().in_pool; + in_pool = res["txs"].front()["in_pool"]; confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - res.txs.front().block_height; + confirmations = bc_height - res["txs"].front()["block_height"].get(); } } std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, std::string_view message) { // fetch tx pubkey from the daemon - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - bool ok = get_pruned_tx(res.txs.front(), tx, tx_hash); + bool ok = get_pruned_tx(res["txs"].front(), tx, tx_hash); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); @@ -12814,25 +12997,28 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, std::string_view message, std::string_view sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations) { // fetch tx pubkey from the daemon - auto res = request_transaction(txid); + nlohmann::json get_transactions_params{ + {"tx_hashes", tools::type_to_hex(txid)} + }; + auto res = m_http_client.json_rpc("get_transactions", get_transactions_params); cryptonote::transaction tx; crypto::hash tx_hash; - bool ok = get_pruned_tx(res.txs.front(), tx, tx_hash); + bool ok = get_pruned_tx(res["txs"].front(), tx, tx_hash); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received)) return false; - in_pool = res.txs.front().in_pool; + in_pool = res["txs"].front()["in_pool"]; confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - res.txs.front().block_height; + confirmations = bc_height - res["txs"].front()["block_height"].get(); } return true; @@ -13097,26 +13283,32 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash); // fetch txes from daemon - auto gettx_res = request_transactions(std::move(txids_hex)); + nlohmann::json get_transactions_params{ + {"tx_hashes", std::move(txids_hex)} + }; + auto gettx_res = m_http_client.json_rpc("get_transactions", get_transactions_params); // check spent status - rpc::IS_KEY_IMAGE_SPENT::request kispent_req{}; - rpc::IS_KEY_IMAGE_SPENT::response kispent_res{}; + std::vector key_images; + key_images.reserve(proofs.size()); for (size_t i = 0; i < proofs.size(); ++i) - kispent_req.key_images.push_back(tools::type_to_hex(proofs[i].key_image)); - bool ok = invoke_http(kispent_req, kispent_res); - THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(), + key_images.push_back(tools::type_to_hex(proofs[i].key_image)); + nlohmann::json req_params{ + {"key_images", key_images} + }; + auto kispent_res = m_http_client.json_rpc("is_key_image_spent", req_params); + THROW_WALLET_EXCEPTION_IF(kispent_res["spent_status"].size() != proofs.size(), error::wallet_internal_error, "Failed to get key image spent status from daemon"); total = spent = 0; for (size_t i = 0; i < proofs.size(); ++i) { const reserve_proof_entry& proof = proofs[i]; - THROW_WALLET_EXCEPTION_IF(gettx_res.txs[i].in_pool, error::wallet_internal_error, "Tx is unconfirmed"); + THROW_WALLET_EXCEPTION_IF(gettx_res["txs"][i]["in_pool"], error::wallet_internal_error, "Tx is unconfirmed"); cryptonote::transaction tx; crypto::hash tx_hash; - ok = get_pruned_tx(gettx_res.txs[i], tx, tx_hash); + bool ok = get_pruned_tx(gettx_res["txs"][i], tx, tx_hash); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); THROW_WALLET_EXCEPTION_IF(tx_hash != proof.txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); @@ -13191,11 +13383,11 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr crypto::secret_key shared_secret; crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG)); + rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tools::equals_any(tx.rct_signatures.type, rct::RCTType::Bulletproof2, rct::RCTType::CLSAG, rct::RCTType::BulletproofPlus)); amount = rct::h2d(ecdh_info.amount); } total += amount; - if (kispent_res.spent_status[i]) + if (kispent_res["spent_status"][i]) spent += amount; } @@ -13260,37 +13452,10 @@ uint64_t wallet2::get_approximate_blockchain_height() const return approx_blockchain_height; } -std::vector wallet2::list_current_stakes() +nlohmann::json wallet2::get_staked_master_nodes() { - - std::vector master_node_states; - - auto [success, all_nodes] = this->get_all_master_nodes(); - if (!success) - { - return master_node_states; - } - - cryptonote::account_public_address const primary_address = this->get_address(); - for (rpc::GET_MASTER_NODES::response::entry const &node_info : all_nodes) - { - for (const auto& contributor : node_info.contributors) - { - address_parse_info address_info = {}; - if (!cryptonote::get_account_address_from_str(address_info, this->nettype(), contributor.address)) - { - continue; - } - - if (primary_address != address_info.address) - continue; - - master_node_states.push_back(node_info); - break; // not necessory to check the other contributor in the same masternode once we got our info - } - } - - return master_node_states; + auto [success, contributed_nodes] = m_node_rpc_proxy.get_contributed_master_nodes(get_address_as_str()); + return std::move(contributed_nodes); } void wallet2::set_bns_cache_record(wallet2::bns_detail detail) @@ -13565,10 +13730,9 @@ bool wallet2::export_key_images_to_file(const fs::path& filename, bool requested std::pair>> ski = export_key_images(requested_only); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string data; - const uint32_t offset = boost::endian::native_to_little(ski.first); - data.reserve(sizeof(offset) + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); - data.resize(sizeof(offset)); - std::memcpy(&data[0], &offset, sizeof(offset)); + data.reserve(sizeof(uint32_t) + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); + data.resize(sizeof(uint32_t)); + oxenc::write_host_as_little(ski.first, data.data()); data += tools::view_guts(keys.m_spend_public_key); data += tools::view_guts(keys.m_view_public_key); for (const auto &i: ski.second) @@ -13666,9 +13830,7 @@ uint64_t wallet2::import_key_images_from_file(const fs::path& filename, uint64_t const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename.u8string()); - uint32_t offset; - std::memcpy(&offset, data.data(), sizeof(offset)); - boost::endian::little_to_native_inplace(offset); + uint32_t offset = oxenc::load_little_to_host(data.data()); THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); // Validate embedded spend/view public keys @@ -13706,8 +13868,6 @@ uint64_t wallet2::import_key_images_from_file(const fs::path& filename, uint64_t uint64_t wallet2::import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent) { PERF_TIMER(import_key_images_lots); - rpc::IS_KEY_IMAGE_SPENT::request req{}; - rpc::IS_KEY_IMAGE_SPENT::response daemon_resp{}; THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error, @@ -13720,7 +13880,8 @@ uint64_t wallet2::import_key_images(const std::vector key_images{}; + key_images.reserve(signed_key_images.size()); PERF_TIMER_START(import_key_images_A_validate_and_extract_key_images); for (size_t n = 0; n < signed_key_images.size(); ++n) @@ -13756,7 +13917,7 @@ uint64_t wallet2::import_key_images(const std::vector(req, daemon_resp); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == rpc::STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != rpc::STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error, + is_key_image_spent_response = m_http_client.json_rpc("is_key_image_spent", req_params); + THROW_WALLET_EXCEPTION_IF(is_key_image_spent_response["status"] == rpc::STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(is_key_image_spent_response["status"] != rpc::STATUS_OK, error::is_key_image_spent_error, is_key_image_spent_response["status"]); + THROW_WALLET_EXCEPTION_IF(is_key_image_spent_response["spent_status"].size() != signed_key_images.size(), error::wallet_internal_error, "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + - std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size())); - for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n) + std::to_string(is_key_image_spent_response["spent_status"].size()) + ", expected " + std::to_string(signed_key_images.size())); + for (size_t n = 0; n < is_key_image_spent_response["spent_status"].size(); ++n) { transfer_details &td = m_transfers[n + offset]; - td.m_spent = daemon_resp.spent_status[n] != rpc::IS_KEY_IMAGE_SPENT::UNSPENT; + td.m_spent = is_key_image_spent_response["spent_status"][n] != rpc::IS_KEY_IMAGE_SPENT::SPENT::UNSPENT; } } std::unordered_set spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input. @@ -13828,9 +13992,9 @@ uint64_t wallet2::import_key_images(const std::vector::const_iterator skii = spent_key_images.find(td.m_key_image); if (skii == spent_key_images.end()) @@ -13847,7 +14011,10 @@ uint64_t wallet2::import_key_images(const std::vectorsecond, e.block_height); + set_spent(it->second, e["block_height"]); if (m_callback) - m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx, td.m_subaddr_index); + m_callback->on_money_spent(e["block_height"], *spent_txid, spent_tx, amount, spent_tx, td.m_subaddr_index); if (subaddr_account != (uint32_t)-1 && subaddr_account != td.m_subaddr_index.major) LOG_PRINT_L0("WARNING: This tx spends outputs received by different subaddress accounts, which isn't supposed to happen"); subaddr_account = td.m_subaddr_index.major; @@ -13936,7 +14103,7 @@ uint64_t wallet2::import_key_images(const std::vector(req, res); + bool r = invoke_http(req, res); if (!r || res.status != rpc::STATUS_OK) { std::ostringstream oss; @@ -14852,7 +15019,7 @@ bool wallet2::is_synced(uint64_t grace_blocks) const //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_segregation_fork_height() const { - if (m_nettype == MAINNET && m_segregation_height > 0) + if (m_nettype == network_type::MAINNET && m_segregation_height > 0) return m_segregation_height; return SEGREGATION_FORK_HEIGHT; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2b4ea021116..69dcdb17f40 100755 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -40,11 +40,13 @@ #include #include #include +#include #include "cryptonote_basic/account.h" #include "cryptonote_basic/account_boost_serialization.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "rpc/core_rpc_server_binary_commands.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_tx_utils.h" #include "cryptonote_core/beldex_name_system.h" @@ -106,6 +108,7 @@ namespace tools uint64_t pick(); gamma_picker(const std::vector &rct_offsets); gamma_picker(const std::vector &rct_offsets, double shape, double scale); + uint64_t get_num_rct_outs() const { return num_rct_outputs; } private: struct gamma_engine @@ -295,7 +298,7 @@ namespace tools static bool verify_password(const fs::path& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); static bool query_device(hw::device::type& device_type, const fs::path& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); + wallet2(cryptonote::network_type nettype = cryptonote::network_type::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); ~wallet2(); struct tx_scan_info_t @@ -394,6 +397,7 @@ namespace tools using transfer_details = wallet::transfer_details; using transfer_container = std::vector; + using unique_index_container = std::set; using pending_tx = wallet::pending_tx; using unsigned_tx_set = wallet::unsigned_tx_set; using signed_tx_set = wallet::signed_tx_set; @@ -449,7 +453,7 @@ namespace tools crypto::hash hash; cryptonote::block block; std::vector txes; - cryptonote::rpc::GET_BLOCKS_FAST::block_output_indices o_indices; + nlohmann::json o_indices; bool error; }; @@ -753,14 +757,14 @@ namespace tools bool parse_unsigned_tx_from_str(std::string_view unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const fs::path& signed_filename, std::vector& ptx, std::function accept_func = NULL); bool parse_tx_from_str(std::string_view signed_tx_st, std::vector &ptx, std::function accept_func); - std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra_base, uint32_t subaddr_account, std::set subaddr_indices, cryptonote::beldex_construct_tx_params &tx_params); + std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra_base, uint32_t subaddr_account, std::set subaddr_indices, cryptonote::beldex_construct_tx_params &tx_params, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, cryptonote::txtype tx_type = cryptonote::txtype::standard); std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, cryptonote::txtype tx_type = cryptonote::txtype::standard); std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, cryptonote::txtype tx_type = cryptonote::txtype::standard); std::vector create_transactions_burn(const std::vector &ki, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, cryptonote::txtype tx_type = cryptonote::txtype::coin_burn); - bool sanity_check(const std::vector &ptx_vector, std::vector dsts) const; + bool sanity_check(const std::vector &ptx_vector, std::vector dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; void cold_tx_aux_import(const std::vector& ptx, const std::vector& tx_device_aux); void cold_sign_tx(const std::vector& ptx_vector, signed_tx_set &exported_txs, std::vector const &dsts_info, std::vector & tx_device_aux); uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent); @@ -793,7 +797,7 @@ namespace tools bool coinbase = false; bool filter_by_height = false; uint64_t min_height = 0; - uint64_t max_height = CRYPTONOTE_MAX_BLOCK_NUMBER; + uint64_t max_height = cryptonote::MAX_BLOCK_NUMBER; std::set subaddr_indices; uint32_t account_index; bool all_accounts; @@ -815,11 +819,11 @@ namespace tools auto get_all_master_nodes() const { return m_node_rpc_proxy.get_all_master_nodes(); } auto get_master_nodes(std::vector const &pubkeys) const { return m_node_rpc_proxy.get_master_nodes(pubkeys); } auto get_master_node_blacklisted_key_images() const { return m_node_rpc_proxy.get_master_node_blacklisted_key_images(); } - std::vector list_current_stakes(); - auto bns_owners_to_names(cryptonote::rpc::BNS_OWNERS_TO_NAMES::request const &request) const { return m_node_rpc_proxy.bns_owners_to_names(request); } - auto bns_names_to_owners(cryptonote::rpc::BNS_NAMES_TO_OWNERS::request const &request) const { return m_node_rpc_proxy.bns_names_to_owners(request); } - auto resolve(cryptonote::rpc::BNS_RESOLVE::request const &request) const { return m_node_rpc_proxy.bns_resolve(request); } - + // List of service nodes this wallet has a stake in: + nlohmann::json get_staked_master_nodes(); + auto bns_owners_to_names(nlohmann::json const &request) const { return m_node_rpc_proxy.bns_owners_to_names(request); } + auto bns_names_to_owners(nlohmann::json const &request) const { return m_node_rpc_proxy.bns_names_to_owners(request); } + auto resolve(nlohmann::json const &request) const { return m_node_rpc_proxy.bns_resolve(request); } struct bns_detail { std::string name; @@ -1069,8 +1073,8 @@ namespace tools const transfer_details &get_transfer_details(size_t idx) const; void get_hard_fork_info (uint8_t version, uint64_t &earliest_height) const; - std::optional get_hard_fork_version() const { return m_node_rpc_proxy.get_hardfork_version(); } - bool use_fork_rules(uint8_t version, uint64_t early_blocks = 0) const; + std::optional get_hard_fork_version() const { return m_node_rpc_proxy.get_hardfork_version(); } + bool use_fork_rules(cryptonote::hf version, uint64_t early_blocks = 0) const; const fs::path& get_wallet_file() const; const fs::path& get_keys_file() const; @@ -1227,7 +1231,7 @@ namespace tools // params constructor, accumulates the burn amounts if the priority is // a flash and, or a bns tx. If it is a flash TX, bns_burn_type is ignored. - static cryptonote::beldex_construct_tx_params construct_params(uint8_t hf_version, cryptonote::txtype tx_type, uint32_t priority, uint64_t extra_burn = 0, bns::mapping_years bns_burn_type = static_cast(0)); + static cryptonote::beldex_construct_tx_params construct_params(cryptonote::hf hf_version, cryptonote::txtype tx_type, uint32_t priority, uint64_t extra_burn = 0, bns::mapping_years bns_burn_type = static_cast(0)); bool is_unattended() const { return m_unattended; } @@ -1308,6 +1312,11 @@ namespace tools return false; } + nlohmann::json json_rpc(std::string command, nlohmann::json params) + { + return m_http_client.json_rpc(command, params); + } + bool set_ring_database(fs::path filename); const fs::path& get_ring_database() const { return m_ring_database; } bool get_ring(const crypto::key_image &key_image, std::vector &outs); @@ -1403,11 +1412,9 @@ namespace tools // signature: (Optional) If set, use the signature given, otherwise by default derive the signature from the wallet spend key as an ed25519 key. // The signature is derived from the hash of the previous txid blob and previous value blob of the mapping. By default this is signed using the wallet's spend key as an ed25519 keypair. - std::vector bns_create_update_mapping_tx(std::string name, std::string const *value_bchat, std::string const *value_wallet, std::string const *value_belnet, std::string const *value_eth_addr, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set subaddr_indices = {}, std::vector *response = {}); - + std::vector bns_create_update_mapping_tx(std::string name, std::string const *value_bchat, std::string const *value_wallet, std::string const *value_belnet, std::string const *value_eth_addr, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set subaddr_indices = {}, nlohmann::json *response = {}); // BNS renewal (for belnet registrations, not for bchat/wallet) - std::vector bns_create_renewal_tx(bns::mapping_years map_years, std::string name, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set subaddr_indices = {}, std::vector *response = {}); - + std::vector bns_create_renewal_tx(bns::mapping_years map_years, std::string name, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set subaddr_indices = {}, nlohmann::json *response = {}); // Generate just the signature required for putting into bns_update_mapping command in the wallet bool bns_make_update_mapping_signature(std::string name, std::string const *value_bchat, std::string const *value_wallet, std::string const *value_belnet, std::string const *value_eth_addr, std::string const *owner, std::string const *backup_owner, bns::generic_signature &signature, uint32_t account_index = 0, std::string *reason = nullptr); @@ -1445,17 +1452,6 @@ namespace tools std::atomic m_long_poll_disabled; static std::string get_default_daemon_address(); - /// Requests transactions from daemon given hex strings of the tx ids; throws a wallet exception - /// on error, otherwise returns the response. - cryptonote::rpc::GET_TRANSACTIONS::response request_transactions(std::vector txids_hex); - - /// Requests transactions from daemon given a vector of crypto::hash. Throws a wallet exception - /// on error, otherwise returns the response. - cryptonote::rpc::GET_TRANSACTIONS::response request_transactions(const std::vector& txids); - - /// Same as above, but for a single transaction. - cryptonote::rpc::GET_TRANSACTIONS::response request_transaction(const crypto::hash& txid) { return request_transactions(std::vector{{txid}}); } - // The wallet's RPC client; public for advanced configuration purposes. cryptonote::rpc::http_client m_http_client; @@ -1481,14 +1477,14 @@ namespace tools */ 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, std::optional& keys_to_encrypt); - void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool flash, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map, size_t> *output_tracker_cache = NULL); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, cryptonote::hf block_version, uint64_t ts, bool miner_tx, bool pool, bool flash, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map, size_t> *output_tracker_cache = NULL); 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); void detach_blockchain(uint64_t height, std::map, size_t> *output_tracker_cache = NULL); void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; bool clear(); void clear_soft(bool keep_key_images=false); - void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list& short_chain_history, std::vector& blocks, std::vector& o_indices, uint64_t& current_height); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception); @@ -1662,7 +1658,7 @@ namespace tools bool m_light_wallet; /* sends view key to daemon for scanning */ uint64_t m_light_wallet_scanned_block_height; uint64_t m_light_wallet_blockchain_height; - uint64_t m_light_wallet_per_kb_fee = FEE_PER_BYTE_V12 * 1024; + uint64_t m_light_wallet_per_kb_fee = cryptonote::old::FEE_PER_BYTE_V12 * 1024; bool m_light_wallet_connected; uint64_t m_light_wallet_balance; uint64_t m_light_wallet_unlocked_balance; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 4fc629e7389..54c19d773ef 100755 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -84,6 +84,7 @@ namespace tools // tx_sum_overflow // tx_too_big // zero_destination + // subtract_fee_from_bad_index // wallet_rpc_error * // daemon_busy // no_connection_to_daemon @@ -747,6 +748,14 @@ namespace tools uint64_t m_tx_weight_limit; }; //---------------------------------------------------------------------------------------------------- + struct zero_amount: public transfer_error + { + explicit zero_amount(std::string&& loc) + : transfer_error(std::move(loc), "destination amount is zero") + { + } + }; + //---------------------------------------------------------------------------------------------------- struct zero_destination : public transfer_error { explicit zero_destination(std::string&& loc) @@ -755,6 +764,15 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct subtract_fee_from_bad_index : public transfer_error + { + explicit subtract_fee_from_bad_index(std::string&& loc, long bad_index) + : transfer_error(std::move(loc), + "subtractfeefrom: bad index: " + std::to_string(bad_index) + " (indexes are 0-based)") + { + } + }; + //---------------------------------------------------------------------------------------------------- struct wallet_rpc_error : public wallet_logic_error { const std::string& request() const { return m_request; } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 02bd6d9d8f9..078aeed88fd 100755 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -51,7 +51,7 @@ #include "epee/wipeable_string.h" #include "crypto/hash.h" #include "mnemonics/electrum-words.h" -#include "rpc/rpc_args.h" +#include "rpc/common/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" #include "daemonizer/daemonizer.h" #include "cryptonote_core/beldex_name_system.h" @@ -221,10 +221,12 @@ namespace tools epee::serialization::portable_storage ps; if(!ps.load_from_json(body)) - return jsonrpc_error_response(res, -32700, "Parse error"); + return jsonrpc_error_response(res, -32700, "Parse error", {}); - epee::serialization::storage_entry id{std::string{}}; - ps.get_value("id", id, nullptr); + epee::serialization::storage_entry epee_id{std::string{}}; + ps.get_value("id", epee_id, nullptr); + + nlohmann::json id = var::get(epee_id); std::string method; if(!ps.get_value("method", method, nullptr)) @@ -246,7 +248,7 @@ namespace tools // If it's a restricted command and we're in restricted mode then deny it if (restricted && m_restricted) { MWARNING("JSON RPC request for restricted command " << method << " in restricted mode from " << get_remote_address(res)); - return jsonrpc_error_response(res, error_code::DENIED, method + " is not available in restricted mode."); + return jsonrpc_error_response(res, error_code::DENIED, method + " is not available in restricted mode.", {}); } // Try to load "params" into a generic epee value; if it fails (because there is no "params") @@ -259,7 +261,7 @@ namespace tools wallet_rpc_error json_error{-32603, "Internal error"}; try { - result = invoke_ptr(ps, std::move(id), std::move(params), *this); + result = invoke_ptr(ps, std::move(epee_id), std::move(params), *this); json_error.code = 0; } catch (const parse_error& e) { json_error = {-32602, "Invalid params"}; // Reserved json code/message value for specifically this failure @@ -302,7 +304,7 @@ namespace tools } if (json_error.code != 0) - return jsonrpc_error_response(res, json_error.code, std::move(json_error.message)); + return jsonrpc_error_response(res, json_error.code, std::move(json_error.message), {}); res.writeHeader("Server", server_header()); res.writeHeader("Content-Type", "application/json"); @@ -984,10 +986,10 @@ namespace tools return hex_tx_keys(ptx.tx_key, ptx.additional_tx_keys); } //------------------------------------------------------------------------------------------------------------------------------ - template + template void wallet_rpc_server::fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, bool flash, - Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata) + bool get_tx_key, Ts& tx_key, Tu &amount, Ta &amounts_by_dest, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, bool flash, + Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, Tk &spent_key_images) { for (const auto & ptx : ptx_vector) { @@ -998,6 +1000,24 @@ namespace tools // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); fill(fee, ptx.fee); + + // add amounts by destination + tools::wallet_rpc::amounts_list abd; + for (const auto& dst : ptx.dests) + abd.amounts.push_back(dst.amount); + fill(amounts_by_dest, abd); + + // add spent key images + tools::wallet_rpc::key_image_list key_image_list; + bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool + { + CHECKED_GET_SPECIFIC_VARIANT(s_e, cryptonote::txin_to_key, in, false); + key_image_list.key_images.push_back(tools::type_to_hex(in.k_image)); + return true; + }); + THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, ptx.tx); + fill(spent_key_images, key_image_list); + } if (m_wallet->multisig()) @@ -1044,11 +1064,11 @@ namespace tools { uint32_t priority = convert_priority(req.priority); - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED}; cryptonote::beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, cryptonote::txtype::standard, priority); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, tx_params); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, tx_params, req.subtract_fee_from_outputs); if (ptx_vector.empty()) throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "No transaction created"}; @@ -1057,8 +1077,8 @@ namespace tools if (ptx_vector.size() != 1) throw wallet_rpc_error{error_code::TX_TOO_LARGE, "Transaction would be too large. try /transfer_split."}; - fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata); + fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images); } return res; } @@ -1080,11 +1100,11 @@ namespace tools if(req.amount) { - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED}; cryptonote::beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, cryptonote::txtype::coin_burn, req.priority, req.amount); - ptx_vector = m_wallet->create_transactions_2({}, CRYPTONOTE_DEFAULT_TX_MIXIN, 0, req.priority, extra, req.account_index, req.subaddr_indices, tx_params); + ptx_vector = m_wallet->create_transactions_2({}, cryptonote::TX_OUTPUT_DECOYS, 0, req.priority, extra, req.account_index, req.subaddr_indices, tx_params); } else { @@ -1112,7 +1132,7 @@ namespace tools throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "The txid already spent."}; if(!available) throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "No incoming available transfers"}; - ptx_vector = m_wallet->create_transactions_burn(ki, outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, 0, req.priority, extra); + ptx_vector = m_wallet->create_transactions_burn(ki, outputs, cryptonote::TX_OUTPUT_DECOYS, 0, req.priority, extra); } if (ptx_vector.empty()) throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "Failed to create coin_burn transaction:"}; @@ -1123,6 +1143,7 @@ namespace tools req.get_tx_key, res.tx_key, res.amount, + res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, @@ -1132,7 +1153,8 @@ namespace tools req.get_tx_hex, res.tx_blob, req.get_tx_metadata, - res.tx_metadata); + res.tx_metadata, + res.spent_key_images); return res; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1151,20 +1173,20 @@ namespace tools { uint32_t priority = convert_priority(req.priority); - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED}; cryptonote::beldex_construct_tx_params tx_params = tools::wallet2::construct_params(*hf_version, cryptonote::txtype::standard, priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, CRYPTONOTE_DEFAULT_TX_MIXIN, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, tx_params); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, cryptonote::TX_OUTPUT_DECOYS, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, tx_params); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); if (ptx_vector.empty()) throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "No transaction created"}; - fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list); + fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list); } return res; } @@ -1403,8 +1425,8 @@ namespace tools std::vector ptx_vector = m_wallet->create_unmixable_sweep_transactions(); - fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list); + fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list); return {}; } @@ -1440,10 +1462,10 @@ namespace tools { uint32_t priority = convert_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, req.unlock_time, priority, extra, req.account_index, subaddr_indices); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, cryptonote::TX_OUTPUT_DECOYS, req.unlock_time, priority, extra, req.account_index, subaddr_indices); - fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, - res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list); + fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, + res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list); } return res; } @@ -1472,7 +1494,7 @@ namespace tools { uint32_t priority = convert_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, CRYPTONOTE_DEFAULT_TX_MIXIN, req.unlock_time, priority, extra); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, cryptonote::TX_OUTPUT_DECOYS, req.unlock_time, priority, extra); if (ptx_vector.empty()) throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "No outputs found"}; @@ -1482,8 +1504,8 @@ namespace tools if (ptx.selected_transfers.size() > 1) throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "The transaction uses multiple inputs, which is not supposed to happen"}; - fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata); + fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, priority == tx_priority_flash, + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images); } return res; } @@ -2397,24 +2419,35 @@ namespace tools if (req.threads_count < 1 || max_mining_threads_count < req.threads_count) throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "The specified number of threads is inappropriate."}; - rpc::START_MINING::request daemon_req{}; - daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); - daemon_req.threads_count = req.threads_count; - - rpc::START_MINING::response daemon_res{}; - bool r = m_wallet->invoke_http(daemon_req, daemon_res); - if (!r || daemon_res.status != rpc::STATUS_OK) + nlohmann::json req_params{ + {"miner_address", m_wallet->get_account().get_public_address_str(m_wallet->nettype())}, + {"threads_count", req.threads_count} + }; + try + { + nlohmann::json res = m_wallet->json_rpc("start_mining", req_params); + if (res["status"] != rpc::STATUS_OK) + throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't start mining due to unknown error."}; + } + catch (...) { throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't start mining due to unknown error."}; + } + return {}; } //------------------------------------------------------------------------------------------------------------------------------ STOP_MINING::response wallet_rpc_server::invoke(STOP_MINING::request&& req) { require_open(); - rpc::STOP_MINING::response daemon_res{}; - bool r = m_wallet->invoke_http({}, daemon_res); - if (!r || daemon_res.status != rpc::STATUS_OK) + try + { + nlohmann::json res = m_wallet->json_rpc("stop_mining", {}); + if (res["status"] != rpc::STATUS_OK) + throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't stop mining due to unknown error."}; + } + catch (...) { throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't stop mining due to unknown error."}; + } return {}; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2484,12 +2517,15 @@ namespace { if (!req.hardware_wallet) wal->set_seed_language(req.language); - rpc::GET_HEIGHT::request hreq{}; - rpc::GET_HEIGHT::response hres{}; - hres.height = 0; - bool r = wal->invoke_http(hreq, hres); - if (r) - wal->set_refresh_from_block_height(hres.height); + nlohmann::json req_params{ + {"height", 0} + }; + try + { + nlohmann::json res = wal->json_rpc("get_height", req_params); + wal->set_refresh_from_block_height(res["height"].get()); + } + catch (...) {} if (req.hardware_wallet) wal->restore_from_device(wallet_file, req.password, req.device_name.empty() ? "Ledger" : req.device_name); @@ -2934,38 +2970,38 @@ namespace { { VALIDATE_ADDRESS::response res{}; cryptonote::address_parse_info info; - const struct { cryptonote::network_type type; const char *stype; } net_types[] = { - { cryptonote::MAINNET, "mainnet" }, - { cryptonote::TESTNET, "testnet" }, - { cryptonote::DEVNET, "devnet" }, + constexpr std::pair net_types[] = { + { cryptonote::network_type::MAINNET, "mainnet" }, + { cryptonote::network_type::TESTNET, "testnet" }, + { cryptonote::network_type::DEVNET, "devnet" }, }; if (!req.any_net_type && !m_wallet) require_open(); - for (const auto &net_type: net_types) + for (const auto& [type, type_str] : net_types) { - if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->nettype())) + if (!req.any_net_type && (!m_wallet || type != m_wallet->nettype())) continue; if (req.allow_openalias) { res.valid = false; try { - info = extract_account_addr(net_type.type, req.address); + info = extract_account_addr(type, req.address); res.valid = true; } catch (...) {} if (res.valid) - res.openalias_address = info.as_str(net_type.type); + res.openalias_address = info.as_str(type); } else { - res.valid = cryptonote::get_account_address_from_str(info, net_type.type, req.address); + res.valid = cryptonote::get_account_address_from_str(info, type, req.address); } if (res.valid) { res.integrated = info.has_payment_id; res.subaddress = info.is_subaddress; - res.nettype = net_type.stype; + res.nettype = type_str; return res; } } @@ -3034,8 +3070,8 @@ namespace { std::vector ptx_vector = {stake_result.ptx}; - fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata); + fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images); return res; } @@ -3060,8 +3096,8 @@ namespace { throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, register_result.msg}; std::vector ptx_vector = {register_result.ptx}; - fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, - res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata); + fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, false /*flash*/, + res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images); return res; } @@ -3149,6 +3185,7 @@ namespace { req.get_tx_key, res.tx_key, res.amount, + res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, @@ -3158,7 +3195,8 @@ namespace { req.get_tx_hex, res.tx_blob, req.get_tx_metadata, - res.tx_metadata); + res.tx_metadata, + res.spent_key_images); return res; } @@ -3183,6 +3221,7 @@ namespace { req.get_tx_key, res.tx_key, res.amount, + res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, @@ -3192,7 +3231,8 @@ namespace { req.get_tx_hex, res.tx_blob, req.get_tx_metadata, - res.tx_metadata); + res.tx_metadata, + res.spent_key_images); return res; } @@ -3232,6 +3272,7 @@ namespace { req.get_tx_key, res.tx_key, res.amount, + res.amounts_by_dest, res.fee, res.multisig_txset, res.unsigned_txset, @@ -3241,7 +3282,8 @@ namespace { req.get_tx_hex, res.tx_blob, req.get_tx_metadata, - res.tx_metadata); + res.tx_metadata, + res.spent_key_images); return res; } @@ -3286,6 +3328,7 @@ namespace { BNS_KNOWN_NAMES::response wallet_rpc_server::invoke(BNS_KNOWN_NAMES::request&& req) { + //TODO sean this needs to fit the new request format require_open(); BNS_KNOWN_NAMES::response res{}; @@ -3299,105 +3342,80 @@ namespace { } auto nettype = m_wallet->nettype(); - rpc::BNS_NAMES_TO_OWNERS::request lookup_req{}; - lookup_req.include_expired = req.include_expired; + nlohmann::json req_params{{"include_expired", req.include_expired}}; + auto& name_hash = (req_params["name_hash"] = nlohmann::json::array()); + name_hash.get_ref().reserve( + std::min(rpc::BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES, res.known_names.size())); uint64_t curr_height = req.include_expired ? m_wallet->get_blockchain_current_height() : 0; // Query beldexd for the full record info for (auto it = res.known_names.begin(); it != res.known_names.end(); ) { - const size_t num_entries = std::distance(it, res.known_names.end()); - const auto end = num_entries < rpc::BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES + const size_t remaining = std::distance(it, res.known_names.end()); + const auto batch_end = remaining < rpc::BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES ? res.known_names.end() : it + rpc::BNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES; - lookup_req.entries.clear(); - lookup_req.entries.reserve(std::distance(it, end)); - for (auto it2 = it; it2 != end; it2++) - { - auto& name_hash = lookup_req.entries.emplace_back(); - name_hash = it2->hashed; - } - if (auto [success, records] = m_wallet->bns_names_to_owners(lookup_req); success) + // Build name_hash list for the batch + name_hash.clear(); + for (auto it2 = it; it2 != batch_end; ++it2) + name_hash.push_back(it2->hashed); + + if (auto [success, records] = m_wallet->bns_names_to_owners(req_params); success) { - size_t type_offset = std::distance(res.known_names.begin(), it); - for (auto& rec : records) - { - if (rec.entry_index >= num_entries) - { - MWARNING("Got back invalid entry_index " << rec.entry_index << " for a request for " << num_entries << " entries"); - continue; - } + size_t base_index = std::distance(res.known_names.begin(), it); - auto& res_e = *(it + rec.entry_index); - res_e.owner = std::move(rec.owner); - res_e.backup_owner = std::move(rec.backup_owner); - res_e.encrypted_bchat_value = std::move(rec.encrypted_bchat_value); - res_e.encrypted_wallet_value = std::move(rec.encrypted_wallet_value); - res_e.encrypted_belnet_value = std::move(rec.encrypted_belnet_value); - res_e.encrypted_eth_addr_value = std::move(rec.encrypted_eth_addr_value); - res_e.update_height = rec.update_height; - res_e.expiration_height = rec.expiration_height; - if (req.include_expired && res_e.expiration_height) - res_e.expired = *res_e.expiration_height < curr_height; - res_e.txid = std::move(rec.txid); + auto decrypt_and_store = [&](const std::optional& encrypted, bns::mapping_type type, std::optional& out_value, const std::string& name) + { + if (!req.decrypt || !encrypted || encrypted->empty() || !oxenc::is_hex(*encrypted)) + return; + + bns::mapping_value value; + std::string errmsg; + if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(*encrypted), &value, &errmsg) && + value.decrypt(name, type)) + out_value = value.to_readable_value(nettype, type); + else + MWARNING("Failed to decrypt BNS value for " << name << (errmsg.empty() ? ""s : ": " + errmsg)); + }; - //BCHAT - if (req.decrypt && !res_e.encrypted_bchat_value.empty() && oxenc::is_hex(res_e.encrypted_bchat_value)) + for (const auto& rec : records) + { + size_t index = rec["entry_index"].get(); + if (index >= remaining) { - bns::mapping_value value; - const auto type = bns::mapping_type::bchat; - std::string errmsg; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(res_e.encrypted_bchat_value), &value, &errmsg) - && value.decrypt(res_e.name, type)) - res_e.value_bchat = value.to_readable_value(nettype, type); - else - MWARNING("Failed to decrypt BNS value for " << res_e.name << (errmsg.empty() ? ""s : ": " + errmsg)); + MWARNING("Invalid entry_index " << index << " for batch size " << remaining); + continue; } - //ETH_ADDR - if (req.decrypt && !res_e.encrypted_eth_addr_value.empty() && oxenc::is_hex(res_e.encrypted_eth_addr_value)) - { - bns::mapping_value value; - const auto type = bns::mapping_type::eth_addr; - std::string errmsg; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(res_e.encrypted_eth_addr_value), &value, &errmsg) - && value.decrypt(res_e.name, type)) - res_e.value_eth_addr = value.to_readable_value(nettype, type); - else - MWARNING("Failed to decrypt BNS value for " << res_e.name << (errmsg.empty() ? ""s : ": " + errmsg)); - } + auto& res_e = *(it + index); + res_e.owner = rec["owner"]; + if (rec.contains("backup_owner") && !rec["backup_owner"].is_null()) + res_e.backup_owner = rec["backup_owner"].get(); + if (rec.contains("encrypted_bchat_value") && !rec["encrypted_bchat_value"].empty()) + res_e.encrypted_bchat_value = rec["encrypted_bchat_value"]; + if (rec.contains("encrypted_wallet_value") && !rec["encrypted_wallet_value"].empty()) + res_e.encrypted_wallet_value = rec["encrypted_wallet_value"]; + if (rec.contains("encrypted_belnet_value") && !rec["encrypted_belnet_value"].empty()) + res_e.encrypted_belnet_value = rec["encrypted_belnet_value"]; + if (rec.contains("encrypted_eth_addr_value") && !rec["encrypted_eth_addr_value"].empty()) + res_e.encrypted_eth_addr_value = rec["encrypted_eth_addr_value"]; + res_e.update_height = rec["update_height"].get(); + res_e.expiration_height = rec["expiration_height"].get(); + res_e.txid = rec["txid"]; - //WALLET - if (req.decrypt && !res_e.encrypted_wallet_value.empty() && oxenc::is_hex(res_e.encrypted_wallet_value)) - { - bns::mapping_value value; - const auto type = bns::mapping_type::wallet; - std::string errmsg; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(res_e.encrypted_wallet_value), &value, &errmsg) - && value.decrypt(res_e.name, type)) - res_e.value_wallet = value.to_readable_value(nettype, type); - else - MWARNING("Failed to decrypt BNS value for " << res_e.name << (errmsg.empty() ? ""s : ": " + errmsg)); - } + if (req.include_expired && res_e.expiration_height) + res_e.expired = res_e.expiration_height < curr_height; - //BELNET - if (req.decrypt && !res_e.encrypted_belnet_value.empty() && oxenc::is_hex(res_e.encrypted_belnet_value)) - { - bns::mapping_value value; - const auto type = bns::mapping_type::belnet; - std::string errmsg; - if (bns::mapping_value::validate_encrypted(type, oxenc::from_hex(res_e.encrypted_belnet_value), &value, &errmsg) - && value.decrypt(res_e.name, type)) - res_e.value_belnet = value.to_readable_value(nettype, type); - else - MWARNING("Failed to decrypt BNS value for " << res_e.name << (errmsg.empty() ? ""s : ": " + errmsg)); - } + decrypt_and_store(res_e.encrypted_bchat_value, bns::mapping_type::bchat, res_e.value_bchat, res_e.name); + decrypt_and_store(res_e.encrypted_eth_addr_value, bns::mapping_type::eth_addr, res_e.value_eth_addr, res_e.name); + decrypt_and_store(res_e.encrypted_wallet_value, bns::mapping_type::wallet, res_e.value_wallet, res_e.name); + decrypt_and_store(res_e.encrypted_belnet_value, bns::mapping_type::belnet, res_e.value_belnet, res_e.name); } } - it = end; + it = batch_end; } // Erase anything we didn't get a response for (it will have update_height of 0) @@ -3456,7 +3474,7 @@ namespace { std::string reason; bns::mapping_type type = {}; - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED}; { if (!bns::validate_mapping_type(req.type, *hf_version, &type, &reason)) @@ -3490,7 +3508,7 @@ namespace { throw wallet_rpc_error{error_code::BNS_VALUE_TOO_LONG, "BNS value '" + req.value + "' is too long"}; std::string reason; - std::optional hf_version = m_wallet->get_hard_fork_version(); + auto hf_version = m_wallet->get_hard_fork_version(); if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED}; bns::mapping_type type; @@ -3504,7 +3522,7 @@ namespace { if (!bns::mapping_value::validate(m_wallet->nettype(), type, req.value, &value, &reason)) throw wallet_rpc_error{error_code::BNS_BAD_VALUE, "Invalid BNS value '" + req.value + "': " + reason}; - bool old_argon2 = type == bns::mapping_type::bchat && *hf_version < cryptonote::network_version_17_POS; + bool old_argon2 = type == bns::mapping_type::bchat && *hf_version < cryptonote::hf::hf17_POS; if (!value.encrypt(req.name, nullptr, old_argon2)) throw wallet_rpc_error{error_code::BNS_VALUE_ENCRYPT_FAILED, "Value encryption failure"}; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 42ee4b69080..961f91bb6df 100755 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -42,7 +42,7 @@ #include "common/periodic_task.h" #include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" -#include "rpc/http_server_base.h" +#include "rpc/common/http_server_base.h" #undef BELDEX_DEFAULT_LOG_CATEGORY #define BELDEX_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -181,10 +181,10 @@ namespace tools // Safely and cleanly closes the currently open wallet (if one is open) void close_wallet(bool save_current); - template + template void fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, bool flash, - Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata); + bool get_tx_key, Ts& tx_key, Tu &amount, Ta &amounts_by_dest, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, bool flash, + Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, Tk &spent_key_images); cryptonote::address_parse_info extract_account_addr(cryptonote::network_type nettype, std::string_view addr_or_url); diff --git a/src/wallet/wallet_rpc_server_commands_defs.cpp b/src/wallet/wallet_rpc_server_commands_defs.cpp index e843c123085..facd445de16 100755 --- a/src/wallet/wallet_rpc_server_commands_defs.cpp +++ b/src/wallet/wallet_rpc_server_commands_defs.cpp @@ -193,11 +193,19 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_HEIGHT::response) KV_SERIALIZE(immutable_height) KV_SERIALIZE_MAP_CODE_END() +KV_SERIALIZE_MAP_CODE_BEGIN(key_image_list) + KV_SERIALIZE(key_images) +KV_SERIALIZE_MAP_CODE_END() + +KV_SERIALIZE_MAP_CODE_BEGIN(amounts_list) + KV_SERIALIZE(amounts) +KV_SERIALIZE_MAP_CODE_END() KV_SERIALIZE_MAP_CODE_BEGIN(TRANSFER::request) KV_SERIALIZE(destinations) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) + KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)()) KV_SERIALIZE(priority) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) @@ -212,11 +220,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(TRANSFER::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -243,11 +253,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(TRANSFER_SPLIT::response) KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) + KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)()) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) KV_SERIALIZE_MAP_CODE_END() @@ -325,11 +337,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(SWEEP_DUST::response) KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) + KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)()) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) KV_SERIALIZE_MAP_CODE_END() @@ -359,11 +373,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(SWEEP_ALL::response) KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) + KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)()) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images_list) KV_SERIALIZE_MAP_CODE_END() @@ -385,11 +401,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(SWEEP_SINGLE::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -645,7 +663,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSFERS::request) KV_SERIALIZE_OPT(coinbase, true); KV_SERIALIZE(filter_by_height); KV_SERIALIZE(min_height); - KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER); + KV_SERIALIZE_OPT(max_height, cryptonote::MAX_BLOCK_NUMBER); KV_SERIALIZE(account_index); KV_SERIALIZE(subaddr_indices); KV_SERIALIZE_OPT(all_accounts, false); @@ -1045,11 +1063,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(STAKE::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -1066,11 +1086,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(REGISTER_MASTER_NODE::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -1161,11 +1183,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(BNS_BUY_MAPPING::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -1205,11 +1229,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(BNS_UPDATE_MAPPING::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() @@ -1315,10 +1341,12 @@ KV_SERIALIZE_MAP_CODE_BEGIN(COIN_BURN::response) KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) + KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)()) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(spent_key_images) KV_SERIALIZE_MAP_CODE_END() } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index f5568315f5e..a8114f71de1 100755 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -394,6 +394,22 @@ namespace tools::wallet_rpc { }; }; + struct key_image_list + { + std::list key_images; + + KV_MAP_SERIALIZABLE + }; + + struct amounts_list + { + std::list amounts; + + bool operator==(const amounts_list& other) const { return amounts == other.amounts; } + + KV_MAP_SERIALIZABLE + }; + BELDEX_RPC_DOC_INTROSPECT // Send beldex to a number of recipients. To preview the transaction fee, set do_not_relay to true and get_tx_metadata to true. // Submit the response using the data in get_tx_metadata in the RPC call, relay_tx. @@ -406,6 +422,7 @@ namespace tools::wallet_rpc { std::list destinations; // Array of destinations to receive BELDEX. uint32_t account_index; // (Optional) Transfer from this account index. (Defaults to 0) std::set subaddr_indices; // (Optional) Transfer from this set of subaddresses. (Defaults to 0) + std::set subtract_fee_from_outputs; uint32_t priority; // Set a priority for the transaction. Accepted values are: 1 for unimportant or 5 for flash. (0 and 2-4 are accepted for backwards compatibility and are equivalent to 5) uint64_t unlock_time; // Number of blocks before the beldex can be spent (0 to use the default lock time). std::string payment_id; // (Optional) Random 64-character hex string to identify a transaction. @@ -422,11 +439,13 @@ namespace tools::wallet_rpc { std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if get_tx_key is true, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction. + amounts_list amounts_by_dest; uint64_t fee; // Fee charged for the txn. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if get_tx_metadata is true. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -466,11 +485,13 @@ namespace tools::wallet_rpc { std::list tx_hash_list; // The tx hashes of every transaction. std::list tx_key_list; // The transaction keys for every transaction. std::list amount_list; // The amount transferred for every transaction. + std::list amounts_by_dest_list; std::list fee_list; // The amount of fees paid for every transaction. std::list tx_blob_list; // The tx as hex string for every transaction. std::list tx_metadata_list; // List of transaction metadata needed to relay the transactions later. std::string multisig_txset; // The set of signing keys used in a multisig transaction (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + std::list spent_key_images_list; KV_MAP_SERIALIZABLE }; @@ -597,11 +618,13 @@ namespace tools::wallet_rpc { std::list tx_hash_list; // The tx hashes of every transaction. std::list tx_key_list; // The transaction keys for every transaction. std::list amount_list; // The amount transferred for every transaction. + std::list amounts_by_dest_list; std::list fee_list; // The amount of fees paid for every transaction. std::list tx_blob_list; // The tx as hex string for every transaction. std::list tx_metadata_list; // List of transaction metadata needed to relay the transactions later. std::string multisig_txset; // The set of signing keys used in a multisig transaction (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + std::list spent_key_images_list; KV_MAP_SERIALIZABLE }; @@ -644,11 +667,13 @@ namespace tools::wallet_rpc { std::list tx_hash_list; // The tx hashes of every transaction. std::list tx_key_list; // The transaction keys for every transaction. std::list amount_list; // The amount transferred for every transaction. + std::list amounts_by_dest_list; std::list fee_list; // The amount of fees paid for every transaction. std::list tx_blob_list; // The tx as hex string for every transaction. std::list tx_metadata_list; // List of transaction metadata needed to relay the transactions later. std::string multisig_txset; // The set of signing keys used in a multisig transaction (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + std::list spent_key_images_list; KV_MAP_SERIALIZABLE }; @@ -681,11 +706,13 @@ namespace tools::wallet_rpc { std::string tx_hash; // The tx hashes of the transaction. std::string tx_key; // The tx key of the transaction. uint64_t amount; // The amount transfered in atomic units. + amounts_list amounts_by_dest; uint64_t fee; // The fee paid in atomic units. std::string tx_blob; // The tx as hex string. std::string tx_metadata; // Transaction metadata needed to relay the transaction later. std::string multisig_txset; // The set of signing keys used in a multisig transaction (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -1354,7 +1381,7 @@ BELDEX_RPC_DOC_INTROSPECT bool coinbase = false; bool filter_by_height = false; uint64_t min_height = 0; - uint64_t max_height = CRYPTONOTE_MAX_BLOCK_NUMBER; + uint64_t max_height = cryptonote::MAX_BLOCK_NUMBER; std::set subaddr_indices; uint32_t account_index; bool all_accounts; @@ -2042,11 +2069,13 @@ BELDEX_RPC_DOC_INTROSPECT std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction in atomic units. + amounts_list amounts_by_dest; uint64_t fee; // Value in atomic units of the fee charged for the tx. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if `get_tx_metadata` is `true`. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -2074,11 +2103,13 @@ BELDEX_RPC_DOC_INTROSPECT std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if get_tx_key is true, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction in atomic units. + amounts_list amounts_by_dest; uint64_t fee; // Value in atomic units of the fee charged for the tx. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if `get_tx_metadata` is `true`. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -2256,11 +2287,13 @@ For more information on updating and signing see the BNS_UPDATE_MAPPING document std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction in atomic units. + amounts_list amounts_by_dest; uint64_t fee; // Value in atomic units of the fee charged for the tx. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if `get_tx_metadata` is `true`. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -2339,11 +2372,13 @@ If signing is performed externally then you must first encrypt the `value` (if b std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction in atomic units. + amounts_list amounts_by_dest; uint64_t fee; // Value in atomic units of the fee charged for the tx. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if `get_tx_metadata` is `true`. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; @@ -2413,16 +2448,16 @@ This command is only required if the open wallet is one of the owners of a BNS r std::string name; // The plaintext name std::string owner; // The public key that purchased the Beldex Name Service entry. std::optional backup_owner; // The backup public key or wallet that the owner specified when purchasing the Beldex Name Service entry. Omitted if no backup owner. - std::string encrypted_bchat_value; // The encrypted value of bchat that the name maps to, in hex. - std::string encrypted_wallet_value; // The encrypted value of wallet that the name maps to, in hex. - std::string encrypted_belnet_value; // The encrypted value of belnet that the name maps to, in hex. - std::string encrypted_eth_addr_value; + std::optional encrypted_bchat_value; // The encrypted value of bchat that the name maps to, in hex. + std::optional encrypted_wallet_value; // The encrypted value of wallet that the name maps to, in hex. + std::optional encrypted_belnet_value; // The encrypted value of belnet that the name maps to, in hex. + std::optional encrypted_eth_addr_value; std::optional value_bchat; // Decrypted value that that name maps to. Only provided if `decrypt: true` was specified in the request. std::optional value_wallet; // Decrypted value that that name maps to. Only provided if `decrypt: true` was specified in the request. std::optional value_belnet; // Decrypted value that that name maps to. Only provided if `decrypt: true` was specified in the request. std::optional value_eth_addr; uint64_t update_height; // The last height that this Beldex Name Service entry was updated on the Blockchain. - std::optional expiration_height; // For records that expire, this will be set to the expiration block height. + uint64_t expiration_height; // For records that expire, this will be set to the expiration block height. std::optional expired; // Indicates whether the record has expired. Only included in the response if "include_expired" is specified in the request. std::string txid; // The txid of the mapping's most recent update or purchase. @@ -2539,11 +2574,13 @@ This command is only required if the open wallet is one of the owners of a BNS r std::string tx_hash; // Publicly searchable transaction hash. std::string tx_key; // Transaction key if get_tx_key is true, otherwise, blank string. uint64_t amount; // Amount transferred for the transaction. + amounts_list amounts_by_dest; uint64_t fee; // Fee charged for the txn. std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true. std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if get_tx_metadata is true. std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig). std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes. + key_image_list spent_key_images; KV_MAP_SERIALIZABLE }; diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp index 33c5a9b38f1..8433ded1b9c 100755 --- a/tests/block_weight/block_weight.cpp +++ b/tests/block_weight/block_weight.cpp @@ -123,13 +123,13 @@ static void test(test_t t, uint64_t blocks) blockchain_objects_t bc_objects = {}; const std::vector hard_forks{ - {cryptonote::network_version_7, 0, 0, 0}, - {cryptonote::network_version_11_infinite_staking, 0, 5000, 0}}; + {cryptonote::hf::hf7, 0, 0, 0}, + {cryptonote::hf::hf11_infinite_staking, 0, 5000, 0}}; const cryptonote::test_options test_options{hard_forks, 5000}; auto& bc = bc_objects.m_blockchain; - if (!bc.init(new TestDB(), nullptr, cryptonote ::FAKECHAIN, true, &test_options, 0)) { + if (!bc.init(new TestDB(), nullptr, cryptonote ::network_type::FAKECHAIN, true, &test_options, 0)) { fprintf(stderr, "Failed to init blockchain\n"); exit(1); }; @@ -137,8 +137,8 @@ static void test(test_t t, uint64_t blocks) for (uint64_t h = 0; h < LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) { cryptonote::block b; - b.major_version = cryptonote::network_version_7; - b.minor_version = cryptonote::network_version_7; + b.major_version = cryptonote::hf::hf7; + b.minor_version = static_cast(cryptonote::hf::hf7); bc.get_db().add_block(std::make_pair(b, ""), 300000, 300000, bc.get_db().height(), bc.get_db().height(), {}); if (!bc.update_next_cumulative_weight_limit()) { @@ -171,8 +171,8 @@ static void test(test_t t, uint64_t blocks) } uint64_t ltw = bc.get_next_long_term_block_weight(w); cryptonote::block b; - b.major_version = HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - b.minor_version = HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + b.major_version = cryptonote::feature::LONG_TERM_BLOCK_WEIGHT; + b.minor_version = static_cast(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); bc.get_db().add_block(std::make_pair(std::move(b), ""), w, ltw, bc.get_db().height(), bc.get_db().height(), {}); if (!bc.update_next_cumulative_weight_limit()) diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index ef7633c60ef..5d0d1190a37 100755 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -97,7 +97,7 @@ namespace tests bool prepare_handle_incoming_blocks(const std::vector &blocks_entry, std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } - size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } + size_t get_block_sync_size(uint64_t height) const { return cryptonote::BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } virtual crypto::hash on_transaction_relayed(const cryptonote::blobdata& tx) { return crypto::null_hash; } cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } @@ -106,7 +106,7 @@ namespace tests uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } - uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(cryptonote::hf version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } diff --git a/tests/core_tests/beldex_tests.cpp b/tests/core_tests/beldex_tests.cpp index 83c6a6ffa0b..a1bb4d9d159 100755 --- a/tests/core_tests/beldex_tests.cpp +++ b/tests/core_tests/beldex_tests.cpp @@ -190,7 +190,7 @@ bool beldex_checkpointing_alt_chain_receive_checkpoint_votes_should_reorg_back:: gen.add_event_msg("Fork generate two checkpoints worth of blocks."); uint64_t first_checkpointed_height = fork.height(); - uint64_t first_checkpointed_height_hf = fork.top().block.major_version; + cryptonote::hf first_checkpointed_height_hf = fork.top().block.major_version; crypto::hash first_checkpointed_hash = cryptonote::get_block_hash(fork.top().block); std::shared_ptr first_quorum = fork.get_quorum(master_nodes::quorum_type::checkpointing, gen.height()); @@ -438,12 +438,12 @@ bool beldex_checkpointing_master_node_checkpoints_check_reorg_windows::generate( bool beldex_core_block_reward_unpenalized_pre_POS::generate(std::vector& events) { - auto hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_17_POS - 1); + auto hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf16); beldex_chain_generator gen(events, hard_forks); gen.add_blocks_until_version(hard_forks.back().version); - uint8_t newest_hf = hard_forks.back().version; - assert(newest_hf >= cryptonote::network_version_14_enforce_checkpoints); + cryptonote::hf newest_hf = hard_forks.back().version; + assert(newest_hf >= cryptonote::hf::hf15_flash); gen.add_mined_money_unlock_blocks(); @@ -477,11 +477,11 @@ bool beldex_core_block_reward_unpenalized_pre_POS::generate(std::vector& events) { - std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_count -1, 150 /*Proof Of Stake Delay*/); + std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf20_bulletproof_plus, 150 /*Proof Of Stake Delay*/); beldex_chain_generator gen(events, hard_forks); - uint8_t const newest_hf = hard_forks.back().version; - assert(newest_hf >= cryptonote::network_version_14_enforce_checkpoints); + const cryptonote::hf newest_hf = hard_forks.back().version; + assert(newest_hf >= cryptonote::hf::hf15_flash); gen.add_blocks_until_version(hard_forks.back().version); gen.add_mined_money_unlock_blocks(); @@ -497,12 +497,12 @@ bool beldex_core_block_reward_unpenalized_post_POS::generate(std::vector& events) beldex_chain_generator gen(events, hard_forks); gen.add_blocks_until_version(hard_forks.back().version); - uint8_t newest_hf = hard_forks.back().version; - assert(newest_hf >= cryptonote::network_version_15_flash); + cryptonote::hf newest_hf = hard_forks.back().version; + assert(newest_hf >= cryptonote::hf::hf15_flash); gen.add_mined_money_unlock_blocks(); @@ -622,12 +622,12 @@ bool beldex_core_fee_burning::generate(std::vector& events) bool beldex_core_governance_batched_reward::generate(std::vector& events) { - auto hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_10_bulletproofs); + auto hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf10_bulletproofs); uint64_t hf10_height = 0; for (const auto& [maj, mn_rev, height, ts] : hard_forks) { - if (maj == cryptonote::network_version_10_bulletproofs) + if (maj == cryptonote::hf::hf10_bulletproofs) { hf10_height = height; break; @@ -638,8 +638,8 @@ bool beldex_core_governance_batched_reward::generate(std::vector other_hard_forks = { - {7,0,0,0}, - {8,0,1,0}, - {9,0,hf10_height,0}}; + {cryptonote::hf::hf7,0,0,0}, + {cryptonote::hf::hf8,0,1,0}, + {cryptonote::hf::hf9_master_nodes,0,hf10_height,0}}; std::vector unused_events; beldex_chain_generator no_batched_governance_generator(unused_events, other_hard_forks); @@ -684,7 +684,7 @@ bool beldex_core_governance_batched_reward::generate(std::vector& events) { - constexpr auto& network = cryptonote::get_config(cryptonote::FAKECHAIN); - std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_16); - hard_forks.push_back({cryptonote::network_version_17_POS,0, hard_forks.back().height + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS + 10}); - hard_forks.push_back({cryptonote::network_version_18_bns,0, hard_forks.back().height + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS}); + constexpr auto& network = cryptonote::get_config(cryptonote::network_type::FAKECHAIN); + std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf16); + hard_forks.push_back({cryptonote::hf::hf17_POS,0, hard_forks.back().height + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS + 10}); + hard_forks.push_back({cryptonote::hf::hf18_bns,0, hard_forks.back().height + network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS}); beldex_chain_generator batched_governance_generator(events, hard_forks); - batched_governance_generator.add_blocks_until_version(cryptonote::network_version_18_bns); + batched_governance_generator.add_blocks_until_version(cryptonote::hf::hf18_bns); batched_governance_generator.add_n_blocks(network.GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS); uint64_t hf15_height = 0, hf16_height = 0, hf17_height = 0; for (const auto &hf : hard_forks) { - if (hf.version == cryptonote::network_version_16) + if (hf.version == cryptonote::hf::hf16) hf15_height = hf.height; - else if (hf.version == cryptonote::network_version_17_POS) + else if (hf.version == cryptonote::hf::hf17_POS) hf16_height = hf.height; else hf17_height = hf.height; @@ -729,12 +729,12 @@ bool beldex_core_block_rewards_lrc6::generate(std::vector& eve for (size_t block_height = hf15_height; block_height < hf16_height; ++block_height) { const cryptonote::block &block = blockchain[block_height]; - CHECK_EQ(block.miner_tx.vout.at(0).amount, MINER_REWARD_HF16); - CHECK_EQ(block.miner_tx.vout.at(1).amount, MN_REWARD_HF16); - if (cryptonote::block_has_governance_output(cryptonote::FAKECHAIN, block)) + CHECK_EQ(block.miner_tx.vout.at(0).amount, beldex::MINER_REWARD_HF16); + CHECK_EQ(block.miner_tx.vout.at(1).amount, beldex::MN_REWARD_HF16); + if (cryptonote::block_has_governance_output(cryptonote::network_type::FAKECHAIN, block)) { hf15_gov++; - CHECK_EQ(block.miner_tx.vout.at(2).amount, FOUNDATION_REWARD_HF17 * interval); + CHECK_EQ(block.miner_tx.vout.at(2).amount, beldex::FOUNDATION_REWARD_HF17 * interval); CHECK_EQ(block.miner_tx.vout.size(), 3); } else @@ -744,8 +744,8 @@ bool beldex_core_block_rewards_lrc6::generate(std::vector& eve for (size_t block_height = hf16_height; block_height < hf17_height; ++block_height) { const cryptonote::block &block = blockchain[block_height]; - CHECK_EQ(block.miner_tx.vout.at(0).amount, MN_REWARD_HF16); - if (cryptonote::block_has_governance_output(cryptonote::FAKECHAIN, block)) + CHECK_EQ(block.miner_tx.vout.at(0).amount, beldex::MN_REWARD_HF16); + if (cryptonote::block_has_governance_output(cryptonote::network_type::FAKECHAIN, block)) { hf16_gov++; CHECK_EQ(block.miner_tx.vout.at(1).amount, 0); @@ -758,11 +758,11 @@ bool beldex_core_block_rewards_lrc6::generate(std::vector& eve for (size_t block_height = hf17_height; block_height < height; ++block_height) { const cryptonote::block &block = blockchain[block_height]; - CHECK_EQ(block.miner_tx.vout.at(0).amount, MN_REWARD_HF16); - if (cryptonote::block_has_governance_output(cryptonote::FAKECHAIN, block)) + CHECK_EQ(block.miner_tx.vout.at(0).amount, beldex::MN_REWARD_HF16); + if (cryptonote::block_has_governance_output(cryptonote::network_type::FAKECHAIN, block)) { hf17_gov++; - CHECK_EQ(block.miner_tx.vout.at(1).amount, FOUNDATION_REWARD_HF17 * interval); + CHECK_EQ(block.miner_tx.vout.at(1).amount, beldex::FOUNDATION_REWARD_HF17 * interval); CHECK_EQ(block.miner_tx.vout.size(), 2); } else @@ -781,7 +781,7 @@ bool beldex_core_block_rewards_lrc6::generate(std::vector& eve bool beldex_core_test_deregister_preferred::generate(std::vector &events) { - std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_9_master_nodes); + std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf9_master_nodes); beldex_chain_generator gen(events, hard_forks); const auto miner = gen.first_miner(); const auto alice = gen.add_account(); @@ -838,7 +838,7 @@ bool beldex_core_test_deregister_preferred::generate(std::vector &events) { - std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_9_master_nodes); + std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf9_master_nodes); beldex_chain_generator gen(events, hard_forks); const auto miner = gen.first_miner(); @@ -887,7 +887,7 @@ bool beldex_core_test_deregister_safety_buffer::generate(std::vector& events) { - std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_9_master_nodes); + std::vector hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf9_master_nodes); beldex_chain_generator gen(events, hard_forks); gen.add_blocks_until_version(hard_forks.back().version); @@ -1052,8 +1052,8 @@ static bool verify_bns_mapping_record(char const *perr_context, if (expiration_height) CHECK_EQ(*record.expiration_height, *expiration_height); CHECK_EQ(record.txid, txid); - CHECK_TEST_CONDITION_MSG(record.owner == owner, record.owner.to_string(cryptonote::FAKECHAIN) << " == "<< owner.to_string(cryptonote::FAKECHAIN)); - CHECK_TEST_CONDITION_MSG(record.backup_owner == backup_owner, record.backup_owner.to_string(cryptonote::FAKECHAIN) << " == "<< backup_owner.to_string(cryptonote::FAKECHAIN)); + CHECK_TEST_CONDITION_MSG(record.owner == owner, record.owner.to_string(cryptonote::network_type::FAKECHAIN) << " == "<< owner.to_string(cryptonote::network_type::FAKECHAIN)); + CHECK_TEST_CONDITION_MSG(record.backup_owner == backup_owner, record.backup_owner.to_string(cryptonote::network_type::FAKECHAIN) << " == "<< backup_owner.to_string(cryptonote::network_type::FAKECHAIN)); return true; } @@ -1115,7 +1115,7 @@ static bns_keys_t make_bns_keys(cryptonote::account_base const &src) // belnet FAKECHAIN BNS expiry blocks uint64_t bns_expiry(bns::mapping_years map_years) { - auto exp = bns::expiry_blocks(cryptonote::FAKECHAIN, map_years); + auto exp = bns::expiry_blocks(cryptonote::network_type::FAKECHAIN, map_years); if (!exp) throw std::logic_error{"test suite bug: bns_expiry called with non-mapping years"}; return *exp; } @@ -1154,8 +1154,8 @@ bool beldex_name_system_expiration::generate(std::vector &even CHECK_EQ(owner.loaded, true); CHECK_EQ(owner.id, 1); CHECK_TEST_CONDITION_MSG(miner_key.owner == owner.address, - miner_key.owner.to_string(cryptonote::FAKECHAIN) - << " == " << owner.address.to_string(cryptonote::FAKECHAIN)); + miner_key.owner.to_string(cryptonote::network_type::FAKECHAIN) + << " == " << owner.address.to_string(cryptonote::network_type::FAKECHAIN)); bns::mapping_record record = bns_db.get_mapping(name_hash); CHECK_TEST_CONDITION(verify_bns_mapping_record(perr_context, record, bns::mapping_type::belnet, record.encrypted_belnet_value, name, miner_key.belnet_value, height_of_bns_entry, height_of_bns_entry + bns_expiry(mapping_years), tx_hash, miner_key.owner, {} /*backup_owner*/)); @@ -1175,8 +1175,8 @@ bool beldex_name_system_expiration::generate(std::vector &even CHECK_EQ(owner.loaded, true); CHECK_EQ(owner.id, 1); CHECK_TEST_CONDITION_MSG(miner_key.owner == owner.address, - miner_key.owner.to_string(cryptonote::FAKECHAIN) - << " == " << owner.address.to_string(cryptonote::FAKECHAIN)); + miner_key.owner.to_string(cryptonote::network_type::FAKECHAIN) + << " == " << owner.address.to_string(cryptonote::network_type::FAKECHAIN)); bns::mapping_record record = bns_db.get_mapping(name_hash); CHECK_TEST_CONDITION(verify_bns_mapping_record(perr_context, record, bns::mapping_type::belnet, record.encrypted_belnet_value, name, miner_key.belnet_value, height_of_bns_entry, height_of_bns_entry + bns_expiry(mapping_years), tx_hash, miner_key.owner, {} /*backup_owner*/)); @@ -1256,7 +1256,7 @@ bool beldex_name_system_get_mappings_by_owner::generate(std::vector void { uint64_t new_height = cryptonote::get_block_height(gen.top().block) + 1; - uint8_t new_hf_version = gen.get_hf_version_at(new_height); + auto new_hf_version = gen.get_hf_version_at(new_height); uint64_t burn_requirement = bns::burn_needed(new_hf_version, mapping_years); std::vector extra; @@ -1903,8 +1903,8 @@ bool beldex_name_system_name_renewal::generate(std::vector &ev CHECK_EQ(owner.loaded, true); CHECK_EQ(owner.id, 1); CHECK_TEST_CONDITION_MSG(miner_key.owner == owner.address, - miner_key.owner.to_string(cryptonote::FAKECHAIN) - << " == " << owner.address.to_string(cryptonote::FAKECHAIN)); + miner_key.owner.to_string(cryptonote::network_type::FAKECHAIN) + << " == " << owner.address.to_string(cryptonote::network_type::FAKECHAIN)); std::string name_hash = bns::name_to_base64_hash(name); bns::mapping_record record = bns_db.get_mapping(name_hash); @@ -1929,8 +1929,8 @@ bool beldex_name_system_name_renewal::generate(std::vector &ev CHECK_EQ(owner.loaded, true); CHECK_EQ(owner.id, 1); CHECK_TEST_CONDITION_MSG(miner_key.owner == owner.address, - miner_key.owner.to_string(cryptonote::FAKECHAIN) - << " == " << owner.address.to_string(cryptonote::FAKECHAIN)); + miner_key.owner.to_string(cryptonote::network_type::FAKECHAIN) + << " == " << owner.address.to_string(cryptonote::network_type::FAKECHAIN)); std::string name_hash = bns::name_to_base64_hash(name); bns::mapping_record record = bns_db.get_mapping(name_hash); @@ -1957,7 +1957,7 @@ bool beldex_name_system_name_value_max_lengths::generate(std::vector void { uint64_t new_height = cryptonote::get_block_height(gen.top().block) + 1; - uint8_t new_hf_version = gen.get_hf_version_at(new_height); + auto new_hf_version = gen.get_hf_version_at(new_height); uint64_t burn_requirement = bns::burn_needed(new_hf_version, mapping_years); std::vector extra; cryptonote::add_beldex_name_system_to_tx_extra(extra, data); @@ -2056,8 +2056,8 @@ bool beldex_name_system_update_mapping_after_expiry_fails::generate(std::vector< CHECK_EQ(owner.loaded, true); CHECK_EQ(owner.id, 1); CHECK_TEST_CONDITION_MSG(miner_key.owner == owner.address, - miner_key.owner.to_string(cryptonote::FAKECHAIN) - << " == " << owner.address.to_string(cryptonote::FAKECHAIN)); + miner_key.owner.to_string(cryptonote::network_type::FAKECHAIN) + << " == " << owner.address.to_string(cryptonote::network_type::FAKECHAIN)); std::string name_hash = bns::name_to_base64_hash(name); bns::mapping_record record = bns_db.get_mapping(name_hash); @@ -2070,8 +2070,8 @@ bool beldex_name_system_update_mapping_after_expiry_fails::generate(std::vector< return true; } -uint8_t beldex_name_system_update_mapping::hf() { return cryptonote::network_version_count - 1; } -uint8_t beldex_name_system_update_mapping_argon2::hf() { return cryptonote::network_version_16; } +cryptonote::hf beldex_name_system_update_mapping::hf() { return cryptonote::hf::hf20_bulletproof_plus; } +cryptonote::hf beldex_name_system_update_mapping_argon2::hf() { return cryptonote::hf::hf16; } bool beldex_name_system_update_mapping::generate(std::vector &events) { auto hard_forks = beldex_generate_hard_fork_table(hf()); @@ -2108,7 +2108,7 @@ bool beldex_name_system_update_mapping::generate(std::vector & }); // Test update mapping with same name fails - if (hf() == cryptonote::network_version_16) { + if (hf() == cryptonote::hf::hf16) { cryptonote::transaction tx1 = gen.create_beldex_name_system_tx_update(miner, gen.hardfork(), bns::mapping_type::bchat, bchat_name1, &miner_key.bchat_value, &miner_key.wallet_value, &miner_key.belnet_value, &miner_key.eth_addr_value); gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not add a BNS TX that re-updates the underlying value to same value"); } @@ -2560,7 +2560,7 @@ bool beldex_name_system_wrong_burn::generate(std::vector &even assert("Unhandled type enum" == nullptr); uint64_t new_height = cryptonote::get_block_height(gen.top().block) + 1; - uint8_t new_hf_version = gen.get_hf_version_at(new_height); + auto new_hf_version = gen.get_hf_version_at(new_height); uint64_t burn = bns::burn_needed(new_hf_version, mapping_years); if (under_burn) burn -= 1; else burn += 1; @@ -2592,7 +2592,7 @@ bool beldex_name_system_wrong_version::generate(std::vector &e data.encrypted_bchat_value = miner_key.bchat_value.make_encrypted(name).to_string(); uint64_t new_height = cryptonote::get_block_height(gen.top().block) + 1; - uint8_t new_hf_version = gen.get_hf_version_at(new_height); + auto new_hf_version = gen.get_hf_version_at(new_height); uint64_t burn_requirement = bns::burn_needed(new_hf_version, mapping_years); std::vector extra; @@ -2701,7 +2701,7 @@ bool beldex_master_nodes_checkpoint_quorum_size::generate(std::vector &events) { - const auto hard_forks = beldex_generate_hard_fork_table(cryptonote::network_version_9_master_nodes); + const auto hard_forks = beldex_generate_hard_fork_table(cryptonote::hf::hf9_master_nodes); beldex_chain_generator gen(events, hard_forks); const auto miner = gen.first_miner(); const auto alice = gen.add_account(); @@ -2742,16 +2742,16 @@ bool beldex_master_nodes_gen_nodes::generate(std::vector &even return true; }); - for (auto i = 0u; i < master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,cryptonote::network_version_17_POS); ++i) + for (auto i = 0u; i < master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,cryptonote::hf::hf17_POS); ++i) gen.create_and_add_next_block(); beldex_register_callback(events, "check_expired", [&events, alice](cryptonote::core &c, size_t ev_index) { DEFINE_TESTS_ERROR_CONTEXT("check_expired"); - const auto stake_lock_time = master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,cryptonote::network_version_17_POS); + const auto stake_lock_time = master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,cryptonote::hf::hf17_POS); std::vector blocks; - size_t count = 15 + (2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) + stake_lock_time; + size_t count = 15 + (2 * cryptonote::MINED_MONEY_UNLOCK_WINDOW) + stake_lock_time; bool r = c.get_blocks((uint64_t)0, count, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -2855,7 +2855,7 @@ bool beldex_master_nodes_test_rollback::generate(std::vector& bool beldex_master_nodes_test_swarms_basic::generate(std::vector& events) { const std::vector hard_forks = { - {7,0,0,0}, {8,0,1,0}, {9,0,2,0}, {10,0,150,0}}; + {cryptonote::hf::hf7,0,0,0}, {cryptonote::hf::hf8,0,1,0}, {cryptonote::hf::hf9_master_nodes,0,2,0}, {cryptonote::hf::hf10_bulletproofs,0,150,0}}; beldex_chain_generator gen(events, hard_forks); gen.add_blocks_until_version(hard_forks.rbegin()[1].version); @@ -2871,9 +2871,9 @@ bool beldex_master_nodes_test_swarms_basic::generate(std::vector &eve result.add_blocks_until_version(hard_forks.back().version); result.add_mined_money_unlock_blocks(); - std::vector registration_txs(master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN)); - for (auto i = 0u; i < master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN); ++i) + std::vector registration_txs(master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN)); + for (auto i = 0u; i < master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN); ++i) registration_txs[i] = result.create_and_add_registration_tx(result.first_miner()); // NOTE: Generate Valid Blocks @@ -3105,7 +3105,7 @@ bool beldex_POS_non_participating_validator::generate(std::vector active_mnode_list = params.prev.master_node_state.active_master_nodes_infos(); std::vector entropy = master_nodes::get_POS_entropy_for_next_block(gen.db_, params.prev.block, entry.block.POS.round); - quorum = generate_POS_quorum(cryptonote::FAKECHAIN, params.block_leader.key, entry.block.major_version, active_mnode_list, entropy, entry.block.POS.round); + quorum = generate_POS_quorum(cryptonote::network_type::FAKECHAIN, params.block_leader.key, entry.block.major_version, active_mnode_list, entropy, entry.block.POS.round); assert(quorum.validators.size() == master_nodes::POS_QUORUM_NUM_VALIDATORS); assert(quorum.workers.size() == 1); } @@ -3195,7 +3195,7 @@ bool beldex_POS_generate_blocks::generate(std::vector &events) gen.add_blocks_until_version(hard_forks.back().version); gen.add_mined_money_unlock_blocks(); - add_master_nodes(gen, master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN)); + add_master_nodes(gen, master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN)); gen.add_n_blocks(40); // Chain genereator will generate blocks via POS quorums beldex_register_callback(events, "check_POS_blocks", [](cryptonote::core &c, size_t ev_index) @@ -3219,7 +3219,7 @@ bool beldex_POS_fallback_to_pow_and_back::generate(std::vector gen.add_blocks_until_version(hard_forks.back().version); gen.add_mined_money_unlock_blocks(); - add_master_nodes(gen, master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN)); + add_master_nodes(gen, master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN)); gen.create_and_add_next_block(); gen.add_event_msg("Deregister 1 node, we now have insufficient nodes for POS"); @@ -3266,7 +3266,7 @@ bool beldex_POS_chain_split::generate(std::vector &events) gen.add_blocks_until_version(hard_forks.back().version); gen.add_mined_money_unlock_blocks(); - add_master_nodes(gen, std::max(master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN), master_nodes::CHECKPOINT_QUORUM_SIZE)); + add_master_nodes(gen, std::max(master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN), master_nodes::CHECKPOINT_QUORUM_SIZE)); gen.create_and_add_next_block(); @@ -3311,7 +3311,7 @@ bool beldex_POS_chain_split_with_no_checkpoints::generate(std::vector& events); }; struct beldex_name_system_name_value_max_lengths : public test_chain_unit_base { bool generate(std::vector& events); }; struct beldex_name_system_update_mapping_after_expiry_fails : public test_chain_unit_base { bool generate(std::vector& events); }; -struct beldex_name_system_update_mapping : public test_chain_unit_base { bool generate(std::vector& events); virtual uint8_t hf(); }; -struct beldex_name_system_update_mapping_argon2 : public beldex_name_system_update_mapping { uint8_t hf() override; }; +struct beldex_name_system_update_mapping : public test_chain_unit_base { bool generate(std::vector& events); virtual cryptonote::hf hf(); }; +struct beldex_name_system_update_mapping_argon2 : public beldex_name_system_update_mapping { cryptonote::hf hf() override; }; struct beldex_name_system_update_mapping_multiple_owners : public test_chain_unit_base { bool generate(std::vector& events); }; struct beldex_name_system_update_mapping_non_existent_name_fails : public test_chain_unit_base { bool generate(std::vector& events); }; struct beldex_name_system_update_mapping_invalid_signature : public test_chain_unit_base { bool generate(std::vector& events); }; diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index bbed7916051..1d190ea1834 100755 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -74,13 +74,13 @@ namespace } bool construct_max_weight_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account, - size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW) + size_t median_block_count = cryptonote::REWARD_BLOCKS_WINDOW) { std::vector block_weights; generator.get_last_n_block_weights(block_weights, get_block_hash(blk_prev), median_block_count); size_t median = tools::median(block_weights.begin(), block_weights.end()); - median = std::max(median, static_cast(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)); + median = std::max(median, static_cast(cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V1)); transaction miner_tx; bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev), @@ -88,7 +88,7 @@ namespace if (!r) return false; - return generator.construct_block_manually(blk, blk_prev, miner_account, test_generator::bf_miner_tx, 0, 0, 0, + return generator.construct_block_manually(blk, blk_prev, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); } @@ -137,19 +137,19 @@ bool gen_block_reward::generate(std::vector& events) const // Test: miner transactions without outputs (block reward == 0) block blk_0r; - if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW)) + if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, cryptonote::REWARD_BLOCKS_WINDOW)) return false; - // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks + // Test: block reward is calculated using median of the latest cryptonote::REWARD_BLOCKS_WINDOW blocks DO_CALLBACK(events, "mark_invalid_block"); block blk_1_bad_1; - if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1)) + if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, cryptonote::REWARD_BLOCKS_WINDOW + 1)) return false; events.push_back(blk_1_bad_1); DO_CALLBACK(events, "mark_invalid_block"); block blk_1_bad_2; - if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1)) + if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, cryptonote::REWARD_BLOCKS_WINDOW - 1)) return false; events.push_back(blk_1_bad_2); @@ -170,7 +170,7 @@ bool gen_block_reward::generate(std::vector& events) const DO_CALLBACK(events, "mark_checked_block"); block blk_5r; - if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)) + if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, cryptonote::MINED_MONEY_UNLOCK_WINDOW)) return false; // Test: fee increases block reward @@ -193,7 +193,7 @@ bool gen_block_reward::generate(std::vector& events) const uint64_t txs_fee = get_tx_miner_fee(tx_1, true) + get_tx_miner_fee(tx_2, true); std::vector block_weights; - generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW); + generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), cryptonote::REWARD_BLOCKS_WINDOW); size_t median = tools::median(block_weights.begin(), block_weights.end()); transaction miner_tx; @@ -208,7 +208,7 @@ bool gen_block_reward::generate(std::vector& events) const block blk_8; generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes, - 0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight); + hf::none, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight); events.push_back(blk_8); DO_CALLBACK(events, "mark_checked_block"); @@ -249,11 +249,11 @@ bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_ DEFINE_TESTS_ERROR_CONTEXT("gen_block_reward_without_txs::check_block_rewards"); std::array blk_rewards; - blk_rewards[0] = MONEY_SUPPLY >> EMISSION_SPEED_FACTOR_PER_MINUTE; + blk_rewards[0] = beldex::MONEY_SUPPLY >> EMISSION_SPEED_FACTOR_PER_MINUTE; uint64_t cumulative_reward = blk_rewards[0]; for (size_t i = 1; i < blk_rewards.size(); ++i) { - blk_rewards[i] = (MONEY_SUPPLY - cumulative_reward) >> EMISSION_SPEED_FACTOR_PER_MINUTE; + blk_rewards[i] = (beldex::MONEY_SUPPLY - cumulative_reward) >> EMISSION_SPEED_FACTOR_PER_MINUTE; cumulative_reward += blk_rewards[i]; } diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 5e6c09ac2bc..223ef73952b 100755 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -46,13 +46,13 @@ namespace for (size_t i = 0; i < new_block_count; ++i) { block blk_next; - difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_OLD), cryptonote::difficulty_calc_mode::normal); + difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12), cryptonote::difficulty_calc_mode::normal); if (!generator.construct_block_manually(blk_next, blk_prev, miner_account, - test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic)) + test_generator::bf_timestamp | test_generator::bf_diffic, hf::none, 0, blk_prev.timestamp, crypto::hash(), diffic)) return false; cummulative_diffic += diffic; - if (timestamps.size() == DIFFICULTY_WINDOW) + if (timestamps.size() == cryptonote::old::DIFFICULTY_WINDOW) { timestamps.erase(timestamps.begin()); cummulative_difficulties.erase(cummulative_difficulties.begin()); @@ -80,7 +80,7 @@ bool gen_block_big_major_version::generate(std::vector& events BLOCK_VALIDATION_INIT_GENERATE(); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, 255); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, hf::none); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -93,7 +93,7 @@ bool gen_block_big_minor_version::generate(std::vector& events BLOCK_VALIDATION_INIT_GENERATE(); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_minor_ver, 0, 255); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_minor_ver, hf::none, 0); events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -107,7 +107,7 @@ bool gen_block_ts_not_checked::generate(std::vector& events) c REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 2); block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, blk_0.timestamp - 60 * 60); + generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, hf::none, 0, blk_0.timestamp - 60 * 60); events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -122,7 +122,7 @@ bool gen_block_ts_in_past::generate(std::vector& events) const uint64_t ts_below_median = var::get(events[BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW / 2 - 1]).timestamp; block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, ts_below_median); + generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, hf::none, 0, ts_below_median); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -135,7 +135,7 @@ bool gen_block_ts_in_future::generate(std::vector& events) con BLOCK_VALIDATION_INIT_GENERATE(); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_timestamp, 0, 0, time(NULL) + 60*60 + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_timestamp, hf::none, 0, time(NULL) + 60*60 + cryptonote::old::BLOCK_FUTURE_TIME_LIMIT_V2); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -150,7 +150,7 @@ bool gen_block_invalid_prev_id::generate(std::vector& events) block blk_1; crypto::hash prev_id = get_block_hash(blk_0); reinterpret_cast(prev_id) ^= 1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_prev_id, 0, 0, 0, prev_id); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_prev_id, hf::none, 0, 0, prev_id); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -176,7 +176,7 @@ bool gen_block_invalid_nonce::generate(std::vector& events) co return false; // Create invalid nonce - difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_OLD), cryptonote::difficulty_calc_mode::normal); + difficulty_type diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12), cryptonote::difficulty_calc_mode::normal); assert(1 < diffic); const block& blk_last = var::get(events.back()); uint64_t timestamp = blk_last.timestamp; @@ -186,7 +186,7 @@ bool gen_block_invalid_nonce::generate(std::vector& events) co ++timestamp; blk_3.miner_tx.set_null(); if (!generator.construct_block_manually(blk_3, blk_last, miner_account, - test_generator::bf_diffic | test_generator::bf_timestamp, 0, 0, timestamp, crypto::hash(), diffic)) + test_generator::bf_diffic | test_generator::bf_timestamp, hf::none, 0, timestamp, crypto::hash(), diffic)) return false; } while (0 == blk_3.nonce); @@ -204,7 +204,7 @@ bool gen_block_no_miner_tx::generate(std::vector& events) cons miner_tx.set_null(); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -220,7 +220,7 @@ bool gen_block_unlock_time_is_low::generate(std::vector& event --miner_tx.unlock_time; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -236,7 +236,7 @@ bool gen_block_unlock_time_is_high::generate(std::vector& even ++miner_tx.unlock_time; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -252,7 +252,7 @@ bool gen_block_unlock_time_is_timestamp_in_past::generate(std::vector& events) co var::get(miner_tx.vin[0]).height--; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -300,7 +300,7 @@ bool gen_block_height_is_high::generate(std::vector& events) c var::get(miner_tx.vin[0]).height++; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -319,7 +319,7 @@ bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector& miner_tx.vin.push_back(in); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -335,14 +335,14 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector& events transaction tmp_tx; - if (!beldex_tx_builder(events, tmp_tx, blk_0r, miner_account, miner_account.get_keys().m_account_address, blk_0.miner_tx.vout[0].amount, cryptonote::network_version_7).build()) + if (!beldex_tx_builder(events, tmp_tx, blk_0r, miner_account, miner_account.get_keys().m_account_address, blk_0.miner_tx.vout[0].amount, cryptonote::hf::hf7).build()) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_0r); miner_tx.vin.push_back(tmp_tx.vin[0]); block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -362,14 +362,14 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); transaction tmp_tx; - if (!beldex_tx_builder(events, tmp_tx, blk_1r, miner_account, miner_account.get_keys().m_account_address, blk_1.miner_tx.vout[0].amount, cryptonote::network_version_7).build()) + if (!beldex_tx_builder(events, tmp_tx, blk_1r, miner_account, miner_account.get_keys().m_account_address, blk_1.miner_tx.vout[0].amount, cryptonote::hf::hf7).build()) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_1); miner_tx.vin[0] = tmp_tx.vin[0]; block blk_2; - generator.construct_block_manually(blk_2, blk_1r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_2, blk_1r, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_2); DO_CALLBACK(events, "check_block_purged"); @@ -385,7 +385,7 @@ bool gen_block_miner_tx_out_is_big::generate(std::vector& even miner_tx.vout[0].amount *= 2; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -402,7 +402,7 @@ bool gen_block_miner_tx_has_no_out::generate(std::vector& even miner_tx.version = txversion::v1; block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -438,10 +438,10 @@ static bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx, in.height = height; tx.vin.push_back(in); - // This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - const int hard_fork_version = 7; // NOTE(beldex): We know this test doesn't need the new block reward formula + // This will work, until size of constructed block is less then cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE + const auto hard_fork_version = hf::hf7; // NOTE(beldex): We know this test doesn't need the new block reward formula uint64_t block_reward, block_reward_unpenalized; - if (!get_base_block_reward(0, 0, already_generated_coins, block_reward, block_reward_unpenalized, 1, 0)) { + if (!get_base_block_reward(0, 0, already_generated_coins, block_reward, block_reward_unpenalized, hf::hf1, 0)) { LOG_PRINT_L0("Block is too big"); return false; } @@ -453,7 +453,7 @@ static bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx, } tx.version = txversion::v1; - tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.unlock_time = height + cryptonote::MINED_MONEY_UNLOCK_WINDOW; /// half of the miner reward goes to the other account const auto miner_reward = block_reward / 2; @@ -479,7 +479,7 @@ static bool construct_miner_tx_with_extra_output(cryptonote::transaction& tx, } tx.vout.push_back({governance_reward, out_eph_public_key}); - tx.output_unlock_times.push_back(height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + tx.output_unlock_times.push_back(height + cryptonote::MINED_MONEY_UNLOCK_WINDOW); } return true; @@ -502,7 +502,7 @@ bool gen_block_miner_tx_has_out_to_alice::generate(std::vector construct_miner_tx_with_extra_output(miner_tx, miner_address, height+1, coins, alice_address); block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -533,7 +533,7 @@ bool gen_block_is_too_big::generate(std::vector& events) const // Creating a huge miner_tx, it will have a lot of outs MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); miner_tx.version = txversion::v1; - static const size_t tx_out_count = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; + static const size_t tx_out_count = cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; uint64_t amount = get_outs_money_amount(miner_tx); uint64_t portion = amount / tx_out_count; @@ -558,7 +558,7 @@ bool gen_block_is_too_big::generate(std::vector& events) const // Block reward will be incorrect, as it must be reduced if cumulative block size is very big, // but in this test it doesn't matter block blk_1; - if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx)) + if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, hf::none, 0, 0, crypto::hash(), 0, miner_tx)) return false; events.push_back(blk_1); @@ -626,8 +626,8 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev // Unlock blk_0 outputs block blk_last = blk_0; - assert(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW < DIFFICULTY_WINDOW); - for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + assert(cryptonote::MINED_MONEY_UNLOCK_WINDOW < DIFFICULTY_WINDOW); + for (size_t i = 0; i < cryptonote::MINED_MONEY_UNLOCK_WINDOW; ++i) { MAKE_NEXT_BLOCK(events, blk_curr, blk_last, miner_account); timestamps.push_back(blk_curr.timestamp); @@ -640,7 +640,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev do { blk_last = var::get(events.back()); - diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_OLD), cryptonote::difficulty_calc_mode::normal); + diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_12), cryptonote::difficulty_calc_mode::normal); if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account)) return false; std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl; @@ -655,9 +655,9 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev std::vector tx_hashes; tx_hashes.push_back(get_transaction_hash(tx_0)); size_t txs_weight = get_transaction_weight(tx_0); - diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_OLD), cryptonote::difficulty_calc_mode::normal); + diffic = next_difficulty_v2(timestamps, cummulative_difficulties,tools::to_seconds(TARGET_BLOCK_TIME_12), cryptonote::difficulty_calc_mode::normal); if (!generator.construct_block_manually(blk_test, blk_last, miner_account, - test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp, + test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, hf::none, 0, blk_last.timestamp, crypto::hash(), diffic, transaction(), tx_hashes, txs_weight)) return false; diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 4cccf4d445e..feaa2525ae2 100755 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -89,12 +89,12 @@ struct gen_block_big_minor_version : public gen_block_accepted_base<2> bool generate(std::vector& events) const; }; -struct gen_block_ts_not_checked : public gen_block_accepted_base +struct gen_block_ts_not_checked : public gen_block_accepted_base { bool generate(std::vector& events) const; }; -struct gen_block_ts_in_past : public gen_block_verification_base +struct gen_block_ts_in_past : public gen_block_verification_base { bool generate(std::vector& events) const; }; @@ -155,12 +155,12 @@ struct gen_block_miner_tx_has_2_tx_gen_in : public gen_block_verification_base<1 bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_has_2_in : public gen_block_verification_base +struct gen_block_miner_tx_has_2_in : public gen_block_verification_base { bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_with_txin_to_key : public gen_block_verification_base +struct gen_block_miner_tx_with_txin_to_key : public gen_block_verification_base { bool generate(std::vector& events) const; }; diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 422cd7932a9..abaee7c7842 100755 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -43,7 +43,7 @@ using namespace cryptonote; // Tests bool gen_bp_tx_validation_base::generate_with(std::vector& events, - size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, uint8_t target_hf, + size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, cryptonote::hf target_hf, const std::function &sources, std::vector &destinations, size_t tx_idx)> &pre_tx, const std::function &post_tx, size_t extra_blocks) const { @@ -52,8 +52,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve GENERATE_ACCOUNT(miner_account); MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); - if (target_hf == 0) - target_hf = cryptonote::network_version_count - 1; + if (target_hf == cryptonote::hf::none) + target_hf = cryptonote::hf::hf20_bulletproof_plus; // NOTE: Monero tests use multiple null terminated entries in their arrays { int amounts_paid_len = 0; @@ -62,7 +62,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve } std::vector hard_forks = { - {7,0,0}, {8,0,1}, {target_hf, 0, NUM_UNLOCKED_BLOCKS + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW + 1}, + {cryptonote::hf::hf7,0,0}, {cryptonote::hf::hf8,0,1}, {target_hf, 0, NUM_UNLOCKED_BLOCKS + cryptonote::MINED_MONEY_UNLOCK_WINDOW + 1}, }; event_replay_settings settings = {}; settings.hard_forks = hard_forks; @@ -73,13 +73,13 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve cryptonote::account_base miner_accounts[NUM_MINERS]; const cryptonote::block *prev_block = &blk_0; - cryptonote::block blocks[NUM_UNLOCKED_BLOCKS + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; + cryptonote::block blocks[NUM_UNLOCKED_BLOCKS + cryptonote::MINED_MONEY_UNLOCK_WINDOW]; for (size_t i = 0; i < NUM_MINERS; ++i) miner_accounts[i].generate(); - uint8_t const first_hf = hard_forks[1].version; - uint8_t const last_hf = hard_forks.back().version; + const cryptonote::hf first_hf = hard_forks[1].version; + const cryptonote::hf last_hf = hard_forks.back().version; generator.m_hf_version = first_hf; for (size_t n = 0; n < NUM_UNLOCKED_BLOCKS; ++n) { CHECK_AND_ASSERT_MES( @@ -88,8 +88,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve miner_accounts[n % NUM_MINERS], test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, first_hf, - first_hf, - prev_block->timestamp + tools::to_seconds((first_hf>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + static_cast(first_hf), + prev_block->timestamp + tools::to_seconds((first_hf >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), @@ -105,7 +105,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve cryptonote::block blk_r, blk_last; { blk_last = blocks[NUM_UNLOCKED_BLOCKS - 1]; - for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + for (size_t i = 0; i < cryptonote::MINED_MONEY_UNLOCK_WINDOW; ++i) { CHECK_AND_ASSERT_MES( generator.construct_block_manually(blocks[NUM_UNLOCKED_BLOCKS + i], @@ -113,8 +113,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, first_hf, - first_hf, - blk_last.timestamp + tools::to_seconds((generator.m_hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + static_cast(first_hf), + blk_last.timestamp + tools::to_seconds((generator.m_hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), @@ -142,8 +142,8 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, generator.m_hf_version, - generator.m_hf_version, - blk_last.timestamp + tools::to_seconds((generator.m_hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + static_cast(generator.m_hf_version), + blk_last.timestamp + tools::to_seconds((generator.m_hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), @@ -182,7 +182,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve amounts_paid, amounts_paid_len, TESTS_DEFAULT_FEE, - CRYPTONOTE_DEFAULT_TX_MIXIN, + cryptonote::TX_OUTPUT_DECOYS, sources, destinations, true, @@ -253,7 +253,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve for (size_t i = 0; i < 8; i++) tx_hash.data[i] = 0x01 + (0x22 * i); static std::mt19937_64 rng{std::random_device{}()}; - std::uniform_int_distribution unif{std::numeric_limits::min()}; + std::uniform_int_distribution unif{std::numeric_limits::min()}; for (size_t i = 8; i < sizeof(tx_hash.data); i++) tx_hash.data[i] = unif(rng); } @@ -296,7 +296,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version, - generator.m_hf_version, generator.m_hf_version, blk_last.timestamp + tools::to_seconds((generator.m_hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + generator.m_hf_version, static_cast(generator.m_hf_version), blk_last.timestamp + tools::to_seconds((generator.m_hf_version >= cryptonote::hf::hf17_POS ? TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, txn_fee), false, "Failed to generate block"); if (!valid) @@ -346,7 +346,7 @@ bool gen_bp_tx_valid_1_old::generate(std::vector& events) cons const uint64_t amounts_paid[] = {MK_COINS(120), (uint64_t)-1}; const size_t bp_sizes[] = {1, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } }; - return generate_with(events, 1, amounts_paid, true, rct_config, HF_VERSION_MIN_2_OUTPUTS-1, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1_before_clsag"); }); + return generate_with(events, 1, amounts_paid, true, rct_config, hf_prev(feature::MIN_2_OUTPUTS), NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1_before_clsag"); }); } bool gen_bp_tx_invalid_1_new::generate(std::vector& events) const @@ -356,7 +356,7 @@ bool gen_bp_tx_invalid_1_new::generate(std::vector& events) co const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const size_t bp_sizes[] = {1, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } }; - return generate_with(events, 1, amounts_paid, false, rct_config, HF_VERSION_MIN_2_OUTPUTS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_from_clsag"); }); + return generate_with(events, 1, amounts_paid, false, rct_config, feature::MIN_2_OUTPUTS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_from_clsag"); }); } bool gen_bp_tx_valid_2::generate(std::vector& events) const @@ -364,7 +364,7 @@ bool gen_bp_tx_valid_2::generate(std::vector& events) const const uint64_t amounts_paid[] = {MK_COINS(60), MK_COINS(60), (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } }; - return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); + return generate_with(events, 1, amounts_paid, true, rct_config, hf::none, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); } bool gen_bp_tx_valid_3::generate(std::vector& events) const @@ -373,7 +373,7 @@ bool gen_bp_tx_valid_3::generate(std::vector& events) const const uint64_t amounts_paid[] = {MK_COINS(40), MK_COINS(40), MK_COINS(40), (uint64_t)-1}; const size_t bp_sizes[] = {4, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof , 0 } }; - return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); }); + return generate_with(events, 1, amounts_paid, true, rct_config, hf::none, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); }); } bool gen_bp_tx_valid_16::generate(std::vector& events) const @@ -382,7 +382,7 @@ bool gen_bp_tx_valid_16::generate(std::vector& events) const const uint64_t amounts_paid[] = {MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), MK_COINS(15), (uint64_t)-1}; const size_t bp_sizes[] = {16, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof , 0 } }; - return generate_with(events, 1, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); }); + return generate_with(events, 1, amounts_paid, true, rct_config, hf::none, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); }); } bool gen_bp_txs_valid_2_and_2::generate(std::vector& events) const @@ -392,7 +392,7 @@ bool gen_bp_txs_valid_2_and_2::generate(std::vector& events) c const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 }, {rct::RangeProofType::PaddedBulletproof, 0 } }; - return generate_with(events, 2, amounts_paid, true, rct_config, 0, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); }); + return generate_with(events, 2, amounts_paid, true, rct_config, hf::none, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); }); } bool gen_bp_txs_valid_2_and_3_and_2_and_4::generate(std::vector& events) const @@ -402,7 +402,7 @@ bool gen_bp_txs_valid_2_and_3_and_2_and_4::generate(std::vector& events) const @@ -410,7 +410,7 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector& eve DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount"); const uint64_t amounts_paid[] = {10, (uint64_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 0 } }; - return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t idx){ + return generate_with(events, 1, amounts_paid, false, rct_config, hf::none, NULL, [&](cryptonote::transaction &tx, size_t idx){ CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type)); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen()); @@ -423,7 +423,7 @@ bool gen_rct2_tx_clsag_malleability::generate(std::vector& eve DEFINE_TESTS_ERROR_CONTEXT("gen_rct_tx_clsag_malleability"); const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const rct::RCTConfig rct_config[] = { { rct::RangeProofType::PaddedBulletproof, 3 } }; - return generate_with(events, 1, amounts_paid, false, rct_config, 0, NULL, [&](cryptonote::transaction &tx, size_t tx_idx) { + return generate_with(events, 1, amounts_paid, false, rct_config, hf::none, NULL, [&](cryptonote::transaction &tx, size_t tx_idx) { CHECK_TEST_CONDITION(tx.version == cryptonote::txversion::v4_tx_types); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTType::CLSAG); CHECK_TEST_CONDITION(!tx.rct_signatures.p.CLSAGs.empty()); diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index e7faa079d73..e8f7be0a535 100755 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -83,7 +83,7 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base } bool generate_with(std::vector& events, - size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, uint8_t hf_version, + size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, cryptonote::hf hf_version, const std::function &sources, std::vector &destinations, size_t)> &pre_tx, const std::function &post_tx, size_t extra_blocks = 1) const; diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 79d09d45ca9..b09f26c1dd8 100755 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -146,8 +146,8 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); - CHECK_EQ(5 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); - CHECK_TEST_CONDITION(blocks.back() == var::get(events[20 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_4 + CHECK_EQ(5 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks.size()); + CHECK_TEST_CONDITION(blocks.back() == var::get(events[20 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW])); // blk_4 CHECK_EQ(2, c.get_alternative_blocks_count()); @@ -183,11 +183,11 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); - CHECK_EQ(6 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); + CHECK_EQ(6 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks.size()); auto it = blocks.end(); --it; --it; --it; CHECK_TEST_CONDITION(std::equal(blocks.begin(), it, m_chain_1.begin())); - CHECK_TEST_CONDITION(blocks.back() == var::get(events[24 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7 + CHECK_TEST_CONDITION(blocks.back() == var::get(events[24 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW])); // blk_7 std::vector alt_blocks; r = c.get_alternative_blocks(alt_blocks); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 87e559c8973..2729a4a1084 100755 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -73,16 +73,16 @@ void beldex_register_callback(std::vector &events, } std::vector -beldex_generate_hard_fork_table(uint8_t hf_version, uint64_t pos_delay) +beldex_generate_hard_fork_table(cryptonote::hf hf_version, uint64_t pos_delay) { - assert(hf_version < cryptonote::network_version_count); + assert(hf_version < cryptonote::hf::_next); // We always need block 0 == v7 for the genesis block: - std::vector result{{cryptonote::network_version_7, 0,0}}; + std::vector result{{cryptonote::hf::hf7, 0,0}}; uint64_t version_height = 1; // HF15 reduces and HF16+ eliminates miner block rewards, so we need to ensure we have enough // HF14 blocks to generate enough BELDEX for tests: - if (hf_version > cryptonote::network_version_15_flash) { - result.push_back({cryptonote::network_version_15_flash,0, version_height}); + if (hf_version > cryptonote::hf::hf15_flash) { + result.push_back({cryptonote::hf::hf15_flash,0, version_height}); version_height += pos_delay; } @@ -168,7 +168,7 @@ beldex_chain_generator::beldex_chain_generator(std::vector &ev : events_(events) , hard_forks_(hard_forks) { - bool init = bns_db_->init(nullptr, cryptonote::FAKECHAIN, bns::init_beldex_name_system("", false /*read_only*/)); + bool init = bns_db_->init(nullptr, cryptonote::network_type::FAKECHAIN, bns::init_beldex_name_system("", false /*read_only*/)); assert(init); first_miner_.generate(); @@ -236,7 +236,7 @@ beldex_blockchain_entry &beldex_chain_generator::add_block(beldex_blockchain_ent db_.tx_table[tx_hash] = tx; } - if (can_be_added_to_blockchain && entry.block.major_version >= cryptonote::network_version_16) + if (can_be_added_to_blockchain && entry.block.major_version >= cryptonote::hf::hf16) { bns_db_->add_block(entry.block, entry.txs); } @@ -268,7 +268,7 @@ cryptonote::account_base beldex_chain_generator::add_account() return account; } -void beldex_chain_generator::add_blocks_until_version(uint8_t hf_version) +void beldex_chain_generator::add_blocks_until_version(cryptonote::hf hf_version) { assert(hard_forks_.size()); assert(hf_version_ <= hard_forks_.back().version); @@ -315,12 +315,12 @@ void beldex_chain_generator::add_master_node_checkpoint(uint64_t block_height, s void beldex_chain_generator::add_mined_money_unlock_blocks() { - add_n_blocks(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + add_n_blocks(cryptonote::MINED_MONEY_UNLOCK_WINDOW); } -void beldex_chain_generator::add_transfer_unlock_blocks(uint8_t hf_version) +void beldex_chain_generator::add_transfer_unlock_blocks(cryptonote::hf hf_version) { - add_n_blocks((hf_version>=cryptonote::network_version_17_POS?CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE_V17:CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE)); + add_n_blocks((hf_version >= cryptonote::hf::hf17_POS ? cryptonote::DEFAULT_TX_SPENDABLE_AGE_V17 : cryptonote::old::DEFAULT_TX_SPENDABLE_AGE)); } void beldex_chain_generator::add_tx(cryptonote::transaction const &tx, bool can_be_added_to_blockchain, std::string const &fail_msg, bool kept_by_block) @@ -330,7 +330,7 @@ void beldex_chain_generator::add_tx(cryptonote::transaction const &tx, bool can_ cryptonote::transaction beldex_chain_generator::create_and_add_beldex_name_system_tx(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, @@ -348,7 +348,7 @@ beldex_chain_generator::create_and_add_beldex_name_system_tx(cryptonote::account cryptonote::transaction beldex_chain_generator::create_and_add_beldex_name_system_tx_update(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, @@ -367,7 +367,7 @@ beldex_chain_generator::create_and_add_beldex_name_system_tx_update(cryptonote:: cryptonote::transaction beldex_chain_generator::create_and_add_beldex_name_system_tx_renew(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature, @@ -469,17 +469,17 @@ beldex_chain_generator::create_registration_tx(const cryptonote::account_base &s } uint64_t new_height = get_block_height(top().block) + 1; - uint8_t new_hf_version = get_hf_version_at(new_height); + cryptonote::hf new_hf_version = get_hf_version_at(new_height); const auto staking_requirement = master_nodes::get_staking_requirement(new_height); uint64_t amount = master_nodes::portions_to_amount(portions[0], staking_requirement); uint64_t unlock_time = 0; - if (new_hf_version < cryptonote::network_version_11_infinite_staking) - unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,cryptonote::network_version_17_POS); + if (new_hf_version < cryptonote::hf::hf11_infinite_staking) + unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,cryptonote::hf::hf17_POS); std::vector extra; cryptonote::add_master_node_pubkey_to_tx_extra(extra, master_node_keys.pub); - const uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW; + const uint64_t exp_timestamp = time(nullptr) + tools::to_seconds(cryptonote::old::STAKING_AUTHORIZATION_EXPIRATION_WINDOW); crypto::hash hash; if (!cryptonote::get_registration_hash(contributors, src_operator_cut, portions, exp_timestamp, hash)) @@ -511,11 +511,11 @@ cryptonote::transaction beldex_chain_generator::create_staking_tx(const crypto:: cryptonote::add_master_node_contributor_to_tx_extra(extra, src.get_keys().m_account_address); uint64_t new_height = get_block_height(top().block) + 1; - uint8_t new_hf_version = get_hf_version_at(new_height); + cryptonote::hf new_hf_version = get_hf_version_at(new_height); uint64_t unlock_time = 0; - if (new_hf_version < cryptonote::network_version_11_infinite_staking) - unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,cryptonote::network_version_17_POS); + if (new_hf_version < cryptonote::hf::hf11_infinite_staking) + unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,cryptonote::hf::hf17_POS); beldex_tx_builder(events_, result, top().block, src /*from*/, src.get_keys().m_account_address /*to*/, amount, new_hf_version) .with_tx_type(cryptonote::txtype::stake) @@ -546,7 +546,7 @@ cryptonote::transaction beldex_chain_generator::create_state_change_tx(master_no using scver = cryptonote::tx_extra_master_node_state_change::version_t; cryptonote::tx_extra_master_node_state_change state_change_extra( - hf_version >= cryptonote::network_version_18_bns ? scver::v4_reasons : scver::v0, + hf_version >= cryptonote::hf::hf18_bns ? scver::v4_reasons : scver::v0, state, height, worker_index, reasons_all, reasons_any, {}); if (voters.size()) { @@ -609,7 +609,7 @@ cryptonote::checkpoint_t beldex_chain_generator::create_master_node_checkpoint(u } cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, @@ -632,7 +632,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx(cry cryptonote::block const &head = top().block; uint64_t new_height = get_block_height(top().block) + 1; - uint8_t new_hf_version = get_hf_version_at(new_height); + auto new_hf_version = get_hf_version_at(new_height); uint64_t burn = burn_override.value_or(bns::burn_needed(new_hf_version,mapping_years)); auto lcname = tools::lowercase_ascii_string(name); @@ -643,19 +643,19 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx(cry prev_txid = mapping.txid; bns::mapping_value encrypted_bchat_value = value_bchat; - bool encrypted_bchat = encrypted_bchat_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_bchat = encrypted_bchat_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); assert(encrypted_bchat); bns::mapping_value encrypted_wallet_value = value_wallet; - bool encrypted_wallet = encrypted_wallet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_wallet = encrypted_wallet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); assert(encrypted_wallet); bns::mapping_value encrypted_belnet_value = value_belnet; - bool encrypted_belnet = encrypted_belnet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_belnet = encrypted_belnet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); assert(encrypted_belnet); bns::mapping_value encrypted_eth_addr_value = value_eth_addr; - bool encrypted_eth_addr = encrypted_eth_addr_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_eth_addr = encrypted_eth_addr_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); assert(encrypted_eth_addr); std::vector extra; @@ -673,7 +673,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx(cry } cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_update(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, @@ -702,7 +702,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd if (!encrypted_bchat_value.encrypted) { assert(!signature); // Can't specify a signature with an unencrypted value because encrypting generates a new nonce and would invalidate it - bool encrypted_bchat = encrypted_bchat_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_bchat = encrypted_bchat_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); if (use_asserts) assert(encrypted_bchat); } } @@ -714,7 +714,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd if (!encrypted_wallet_value.encrypted) { assert(!signature); // Can't specify a signature with an unencrypted value because encrypting generates a new nonce and would invalidate it - bool encrypted_wallet = encrypted_wallet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_wallet = encrypted_wallet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); if (use_asserts) assert(encrypted_wallet); } } @@ -726,7 +726,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd if (!encrypted_belnet_value.encrypted) { assert(!signature); // Can't specify a signature with an unencrypted value because encrypting generates a new nonce and would invalidate it - bool encrypted_belnet = encrypted_belnet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_belnet = encrypted_belnet_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); if (use_asserts) assert(encrypted_belnet); } } @@ -738,7 +738,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd if (!encrypted_eth_addr_value.encrypted) { assert(!signature); // Can't specify a signature with an unencrypted value because encrypting generates a new nonce and would invalidate it - bool encrypted_eth_addr = encrypted_eth_addr_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::network_version_16); + bool encrypted_eth_addr = encrypted_eth_addr_value.encrypt(lcname, &name_hash, hf_version <= cryptonote::hf::hf16); if (use_asserts) assert(encrypted_eth_addr); } } @@ -761,7 +761,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd cryptonote::block const &head = top().block; uint64_t new_height = get_block_height(top().block) + 1; - uint8_t new_hf_version = get_hf_version_at(new_height); + auto new_hf_version = get_hf_version_at(new_height); cryptonote::transaction result = {}; beldex_tx_builder(events_, result, head, src /*from*/, src.get_keys().m_account_address, 0 /*amount*/, new_hf_version) @@ -774,14 +774,14 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_upd } cryptonote::transaction -beldex_chain_generator::create_beldex_name_system_tx_update_w_extra(cryptonote::account_base const &src, uint8_t hf_version, cryptonote::tx_extra_beldex_name_system const &ons_extra) const +beldex_chain_generator::create_beldex_name_system_tx_update_w_extra(cryptonote::account_base const &src, cryptonote::hf hf_version, cryptonote::tx_extra_beldex_name_system const &ons_extra) const { std::vector extra; cryptonote::add_beldex_name_system_to_tx_extra(extra, ons_extra); cryptonote::block const &head = top().block; uint64_t new_height = get_block_height(top().block) + 1; - uint8_t new_hf_version = get_hf_version_at(new_height); + auto new_hf_version = get_hf_version_at(new_height); cryptonote::transaction result = {}; beldex_tx_builder(events_, result, head, src /*from*/, src.get_keys().m_account_address, 0 /*amount*/, new_hf_version) @@ -793,7 +793,7 @@ beldex_chain_generator::create_beldex_name_system_tx_update_w_extra(cryptonote:: } cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_renew(cryptonote::account_base const &src, - uint8_t hf_version, + cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature, @@ -808,7 +808,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_ren prev_txid = mapping.txid; } - uint8_t new_hf_version = get_hf_version_at(get_block_height(top().block) + 1); + auto new_hf_version = get_hf_version_at(get_block_height(top().block) + 1); uint64_t burn = burn_override.value_or(bns::burn_needed(new_hf_version, mapping_years)); bns::generic_signature signature_ = {}; @@ -843,7 +843,7 @@ cryptonote::transaction beldex_chain_generator::create_beldex_name_system_tx_ren static void fill_nonce_with_test_generator(test_generator *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height) { cryptonote::randomx_longhash_context randomx_context = {}; - if (generator->m_hf_version >= cryptonote::network_version_13_checkpointing) + if (generator->m_hf_version >= cryptonote::hf::hf13_checkpointing) { randomx_context.seed_height = crypto::rx_seedheight(height); cryptonote::block prev = blk; @@ -859,7 +859,7 @@ static void fill_nonce_with_test_generator(test_generator *generator, cryptonote blk.nonce = 0; auto get_block_hash = [&randomx_context](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { - hash = cryptonote::get_block_longhash(cryptonote::FAKECHAIN, randomx_context, b, height, threads); + hash = cryptonote::get_block_longhash(cryptonote::network_type::FAKECHAIN, randomx_context, b, height, threads); return true; }; @@ -870,7 +870,7 @@ static void fill_nonce_with_test_generator(test_generator *generator, cryptonote void fill_nonce_with_beldex_generator(beldex_chain_generator const *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height) { cryptonote::randomx_longhash_context randomx_context = {}; - if (generator->blocks().size() && generator->hardfork() >= cryptonote::network_version_13_checkpointing) + if (generator->blocks().size() && generator->hardfork() >= cryptonote::hf::hf13_checkpointing) { randomx_context.seed_height = crypto::rx_seedheight(height); randomx_context.seed_block_hash = cryptonote::get_block_hash(generator->blocks()[randomx_context.seed_height].block); @@ -879,7 +879,7 @@ void fill_nonce_with_beldex_generator(beldex_chain_generator const *generator, c blk.nonce = 0; auto get_block_hash = [&randomx_context](const cryptonote::block &blk, uint64_t height, unsigned int threads, crypto::hash &hash) { - hash = cryptonote::get_block_longhash(cryptonote::FAKECHAIN, randomx_context, blk, height, threads); + hash = cryptonote::get_block_longhash(cryptonote::network_type::FAKECHAIN, randomx_context, blk, height, threads); return true; }; @@ -893,7 +893,7 @@ beldex_blockchain_entry beldex_chain_generator::create_genesis_block(const crypt beldex_blockchain_entry result = {}; cryptonote::block &blk = result.block; blk.major_version = hf_version_; - blk.minor_version = hf_version_; + blk.minor_version = static_cast(hf_version_); blk.timestamp = timestamp; blk.prev_id = crypto::null_hash; @@ -908,7 +908,7 @@ beldex_blockchain_entry beldex_chain_generator::create_genesis_block(const crypt target_block_weight, 0 /*total_fee*/, blk.miner_tx, - cryptonote::beldex_miner_tx_context::miner_block(cryptonote::FAKECHAIN, miner.get_keys().m_account_address), + cryptonote::beldex_miner_tx_context::miner_block(cryptonote::network_type::FAKECHAIN, miner.get_keys().m_account_address), cryptonote::blobdata(), hf_version_); assert(constructed); @@ -966,7 +966,7 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ entry = {}; cryptonote::block &blk = entry.block; blk.major_version = params.hf_version; - blk.minor_version = params.hf_version; + blk.minor_version = static_cast(params.hf_version); blk.timestamp = params.timestamp; blk.prev_id = get_block_hash(params.prev.block); @@ -978,7 +978,7 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ { blk.tx_hashes.push_back(get_transaction_hash(tx)); uint64_t fee = 0; - bool r = get_tx_miner_fee(tx, fee, blk.major_version >= HF_VERSION_FEE_BURNING); + bool r = get_tx_miner_fee(tx, fee, blk.major_version >= cryptonote::feature::FEE_BURNING); CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); txs_weight += get_transaction_weight(tx); if (calc_total_fee) total_fee += fee; @@ -990,7 +990,7 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ std::vector active_snode_list = params.prev.master_node_state.active_master_nodes_infos(); - bool POS_block_is_possible = blk.major_version >= cryptonote::network_version_17_POS && active_snode_list.size() >= master_nodes::POS_min_master_nodes(cryptonote::FAKECHAIN); + bool POS_block_is_possible = blk.major_version >= cryptonote::hf::hf17_POS && active_snode_list.size() >= master_nodes::POS_min_master_nodes(cryptonote::network_type::FAKECHAIN); bool make_POS_block = (params.type == beldex_create_block_type::automatic && POS_block_is_possible) || params.type == beldex_create_block_type::POS; if (make_POS_block) @@ -1003,7 +1003,7 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ // NOTE: Get POS Quorum necessary for this block std::vector entropy = master_nodes::get_POS_entropy_for_next_block(db_, params.prev.block, blk.POS.round); - POS_quorum = master_nodes::generate_POS_quorum(cryptonote::FAKECHAIN, params.block_leader.key, blk.major_version, active_snode_list, entropy, blk.POS.round); + POS_quorum = master_nodes::generate_POS_quorum(cryptonote::network_type::FAKECHAIN, params.block_leader.key, blk.major_version, active_snode_list, entropy, blk.POS.round); assert(POS_quorum.validators.size() == master_nodes::POS_QUORUM_NUM_VALIDATORS); assert(POS_quorum.workers.size() == 1); @@ -1020,25 +1020,23 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ block_producer = master_nodes::master_node_info_to_payout(block_producer_key, *(it->second)); } - miner_tx_context = cryptonote::beldex_miner_tx_context::POS_block(cryptonote::FAKECHAIN, block_producer, params.block_leader); + miner_tx_context = cryptonote::beldex_miner_tx_context::POS_block(cryptonote::network_type::FAKECHAIN, block_producer, params.block_leader); } else { - miner_tx_context = cryptonote::beldex_miner_tx_context::miner_block(cryptonote::FAKECHAIN, params.miner_acc.get_keys().m_account_address, params.block_leader); + miner_tx_context = cryptonote::beldex_miner_tx_context::miner_block(cryptonote::network_type::FAKECHAIN, params.miner_acc.get_keys().m_account_address, params.block_leader); } - if (blk.major_version >= cryptonote::network_version_10_bulletproofs && - cryptonote::height_has_governance_output(cryptonote::FAKECHAIN, blk.major_version, height)) + if (blk.major_version >= cryptonote::hf::hf10_bulletproofs && + cryptonote::height_has_governance_output(cryptonote::network_type::FAKECHAIN, blk.major_version, height)) { - constexpr uint64_t num_blocks = cryptonote::get_config(cryptonote::FAKECHAIN).GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; + constexpr uint64_t num_blocks = cryptonote::get_config(cryptonote::network_type::FAKECHAIN).GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; uint64_t start_height = height - num_blocks; - static_assert(cryptonote::network_version_count == cryptonote::network_version_19 + 1, - "The code below needs to be updated to support higher hard fork versions"); - if (blk.major_version <= cryptonote::network_version_16) + if (blk.major_version <= cryptonote::hf::hf16) miner_tx_context.batched_governance = 0; - else if (blk.major_version >= cryptonote::network_version_17_POS) - miner_tx_context.batched_governance = (FOUNDATION_REWARD_HF17) * num_blocks; + else if (blk.major_version >= cryptonote::hf::hf17_POS) + miner_tx_context.batched_governance = (beldex::FOUNDATION_REWARD_HF17) * num_blocks; else { for (int i = (int)get_block_height(params.prev.block), count = 0; @@ -1046,8 +1044,8 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ i--, count++) { beldex_blockchain_entry const &historical_entry = db_.blocks[i]; - if (historical_entry.block.major_version < cryptonote::network_version_10_bulletproofs) break; - miner_tx_context.batched_governance += cryptonote::derive_governance_from_block_reward(cryptonote::FAKECHAIN, historical_entry.block, blk.major_version); + if (historical_entry.block.major_version < cryptonote::hf::hf10_bulletproofs) break; + miner_tx_context.batched_governance += cryptonote::derive_governance_from_block_reward(cryptonote::network_type::FAKECHAIN, historical_entry.block, blk.major_version); } } } @@ -1135,7 +1133,7 @@ bool beldex_chain_generator::block_begin(beldex_blockchain_entry &entry, beldex_ void beldex_chain_generator::block_end(beldex_blockchain_entry &entry, beldex_create_block_params const ¶ms) const { entry.master_node_state = params.prev.master_node_state; - entry.master_node_state.update_from_block(db_, cryptonote::FAKECHAIN, state_history_, {} /*state_archive*/, {} /*alt_states*/, entry.block, entry.txs, nullptr); + entry.master_node_state.update_from_block(db_, cryptonote::network_type::FAKECHAIN, state_history_, {} /*state_archive*/, {} /*alt_states*/, entry.block, entry.txs, nullptr); } bool beldex_chain_generator::create_block(beldex_blockchain_entry &entry, @@ -1160,9 +1158,9 @@ beldex_create_block_params beldex_chain_generator::next_block_params() const beldex_create_block_params result = {}; result.prev = prev; result.miner_acc = first_miner_; - result.block_weights = last_n_block_weights(height(), CRYPTONOTE_REWARD_BLOCKS_WINDOW); + result.block_weights = last_n_block_weights(height(), cryptonote::REWARD_BLOCKS_WINDOW); result.hf_version = get_hf_version_at(next_height); - result.timestamp = prev.block.timestamp + tools::to_seconds((result.hf_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)); + result.timestamp = prev.block.timestamp + tools::to_seconds((result.hf_version>=cryptonote::hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)); result.block_leader = prev.master_node_state.get_block_leader(); result.total_fee = 0; // Request chain generator to calculate the fee return result; @@ -1183,9 +1181,9 @@ beldex_blockchain_entry beldex_chain_generator::create_next_block(const std::vec return result; } -uint8_t beldex_chain_generator::get_hf_version_at(uint64_t height) const { +cryptonote::hf beldex_chain_generator::get_hf_version_at(uint64_t height) const { - uint8_t cur_hf_ver = 0; + cryptonote::hf cur_hf_ver = cryptonote::hf::none; for (auto i = 0u; i < hard_forks_.size(); ++i) { if (height < hard_forks_[i].height) break; @@ -1289,17 +1287,17 @@ void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, static void manual_calc_batched_governance(const test_generator &generator, const crypto::hash &head, cryptonote::beldex_miner_tx_context &miner_tx_context, - int hard_fork_version, + cryptonote::hf hard_fork_version, uint64_t height) { miner_tx_context.batched_governance = 0; - if (hard_fork_version >= cryptonote::network_version_10_bulletproofs && - cryptonote::height_has_governance_output(cryptonote::FAKECHAIN, hard_fork_version, height)) + if (hard_fork_version >= cryptonote::hf::hf10_bulletproofs && + cryptonote::height_has_governance_output(cryptonote::network_type::FAKECHAIN, hard_fork_version, height)) { - uint64_t num_blocks = cryptonote::get_config(cryptonote::FAKECHAIN).GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; + uint64_t num_blocks = cryptonote::get_config(cryptonote::network_type::FAKECHAIN).GOVERNANCE_REWARD_INTERVAL_IN_BLOCKS; uint64_t start_height = height - num_blocks; - if (hard_fork_version >= cryptonote::network_version_16) + if (hard_fork_version >= cryptonote::hf::hf16) { miner_tx_context.batched_governance = num_blocks * cryptonote::governance_reward_formula(0, hard_fork_version); return; @@ -1321,8 +1319,8 @@ static void manual_calc_batched_governance(const test_generator &generator, if (block_height < start_height) continue; - if (entry.major_version >= cryptonote::network_version_10_bulletproofs) - miner_tx_context.batched_governance += cryptonote::derive_governance_from_block_reward(cryptonote::FAKECHAIN, entry, hard_fork_version); + if (entry.major_version >= cryptonote::hf::hf10_bulletproofs) + miner_tx_context.batched_governance += cryptonote::derive_governance_from_block_reward(cryptonote::network_type::FAKECHAIN, entry, hard_fork_version); } } } @@ -1339,7 +1337,7 @@ bool test_generator::construct_block(cryptonote::block &blk, { /// a temporary workaround blk.major_version = m_hf_version; - blk.minor_version = m_hf_version; + blk.minor_version = static_cast(m_hf_version); blk.timestamp = timestamp; blk.prev_id = prev_id; @@ -1357,13 +1355,13 @@ bool test_generator::construct_block(cryptonote::block &blk, for (auto& tx : tx_list) { uint64_t fee = 0; - bool r = get_tx_miner_fee(tx, fee, blk.major_version >= HF_VERSION_FEE_BURNING); + bool r = get_tx_miner_fee(tx, fee, blk.major_version >= cryptonote::feature::FEE_BURNING); CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); total_fee += fee; txs_weight += get_transaction_weight(tx); } - auto miner_tx_context = cryptonote::beldex_miner_tx_context::miner_block(cryptonote::FAKECHAIN, miner_acc.get_keys().m_account_address, block_leader); + auto miner_tx_context = cryptonote::beldex_miner_tx_context::miner_block(cryptonote::network_type::FAKECHAIN, miner_acc.get_keys().m_account_address, block_leader); blk.miner_tx = {}; size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); manual_calc_batched_governance(*this, prev_id, miner_tx_context, m_hf_version, height); @@ -1444,10 +1442,10 @@ bool test_generator::construct_block(cryptonote::block &blk, uint64_t height = var::get(blk_prev.miner_tx.vin.front()).height + 1; crypto::hash prev_id = get_block_hash(blk_prev); // Keep difficulty unchanged - uint64_t timestamp = blk_prev.timestamp + tools::to_seconds((blk_prev.major_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)); + uint64_t timestamp = blk_prev.timestamp + tools::to_seconds((blk_prev.major_version >= cryptonote::hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)); uint64_t already_generated_coins = get_already_generated_coins(prev_id); std::vector block_weights; - get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_block_weights(block_weights, prev_id, cryptonote::REWARD_BLOCKS_WINDOW); return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list, block_leader); } @@ -1457,7 +1455,7 @@ bool test_generator::construct_block_manually( const cryptonote::block &prev_block, const cryptonote::account_base &miner_acc, int actual_params /* = bf_none*/, - uint8_t major_ver /* = 0*/, + cryptonote::hf major_ver /* = 0*/, uint8_t minor_ver /* = 0*/, uint64_t timestamp /* = 0*/, const crypto::hash &prev_id /* = crypto::hash()*/, @@ -1467,29 +1465,29 @@ bool test_generator::construct_block_manually( size_t txs_weight /* = 0*/, size_t miner_fee /*= 0*/) { - blk.major_version = actual_params & bf_major_ver ? major_ver : static_cast(cryptonote::network_version_7); - blk.minor_version = actual_params & bf_minor_ver ? minor_ver : static_cast(cryptonote::network_version_7); - blk.timestamp = actual_params & bf_timestamp ? timestamp : prev_block.timestamp + tools::to_seconds(blk.major_version>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD); // Keep difficulty unchanged + blk.major_version = actual_params & bf_major_ver ? major_ver : cryptonote::hf::hf7; + blk.minor_version = actual_params & bf_minor_ver ? minor_ver : static_cast(cryptonote::hf::hf7); + blk.timestamp = actual_params & bf_timestamp ? timestamp : prev_block.timestamp + tools::to_seconds(blk.major_version>=cryptonote::hf::hf17_POS? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12); // Keep difficulty unchanged blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block); blk.tx_hashes = actual_params & bf_tx_hashes ? tx_hashes : std::vector(); size_t height = get_block_height(prev_block) + 1; uint64_t already_generated_coins = get_already_generated_coins(prev_block); std::vector block_weights; - get_last_n_block_weights(block_weights, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_block_weights(block_weights, get_block_hash(prev_block), cryptonote::REWARD_BLOCKS_WINDOW); if (actual_params & bf_miner_tx) { blk.miner_tx = miner_tx; } else { - // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + // TODO: This will work, until size of constructed block is less then cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE cryptonote::beldex_miner_tx_context miner_tx_context = {}; - miner_tx_context.nettype = cryptonote::FAKECHAIN; + miner_tx_context.nettype = cryptonote::network_type::FAKECHAIN; manual_calc_batched_governance(*this, prev_id, miner_tx_context, m_hf_version, height); size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); - if (!construct_miner_tx(height, tools::median(block_weights.begin(), block_weights.end()), already_generated_coins, current_block_weight, miner_fee, blk.miner_tx, cryptonote::beldex_miner_tx_context::miner_block(cryptonote::FAKECHAIN, miner_acc.get_keys().m_account_address), cryptonote::blobdata(), m_hf_version)) + if (!construct_miner_tx(height, tools::median(block_weights.begin(), block_weights.end()), already_generated_coins, current_block_weight, miner_fee, blk.miner_tx, cryptonote::beldex_miner_tx_context::miner_block(cryptonote::network_type::FAKECHAIN, miner_acc.get_keys().m_account_address), cryptonote::blobdata(), m_hf_version)) return false; } @@ -1507,7 +1505,7 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c const cryptonote::account_base& miner_acc, const std::vector& tx_hashes, size_t txs_weight) { - return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, cryptonote::transaction(), tx_hashes, txs_weight, 0); + return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, cryptonote::hf::none, 0, 0, crypto::hash(), 0, cryptonote::transaction(), tx_hashes, txs_weight, 0); } cryptonote::transaction make_registration_tx(std::vector& events, @@ -1517,7 +1515,7 @@ cryptonote::transaction make_registration_tx(std::vector& even const std::vector& contributors, const std::vector& portions, const cryptonote::block& head, - uint8_t hf_version) + cryptonote::hf hf_version) { const auto new_height = cryptonote::get_block_height(head) + 1; const auto staking_requirement = master_nodes::get_staking_requirement(new_height); @@ -1525,12 +1523,12 @@ cryptonote::transaction make_registration_tx(std::vector& even cryptonote::transaction tx; uint64_t unlock_time = 0; - if (hf_version < cryptonote::network_version_11_infinite_staking) - unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,cryptonote::network_version_17_POS); + if (hf_version < cryptonote::hf::hf11_infinite_staking) + unlock_time = new_height + master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,cryptonote::hf::hf17_POS); std::vector extra; cryptonote::add_master_node_pubkey_to_tx_extra(extra, master_node_keys.pub); - const uint64_t exp_timestamp = time(nullptr) + STAKING_AUTHORIZATION_EXPIRATION_WINDOW; + const uint64_t exp_timestamp = time(nullptr) + tools::to_seconds(cryptonote::old::STAKING_AUTHORIZATION_EXPIRATION_WINDOW); crypto::hash hash; if (!cryptonote::get_registration_hash(contributors, operator_cut, portions, exp_timestamp, hash)) @@ -1545,7 +1543,7 @@ cryptonote::transaction make_registration_tx(std::vector& even add_master_node_contributor_to_tx_extra(extra, contributors.at(0)); cryptonote::txtype tx_type = cryptonote::txtype::standard; - if (hf_version >= cryptonote::network_version_16) tx_type = cryptonote::txtype::stake; // NOTE: txtype stake was not introduced until HF14 + if (hf_version >= cryptonote::hf::hf16) tx_type = cryptonote::txtype::stake; // NOTE: txtype stake was not introduced until HF14 beldex_tx_builder(events, tx, head, account, account.get_keys().m_account_address, amount, hf_version).with_tx_type(tx_type).with_extra(extra).with_unlock_time(unlock_time).build(); events.push_back(tx); return tx; @@ -1788,7 +1786,7 @@ bool fill_tx_sources(std::vector& sources, const st const output_index& oi = outs[sender_out]; if (oi.spent) continue; - if (!cryptonote::rules::is_output_unlocked(oi.unlock_time, cryptonote::get_block_height(blk_head),cryptonote::FAKECHAIN)) continue; + if (!cryptonote::rules::is_output_unlocked(oi.unlock_time, cryptonote::get_block_height(blk_head),cryptonote::network_type::FAKECHAIN)) continue; cryptonote::tx_source_entry ts; const auto& tx = *oi.p_tx; @@ -2289,7 +2287,7 @@ cryptonote::transaction construct_tx_with_fee(std::vector &eve uint64_t fee) { cryptonote::transaction tx; - beldex_tx_builder(events, tx, blk_head, acc_from, acc_to.get_keys().m_account_address, amount, cryptonote::network_version_7).with_fee(fee).build(); + beldex_tx_builder(events, tx, blk_head, acc_from, acc_to.get_keys().m_account_address, amount, cryptonote::hf::hf7).with_fee(fee).build(); events.push_back(tx); return tx; } @@ -2334,7 +2332,7 @@ uint64_t get_unlocked_balance(const cryptonote::account_base& addr, const std::v return false; for (const size_t out_idx : outs_mine) { - const auto unlocked = cryptonote::rules::is_output_unlocked(outs[out_idx].unlock_time, get_block_height(blockchain.back()),cryptonote::FAKECHAIN); + const auto unlocked = cryptonote::rules::is_output_unlocked(outs[out_idx].unlock_time, get_block_height(blockchain.back()),cryptonote::network_type::FAKECHAIN); if (outs[out_idx].spent || !unlocked) continue; res += outs[out_idx].amount; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index d3638c1cb8c..9f7e7a2fae9 100755 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -315,7 +315,7 @@ class test_generator bf_hf_version= 1 << 8 }; - explicit test_generator(int hf_version = 7) : m_hf_version(hf_version) {} + explicit test_generator(cryptonote::hf hf_version = cryptonote::hf::hf7) : m_hf_version(hf_version) {} void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector& block_weights, const crypto::hash& head, size_t n) const; @@ -331,7 +331,7 @@ class test_generator const std::list& tx_list = std::list(), const master_nodes::payout &block_leader = {}); bool construct_block_manually(cryptonote::block& blk, const cryptonote::block& prev_block, - const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0, + const cryptonote::account_base& miner_acc, int actual_params = bf_none, cryptonote::hf major_ver = cryptonote::hf::none, uint8_t minor_ver = 0, uint64_t timestamp = 0, const crypto::hash& prev_id = crypto::hash(), const cryptonote::difficulty_type& diffic = 1, const cryptonote::transaction& miner_tx = cryptonote::transaction(), const std::vector& tx_hashes = std::vector(), size_t txs_sizes = 0, size_t txn_fee = 0); @@ -339,7 +339,7 @@ class test_generator const cryptonote::account_base& miner_acc, const std::vector& tx_hashes, size_t txs_size); - int m_hf_version; + cryptonote::hf m_hf_version = cryptonote::hf::none; std::unordered_map m_blocks_info; private: @@ -907,7 +907,7 @@ inline bool replay_events_through_core_plain(cryptonote::core& cr, const std::ve //-------------------------------------------------------------------------- template struct get_test_options { - const std::vector hard_forks = {{7, 0, 0, 0}}; + const std::vector hard_forks = {{cryptonote::hf::hf7, 0, 0, 0}}; const cryptonote::test_options test_options = { hard_forks, 0 }; @@ -1082,12 +1082,12 @@ inline bool do_replay_file(const std::string& filename) BLK_NAME = blk_last; \ } -#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) +#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, cryptonote::MINED_MONEY_UNLOCK_WINDOW) // NOTE(beldex): These macros assume hardfork version 7 and are from the old Monero testing code #define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ cryptonote::transaction TX_NAME; \ - beldex_tx_builder(VEC_EVENTS, TX_NAME, HEAD, FROM, TO.get_keys().m_account_address, AMOUNT, cryptonote::network_version_7).build(); \ + beldex_tx_builder(VEC_EVENTS, TX_NAME, HEAD, FROM, TO.get_keys().m_account_address, AMOUNT, cryptonote::hf::hf7).build(); \ VEC_EVENTS.push_back(TX_NAME); #define MAKE_TX_MIX_RCT(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ @@ -1100,7 +1100,7 @@ inline bool do_replay_file(const std::string& filename) #define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ { \ cryptonote::transaction t; \ - beldex_tx_builder(VEC_EVENTS, t, HEAD, FROM, TO.get_keys().m_account_address, AMOUNT, cryptonote::network_version_7).build(); \ + beldex_tx_builder(VEC_EVENTS, t, HEAD, FROM, TO.get_keys().m_account_address, AMOUNT, cryptonote::hf::hf7).build(); \ SET_NAME.push_back(t); \ VEC_EVENTS.push_back(t); \ } @@ -1142,7 +1142,7 @@ inline bool do_replay_file(const std::string& filename) TX, \ cryptonote::beldex_miner_tx_context::miner_block(cryptonote::FAKECHAIN, miner_account.get_keys().m_account_address), \ {}, \ - 7)) \ + cryptonote::hf::hf7)) \ return false; #define MAKE_TX_LIST_START_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ @@ -1235,7 +1235,7 @@ inline bool do_replay_file(const std::string& filename) #define CHECK_TEST_CONDITION_MSG(cond, msg) CHECK_AND_ASSERT_MES(cond, false, "[" << perr_context << "] failed: \"" << QUOTEME(cond) << "\", msg: " << msg) #define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2) #define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2) -#define MK_COINS(amount) (UINT64_C(amount) * COIN) +#define MK_COINS(amount) (UINT64_C(amount) * beldex::COIN) inline std::string make_junk() { std::string junk; @@ -1276,7 +1276,7 @@ class beldex_tx_builder { const cryptonote::account_base& from, const cryptonote::account_public_address& to, uint64_t amount, - uint8_t hf_version) + cryptonote::hf hf_version) : m_events(events) , m_tx(tx) , m_head(head) @@ -1367,7 +1367,7 @@ class beldex_tx_builder { void fill_nonce_with_beldex_generator (struct beldex_chain_generator const *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); void beldex_register_callback (std::vector &events, std::string const &callback_name, beldex_callback callback); -std::vector beldex_generate_hard_fork_table(uint8_t max_hf_version = cryptonote::network_version_count - 1, uint64_t pos_delay = 60); +std::vector beldex_generate_hard_fork_table(cryptonote::hf max_hf_version = cryptonote::hf::hf20_bulletproof_plus, uint64_t pos_delay = 60); struct beldex_blockchain_entry { @@ -1411,7 +1411,7 @@ enum struct beldex_create_block_type struct beldex_create_block_params { beldex_create_block_type type; - uint8_t hf_version; + cryptonote::hf hf_version; beldex_blockchain_entry prev; cryptonote::account_base miner_acc; uint64_t timestamp; @@ -1432,7 +1432,7 @@ struct beldex_chain_generator uint64_t last_cull_height_ = 0; std::shared_ptr bns_db_ = std::make_shared(); beldex_chain_generator_db db_; - uint8_t hf_version_ = cryptonote::network_version_7; + cryptonote::hf hf_version_ = cryptonote::hf::hf7; std::vector& events_; const std::vector hard_forks_; cryptonote::account_base first_miner_; @@ -1441,41 +1441,41 @@ struct beldex_chain_generator uint64_t height() const { return cryptonote::get_block_height(db_.blocks.back().block); } uint64_t chain_height() const { return height() + 1; } - const std::vector& blocks() const { return db_.blocks; } + const std::vector& blocks() const { return db_.blocks; } size_t event_index() const { return events_.size() - 1; } - uint8_t hardfork() const { return get_hf_version_at(height()); } + cryptonote::hf hardfork() const { return get_hf_version_at(height()); } - const beldex_blockchain_entry& top() const { return db_.blocks.back(); } + const beldex_blockchain_entry& top() const { return db_.blocks.back(); } master_nodes::quorum_manager top_quorum() const; master_nodes::quorum_manager quorum(uint64_t height) const; std::shared_ptr get_quorum(master_nodes::quorum_type type, uint64_t height) const; - master_nodes::master_node_keys get_cached_keys(const crypto::public_key &pubkey) const; + master_nodes::master_node_keys get_cached_keys(const crypto::public_key &pubkey) const; cryptonote::account_base add_account(); - beldex_blockchain_entry &add_block(beldex_blockchain_entry const &entry, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {}); - void add_blocks_until_version(uint8_t hf_version); + beldex_blockchain_entry &add_block(beldex_blockchain_entry const &entry, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {}); + void add_blocks_until_version(cryptonote::hf hf_version); void add_n_blocks(int n); bool add_blocks_until_next_checkpointable_height(); void add_master_node_checkpoint(uint64_t block_height, size_t num_votes); - void add_mined_money_unlock_blocks(); // NOTE: Unlock all Loki generated from mining prior to this call i.e. CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - void add_transfer_unlock_blocks(uint8_t hf_version); // Unlock funds from (standard) transfers prior to this call, i.e. CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE + void add_mined_money_unlock_blocks(); // NOTE: Unlock all Loki generated from mining prior to this call i.e. cryptonote::MINED_MONEY_UNLOCK_WINDOW + void add_transfer_unlock_blocks(cryptonote::hf hf_version); // Unlock funds from (standard) transfers prior to this call, i.e. DEFAULT_TX_SPENDABLE_AGE // NOTE: Add an event that is just a user specified message to signify progress in the test void add_event_msg(std::string const &msg) { events_.push_back(msg); } void add_tx(cryptonote::transaction const &tx, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {}, bool kept_by_block = false); - beldex_create_block_params next_block_params() const; + beldex_create_block_params next_block_params() const; // NOTE: Add constructed TX to events_ and assume that it is valid to add to the blockchain. If the TX is meant to be unaddable to the blockchain use the individual create + add functions to // be able to mark the add TX event as something that should trigger a failure. - cryptonote::transaction create_and_add_beldex_name_system_tx(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, bns::mapping_value const &value_wallet, bns::mapping_value const &value_belnet, bns::mapping_value const &value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bool kept_by_block = false); - cryptonote::transaction create_and_add_beldex_name_system_tx_update(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, bns::mapping_value const *value_wallet, bns::mapping_value const *value_belnet, bns::mapping_value const *value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bns::generic_signature *signature = nullptr, bool kept_by_block = false); - cryptonote::transaction create_and_add_beldex_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature = nullptr, bool kept_by_block = false); + cryptonote::transaction create_and_add_beldex_name_system_tx(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, bns::mapping_value const &value_wallet, bns::mapping_value const &value_belnet, bns::mapping_value const &value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bool kept_by_block = false); + cryptonote::transaction create_and_add_beldex_name_system_tx_update(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, bns::mapping_value const *value_wallet, bns::mapping_value const *value_belnet, bns::mapping_value const *value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bns::generic_signature *signature = nullptr, bool kept_by_block = false); + cryptonote::transaction create_and_add_beldex_name_system_tx_renew(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature = nullptr, bool kept_by_block = false); cryptonote::transaction create_and_add_tx (const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false); cryptonote::transaction create_and_add_state_change_tx(master_nodes::new_state state, const crypto::public_key& pub_key, uint16_t reasons_all, uint16_t reasons_any, uint64_t height = -1, const std::vector& voters = {}, uint64_t fee = 0, bool kept_by_block = false); cryptonote::transaction create_and_add_registration_tx(const cryptonote::account_base& src, const cryptonote::keypair& sn_keys = cryptonote::keypair{hw::get_device("default")}, bool kept_by_block = false); cryptonote::transaction create_and_add_staking_tx (const crypto::public_key &pub_key, const cryptonote::account_base &src, uint64_t amount, bool kept_by_block = false); - beldex_blockchain_entry &create_and_add_next_block (const std::vector& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {}); + beldex_blockchain_entry &create_and_add_next_block (const std::vector& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr, bool can_be_added_to_blockchain = true, std::string const &fail_msg = {}); // Same as create_and_add_tx, but also adds 95kB of junk into tx_extra to bloat up the tx size. cryptonote::transaction create_and_add_big_tx(const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t junk_size = 95000, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false); @@ -1483,7 +1483,7 @@ struct beldex_chain_generator cryptonote::transaction create_tx(const cryptonote::account_base &src, const cryptonote::account_public_address &dest, uint64_t amount, uint64_t fee) const; cryptonote::transaction create_registration_tx(const cryptonote::account_base &src, const cryptonote::keypair &master_node_keys = cryptonote::keypair{hw::get_device("default")}, - uint64_t src_portions = STAKING_PORTIONS, + uint64_t src_portions = cryptonote::old::STAKING_PORTIONS, uint64_t src_operator_cut = 0, std::array const &contributors = {}, int num_contributors = 0) const; @@ -1493,20 +1493,20 @@ struct beldex_chain_generator // value: Takes the binary value NOT the human readable version, of the name->value mapping static const uint64_t ONS_AUTO_BURN = static_cast(-1); - cryptonote::transaction create_beldex_name_system_tx(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, bns::mapping_value const &value_wallet, bns::mapping_value const &value_belnet, bns::mapping_value const &value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, std::optional burn_override = std::nullopt) const; - cryptonote::transaction create_beldex_name_system_tx_update(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, bns::mapping_value const *value_wallet, bns::mapping_value const *value_belnet, bns::mapping_value const *value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bns::generic_signature *signature = nullptr, bool use_asserts = false) const; - cryptonote::transaction create_beldex_name_system_tx_update_w_extra(cryptonote::account_base const &src, uint8_t hf_version, cryptonote::tx_extra_beldex_name_system const &ons_extra) const; - cryptonote::transaction create_beldex_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature = nullptr, std::optional burn_override = std::nullopt) const; + cryptonote::transaction create_beldex_name_system_tx(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::mapping_value const &value_bchat, bns::mapping_value const &value_wallet, bns::mapping_value const &value_belnet, bns::mapping_value const &value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, std::optional burn_override = std::nullopt) const; + cryptonote::transaction create_beldex_name_system_tx_update(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_type type, std::string const &name, bns::mapping_value const *value_bchat, bns::mapping_value const *value_wallet, bns::mapping_value const *value_belnet, bns::mapping_value const *value_eth_addr, bns::generic_owner const *owner = nullptr, bns::generic_owner const *backup_owner = nullptr, bns::generic_signature *signature = nullptr, bool use_asserts = false) const; + cryptonote::transaction create_beldex_name_system_tx_update_w_extra(cryptonote::account_base const &src, cryptonote::hf hf_version, cryptonote::tx_extra_beldex_name_system const &ons_extra) const; + cryptonote::transaction create_beldex_name_system_tx_renew(cryptonote::account_base const &src, cryptonote::hf hf_version, bns::mapping_years mapping_years, std::string const &name, bns::generic_signature *signature = nullptr, std::optional burn_override = std::nullopt) const; - beldex_blockchain_entry create_genesis_block(const cryptonote::account_base &miner, uint64_t timestamp); - beldex_blockchain_entry create_next_block(const std::vector& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr); + beldex_blockchain_entry create_genesis_block(const cryptonote::account_base &miner, uint64_t timestamp); + beldex_blockchain_entry create_next_block(const std::vector& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr); bool create_block(beldex_blockchain_entry &entry, beldex_create_block_params ¶ms, const std::vector &tx_list) const; bool block_begin(beldex_blockchain_entry &entry, beldex_create_block_params ¶ms, const std::vector &tx_list) const; void block_fill_POS_data(beldex_blockchain_entry &entry, beldex_create_block_params const ¶ms, uint8_t round) const; void block_end(beldex_blockchain_entry &entry, beldex_create_block_params const ¶ms) const; - uint8_t get_hf_version_at(uint64_t height) const; + cryptonote::hf get_hf_version_at(uint64_t height) const; std::vector last_n_block_weights(uint64_t height, uint64_t num) const; const cryptonote::account_base& first_miner() const { return first_miner_; } }; diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index 1ea1e509c7e..7d505caf78f 100755 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -108,7 +108,7 @@ static void make_rct_tx(eventV& events, { txs.emplace_back(); - bool success = beldex_tx_builder(events, txs.back(), blk_head, from, to.get_keys().m_account_address, amount, cryptonote::network_version_7).build(); + bool success = beldex_tx_builder(events, txs.back(), blk_head, from, to.get_keys().m_account_address, amount, cryptonote::hf::hf7).build(); /// TODO: beter error message if (!success) throw std::exception(); events.push_back(txs.back()); @@ -117,7 +117,7 @@ static void make_rct_tx(eventV& events, /// generate 30 more blocks to unlock outputs static void rewind_blocks(test_generator& gen, eventV& events, std::vector& chain, const cryptonote::account_base& miner) { - for (auto i = 0u; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) { + for (auto i = 0u; i < cryptonote::MINED_MONEY_UNLOCK_WINDOW; ++i) { chain.emplace_back(); const auto idx = chain.size() - 1; gen.construct_block(chain[idx], chain[idx - 1], miner); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index c4719571219..5550a58078c 100755 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -233,7 +233,7 @@ int main(int argc, char* argv[]) // TODO(beldex): Tests we need to fix #if 0 - //GENERATE_AND_PLAY(gen_ring_signature_big); // Takes up to XXX hours (if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10) + //GENERATE_AND_PLAY(gen_ring_signature_big); // Takes up to XXX hours (if cryptonote::MINED_MONEY_UNLOCK_WINDOW == 10) // Transaction verification tests GENERATE_AND_PLAY(gen_tx_mixed_key_offset_not_exist); // TODO(beldex): See comment in the function diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 44ded7edf1d..6d171b81c7c 100755 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -97,7 +97,7 @@ bool gen_double_spend_base::check_double_spend(cryptonote::core& CHECK_NOT_EQ(invalid_index_value, m_invalid_block_index); std::vector blocks; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + bool r = c.get_blocks(0, 100 + 2 * cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); CHECK_TEST_CONDITION(m_last_valid_block == blocks.back()); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index c95bcd0c4e8..75eacb19332 100755 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -112,8 +112,8 @@ bool gen_uint_overflow_1::generate(std::vector& events) const { beldex_blockchain_entry entry = gen.create_next_block(); cryptonote::transaction &miner_tx = entry.block.miner_tx; - split_miner_tx_outs(miner_tx, MONEY_SUPPLY); - gen.add_block(entry, false /*can_be_added_to_blockchain*/, "We purposely overflow miner tx by MONEY_SUPPLY in the miner tx"); + split_miner_tx_outs(miner_tx, beldex::MONEY_SUPPLY); + gen.add_block(entry, false /*can_be_added_to_blockchain*/, "We purposely overflow miner tx by beldex::MONEY_SUPPLY in the miner tx"); } // Problem 2. block_reward overflow @@ -173,10 +173,10 @@ bool gen_uint_overflow_2::generate(std::vector& events) const std::vector destinations; const account_public_address& bob_addr = bob_account.get_keys().m_account_address; - destinations.push_back(tx_destination_entry(MONEY_SUPPLY, bob_addr, false)); - destinations.push_back(tx_destination_entry(MONEY_SUPPLY - 1, bob_addr, false)); + destinations.push_back(tx_destination_entry(beldex::MONEY_SUPPLY, bob_addr, false)); + destinations.push_back(tx_destination_entry(beldex::MONEY_SUPPLY - 1, bob_addr, false)); // sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + TESTS_DEFAULT_FEE - destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr, false)); + destinations.push_back(tx_destination_entry(sources.front().amount - beldex::MONEY_SUPPLY - beldex::MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr, false)); cryptonote::transaction tx_1; if (!construct_tx(miner_account.get_keys(), sources, destinations, std::nullopt, std::vector(), tx_1, 0)) @@ -191,7 +191,7 @@ bool gen_uint_overflow_2::generate(std::vector& events) const for (size_t i = 0; i < tx_1.vout.size(); ++i) { auto& tx_1_out = tx_1.vout[i]; - if (tx_1_out.amount < MONEY_SUPPLY - 1) + if (tx_1_out.amount < beldex::MONEY_SUPPLY - 1) continue; append_tx_source_entry(sources, tx_1, i); @@ -200,7 +200,7 @@ bool gen_uint_overflow_2::generate(std::vector& events) const destinations.clear(); cryptonote::tx_destination_entry de; de.addr = alice_account.get_keys().m_account_address; - de.amount = MONEY_SUPPLY - TESTS_DEFAULT_FEE; + de.amount = beldex::MONEY_SUPPLY - TESTS_DEFAULT_FEE; destinations.push_back(de); destinations.push_back(de); diff --git a/tests/core_tests/master_nodes.cpp b/tests/core_tests/master_nodes.cpp index 403e745394f..69e956c8327 100755 --- a/tests/core_tests/master_nodes.cpp +++ b/tests/core_tests/master_nodes.cpp @@ -129,7 +129,7 @@ bool gen_master_nodes::generate(std::vector &events) const const auto miner = gen.first_miner(); const auto alice = gen.create_account(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); gen.rewind_blocks_n(10); gen.rewind_blocks(); @@ -145,7 +145,7 @@ bool gen_master_nodes::generate(std::vector &events) const DO_CALLBACK(events, "check_registered"); - for (auto i = 0u; i < master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,network_version_9_master_nodes); ++i) { + for (auto i = 0u; i < master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN, hf::hf9_master_nodes); ++i) { gen.create_block(); } @@ -161,7 +161,7 @@ bool gen_master_nodes::check_registered(cryptonote::core& c, size_t ev_index, co cryptonote::account_base alice = boost::get(events[1]); std::vector blocks; - size_t count = 15 + (2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + size_t count = 15 + (2 * cryptonote::MINED_MONEY_UNLOCK_WINDOW); bool r = c.get_blocks((uint64_t)0, count, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -189,10 +189,10 @@ bool gen_master_nodes::check_expired(cryptonote::core& c, size_t ev_index, const cryptonote::account_base alice = boost::get(events[1]); - const auto stake_lock_time = master_nodes::staking_num_lock_blocks(cryptonote::FAKECHAIN,0); + const auto stake_lock_time = master_nodes::staking_num_lock_blocks(cryptonote::network_type::FAKECHAIN,0); std::vector blocks; - size_t count = 15 + (2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) + stake_lock_time; + size_t count = 15 + (2 * cryptonote::MINED_MONEY_UNLOCK_WINDOW) + stake_lock_time; bool r = c.get_blocks((uint64_t)0, count, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -227,7 +227,7 @@ bool test_prefer_deregisters::generate(std::vector &events) const auto miner = gen.first_miner(); const auto alice = gen.create_account(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// give miner some outputs to spend and unlock them gen.rewind_blocks_n(60); @@ -311,7 +311,7 @@ bool test_zero_fee_deregister::generate(std::vector &events) gen.create_genesis_block(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// give miner some outputs to spend and unlock them gen.rewind_blocks_n(20); @@ -354,7 +354,7 @@ bool test_deregister_safety_buffer::generate(std::vector &even const auto miner = gen.first_miner(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// give miner some outputs to spend and unlock them gen.rewind_blocks_n(40); @@ -462,7 +462,7 @@ bool test_deregisters_on_split::generate(std::vector &events) linear_chain_generator gen(events, test_options.hard_forks); gen.create_genesis_block(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// generate some outputs and unlock them gen.rewind_blocks_n(20); @@ -565,7 +565,7 @@ bool deregister_too_old::generate(std::vector& events) linear_chain_generator gen(events, test_options.hard_forks); gen.create_genesis_block(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// generate some outputs and unlock them gen.rewind_blocks_n(20); @@ -610,7 +610,7 @@ bool mn_test_rollback::generate(std::vector& events) linear_chain_generator gen(events, test_options.hard_forks); gen.create_genesis_block(); - gen.rewind_until_version(network_version_9_master_nodes); + gen.rewind_until_version(hf::hf9_master_nodes); /// generate some outputs and unlock them gen.rewind_blocks_n(20); @@ -755,12 +755,12 @@ bool test_swarms_basic::generate(std::vector& events) /// create a few blocks with active master nodes gen.rewind_blocks_n(5); - if (gen.get_hf_version() != network_version_9_master_nodes) { + if (gen.get_hf_version() != hf::hf9_master_nodes) { std::cerr << "wrong hf version\n"; return false; } - gen.rewind_until_version(network_version_10_bulletproofs); + gen.rewind_until_version(hf::hf10_bulletproofs); /// test that we now have swarms DO_CALLBACK(events, "test_initial_swarms"); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index ce7123c0766..7b724a84cfc 100755 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -164,7 +164,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vectortimestamp + tools::to_seconds((block_major>=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + block_major, block_minor, prev_block->timestamp + tools::to_seconds((block_major >= cryptonote::hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -190,12 +190,12 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector=cryptonote::network_version_17_POS?TARGET_BLOCK_TIME:TARGET_BLOCK_TIME_OLD)) * 2, // v2 has blocks twice as long + block_major, block_minor, blk_last.timestamp + tools::to_seconds((block_major >= cryptonote::hf::hf17_POS ? cryptonote::TARGET_BLOCK_TIME : cryptonote::old::TARGET_BLOCK_TIME_12)) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blk); @@ -368,7 +368,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector additional_tx_secret_keys; auto sources_copy = sources; beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_8; + tx_params.hf_version = cryptonote::hf::hf8; r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, std::nullopt, std::vector(), tx, 0, tx_key, additional_tx_secret_keys, { rct::RangeProofType::PaddedBulletproof, 3 }, msoutp, tx_params); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); @@ -483,167 +483,167 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 2, 1, {2}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 2, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_22_1_2_many_inputs::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 4, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 2, 1, {2}, NULL, NULL); + return generate_with(events, 4, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 2, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_22_2_1::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 2, 2, {1}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 2, 2, {1}, NULL, NULL); } bool gen_multisig_tx_valid_33_1_23::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 3, 3, 1, {2, 3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 3, 3, 1, {2, 3}, NULL, NULL); } bool gen_multisig_tx_valid_33_3_21::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 3, 3, 3, {2, 1}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 3, 3, 3, {2, 1}, NULL, NULL); } bool gen_multisig_tx_valid_23_1_2::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 3, 1, {2}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 3, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_23_1_3::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 3, 1, {3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 3, 1, {3}, NULL, NULL); } bool gen_multisig_tx_valid_23_2_1::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 3, 2, {1}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 3, 2, {1}, NULL, NULL); } bool gen_multisig_tx_valid_23_2_3::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 3, 2, {3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 3, 2, {3}, NULL, NULL); } bool gen_multisig_tx_valid_45_1_234::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 4, 5, 1, {2, 3, 4}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 4, 5, 1, {2, 3, 4}, NULL, NULL); } bool gen_multisig_tx_valid_45_4_135_many_inputs::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 4, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 4, 5, 4, {1, 3, 5}, NULL, NULL); + return generate_with(events, 4, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 4, 5, 4, {1, 3, 5}, NULL, NULL); } bool gen_multisig_tx_valid_89_3_1245789::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 8, 9, 3, {1, 2, 4, 5, 7, 8, 9}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 8, 9, 3, {1, 2, 4, 5, 7, 8, 9}, NULL, NULL); } bool gen_multisig_tx_valid_24_1_2::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_24_1_2_many_inputs::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 4, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); + return generate_with(events, 4, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_25_1_2::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_25_1_2_many_inputs::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 4, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); + return generate_with(events, 4, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); } bool gen_multisig_tx_valid_48_1_234::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); } bool gen_multisig_tx_valid_48_1_234_many_inputs::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 4, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); + return generate_with(events, 4, cryptonote::TX_OUTPUT_DECOYS, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); } bool gen_multisig_tx_invalid_22_1__no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 2, 2, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 2, 2, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_33_1__no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 3, 3, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 3, 3, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_33_1_2_no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 3, 3, 1, {2}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 3, 3, 1, {2}, NULL, NULL); } bool gen_multisig_tx_invalid_33_1_3_no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 3, 3, 1, {3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 3, 3, 1, {3}, NULL, NULL); } bool gen_multisig_tx_invalid_23_1__no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 2, 3, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 2, 3, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_45_5_23_no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 4, 5, 5, {2, 3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 4, 5, 5, {2, 3}, NULL, NULL); } bool gen_multisig_tx_invalid_24_1_no_signers::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 2, 4, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 2, 4, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_25_1_no_signers::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 2, 5, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 2, 5, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_48_1_no_signers::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 4, 8, 1, {}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 4, 8, 1, {}, NULL, NULL); } bool gen_multisig_tx_invalid_48_1_23_no_threshold::generate(std::vector& events) const { const uint64_t amount_paid = 10000; - return generate_with(events, 2, CRYPTONOTE_DEFAULT_TX_MIXIN, amount_paid, false, 4, 8, 1, {2, 3}, NULL, NULL); + return generate_with(events, 2, cryptonote::TX_OUTPUT_DECOYS, amount_paid, false, 4, 8, 1, {2, 3}, NULL, NULL); } diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index c80f9cad511..8c10a6520eb 100755 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -82,7 +82,7 @@ struct gen_multisig_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::vector hard_forks = {{7,0,0,0}}; + const std::vector hard_forks = {{cryptonote::hf::hf7,0,0,0}}; const cryptonote::test_options test_options = { hard_forks, 0 }; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 03b048fa8d1..89f17f19669 100755 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -42,7 +42,7 @@ using namespace cryptonote; // Tests bool gen_rct_tx_validation_base::generate_with_full(std::vector& events, - const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid, + const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, cryptonote::hf last_version, const rct::RCTConfig &rct_config, bool valid, const std::function &sources, std::vector &destinations)> &pre_tx, const std::function &post_tx) const { @@ -59,7 +59,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vectortimestamp + tools::to_seconds(TARGET_BLOCK_TIME_OLD) * 2, // v2 has blocks twice as long + hf::hf1, 2, prev_block->timestamp + tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -70,12 +70,12 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector(), 0), false, "Failed to generate block"); events.push_back(blk); @@ -122,7 +122,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector subaddresses; subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0}; beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_8; + tx_params.hf_version = cryptonote::hf::hf8; bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::tx_destination_entry{}, std::vector(), rct_txes[n], 0, tx_key, additional_tx_keys, {}, nullptr, tx_params); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); events.push_back(rct_txes[n]); @@ -144,7 +144,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector(last_version), blk_last.timestamp + tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blk); @@ -228,7 +228,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector subaddresses; subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0}; beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_8; + tx_params.hf_version = cryptonote::hf::hf8; bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::tx_destination_entry{}, std::vector(), tx, 0, tx_key, additional_tx_keys, rct_config, nullptr, tx_params); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); @@ -249,7 +249,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev const std::function &post_tx) const { const rct::RCTConfig rct_config { rct::RangeProofType::Borromean, 0 }; - return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx); + return generate_with_full(events, out_idx, mixin, amount_paid, cryptonote::old::DEFAULT_TX_SPENDABLE_AGE, hf::hf15_flash, rct_config, valid, pre_tx, post_tx); } bool gen_rct_tx_valid_from_pre_rct::generate(std::vector& events) const @@ -528,5 +528,5 @@ bool gen_rct_tx_uses_output_too_early::generate(std::vector& e const int out_idx[] = {1, -1}; const uint64_t amount_paid = 10000; const rct::RCTConfig rct_config { rct::RangeProofType::PaddedBulletproof, 2 }; - return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-1, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL); + return generate_with_full(events, out_idx, mixin, amount_paid, cryptonote::old::DEFAULT_TX_SPENDABLE_AGE-1, cryptonote::feature::ENFORCE_MIN_AGE, rct_config, false, NULL, NULL); } diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index d1b08856173..77152bfd59b 100755 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -70,7 +70,7 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base } bool generate_with_full(std::vector& events, const int *out_idx, int mixin, - uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid, + uint64_t amount_paid, size_t second_rewind, cryptonote::hf last_version, const rct::RCTConfig &rct_config, bool valid, const std::function &sources, std::vector &destinations)> &pre_tx, const std::function &post_tx) const; bool generate_with(std::vector& events, const int *out_idx, int mixin, @@ -85,7 +85,7 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::vector hard_forks = {{1,0,0,0}, {2,0,1,0}, {4,0,65,0}}; + const std::vector hard_forks = {{cryptonote::hf::hf7,0,0,0}, {cryptonote::hf::hf8,0,1,0}, {cryptonote::hf::hf9_master_nodes,0,65,0}}; const cryptonote::test_options test_options = { hard_forks, 0 }; @@ -271,7 +271,7 @@ struct gen_rct_tx_uses_output_too_early : public gen_rct_tx_validation_base bool generate(std::vector& events) const; }; template<> struct get_test_options { - const std::vector hard_forks = {{1,0,0,0}, {2,0,1,0}, {4,0,65,0}, {12,0,69,0}, {0,0,0,0}}; + const std::vector hard_forks = {{cryptonote::hf::hf7,0,0,0}, {cryptonote::hf::hf8,0,1,0}, {cryptonote::hf::hf9_master_nodes,0,65,0}, {cryptonote::hf::hf12_security_signature,0,69,0}, {cryptonote::hf::none,0,0,0}}; const cryptonote::test_options test_options = { hard_forks, 0 }; diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index 5fadc6096f9..bd0c6acf376 100755 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -187,7 +187,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index m_alice_account = var::get(events[2]); std::vector blocks; - bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + bool r = c.get_blocks(0, 100 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -205,7 +205,7 @@ bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2"); std::vector blocks; - bool r = c.get_blocks(0, 100 + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + bool r = c.get_blocks(0, 100 + 3 * cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -241,7 +241,7 @@ bool gen_ring_signature_big::generate(std::vector& events) con { std::vector accounts(m_test_size); std::vector blocks; - blocks.reserve(m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + blocks.reserve(m_test_size + cryptonote::MINED_MONEY_UNLOCK_WINDOW); uint64_t ts_start = 1338224400; GENERATE_ACCOUNT(miner_account); @@ -265,7 +265,7 @@ bool gen_ring_signature_big::generate(std::vector& events) con for (size_t i = 0; i < m_test_size; ++i) { - block blk_with_unlocked_out = blocks[blocks.size() - 1 - CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; + block blk_with_unlocked_out = blocks[blocks.size() - 1 - cryptonote::MINED_MONEY_UNLOCK_WINDOW]; MAKE_TX_LIST_START(events, txs_blk_i, miner_account, accounts[i], m_tx_amount, blk_with_unlocked_out); for (size_t j = 0; j <= i; ++j) { @@ -297,7 +297,7 @@ bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_ind m_alice_account = var::get(events[1 + m_test_size]); std::vector blocks; - bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + bool r = c.get_blocks(0, 2 * m_test_size + cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); std::vector chain; @@ -322,7 +322,7 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_2"); std::vector blocks; - bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + bool r = c.get_blocks(0, 2 * m_test_size + cryptonote::MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); std::vector chain; diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index ed376a905b8..7cc0b0d8c75 100755 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -194,43 +194,43 @@ bool gen_tx_unlock_time::generate(std::vector& events) const transaction tx = {}; uint64_t unlock_time = 0; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = get_block_height(blk_money_unlocked) - 1; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = get_block_height(blk_money_unlocked); - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = get_block_height(blk_money_unlocked) + 1; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = get_block_height(blk_money_unlocked) + 2; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = ts_start - 1; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); tx = {}; unlock_time = time(0) + 60 * 60; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_unlock_time(unlock_time).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_unlock_time(unlock_time).build(); events.push_back(tx); txs_0.push_back(tx); @@ -301,7 +301,7 @@ bool gen_tx_no_inputs_has_outputs::generate(std::vector& event REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); tx.vin.clear(); DO_CALLBACK(events, "mark_invalid_tx"); @@ -319,7 +319,7 @@ bool gen_tx_has_inputs_no_outputs::generate(std::vector& event REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); tx.vout.clear(); DO_CALLBACK(events, "mark_invalid_tx"); // NOTE(beldex): This used to be valid in Monero pre RCT, but not anymore with our transactions because we start with RCT type TXs @@ -339,7 +339,7 @@ bool gen_tx_invalid_input_amount::generate(std::vector& events std::vector sources; std::vector destinations; uint64_t change_amount; - fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_DEFAULT_TX_MIXIN, sources, destinations, &change_amount); + fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, cryptonote::TX_OUTPUT_DECOYS, sources, destinations, &change_amount); sources.front().amount++; transaction tx = {}; @@ -362,10 +362,10 @@ bool gen_tx_input_wo_key_offsets::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_DEFAULT_TX_MIXIN, sources, destinations); + fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, cryptonote::TX_OUTPUT_DECOYS, sources, destinations); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); txin_to_key& in_to_key = var::get(tx.vin.front()); while (!in_to_key.key_offsets.empty()) in_to_key.key_offsets.pop_back(); @@ -392,11 +392,11 @@ bool gen_tx_key_offset_points_to_foreign_key::generate(std::vector sources_alice; std::vector destinations_alice; - fill_tx_sources_and_destinations(events, blk_money_unlocked, alice_account, get_address(miner_account), MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, CRYPTONOTE_DEFAULT_TX_MIXIN, sources_alice, destinations_alice); + fill_tx_sources_and_destinations(events, blk_money_unlocked, alice_account, get_address(miner_account), MK_COINS(15) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, cryptonote::TX_OUTPUT_DECOYS, sources_alice, destinations_alice); txin_to_key& bob_in_to_key = var::get(bob_tx.vin.front()); bob_in_to_key.key_offsets.front() = sources_alice.front().outputs.back().first; @@ -423,7 +423,7 @@ bool gen_tx_sender_key_offset_not_exist::generate(std::vector& REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); txin_to_key& in_to_key = var::get(tx.vin.front()); in_to_key.key_offsets.front() = std::numeric_limits::max(); @@ -489,12 +489,12 @@ bool gen_tx_mixed_key_offset_not_exist::generate(std::vector& std::vector sources; std::vector destinations; uint64_t change_amount; - fill_tx_sources_and_destinations(events, blk_money_unlocked, bob_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_DEFAULT_TX_MIXIN, sources, destinations, &change_amount); + fill_tx_sources_and_destinations(events, blk_money_unlocked, bob_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, cryptonote::TX_OUTPUT_DECOYS, sources, destinations, &change_amount); sources.front().outputs[(sources.front().real_output + 1) % 2].first = std::numeric_limits::max(); transaction tx = {}; cryptonote::tx_destination_entry change_addr{ change_amount, miner_account.get_keys().m_account_address, false /*is_subaddress*/ }; - assert(cryptonote::construct_tx(miner_account.get_keys(), sources, destinations, change_addr, {} /*tx_extra*/, tx, 0 /*unlock_time*/, cryptonote::network_version_7)); + assert(cryptonote::construct_tx(miner_account.get_keys(), sources, destinations, change_addr, {} /*tx_extra*/, tx, 0 /*unlock_time*/, cryptonote::hf::hf7)); DO_CALLBACK(events, "mark_invalid_tx"); events.push_back(tx); @@ -511,7 +511,7 @@ bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector(tx.vin.front()); // Use fake key image @@ -539,7 +539,7 @@ bool gen_tx_key_image_is_invalid::generate(std::vector& events REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); txin_to_key& in_to_key = var::get(tx.vin.front()); in_to_key.k_image = generate_invalid_key_image(); @@ -623,7 +623,7 @@ bool gen_tx_txout_to_key_has_invalid_key::generate(std::vector REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); txout_to_key& out_to_key = var::get(tx.vout.front().target); out_to_key.key = generate_invalid_pub_key(); @@ -648,7 +648,7 @@ bool gen_tx_output_with_zero_amount::generate(std::vector& eve std::vector sources; std::vector destinations; uint64_t change_amount; - fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, CRYPTONOTE_DEFAULT_TX_MIXIN, sources, destinations, &change_amount); + fill_tx_sources_and_destinations(events, blk_money_unlocked, miner_account, get_address(miner_account), MK_COINS(1), TESTS_DEFAULT_FEE, cryptonote::TX_OUTPUT_DECOYS, sources, destinations, &change_amount); for (tx_destination_entry &entry : destinations) entry.amount = 0; @@ -659,7 +659,7 @@ bool gen_tx_output_with_zero_amount::generate(std::vector& eve #else transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); tx.vout.front().amount = 0; #endif @@ -678,14 +678,14 @@ bool gen_tx_output_is_not_txout_to_key::generate(std::vector& REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); tx.vout.back().target = txout_to_script(); DO_CALLBACK(events, "mark_invalid_tx"); events.push_back(tx); tx = {}; - beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).build(); + beldex_tx_builder(events, tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).build(); tx.vout.back().target = txout_to_scripthash(); DO_CALLBACK(events, "mark_invalid_tx"); @@ -710,7 +710,7 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even REWIND_BLOCKS (events, blk_head, blk_money_unlocked, miner_account); transaction miner_tx = {}; - beldex_tx_builder(events, miner_tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(60), cryptonote::network_version_7).with_fee(TESTS_DEFAULT_FEE).build(); + beldex_tx_builder(events, miner_tx, blk_money_unlocked, miner_account, miner_account.get_keys().m_account_address, MK_COINS(60), cryptonote::hf::hf7).with_fee(TESTS_DEFAULT_FEE).build(); // TX without signatures DO_CALLBACK(events, "mark_invalid_tx"); @@ -730,7 +730,7 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even events.push_back(serialized_transaction(sr_tx)); transaction bob_tx = {}; - beldex_tx_builder(events, bob_tx, blk_money_unlocked, bob_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::network_version_7).with_fee(TESTS_DEFAULT_FEE).build(); + beldex_tx_builder(events, bob_tx, blk_money_unlocked, bob_account, miner_account.get_keys().m_account_address, MK_COINS(1), cryptonote::hf::hf7).with_fee(TESTS_DEFAULT_FEE).build(); // TX without signatures DO_CALLBACK(events, "mark_invalid_tx"); diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index b6472e69931..3480ae531f4 100755 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -54,7 +54,7 @@ bool gen_v2_tx_validation_base::generate_with(std::vector& eve miner_accounts[n].generate(); CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp, - 2, 2, prev_block->timestamp + tools::to_seconds(TARGET_BLOCK_TIME_OLD) * 2, // v2 has blocks twice as long + cryptonote::hf::hf1, 2, prev_block->timestamp + tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -65,12 +65,12 @@ bool gen_v2_tx_validation_base::generate_with(std::vector& eve cryptonote::block blk_r; { cryptonote::block blk_last = blocks[3]; - for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + for (size_t i = 0; i < cryptonote::MINED_MONEY_UNLOCK_WINDOW; ++i) { cryptonote::block blk; CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp, - 2, 2, blk_last.timestamp + tools::to_seconds(TARGET_BLOCK_TIME_OLD) * 2, // v2 has blocks twice as long + cryptonote::hf::hf1, 2, blk_last.timestamp + tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12) * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0), false, "Failed to generate block"); events.push_back(blk); diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index 14f75923a3f..bbdb42898e1 100755 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -79,7 +79,7 @@ struct gen_v2_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::vector hard_forks = {{1,0,0,0}, {2,0,0,0}}; + const std::vector hard_forks = {{cryptonote::hf::hf7,0,0,0}, {cryptonote::hf::hf8,0,0,0}}; const cryptonote::test_options test_options = { hard_forks, 0 }; diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp index 74437d60e3e..4e27ae9cc22 100755 --- a/tests/core_tests/wallet_tools.cpp +++ b/tests/core_tests/wallet_tools.cpp @@ -105,7 +105,7 @@ bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector cur_height) + if (td.m_block_height + cryptonote::MINED_MONEY_UNLOCK_WINDOW > cur_height) continue; if (selected_idx.find((size_t)i) != selected_idx.end()){ MERROR("Should not happen (selected_idx not found): " << i); diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 2726938777a..72b35201512 100755 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -58,17 +58,17 @@ int main(int argc, char *argv[]) { size_t n = 0; while (data >> timestamp >> difficulty) { size_t begin, end; - if (n < (DIFFICULTY_WINDOW + 1) + DIFFICULTY_LAG) { + if (n < ( cryptonote::old::DIFFICULTY_WINDOW + 1) + DIFFICULTY_LAG) { begin = 0; - end = std::min(n, (size_t) (DIFFICULTY_WINDOW + 1)); + end = std::min(n, (size_t) ( cryptonote::old::DIFFICULTY_WINDOW + 1)); } else { end = n - DIFFICULTY_LAG; - begin = end - (DIFFICULTY_WINDOW + 1); + begin = end - ( cryptonote::old::DIFFICULTY_WINDOW + 1); } uint64_t res = cryptonote::next_difficulty_v2( std::vector(timestamps.begin() + begin, timestamps.begin() + end), std::vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), - tools::to_seconds(TARGET_BLOCK_TIME_OLD), //use old Blocktime + tools::to_seconds(cryptonote::old::TARGET_BLOCK_TIME_12), //use old Blocktime cryptonote::difficulty_calc_mode::normal); if (res != difficulty) { std::cerr << "Wrong difficulty for block " << n diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 06c37bd65a4..837a6d5a144 100755 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -268,12 +268,12 @@ bool transactions_flow_test(std::string& working_folder, LOG_PRINT_L0( "waiting some new blocks..."); - std::this_thread::sleep_for(TARGET_BLOCK_TIME_OLD*20*1s);//wait two blocks before sync on another wallet on another daemon + std::this_thread::sleep_for(TARGET_BLOCK_TIME_12*20*1s);//wait two blocks before sync on another wallet on another daemon LOG_PRINT_L0( "refreshing..."); bool recvd_money = false; while(w2.refresh(true, blocks_fetched, recvd_money, ok) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) ) { - std::this_thread::sleep_for(TARGET_BLOCK_TIME_OLD*1s);//wait two blocks before sync on another wallet on another daemon + std::this_thread::sleep_for(TARGET_BLOCK_TIME_12*1s);//wait two blocks before sync on another wallet on another daemon } uint64_t money_2 = w2.balance(0, true); diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 6c305755890..629bbcd5e36 100755 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -192,7 +192,7 @@ namespace m_thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2); m_tcp_server.get_config_object().set_handler(&m_commands_handler); - m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; + m_tcp_server.get_config_object().m_invoke_timeout = 1ms * CONNECTION_TIMEOUT; ASSERT_TRUE(m_tcp_server.init_server(clt_port, "127.0.0.1")); ASSERT_TRUE(m_tcp_server.run_server(m_thread_count, false)); diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 13a55cb2632..a6294e4cdb4 100755 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -229,7 +229,7 @@ int main(int argc, char** argv) srv_levin_commands_handler *commands_handler = new srv_levin_commands_handler(tcp_server); tcp_server.get_config_object().set_handler(commands_handler, [](epee::levin::levin_commands_handler *handler) { delete handler; }); - tcp_server.get_config_object().m_invoke_timeout = 10000; + tcp_server.get_config_object().m_invoke_timeout = 1s; //tcp_server.get_config_object().m_max_packet_size = max_packet_size; if (!tcp_server.run_server(thread_count, true)) diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index d0aeb790dec..ca3991a624e 100755 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -72,7 +72,7 @@ class test_check_tx_signature : private multi_tx_test_base std::unordered_map subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_count - 1; + tx_params.hf_version = cryptonote::hf::hf20_bulletproof_plus; rct::RCTConfig rct_config{range_proof_type, bp_version}; if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::tx_destination_entry{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct_config, nullptr, tx_params)) return false; @@ -136,7 +136,7 @@ class test_check_tx_signature_aggregated_bulletproofs : private multi_tx_test_ba subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_count - 1; + tx_params.hf_version = cryptonote::hf::hf20_bulletproof_plus; m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0)); for (size_t n = 0; n < a_num_txes; ++n) { diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 93a7668bbe0..1b712c10a4d 100755 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -75,7 +75,7 @@ class test_construct_tx : private multi_tx_test_base subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; rct::RCTConfig rct_config{range_proof_type, bp_version}; cryptonote::beldex_construct_tx_params tx_params; - tx_params.hf_version = cryptonote::network_version_count - 1; + tx_params.hf_version = cryptonote::hf::hf20_bulletproof_plus; return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::tx_destination_entry{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct_config, nullptr, tx_params); } diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index f63fd48c5de..df010dd7ef6 100755 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -741,7 +741,7 @@ bool gen_trezor_base::generate(std::vector& events) const crypto::hash prev_id = get_block_hash(blk_gen); const uint64_t already_generated_coins = generator.get_already_generated_coins(prev_id); block_weights.clear(); - generator.get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + generator.get_last_n_block_weights(block_weights, prev_id, cryptonote::REWARD_BLOCKS_WINDOW); generator.construct_block(blk_0, 1, prev_id, m_miner_account, m_ts_start, already_generated_coins, block_weights, tx_list); } diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 88d47fa21ee..10de07b15c4 100755 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -474,14 +474,14 @@ TEST(get_account_address_as_str, works_correctly) { cryptonote::account_public_address addr; ASSERT_NO_THROW(serialization::parse_binary(test_serialized_keys, addr)); - std::string addr_str = cryptonote::get_account_address_as_str(cryptonote::MAINNET, false, addr); + std::string addr_str = cryptonote::get_account_address_as_str(cryptonote::network_type::MAINNET, false, addr); ASSERT_EQ(addr_str, test_keys_addr_str); } TEST(get_account_address_from_str, handles_valid_address) { cryptonote::address_parse_info info; - ASSERT_TRUE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, test_keys_addr_str)); + ASSERT_TRUE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, test_keys_addr_str)); std::string blob; ASSERT_NO_THROW(blob = serialization::dump_binary(info.address)); @@ -494,7 +494,7 @@ TEST(get_account_address_from_str, fails_on_invalid_address_format) std::string addr_str = test_keys_addr_str; addr_str[0] = '0'; - ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, addr_str)); + ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, addr_str)); } TEST(get_account_address_from_str, fails_on_invalid_address_prefix) @@ -502,33 +502,33 @@ TEST(get_account_address_from_str, fails_on_invalid_address_prefix) std::string addr_str = base58::encode_addr(0, test_serialized_keys); cryptonote::address_parse_info info; - ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, addr_str)); + ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, addr_str)); } TEST(get_account_address_from_str, fails_on_invalid_address_content) { - std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1)); + std::string addr_str = base58::encode_addr(cryptonote::config::PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1)); cryptonote::address_parse_info info; - ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, addr_str)); + ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, addr_str)); } TEST(get_account_address_from_str, fails_on_invalid_address_spend_key) { std::string serialized_keys_copy = test_serialized_keys; serialized_keys_copy[0] = '\0'; - std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); + std::string addr_str = base58::encode_addr(cryptonote::config::PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); cryptonote::address_parse_info info; - ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, addr_str)); + ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, addr_str)); } TEST(get_account_address_from_str, fails_on_invalid_address_view_key) { std::string serialized_keys_copy = test_serialized_keys; serialized_keys_copy.back() = '\0'; - std::string addr_str = base58::encode_addr(config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); + std::string addr_str = base58::encode_addr(cryptonote::config::PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); cryptonote::address_parse_info info; - ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, addr_str)); + ASSERT_FALSE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::MAINNET, addr_str)); } diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 796225ec8f2..73968243e58 100755 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -41,7 +41,7 @@ namespace class block_reward_and_already_generated_coins : public ::testing::Test { protected: - static const size_t current_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; + static const size_t current_block_weight = cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; union { @@ -53,12 +53,12 @@ namespace }; #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ - m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward, m_block_reward_unpenalized, 7,0); \ + m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward, m_block_reward_unpenalized, cryptonote::hf::hf7, 0); \ ASSERT_TRUE(m_block_not_too_big); \ ASSERT_EQ(m_block_reward, expected_reward); #define TEST_ALREADY_GENERATED_COINS_V2(already_generated_coins, expected_reward, h) \ - m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward, m_block_reward_unpenalized, 8,h); \ + m_block_not_too_big = get_base_block_reward(0, current_block_weight, already_generated_coins, m_block_reward, m_block_reward_unpenalized, cryptonote::hf::hf8, h); \ ASSERT_TRUE(m_block_not_too_big); \ ASSERT_EQ(m_block_reward, expected_reward); @@ -87,9 +87,9 @@ namespace TEST_F(block_reward_and_already_generated_coins, handles_max) { - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((1 << 20) + 1), UINT64_C(0)); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - (1 << 20) , UINT64_C(0)); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((1 << 20) - 1), UINT64_C(0)); + TEST_ALREADY_GENERATED_COINS(beldex::MONEY_SUPPLY - ((1 << 20) + 1), UINT64_C(0)); + TEST_ALREADY_GENERATED_COINS(beldex::MONEY_SUPPLY - (1 << 20) , UINT64_C(0)); + TEST_ALREADY_GENERATED_COINS(beldex::MONEY_SUPPLY - ((1 << 20) - 1), UINT64_C(0)); } TEST_F(block_reward_and_already_generated_coins, reward_parity_between_orig_and_beldex_algo) @@ -99,24 +99,24 @@ namespace cryptonote::block_reward_parts reward_parts_v7 = {}; cryptonote::beldex_block_reward_context reward_context_v7 = {}; - m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, 7, reward_parts_v7, reward_context_v7); + m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, cryptonote::hf::hf7, reward_parts_v7, reward_context_v7); cryptonote::block_reward_parts reward_parts_v8 = {}; cryptonote::beldex_block_reward_context reward_context_v8 = {}; - m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, 8, reward_parts_v8, reward_context_v8); + m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, cryptonote::hf::hf8, reward_parts_v8, reward_context_v8); cryptonote::block_reward_parts reward_parts_v9 = {}; cryptonote::beldex_block_reward_context reward_context_v9 = {}; - m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, 9, reward_parts_v9, reward_context_v9); + m_block_reward_calc_success &= get_beldex_block_reward(0, current_block_weight, already_generated_coins, cryptonote::hf::hf9_master_nodes, reward_parts_v9, reward_context_v9); uint64_t reward_v7_orig = 0, reward_unpenalized_unused_; - m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v7_orig, reward_unpenalized_unused_, 7, 0); + m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v7_orig, reward_unpenalized_unused_, cryptonote::hf::hf7, 0); uint64_t reward_v8_orig = 0; - m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v8_orig, reward_unpenalized_unused_, 8, 0); + m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v8_orig, reward_unpenalized_unused_, cryptonote::hf::hf8, 0); uint64_t reward_v9_orig = 0; - m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v9_orig, reward_unpenalized_unused_, 9, 0); + m_block_reward_calc_success &= cryptonote::get_base_block_reward(0, current_block_weight, already_generated_coins, reward_v9_orig, reward_unpenalized_unused_, cryptonote::hf::hf9_master_nodes, 0); ASSERT_TRUE(m_block_reward_calc_success); \ ASSERT_EQ(reward_parts_v7.original_base_reward, reward_v7_orig); @@ -125,5 +125,5 @@ namespace } //-------------------------------------------------------------------------------------------------------------------- - // TODO(doyle): The following tests, test using CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 which is not relevant to us. + // TODO(doyle): The following tests, test using cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V1 which is not relevant to us. } diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 3387012ce2e..a64f85f1515 100755 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include @@ -460,9 +460,9 @@ TEST(NetUtils, IPv4NetworkAddress) { static_assert(epee::net_utils::ipv4_network_address::get_type_id() == epee::net_utils::address_type::ipv4, "bad ipv4 type id"); - const auto ip1 = boost::endian::native_to_big(0x330012FFu); - const auto ip_loopback = boost::endian::native_to_big(0x7F000001u); - const auto ip_local = boost::endian::native_to_big(0x0A000000u); + const auto ip1 = oxenc::host_to_big(0x330012FFu); + const auto ip_loopback = oxenc::host_to_big(0x7F000001u); + const auto ip_local = oxenc::host_to_big(0x0A000000u); epee::net_utils::ipv4_network_address address1{ip1, 65535}; CHECK_EQUAL(address1, address1); @@ -527,9 +527,9 @@ TEST(NetUtils, IPv4NetworkAddress) TEST(NetUtils, NetworkAddress) { - const auto ip1 = boost::endian::native_to_big(0x330012FFu); - const auto ip_loopback = boost::endian::native_to_big(0x7F000001u); - const auto ip_local = boost::endian::native_to_big(0x0A000000u); + const auto ip1 = oxenc::host_to_big(0x330012FFu); + const auto ip_loopback = oxenc::host_to_big(0x7F000001u); + const auto ip_local = oxenc::host_to_big(0x0A000000u); struct custom_address { constexpr static bool equal(const custom_address&) noexcept { return false; } diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp index a50525cc474..a0be561cd31 100755 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -111,7 +111,7 @@ static uint32_t lcg() #define PREFIX_WINDOW(hf_version,window) \ blockchain_objects_t bc_objects = {}; \ const std::vector hard_forks{ \ - {7,0,0,0}, {hf_version,0,1,0}}; \ + {cryptonote::hf::hf7,0,0,0}, {hf_version,0,1,0}}; \ const cryptonote::test_options test_options = { \ hard_forks, \ window, \ @@ -124,21 +124,21 @@ static uint32_t lcg() TEST(long_term_block_weight, empty_short) { - PREFIX(9); + PREFIX(cryptonote::hf::hf9_master_nodes); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); - ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5); - ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * 2); + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * 2); } TEST(long_term_block_weight, identical_before_fork) { - PREFIX(9); + PREFIX(cryptonote::hf::hf9_master_nodes); for (uint64_t h = 1; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) { - size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = h < cryptonote::REWARD_BLOCKS_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -151,11 +151,11 @@ TEST(long_term_block_weight, identical_before_fork) TEST(long_term_block_weight, identical_after_fork_before_long_term_window) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) { - size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -168,11 +168,11 @@ TEST(long_term_block_weight, identical_after_fork_before_long_term_window) TEST(long_term_block_weight, ceiling_at_30000000) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 2 - 1; ++h) { - size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -183,11 +183,11 @@ TEST(long_term_block_weight, ceiling_at_30000000) TEST(long_term_block_weight, multi_pop) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + 20; ++h) { - size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -217,11 +217,11 @@ TEST(long_term_block_weight, multi_pop) TEST(long_term_block_weight, multiple_updates) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 1; h <= 3 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) { - size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -241,11 +241,11 @@ TEST(long_term_block_weight, multiple_updates) TEST(long_term_block_weight, pop_invariant_max) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) { - size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -273,7 +273,7 @@ TEST(long_term_block_weight, pop_invariant_max) } for (int i = 0; i < add; ++i) { - size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -289,13 +289,13 @@ TEST(long_term_block_weight, pop_invariant_max) TEST(long_term_block_weight, pop_invariant_random) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); for (uint64_t h = 1; h < 2 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) { lcg_seed = bc->get_db().height(); uint32_t r = lcg(); - size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -330,7 +330,7 @@ TEST(long_term_block_weight, pop_invariant_random) { lcg_seed = bc->get_db().height(); uint32_t r = lcg(); - size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -351,14 +351,14 @@ TEST(long_term_block_weight, pop_invariant_random) TEST(long_term_block_weight, long_growth_spike_and_drop) { - PREFIX(HF_VERSION_LONG_TERM_BLOCK_WEIGHT); + PREFIX(cryptonote::feature::LONG_TERM_BLOCK_WEIGHT); uint64_t long_term_effective_median_block_weight; // constant init for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) { - size_t w = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + size_t w = cryptonote::BLOCK_GRANTED_FULL_REWARD_ZONE_V5; uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); diff --git a/tests/unit_tests/master_nodes.cpp b/tests/unit_tests/master_nodes.cpp index 1b704f2825a..5cac7129e41 100755 --- a/tests/unit_tests/master_nodes.cpp +++ b/tests/unit_tests/master_nodes.cpp @@ -39,14 +39,14 @@ TEST(master_nodes, staking_requirement) { // NOTE: Thanks for the values @Sonofotis - const uint64_t atomic_epsilon = config::DEFAULT_DUST_THRESHOLD; + const uint64_t atomic_epsilon = cryptonote::config::DEFAULT_DUST_THRESHOLD; // LHS of Equation // Try underflow { uint64_t height = 100; uint64_t mainnet_requirement = master_nodes::get_staking_requirement(height); - ASSERT_EQ(mainnet_requirement, (45000 * COIN)); + ASSERT_EQ(mainnet_requirement, (45000 * beldex::COIN)); } // Starting height for mainnet @@ -56,14 +56,14 @@ TEST(master_nodes, staking_requirement) uint64_t height = 101250; int64_t mainnet_requirement = (int64_t)master_nodes::get_staking_requirement(height); - ASSERT_EQ(mainnet_requirement, (45000 * COIN)); + ASSERT_EQ(mainnet_requirement, (45000 * beldex::COIN)); } // Check the requirements are decreasing { uint64_t height = 209250; int64_t mainnet_requirement = (int64_t)master_nodes::get_staking_requirement(height); - int64_t mainnet_expected = (int64_t)((29643 * COIN) + 670390000); + int64_t mainnet_expected = (int64_t)((29643 * beldex::COIN) + 670390000); int64_t mainnet_delta = std::abs(mainnet_requirement - mainnet_expected); ASSERT_LT(mainnet_delta, atomic_epsilon); } @@ -74,7 +74,7 @@ TEST(master_nodes, staking_requirement) uint64_t height = 230704; int64_t mainnet_requirement = (int64_t)master_nodes::get_staking_requirement(height); - int64_t mainnet_expected = (int64_t)((27164 * COIN) + 648610000); + int64_t mainnet_expected = (int64_t)((27164 * beldex::COIN) + 648610000); int64_t mainnet_delta = std::abs(mainnet_requirement - mainnet_expected); ASSERT_LT(mainnet_delta, atomic_epsilon); } @@ -84,7 +84,7 @@ TEST(master_nodes, staking_requirement) uint64_t height = 373200; int64_t mainnet_requirement = (int64_t)master_nodes::get_staking_requirement(height); - int64_t mainnet_expected = (int64_t)((20839 * COIN) + 644149350); + int64_t mainnet_expected = (int64_t)((20839 * beldex::COIN) + 644149350); ASSERT_EQ(mainnet_requirement, mainnet_expected); } @@ -93,7 +93,7 @@ TEST(master_nodes, staking_requirement) uint64_t height = 450000; uint64_t mainnet_requirement = master_nodes::get_staking_requirement(height); - uint64_t mainnet_expected = (18898 * COIN) + 351896001; + uint64_t mainnet_expected = (18898 * beldex::COIN) + 351896001; ASSERT_EQ(mainnet_requirement, mainnet_expected); } @@ -102,7 +102,7 @@ TEST(master_nodes, staking_requirement) uint64_t height = 999999; uint64_t mainnet_requirement = master_nodes::get_staking_requirement(height); - uint64_t mainnet_expected = (15000 * COIN) + 3122689; + uint64_t mainnet_expected = (15000 * beldex::COIN) + 3122689; ASSERT_EQ(mainnet_requirement, mainnet_expected); } @@ -111,7 +111,7 @@ TEST(master_nodes, staking_requirement) uint64_t height = 1000000; uint64_t mainnet_requirement = master_nodes::get_staking_requirement(height); - uint64_t mainnet_expected = 15000 * COIN; + uint64_t mainnet_expected = 15000 * beldex::COIN; ASSERT_EQ(mainnet_requirement, mainnet_expected); } } @@ -121,8 +121,8 @@ static bool verify_vote(master_nodes::quorum_vote_t const &vote, cryptonote::vote_verification_context &vvc, master_nodes::quorum const &quorum) { - bool result = master_nodes::verify_vote_age(vote, latest_height,vvc, cryptonote::network_version_17_POS ); - result &= master_nodes::verify_vote_signature(cryptonote::network_version_count - 1, vote, vvc, quorum); + bool result = master_nodes::verify_vote_age(vote, latest_height,vvc, cryptonote::hf::hf17_POS); + result &= master_nodes::verify_vote_signature(cryptonote::hf::hf20_bulletproof_plus, vote, vvc, quorum); return result; } @@ -230,7 +230,7 @@ TEST(master_nodes, tx_extra_state_change_validation) // Valid state_change cryptonote::tx_extra_master_node_state_change valid_state_change = {}; - uint8_t hf_version = cryptonote::network_version_11_infinite_staking; + cryptonote::hf hf_version = cryptonote::hf::hf11_infinite_staking; const uint64_t HEIGHT = 100; { valid_state_change.block_height = HEIGHT - 1; @@ -322,114 +322,82 @@ TEST(master_nodes, tx_extra_state_change_validation) TEST(master_nodes, min_portions) { - uint8_t hf_version = cryptonote::network_version_9_master_nodes; + cryptonote::hf hf_version = cryptonote::hf::hf9_master_nodes; // Test new contributors can *NOT* stake to a registration with under 25% of the total stake if there is more than 25% available. { - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {0, STAKING_PORTIONS})); + ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {0, cryptonote::old::STAKING_PORTIONS})); } - - { - const auto small = MIN_PORTIONS - 1; - const auto rest = STAKING_PORTIONS - small; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {small, rest})); - } - - { - /// TODO: fix this test - const auto small = MIN_PORTIONS - 1; - const auto rest = STAKING_PORTIONS - small - STAKING_PORTIONS / 2; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {STAKING_PORTIONS / 2, small, rest})); - } - - { - const auto small = MIN_PORTIONS - 1; - const auto rest = STAKING_PORTIONS - small - 2 * MIN_PORTIONS; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {MIN_PORTIONS, MIN_PORTIONS, small, rest})); - } - // Test new contributors *CAN* stake as the last person with under 25% if there is less than 25% available. // Two contributers { - const auto large = 4 * (STAKING_PORTIONS / 5); - const auto rest = STAKING_PORTIONS - large; + const auto large = 4 * (cryptonote::old::STAKING_PORTIONS / 5); + const auto rest = cryptonote::old::STAKING_PORTIONS - large; bool result = master_nodes::check_master_node_portions(hf_version, {large, rest}); ASSERT_TRUE(result); } // Three contributers { - const auto half = STAKING_PORTIONS / 2 - 1; - const auto rest = STAKING_PORTIONS - 2 * half; + const auto half = cryptonote::old::STAKING_PORTIONS / 2 - 1; + const auto rest = cryptonote::old::STAKING_PORTIONS - 2 * half; bool result = master_nodes::check_master_node_portions(hf_version, {half, half, rest}); ASSERT_TRUE(result); } // Four contributers { - const auto third = STAKING_PORTIONS / 3 - 1; - const auto rest = STAKING_PORTIONS - 3 * third; + const auto third = cryptonote::old::STAKING_PORTIONS / 3 - 1; + const auto rest = cryptonote::old::STAKING_PORTIONS - 3 * third; bool result = master_nodes::check_master_node_portions(hf_version, {third, third, third, rest}); ASSERT_TRUE(result); } // ===== After hard fork v11 ===== - hf_version = cryptonote::network_version_11_infinite_staking; + hf_version = cryptonote::hf::hf11_infinite_staking; { - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {0, STAKING_PORTIONS})); + ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {0, cryptonote::old::STAKING_PORTIONS})); } { - const auto small = MIN_PORTIONS - 1; - const auto rest = STAKING_PORTIONS - small; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {small, rest})); - } - - { - const auto small = STAKING_PORTIONS / 8; - const auto rest = STAKING_PORTIONS - small - STAKING_PORTIONS / 2; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {STAKING_PORTIONS / 2, small, rest})); - } - - { - const auto small = MIN_PORTIONS - 1; - const auto rest = STAKING_PORTIONS - small - 2 * MIN_PORTIONS; - ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {MIN_PORTIONS, MIN_PORTIONS, small, rest})); + const auto small = cryptonote::old::STAKING_PORTIONS / 8; + const auto rest = cryptonote::old::STAKING_PORTIONS - small - cryptonote::old::STAKING_PORTIONS / 2; + ASSERT_FALSE(master_nodes::check_master_node_portions(hf_version, {cryptonote::old::STAKING_PORTIONS / 2, small, rest})); } // Test new contributors *CAN* stake as the last person with under 25% if there is less than 25% available. // Two contributers { - const auto large = 4 * (STAKING_PORTIONS / 5); - const auto rest = STAKING_PORTIONS - large; + const auto large = 4 * (cryptonote::old::STAKING_PORTIONS / 5); + const auto rest = cryptonote::old::STAKING_PORTIONS - large; bool result = master_nodes::check_master_node_portions(hf_version, {large, rest}); ASSERT_TRUE(result); } // Three contributers { - const auto half = STAKING_PORTIONS / 2 - 1; - const auto rest = STAKING_PORTIONS - 2 * half; + const auto half = cryptonote::old::STAKING_PORTIONS / 2 - 1; + const auto rest = cryptonote::old::STAKING_PORTIONS - 2 * half; bool result = master_nodes::check_master_node_portions(hf_version, {half, half, rest}); ASSERT_TRUE(result); } // Four contributers { - const auto third = STAKING_PORTIONS / 3 - 1; - const auto rest = STAKING_PORTIONS - 3 * third; + const auto third = cryptonote::old::STAKING_PORTIONS / 3 - 1; + const auto rest = cryptonote::old::STAKING_PORTIONS - 3 * third; bool result = master_nodes::check_master_node_portions(hf_version, {third, third, third, rest}); ASSERT_TRUE(result); } // New test for hf_v11: allow less than 25% stake in the presence of large existing contributions { - const auto large = STAKING_PORTIONS / 2; - const auto small_1 = STAKING_PORTIONS / 6; - const auto small_2 = STAKING_PORTIONS / 6; - const auto rest = STAKING_PORTIONS - large - small_1 - small_2; + const auto large = cryptonote::old::STAKING_PORTIONS / 2; + const auto small_1 = cryptonote::old::STAKING_PORTIONS / 6; + const auto small_2 = cryptonote::old::STAKING_PORTIONS / 6; + const auto rest = cryptonote::old::STAKING_PORTIONS - large - small_1 - small_2; bool result = master_nodes::check_master_node_portions(hf_version, {large, small_1, small_2, rest}); ASSERT_TRUE(result); } @@ -442,7 +410,7 @@ TEST(master_nodes, min_stake_amount) { /// pre v11 uint64_t height = 101250; - uint8_t hf_version = cryptonote::network_version_9_master_nodes; + cryptonote::hf hf_version = cryptonote::hf::hf9_master_nodes; uint64_t stake_requirement = master_nodes::get_staking_requirement(height); { const uint64_t reserved = stake_requirement / 2; @@ -457,7 +425,7 @@ TEST(master_nodes, min_stake_amount) } /// post v11 - hf_version = cryptonote::network_version_11_infinite_staking; + hf_version = cryptonote::hf::hf11_infinite_staking; stake_requirement = master_nodes::get_staking_requirement(height); { // 50% reserved, with 1 contribution, max of 4- the minimum stake should be (50% / 3) @@ -490,14 +458,8 @@ TEST(master_nodes, min_stake_amount) TEST(master_nodes, master_node_rewards_proportional_to_portions) { { - const auto reward_a = cryptonote::get_portion_of_reward(MIN_PORTIONS, COIN); - const auto reward_b = cryptonote::get_portion_of_reward(3 * MIN_PORTIONS, COIN); - ASSERT_TRUE(3 * reward_a == reward_b); - } - - { - const auto reward_a = cryptonote::get_portion_of_reward(STAKING_PORTIONS/2, COIN); - const auto reward_b = cryptonote::get_portion_of_reward(STAKING_PORTIONS, COIN); + const auto reward_a = cryptonote::get_portion_of_reward(cryptonote::old::STAKING_PORTIONS/2, beldex::COIN); + const auto reward_b = cryptonote::get_portion_of_reward(cryptonote::old::STAKING_PORTIONS, beldex::COIN); ASSERT_TRUE(2 * reward_a == reward_b); } @@ -505,32 +467,32 @@ TEST(master_nodes, master_node_rewards_proportional_to_portions) TEST(master_nodes, master_node_get_locked_key_image_unlock_height) { - uint64_t lock_duration = master_nodes::staking_num_lock_blocks(cryptonote::MAINNET, cryptonote::network_version_17_POS) / 2; + uint64_t lock_duration = master_nodes::staking_num_lock_blocks(cryptonote::MAINNET, cryptonote::hf::hf17_POS) / 2; { uint64_t curr_height = 100; uint64_t expected = curr_height + lock_duration; - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::network_version_17_POS); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::hf::hf17_POS); ASSERT_EQ(unlock_height, expected); } { uint64_t curr_height = lock_duration - 1; uint64_t expected = curr_height + lock_duration; - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::network_version_17_POS); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::hf::hf17_POS); ASSERT_EQ(unlock_height, expected); } { uint64_t curr_height = lock_duration + 100; uint64_t expected = curr_height + lock_duration; - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::network_version_17_POS); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, curr_height, cryptonote::hf::hf17_POS); ASSERT_EQ(unlock_height, expected); } { uint64_t expected = lock_duration + lock_duration; - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, lock_duration, cryptonote::network_version_17_POS); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET, lock_duration, cryptonote::hf::hf17_POS); ASSERT_EQ(unlock_height, expected); } @@ -538,7 +500,7 @@ TEST(master_nodes, master_node_get_locked_key_image_unlock_height) uint64_t register_height = lock_duration + 1; uint64_t curr_height = register_height + 2; uint64_t expected = curr_height + lock_duration; - uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET,curr_height, cryptonote::network_version_17_POS); + uint64_t unlock_height = master_nodes::get_locked_key_image_unlock_height(cryptonote::MAINNET,curr_height, cryptonote::hf::hf17_POS); ASSERT_EQ(unlock_height, expected); } } diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index c7aba2aa516..f8fbdce5225 100755 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index bd504cc1adf..61c5d0c47fe 100755 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -76,7 +76,7 @@ class test_core bool prepare_handle_incoming_blocks(const std::vector &blocks_entry, std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } - size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } + size_t get_block_sync_size(uint64_t height) const { return cryptonote::BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } virtual crypto::hash on_transaction_relayed(const cryptonote::blobdata& tx) { return crypto::null_hash; } cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } @@ -85,7 +85,7 @@ class test_core uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } - uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(cryptonote::hf version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } bool pad_transactions() { return false; } diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp index a0d38ddb739..60d2ccc628d 100755 --- a/tests/unit_tests/output_distribution.cpp +++ b/tests/unit_tests/output_distribution.cpp @@ -83,7 +83,7 @@ bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64 { blockchain_objects_t bc = {}; struct get_test_options { - const std::vector hard_forks{{1,0,0,0}}; + const std::vector hard_forks{{cryptonote::hf::hf1,0,0,0}}; const cryptonote::test_options test_options = { hard_forks }; diff --git a/tests/unit_tests/pruning.cpp b/tests/unit_tests/pruning.cpp index 0a007cb8ee8..f69bf88c967 100755 --- a/tests/unit_tests/pruning.cpp +++ b/tests/unit_tests/pruning.cpp @@ -76,49 +76,49 @@ TEST(pruning, fits) TEST(pruning, tip) { - static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + 1000; - static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS, "H must be >= CRYPTONOTE_PRUNING_TIP_BLOCKS"); - for (uint64_t h = H - CRYPTONOTE_PRUNING_TIP_BLOCKS; h < H; ++h) + static constexpr uint64_t H = cryptonote::PRUNING_TIP_BLOCKS + 1000; + static_assert(H >= cryptonote::PRUNING_TIP_BLOCKS, "H must be >= cryptonote::PRUNING_TIP_BLOCKS"); + for (uint64_t h = H - cryptonote::PRUNING_TIP_BLOCKS; h < H; ++h) { - uint32_t pruning_seed = tools::get_pruning_seed(h, H, CRYPTONOTE_PRUNING_LOG_STRIPES); + uint32_t pruning_seed = tools::get_pruning_seed(h, H, cryptonote::PRUNING_LOG_STRIPES); ASSERT_EQ(pruning_seed, 0); - for (pruning_seed = 0; pruning_seed <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++pruning_seed) + for (pruning_seed = 0; pruning_seed <= (1 << cryptonote::PRUNING_LOG_STRIPES); ++pruning_seed) ASSERT_TRUE(tools::has_unpruned_block(h, H, pruning_seed)); } } TEST(pruning, seed) { - const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; - const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; + const uint64_t SS = cryptonote::PRUNING_STRIPE_SIZE; + const uint64_t NS = 1 << cryptonote::PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; for (uint64_t cycle = 0; cycle < 10; ++cycle) { const uint64_t O = TB * cycle; - ASSERT_EQ(tools::get_pruning_stripe(O + 0, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); - ASSERT_EQ(tools::get_pruning_stripe(O + 1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); - ASSERT_EQ(tools::get_pruning_stripe(O + SS-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); - ASSERT_EQ(tools::get_pruning_stripe(O + SS, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 2); - ASSERT_EQ(tools::get_pruning_stripe(O + SS*2-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 2); - ASSERT_EQ(tools::get_pruning_stripe(O + SS*2, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 3); - ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), NS); - ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); + ASSERT_EQ(tools::get_pruning_stripe(O + 0, 10000000, cryptonote::PRUNING_LOG_STRIPES), 1); + ASSERT_EQ(tools::get_pruning_stripe(O + 1, 10000000, cryptonote::PRUNING_LOG_STRIPES), 1); + ASSERT_EQ(tools::get_pruning_stripe(O + SS-1, 10000000, cryptonote::PRUNING_LOG_STRIPES), 1); + ASSERT_EQ(tools::get_pruning_stripe(O + SS, 10000000, cryptonote::PRUNING_LOG_STRIPES), 2); + ASSERT_EQ(tools::get_pruning_stripe(O + SS*2-1, 10000000, cryptonote::PRUNING_LOG_STRIPES), 2); + ASSERT_EQ(tools::get_pruning_stripe(O + SS*2, 10000000, cryptonote::PRUNING_LOG_STRIPES), 3); + ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS-1, 10000000, cryptonote::PRUNING_LOG_STRIPES), NS); + ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS, 10000000, cryptonote::PRUNING_LOG_STRIPES), 1); } } TEST(pruning, match) { - static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + 1000; - static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS, "H must be >= CRYPTONOTE_PRUNING_TIP_BLOCKS"); - for (uint64_t h = 0; h < H - CRYPTONOTE_PRUNING_TIP_BLOCKS; ++h) + static constexpr uint64_t H = cryptonote::PRUNING_TIP_BLOCKS + 1000; + static_assert(H >= cryptonote::PRUNING_TIP_BLOCKS, "H must be >= cryptonote::PRUNING_TIP_BLOCKS"); + for (uint64_t h = 0; h < H - cryptonote::PRUNING_TIP_BLOCKS; ++h) { - uint32_t pruning_seed = tools::get_pruning_seed(h, H, CRYPTONOTE_PRUNING_LOG_STRIPES); + uint32_t pruning_seed = tools::get_pruning_seed(h, H, cryptonote::PRUNING_LOG_STRIPES); uint32_t pruning_stripe = tools::get_pruning_stripe(pruning_seed); - ASSERT_TRUE(pruning_stripe > 0 && pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES)); - for (uint32_t other_pruning_stripe = 1; other_pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++other_pruning_stripe) + ASSERT_TRUE(pruning_stripe > 0 && pruning_stripe <= (1 << cryptonote::PRUNING_LOG_STRIPES)); + for (uint32_t other_pruning_stripe = 1; other_pruning_stripe <= (1 << cryptonote::PRUNING_LOG_STRIPES); ++other_pruning_stripe) { - uint32_t other_pruning_seed = tools::make_pruning_seed(other_pruning_stripe, CRYPTONOTE_PRUNING_LOG_STRIPES); + uint32_t other_pruning_seed = tools::make_pruning_seed(other_pruning_stripe, cryptonote::PRUNING_LOG_STRIPES); ASSERT_TRUE(tools::has_unpruned_block(h, H, other_pruning_seed) == (other_pruning_seed == pruning_seed)); } } @@ -126,13 +126,13 @@ TEST(pruning, match) TEST(pruning, stripe_size) { - static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + CRYPTONOTE_PRUNING_STRIPE_SIZE * (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) + 1000; - static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS + CRYPTONOTE_PRUNING_STRIPE_SIZE * (1 << CRYPTONOTE_PRUNING_LOG_STRIPES), "H must be >= that stuff in front"); - for (uint32_t pruning_stripe = 1; pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++pruning_stripe) + static constexpr uint64_t H = cryptonote::PRUNING_TIP_BLOCKS + cryptonote::PRUNING_STRIPE_SIZE * (1 << cryptonote::PRUNING_LOG_STRIPES) + 1000; + static_assert(H >= cryptonote::PRUNING_TIP_BLOCKS + cryptonote::PRUNING_STRIPE_SIZE * (1 << cryptonote::PRUNING_LOG_STRIPES), "H must be >= that stuff in front"); + for (uint32_t pruning_stripe = 1; pruning_stripe <= (1 << cryptonote::PRUNING_LOG_STRIPES); ++pruning_stripe) { - uint32_t pruning_seed = tools::make_pruning_seed(pruning_stripe, CRYPTONOTE_PRUNING_LOG_STRIPES); + uint32_t pruning_seed = tools::make_pruning_seed(pruning_stripe, cryptonote::PRUNING_LOG_STRIPES); unsigned int current_run = 0, best_run = 0; - for (uint64_t h = 0; h < H - CRYPTONOTE_PRUNING_TIP_BLOCKS; ++h) + for (uint64_t h = 0; h < H - cryptonote::PRUNING_TIP_BLOCKS; ++h) { if (tools::has_unpruned_block(h, H, pruning_seed)) { @@ -140,31 +140,31 @@ TEST(pruning, stripe_size) } else if (current_run) { - ASSERT_EQ(current_run, CRYPTONOTE_PRUNING_STRIPE_SIZE); + ASSERT_EQ(current_run, cryptonote::PRUNING_STRIPE_SIZE); best_run = std::max(best_run, current_run); current_run = 0; } } - ASSERT_EQ(best_run, CRYPTONOTE_PRUNING_STRIPE_SIZE); + ASSERT_EQ(best_run, cryptonote::PRUNING_STRIPE_SIZE); } } TEST(pruning, next_unpruned) { - static_assert((1 << CRYPTONOTE_PRUNING_LOG_STRIPES) >= 4, "CRYPTONOTE_PRUNING_LOG_STRIPES too low"); + static_assert((1 << cryptonote::PRUNING_LOG_STRIPES) >= 4, "cryptonote::PRUNING_LOG_STRIPES too low"); - const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; - const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; + const uint64_t SS = cryptonote::PRUNING_STRIPE_SIZE; + const uint64_t NS = 1 << cryptonote::PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; for (uint64_t h = 0; h < 100; ++h) ASSERT_EQ(tools::get_next_unpruned_block_height(h, 10000000, 0), h); - const uint32_t seed1 = tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seed2 = tools::make_pruning_seed(2, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seed3 = tools::make_pruning_seed(3, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seed4 = tools::make_pruning_seed(4, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seedNS = tools::make_pruning_seed(NS, CRYPTONOTE_PRUNING_LOG_STRIPES); + const uint32_t seed1 = tools::make_pruning_seed(1, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seed2 = tools::make_pruning_seed(2, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seed3 = tools::make_pruning_seed(3, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seed4 = tools::make_pruning_seed(4, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seedNS = tools::make_pruning_seed(NS, cryptonote::PRUNING_LOG_STRIPES); ASSERT_EQ(tools::get_next_unpruned_block_height(0, 10000000, seed1), 0); ASSERT_EQ(tools::get_next_unpruned_block_height(1, 10000000, seed1), 1); @@ -201,20 +201,20 @@ TEST(pruning, next_unpruned) TEST(pruning, next_pruned) { - static_assert((1 << CRYPTONOTE_PRUNING_LOG_STRIPES) >= 4, "CRYPTONOTE_PRUNING_LOG_STRIPES too low"); + static_assert((1 << cryptonote::PRUNING_LOG_STRIPES) >= 4, "cryptonote::PRUNING_LOG_STRIPES too low"); - const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; - const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; + const uint64_t SS = cryptonote::PRUNING_STRIPE_SIZE; + const uint64_t NS = 1 << cryptonote::PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; - const uint32_t seed1 = tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seed2 = tools::make_pruning_seed(2, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seedNS_1 = tools::make_pruning_seed(NS-1, CRYPTONOTE_PRUNING_LOG_STRIPES); - const uint32_t seedNS = tools::make_pruning_seed(NS, CRYPTONOTE_PRUNING_LOG_STRIPES); + const uint32_t seed1 = tools::make_pruning_seed(1, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seed2 = tools::make_pruning_seed(2, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seedNS_1 = tools::make_pruning_seed(NS-1, cryptonote::PRUNING_LOG_STRIPES); + const uint32_t seedNS = tools::make_pruning_seed(NS, cryptonote::PRUNING_LOG_STRIPES); for (uint64_t h = 0; h < 100; ++h) ASSERT_EQ(tools::get_next_pruned_block_height(h, 10000000, 0), 10000000); - for (uint64_t h = 10000000 - 1 - CRYPTONOTE_PRUNING_TIP_BLOCKS; h < 10000000; ++h) + for (uint64_t h = 10000000 - 1 - cryptonote::PRUNING_TIP_BLOCKS; h < 10000000; ++h) ASSERT_EQ(tools::get_next_pruned_block_height(h, 10000000, 0), 10000000); ASSERT_EQ(tools::get_next_pruned_block_height(1, 10000000, seed1), SS); diff --git a/utils/build_scripts/drone-static-upload.sh b/utils/build_scripts/drone-static-upload.sh index 738bb4f652b..1346424d31c 100755 --- a/utils/build_scripts/drone-static-upload.sh +++ b/utils/build_scripts/drone-static-upload.sh @@ -5,19 +5,19 @@ -set -o errexit +# set -o errexit -if [ -z "$SSH_KEY" ]; then - echo -e "\n\n\n\e[31;1mUnable to upload artifact: SSH_KEY not set\e[0m" - # Just warn but don't fail, so that this doesn't trigger a build failure for untrusted builds - exit 0 -fi +# if [ -z "$SSH_KEY" ]; then +# echo -e "\n\n\n\e[31;1mUnable to upload artifact: SSH_KEY not set\e[0m" +# # Just warn but don't fail, so that this doesn't trigger a build failure for untrusted builds +# exit 0 +# fi -echo "$SSH_KEY" >ssh_key +# echo "$SSH_KEY" >ssh_key -set -o xtrace # Don't start tracing until *after* we write the ssh key +# set -o xtrace # Don't start tracing until *after* we write the ssh key -chmod 600 ssh_key +# chmod 600 ssh_key branch_or_tag=${DRONE_BRANCH:-${DRONE_TAG:-unknown}} @@ -49,12 +49,12 @@ for p in "${upload_dirs[@]}"; do -mkdir $dir_tmp" done -sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@beldex.rocks < 1 and any(sys.argv[1].startswith(x) for x in ("ipc://", "tcp://")): +if len(sys.argv) > 1 and any(sys.argv[1].startswith(x) for x in ("ipc://", "tcp://", "curve://")): remote = sys.argv[1] del sys.argv[1] else: @@ -28,6 +28,18 @@ curve_pubkey = b'' my_privkey, my_pubkey = b'', b'' + +# If given a curve://whatever/pubkey argument then transform it into 'tcp://whatever' and put the +# 'pubkey' back into argv to be handled below. +if remote.startswith("curve://"): + pos = remote.rfind('/') + pkhex = remote[pos+1:] + remote = "tcp://" + remote[8:pos] + if len(pkhex) != 64 or not all(x in "0123456789abcdefABCDEF" for x in pkhex): + print("curve:// addresses must be in the form curve://HOST:PORT/REMOTE_PUBKEY_HEX", file=sys.stderr) + sys.exit(1) + sys.argv[1:0] = [pkhex] + if len(sys.argv) > 1 and len(sys.argv[1]) == 64 and all(x in "0123456789abcdefABCDEF" for x in sys.argv[1]): curve_pubkey = bytes.fromhex(sys.argv[1]) del sys.argv[1] @@ -72,7 +84,12 @@ print("(empty reply data)", file=sys.stderr) else: for x in m[3:]: - print(x.decode(), end="\n\n") + print("{} bytes data part:".format(len(x)), file=sys.stderr) + if any(x.startswith(y) for y in (b'd', b'l', b'i')) and x.endswith(b'e'): + sys.stdout.buffer.write(x) + else: + print(x.decode(), end="\n\n") + else: print("Request timed out", file=sys.stderr) socket.close(linger=0)