diff --git a/.gitmodules b/.gitmodules index 191af28d..97a855bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,4 +8,7 @@ [submodule "contrib/tor-connect"] path = contrib/tor-connect url = https://github.com/hyle-team/tor-connect.git - branch = main \ No newline at end of file + branch = main +[submodule "contrib/jwt-cpp"] + path = contrib/jwt-cpp + url = https://github.com/Thalhammer/jwt-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 31c80a7a..04790815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) PROJECT(Zano) @@ -18,6 +18,9 @@ endif() if(POLICY CMP0043) cmake_policy(SET CMP0074 NEW) endif() +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif() set(CMAKE_CXX_STANDARD 17) @@ -50,7 +53,7 @@ if (UNIX AND NOT APPLE) else() # multi configurations for MSVC and XCode if(CMAKE_SYSTEM_NAME STREQUAL "iOS") - set(CMAKE_CONFIGURATION_TYPES "Release") + set(CMAKE_CONFIGURATION_TYPES "Debug;Release") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") set(CMAKE_CONFIGURATION_TYPES "Debug;Release") else() @@ -58,7 +61,7 @@ else() endif() endif() message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}, and built type: ${CMAKE_BUILD_TYPE}") - + enable_testing() set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically @@ -66,7 +69,7 @@ find_package(OpenSSL REQUIRED) if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) endif() set(USE_PCH FALSE CACHE BOOL "Use shared precompiled headers") @@ -74,7 +77,7 @@ set(DISABLE_TOR FALSE CACHE BOOL "Disable TOR library(and related tor-connect su set(TESTNET FALSE CACHE BOOL "Compile for testnet") set(BUILD_GUI FALSE CACHE BOOL "Build qt-daemon") -include_directories(src contrib/eos_portable_archive contrib contrib/epee/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib") +include_directories(src contrib/eos_portable_archive contrib contrib/epee/include contrib/jwt-cpp/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib") add_definitions(-DSTATICLIB) @@ -122,6 +125,7 @@ if(MSVC) endforeach() endif() include_directories(SYSTEM src/platform/msc) + configure_file(utils/Directory.Build.props.in ${CMAKE_BINARY_DIR}/Directory.Build.props) else() set(ARCH default CACHE STRING "CPU to build for: -march value or default") if("${ARCH}" STREQUAL "default") @@ -225,14 +229,16 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(Boost_LIBRARIES "libboost.a") #workaround for new XCode 12 policy for builds(now it includes a slice for the "arm64" when builds for simulator) set(__iphoneos_archs "arm64") - set(__iphonesimulator_archs "x86_64") + #set(__iphonesimulator_archs "arm64,x86_64") set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") - set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") - set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") + #set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") + #set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") set(Boost_LIBRARY_DIRS "${Boost_LIBRARY_DIRS}/${CMAKE_ANDROID_ARCH_ABI}/") set(Boost_LIBRARIES "${Boost_LIBRARY_DIRS}libboost_system.a;${Boost_LIBRARY_DIRS}libboost_filesystem.a;${Boost_LIBRARY_DIRS}libboost_thread.a;${Boost_LIBRARY_DIRS}libboost_timer.a;${Boost_LIBRARY_DIRS}libboost_date_time.a;${Boost_LIBRARY_DIRS}libboost_chrono.a;${Boost_LIBRARY_DIRS}libboost_regex.a;${Boost_LIBRARY_DIRS}libboost_serialization.a;${Boost_LIBRARY_DIRS}libboost_atomic.a;${Boost_LIBRARY_DIRS}libboost_program_options.a") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC") elseif(APPLE) find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale) else() @@ -258,8 +264,10 @@ elseif(NOT MSVC) endif() if(BUILD_GUI) - cmake_minimum_required(VERSION 3.1) + cmake_minimum_required(VERSION 3.1) # <-- this is important for Linux GUI build, don't touch it -- sowle find_package(Qt5Widgets REQUIRED) + find_package(Qt5WebEngineWidgets REQUIRED) + find_package(Qt5WebChannel REQUIRED) endif() set(COMMIT_ID_IN_VERSION ON CACHE BOOL "Include commit ID in version") diff --git a/README.md b/README.md index 1b661e0a..e4515a47 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ Be sure to clone the repository properly:\ |--|--|--|--| | gcc (Linux) | 5.4.0 | 9.4.0 | 12.3.0 | | llvm/clang (Linux) | UNKNOWN | 7.0.1 | 8.0.0 | -| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2017 (15.9.30) | 2022 (17.7.5) | -| [XCode](https://developer.apple.com/downloads/) (macOS) | 12.3 | 14.3 | 14.3 | -| [CMake](https://cmake.org/download/) | 3.15.5 | 3.22.1 | 3.26.3 | -| [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.76 | +| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2019 (16.11.34) | 2022 (17.9.5) | +| [XCode](https://developer.apple.com/downloads/) (macOS) | 12.3 | 14.3 | 15.2 | +| [CMake](https://cmake.org/download/) | 3.15.5 | 3.26.3 | 3.29.0 | +| [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.84 | | [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | 1.1.1n | 1.1.1w | 1.1.1w | | [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.15.2 | diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index a3220829..abfc4885 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -14,9 +14,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android") message("excluded upnp support for IOS build") return() endif() + add_subdirectory(miniupnp/miniupnpc) + set_property(TARGET libminiupnpc-static PROPERTY FOLDER "contrib") set_property(TARGET zlibstatic PROPERTY FOLDER "contrib") set_property(TARGET mdbx PROPERTY FOLDER "contrib") diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h index 7ece78b1..9d5118cb 100644 --- a/contrib/epee/include/file_io_utils.h +++ b/contrib/epee/include/file_io_utils.h @@ -35,6 +35,7 @@ #include #include #include +#include #ifndef MAKE64 #define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32)) @@ -561,16 +562,15 @@ namespace file_io_utils try { - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for ( boost::filesystem::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr ) + std::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for ( std::filesystem::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr ) { - if ( only_files && boost::filesystem::is_directory(itr->status()) ) + if ( only_files && std::filesystem::is_directory(itr->status()) ) { continue; } target_list.push_back(itr->path().filename().string()); } - } catch(...) diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index 5855d977..3ee7001e 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -82,6 +82,13 @@ namespace epee namespace misc_utils { + + template + void cast_assign_a_to_b(t_type_a& a, const t_type_b& b) + { + *static_cast(&a) = b; + } + template @@ -532,6 +539,92 @@ namespace misc_utils }; + template + struct expirating_set + { + typedef std::set main_set; + main_set m_set; + std::multimap m_expirations; + + const main_set& get_set() + { + return m_set; + } + void add(const key& k, const expiration_type& e) + { + auto res = m_set.insert(k); + m_expirations.insert({ e, res.first }); + } + + void remove_if_expiration_less_than(const expiration_type& e) + { + while(m_expirations.size() && m_expirations.begin()->first < e) + { + m_set.erase(m_expirations.begin()->second); + m_expirations.erase(m_expirations.begin()); + } + } + }; + + template + struct expirating_map + { + typedef std::map main_map; + main_map m_map; + std::multimap m_expirations; + + const main_map& get_map() + { + return m_map; + } + void add(const key& k, const value_type& v, const expiration_type& e) + { + auto res = m_map.insert(k, v); + m_expirations.insert({ e, res.first }); + } + + void remove_if_expiration_less_than(const expiration_type& e) + { + while (m_expirations.size() && m_expirations.begin()->first < e) + { + m_map.erase(m_expirations.begin()->second); + m_expirations.erase(m_expirations.begin()); + } + } + + template + inline void serialize(t_archive& a, const unsigned int ver) + { + std::vector > items; + if constexpr (t_archive::is_saving::value) + { + for (const auto& item: m_expirations) + { + items.resize(items.size + 1); + std::get<2>(items.back()) = item.first; + std::get<0>(items.back()) = item.second.first; + std::get<1>(items.back()) = item.second.second; + } + } + a & items; + + if constexpr (!t_archive::is_saving::value) + { + for (const auto& item : items) + { + this->add(std::get<0>(item), std::get<1>(item), std::get<2>(item)); + + + items.resize(items.size + 1); + std::get<2>(items.back()) = item.first; + std::get<0>(items.back()) = item.second.first; + std::get<1>(items.back()) = item.second.second; + } + } + + } + + }; } // namespace misc_utils diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index cca1add3..afaadbf4 100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -98,7 +98,7 @@ namespace net_utils std::string m_transfer_encoding;//"Transfer-Encoding:" std::string m_content_encoding; //"Content-Encoding:" std::string m_host; //"Host:" - std::string m_cookie; //"Cookie:" + std::string m_cookie; //"Cookie:" fields_list m_etc_fields; void clear() @@ -147,10 +147,10 @@ namespace net_utils std::string m_http_method_str; std::string m_full_request_str; std::string m_replace_html; - std::string m_request_head; + std::string m_request_head; int m_http_ver_hi; int m_http_ver_lo; - bool m_have_to_block; + bool m_have_to_block; http_header_info m_header_info; uri_content m_uri_content; size_t m_full_request_buf_size; @@ -166,11 +166,11 @@ namespace net_utils struct http_response_info { - int m_response_code; - std::string m_response_comment; + int m_response_code; + std::string m_response_comment; fields_list m_additional_fields; - std::string m_body; - std::string m_mime_tipe; + std::string m_body; + std::string m_mime_tipe; http_header_info m_header_info; int m_http_ver_hi;// OUT paramter only int m_http_ver_lo;// OUT paramter only diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 3102cb2d..b4466ad4 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -30,133 +30,11 @@ #include "storages/portable_storage_template_helper.h" #include "http_base.h" #include "net/net_utils_base.h" +#include "storages/portable_storage_extended_for_doc.h" -template -typename_t get_documentation_json_struct() -{ - return AUTO_VAL_INIT_T(typename_t); -} - -template -bool auto_doc_t(const std::string& prefix_name, std::string& generate_reference) -{ - if (!generate_reference.size()) return true; - request_t req = get_documentation_json_struct(); - response_t res = get_documentation_json_struct(); - std::stringstream ss; - ss << prefix_name << ENDL - << "REQUEST: " << ENDL << epee::serialization::store_t_to_json(req) << ENDL << "--------------------------------" << ENDL - << "RESPONSE: " << ENDL << epee::serialization::store_t_to_json(res) << ENDL << "################################" << ENDL; - generate_reference += ss.str(); - return true; -} - - -template -bool auto_doc(const std::string& prefix_name, std::string& generate_reference) -{ - return auto_doc_t(prefix_name, generate_reference); -} - -namespace epee { - namespace net_utils { - namespace http { - struct i_chain_handler - { - virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info, - epee::net_utils::connection_context_base& m_conn_context, bool& call_found, std::string& generate_reference) = 0; - }; - } - } -} - - - - -#define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \ - epee::net_utils::http::http_response_info& response, \ - context_type& m_conn_context) \ -{\ - response.m_response_code = 200; \ - response.m_response_comment = "Ok"; \ - std::string reference_stub; \ - bool call_found = false; \ - if(!handle_http_request_map(query_info, response, m_conn_context, call_found, reference_stub) && response.m_response_code == 200) \ - { response.m_response_code = 500; response.m_response_comment = "Internal Server Error"; return true; } \ - if (!call_found) \ - { response.m_response_code = 404; response.m_response_comment = "Not Found"; return true; } \ - return true; \ -} - -#define BEGIN_URI_MAP2() template bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ - epee::net_utils::http::http_response_info& response_info, \ - t_context& m_conn_context, bool& call_found, std::string& generate_reference) { \ - call_found = false; \ - if(false) return true; //just a stub to have "else if" - -#define BEGIN_URI_MAP2_VIRTUAL() virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ - epee::net_utils::http::http_response_info& response_info, \ - epee::net_utils::connection_context_base& m_conn_context, bool& call_found, std::string& generate_reference) { \ - call_found = false; \ - if(false) return true; //just a stub to have "else if" - - -#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context); - -#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format - -#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \ - else if(auto_doc(s_pattern "[JSON]", generate_reference) && query_info.m_URI == s_pattern) \ - { \ - call_found = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ - boost::value_initialized req; \ - bool res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ - CHECK_AND_ASSERT_MES(res, false, "Failed to parse json: \r\n" << query_info.m_body); \ - uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ - boost::value_initialized resp;\ - res = callback_f(static_cast(req), static_cast(resp), m_conn_context); \ - CHECK_AND_ASSERT_MES(res, false, "Failed to call " << #callback_f << "() while handling " << s_pattern); \ - uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_json(static_cast(resp), response_info.m_body); \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - response_info.m_mime_tipe = "application/json"; \ - response_info.m_header_info.m_content_type = " application/json"; \ - LOG_PRINT("[HTTP/JSON][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ - } - -#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \ - else if(auto_doc(s_pattern "[BIN]", generate_reference) && query_info.m_URI == s_pattern) \ - { \ - call_found = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ - boost::value_initialized req; \ - bool res = epee::serialization::load_t_from_binary(static_cast(req), query_info.m_body); \ - CHECK_AND_ASSERT_MES(res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ - uint64_t ticks1 = misc_utils::get_tick_count(); \ - boost::value_initialized resp;\ - res = callback_f(static_cast(req), static_cast(resp), m_conn_context); \ - CHECK_AND_ASSERT_MES(res, false, "Failed to call " << #callback_f << "() while handling " << s_pattern); \ - uint64_t ticks2 = misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_binary(static_cast(resp), response_info.m_body); \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - response_info.m_mime_tipe = " application/octet-stream"; \ - response_info.m_header_info.m_content_type = " application/octet-stream"; \ - LOG_PRINT( "[HTTP/BIN][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ - } - -#define CHAIN_TO_PHANDLER(pi_chain_handler) else if (pi_chain_handler && pi_chain_handler->handle_http_request_map(query_info, response_info, m_conn_context, call_found, generate_reference) && call_found) { return true;} - -#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);call_found = true;} - -#define END_URI_MAP2() return true;} - - - - -namespace epee +namespace epee { namespace json_rpc { @@ -169,7 +47,7 @@ namespace epee t_param params; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) + KV_SERIALIZE(jsonrpc) DOC_DSCR("") DOC_EXMP("2.0") DOC_END KV_SERIALIZE(id) KV_SERIALIZE(method) KV_SERIALIZE(params) @@ -185,7 +63,7 @@ namespace epee KV_SERIALIZE(message) END_KV_SERIALIZE_MAP() }; - + struct dummy_error { BEGIN_KV_SERIALIZE_MAP() @@ -220,7 +98,7 @@ namespace epee t_param result; epee::serialization::storage_entry id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) + KV_SERIALIZE(jsonrpc) DOC_DSCR("") DOC_EXMP("2.0") DOC_END KV_SERIALIZE(id) KV_SERIALIZE(result) END_KV_SERIALIZE_MAP() @@ -245,20 +123,270 @@ namespace epee } } -template -struct json_command_type_t + +template +typename_t get_documentation_json_struct() { - typedef typename epee::json_rpc::request request; - typedef typename epee::json_rpc::request response; + return AUTO_VAL_INIT_T(typename_t); +} + +struct documentation_entry +{ + std::string uri; + bool is_binary = false; //if not - then it's JSON + std::string json_method_name; + std::string request_json_example; + std::string request_json_descriptions; + + std::string response_json_example; + std::string response_json_descriptions; + + std::string method_general_decription; }; -#define JSON_RPC_REFERENCE_MARKER "JSON_RPC" +struct documentation +{ + bool do_generate_documentation = false; + std::list entries; +}; + +// Primary template +template +struct has_static_member_description { +private: + // SFINAE test function + template + static auto test(int) -> decltype(U::description, std::true_type{}); + + // Fallback function + template + static auto test(...) -> std::false_type; + +public: + // Member constant indicating whether T has a static member + static constexpr bool value = decltype(test(0))::value; +}; -#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == JSON_RPC_REFERENCE_MARKER || query_info.m_URI == uri) \ + +template +const char* get_command_description() +{ + if constexpr (has_static_member_description::value) + { + return T::description; + } + else + { + return "NO DESCRIPTION"; + } +} + +template +void f(T) {} // Definition #2 + + +// Base template +template +struct is_std_simple_container : std::false_type {}; + +// Specializations for each container +template +struct is_std_simple_container> : std::true_type {}; + +template +struct is_std_simple_container> : std::true_type {}; + +template +struct is_std_simple_container> : std::true_type {}; + +template +struct is_std_simple_container> : std::true_type {}; + + +template +bool auto_doc(const std::string& uri, const std::string& method, bool is_json, documentation& docs) +{ + if (!docs.do_generate_documentation) return true; + + docs.entries.resize(docs.entries.size()+1); + docs.entries.back().is_binary = !is_json; + docs.entries.back().uri = uri; + docs.entries.back().json_method_name = method; + docs.entries.back().method_general_decription = get_command_description(); + + if constexpr (is_json_rpc_method) + { + //json rpc-like call + typedef typename epee::json_rpc::request request_t; + typedef typename epee::json_rpc::response response_t; + request_t req = AUTO_VAL_INIT(req); //get_documentation_json_struct(); + if constexpr (is_std_simple_container::value) + { + req.params.resize(1); + } + + response_t res = AUTO_VAL_INIT(res); + if constexpr (is_std_simple_container::value) + { + req.result.resize(1); + } + + req.method = method; + epee::serialization::portable_storage_extended_doc ps; + req.store(ps, nullptr); + ps.dump_as_json(docs.entries.back().request_json_example); + ps.dump_as_decriptions(docs.entries.back().request_json_descriptions); + + epee::serialization::portable_storage_extended_doc ps_res; + res.store(ps_res, nullptr); + ps_res.dump_as_json(docs.entries.back().response_json_example); + ps_res.dump_as_decriptions(docs.entries.back().response_json_descriptions); + + } + else + { + //json/bin uri/based + typedef typename command_type_t::request request_t; + typedef typename command_type_t::response response_t; + + request_t req = AUTO_VAL_INIT(req); //get_documentation_json_struct(); + response_t res = AUTO_VAL_INIT(res); //get_documentation_json_struct(); + + + epee::serialization::portable_storage_extended_doc ps; + req.store(ps, nullptr); + ps.dump_as_json(docs.entries.back().request_json_example); + ps.dump_as_decriptions(docs.entries.back().request_json_descriptions); + + epee::serialization::portable_storage_extended_doc ps_res; + res.store(ps_res, nullptr); + ps_res.dump_as_json(docs.entries.back().response_json_example); + ps_res.dump_as_decriptions(docs.entries.back().response_json_descriptions); + } + + +// std::stringstream ss; +// ss << prefix_name << ENDL +// << "REQUEST: " << ENDL << req_str << ENDL << req_str_descr << "--------------------------------" << ENDL +// << "RESPONSE: " << ENDL << res_str << ENDL << res_str_descr << "################################" << ENDL; +// generate_reference += ss.str(); + return true; + + //return auto_doc_t(prefix_name, generate_reference); +} + +namespace epee { + namespace net_utils { + namespace http { + struct i_chain_handler + { + virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info, + epee::net_utils::connection_context_base& m_conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation) = 0; + + static inline documentation m_empty_documentation; + }; + } + } +} + + + + +#define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response, \ + context_type& m_conn_context) \ +{\ + response.m_response_code = 200; \ + response.m_response_comment = "Ok"; \ + bool call_found = false; \ + if(!handle_http_request_map(query_info, response, m_conn_context, call_found) && response.m_response_code == 200) \ + { response.m_response_code = 500; response.m_response_comment = "Internal Server Error"; return true; } \ + if (!call_found) \ + { response.m_response_code = 404; response.m_response_comment = "Not Found"; return true; } \ + return true; \ +} + +#define BEGIN_URI_MAP2() template bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response_info, \ + t_context& m_conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation) { \ + call_found = false; \ + if(false) return true; //just a stub to have "else if" + +#define BEGIN_URI_MAP2_VIRTUAL() virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response_info, \ + epee::net_utils::connection_context_base& m_conn_context, bool& call_found, documentation& docs = epee::net_utils::http::i_chain_handler::m_empty_documentation) { \ + call_found = false; \ + if(false) return true; //just a stub to have "else if" + + +#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context); + +#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + +#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \ + else if(auto_doc(s_pattern, "", true, docs) && query_info.m_URI == s_pattern) \ { \ - if(query_info.m_URI == JSON_RPC_REFERENCE_MARKER) {generate_reference = "JSON RPC URL: " uri "\n";} \ + call_found = true; \ + uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized req; \ + bool res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(res, false, "Failed to parse json: \r\n" << query_info.m_body); \ + uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized resp;\ + res = callback_f(static_cast(req), static_cast(resp), m_conn_context); \ + CHECK_AND_ASSERT_MES(res, false, "Failed to call " << #callback_f << "() while handling " << s_pattern); \ + uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(static_cast(resp), response_info.m_body); \ + uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT("[HTTP/JSON][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \ + else if(auto_doc(s_pattern, "", false, docs) && query_info.m_URI == s_pattern) \ + { \ + call_found = true; \ + uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized req; \ + bool res = epee::serialization::load_t_from_binary(static_cast(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ + uint64_t ticks1 = misc_utils::get_tick_count(); \ + boost::value_initialized resp;\ + res = callback_f(static_cast(req), static_cast(resp), m_conn_context); \ + CHECK_AND_ASSERT_MES(res, false, "Failed to call " << #callback_f << "() while handling " << s_pattern); \ + uint64_t ticks2 = misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_binary(static_cast(resp), response_info.m_body); \ + uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = " application/octet-stream"; \ + response_info.m_header_info.m_content_type = " application/octet-stream"; \ + LOG_PRINT( "[HTTP/BIN][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define CHAIN_TO_PHANDLER(pi_chain_handler) else if (pi_chain_handler && pi_chain_handler->handle_http_request_map(query_info, response_info, m_conn_context, call_found, docs) && call_found) { return true;} + +#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);call_found = true;} + +#define END_URI_MAP2() return true;} + + + +//template +//struct json_command_type_t +//{ +// typedef typename epee::json_rpc::request request; +// typedef typename epee::json_rpc::request response; +//}; + +//#define JSON_RPC_REFERENCE_MARKER "JSON_RPC" + +// if(query_info.m_URI == JSON_RPC_REFERENCE_MARKER) {generate_reference = "JSON RPC URL: " uri "\n";} \ + +#define BEGIN_JSON_RPC_MAP(uri) else if(docs.do_generate_documentation || query_info.m_URI == uri) \ + { \ + const char* current_zone_json_uri = uri;\ LOG_PRINT_L4("[JSON_REQUEST_BODY]: " << ENDL << query_info.m_body); \ uint64_t ticks = epee::misc_utils::get_tick_count(); \ epee::serialization::portable_storage ps; \ @@ -317,7 +445,7 @@ struct json_command_type_t LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); #define MAP_JON_RPC_WE(method_name, callback_f, command_type) \ - else if(auto_doc>("[" method_name "]", generate_reference) && callback_name == method_name) \ + else if(auto_doc(current_zone_json_uri, method_name, true, docs) && callback_name == method_name) \ { \ call_found = true; \ PREPARE_OBJECTS_FROM_JSON(command_type) \ @@ -335,7 +463,7 @@ struct json_command_type_t } #define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \ - else if(auto_doc>("[" method_name "]", generate_reference) && callback_name == method_name) \ + else if(auto_doc(current_zone_json_uri, method_name, true, docs) && callback_name == method_name) \ { \ call_found = true; \ PREPARE_OBJECTS_FROM_JSON(command_type) \ @@ -353,7 +481,7 @@ struct json_command_type_t } #define MAP_JON_RPC(method_name, callback_f, command_type) \ - else if(auto_doc>(std::string("[") + method_name + "]", generate_reference) && callback_name == method_name) \ + else if(auto_doc(current_zone_json_uri, method_name, true, docs) && callback_name == method_name) \ { \ call_found = true; \ PREPARE_OBJECTS_FROM_JSON(command_type) \ @@ -386,4 +514,51 @@ struct json_command_type_t return true; \ } +namespace epee +{ + template + bool generate_doc_as_md_files(const std::string& folder, t_rpc_server& server) + { + LOG_PRINT_L0("Dumping RPC auto-generated documents!"); + epee::net_utils::http::http_request_info query_info; + epee::net_utils::http::http_response_info response_info; + epee::net_utils::connection_context_base conn_context; + //std::string generate_reference = std::string("WALLET_RPC_COMMANDS_LIST:\n"); + bool call_found = false; + documentation docs; + docs.do_generate_documentation = true; + // query_info.m_URI = JSON_RPC_REFERENCE_MARKER; + query_info.m_body = "{\"jsonrpc\": \"2.0\", \"method\": \"nonexisting_method\", \"params\": {}},"; + server.handle_http_request_map(query_info, response_info, conn_context, call_found, docs); + + for (const auto& de : docs.entries) + { + std::stringstream ss; + ss << de.method_general_decription << ENDL << ENDL;; + + ss << "URL: ```http:://127.0.0.1:11211" << de.uri << "```" << ENDL; + + ss << "### Request: " << ENDL << "```json" << ENDL << de.request_json_example << ENDL << "```" << ENDL; + ss << "### Request description: " << ENDL << "```" << ENDL << de.request_json_descriptions << ENDL << "```" << ENDL; + ss << "### Response: " << ENDL << "```json" << ENDL << de.response_json_example << ENDL << "```" << ENDL; + ss << "### Response description: " << ENDL << "```" << ENDL << de.response_json_descriptions << ENDL << "```" << ENDL; + + std::string filename = de.json_method_name; + if (!filename.size()) + { + filename = de.uri; + if (filename.front() == '/') + filename.erase(filename.begin()); + } + filename += ".md"; + bool r = epee::file_io_utils::save_string_to_file(folder + "/" + filename, ss.str()); + if (!r) + { + LOG_ERROR("Failed to save file " << filename); + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 4a42e679..fb3be17c 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, anonimal, + // Copyright (c) 2019, anonimal, // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // diff --git a/contrib/epee/include/serialization/keyvalue_helpers.h b/contrib/epee/include/serialization/keyvalue_helpers.h index 79a4886b..7dc812ad 100644 --- a/contrib/epee/include/serialization/keyvalue_helpers.h +++ b/contrib/epee/include/serialization/keyvalue_helpers.h @@ -85,6 +85,52 @@ namespace epee } return res; } + + // helper for blob-to-base64 serialization + inline std::string transfrom_binbuf_to_base64(const std::string& a) + { + return epee::string_encoding::base64_encode(a); + } + + inline std::string transform_base64_to_binbuf(const std::string& a) + { + return epee::string_encoding::base64_decode(a); + } + + + //basic helpers for pod-to-hex serialization + template + std::string transform_t_pod_array_to_hex_str_array(const t_pod_container_type& a) + { + std::string res; + for (const auto& item : a) + { + res += epee::string_tools::pod_to_hex(a) + ", "; + } + if (a.size()) + { + res.erase(res.size() - 2); + } + + return res; + } + template + t_pod_container_type transform_hex_str_array_to_t_pod_array(const std::string& a) + { + std::vector pod_items; + boost::split(pod_items, a, boost::is_any_of(", ][\"")); + + t_pod_container_type res; + for (const auto& item : pod_items) + { + res.resize(res.size() + 1); + typename t_pod_container_type::value_type& pod_val = res.back(); + + if (!epee::string_tools::hex_to_pod(item, pod_val)) + throw std::runtime_error(std::string("Unable to transform \"") + item + "\" to pod type " + typeid(typename t_pod_container_type::value_type).name()); + } + return res; + } //------------------------------------------------------------------------------------------------------------------- #pragma pack(push, 1) template diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index 31c90df7..91f1aeda 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -32,7 +32,7 @@ #include "keyvalue_helpers.h" #include "keyvalue_serialization_overloads.h" namespace epee -{ +{ /************************************************************************/ /* Serialize map declarations */ /************************************************************************/ @@ -65,13 +65,71 @@ public: \ static bool serialize_map(this_type& this_ref, t_storage& stg, typename t_storage::hsection hparent_section) \ { + +#define KV_CAT_(a, b) a ## b +#define KV_CAT(a, b) KV_CAT_(a, b) +#define VARNAME(Var) KV_CAT(Var, __LINE__) + +#define KV_MAKE_ALIAS_NAME() VARNAME(alias_tmp_name) +#define KV_MAKE_VAR_NAME() VARNAME(val_tmp_name) + #define KV_SERIALIZE_N(varialble, val_name) \ + using KV_MAKE_ALIAS_NAME() [[maybe_unused]] = decltype(this_ref.varialble); \ + [[maybe_unused]] const char* KV_MAKE_VAR_NAME() = val_name;\ epee::serialization::selector::serialize(this_ref.varialble, stg, hparent_section, val_name); +//#define KV_SERIALIZE_N_DOC(varialble, val_name) \ +// using KV_MAKE_ALIAS_NAME() = decltype(this_ref.varialble); \ +// epee::serialization::selector::serialize(this_ref.varialble, stg, hparent_section, val_name); \ +// if constexpr (t_storage::use_descriptions::value) \ +// { \ +// epee::serialization::selector::template serialize_and_doc(stg, hparent_section, val_name + + +#define DOC_DSCR(description) if constexpr (t_storage::use_descriptions::value) \ + { \ + epee::serialization::selector::template serialize_and_doc(stg, hparent_section, KV_MAKE_VAR_NAME(), description + + + /* + {using var_type = decltype(this_ref.varialble); \ + epee::serialization::selector::serialize(this_ref.varialble, stg, hparent_section, val_name); \ + if constexpr (t_storage::use_descriptions::value) \ + { \ + epee::serialization::selector::set_descr(stg, hparent_section, val_name, description, default = var_type()); \ + } \ + } +*/ + +//#define DOC_DSCR(description) , description +#define DOC_EXMP(substitute) , substitute +//#define DOC_EXMP_AUTO_1(arg_1) , KV_MAKE_ALIAS_NAME() (arg_1) +//#define DOC_EXMP_AUTO_2(arg_1, arg_2) , KV_MAKE_ALIAS_NAME() (arg_1, arg_2) +#define DOC_END ); } +#define DOC_EXMP_AUTO(...) , epee::create_t_object(__VA_ARGS__) + + +// Function template to create an object with forwarded constructor arguments + template + T create_t_object(Args&&... args) { + return T(std::forward(args)...); + } + + //substitute, description); +//#define DOC_EXAMPLE(substitute) substitute, +//#define DOC_EX(substitute__) var_type(substitute__), + +//#define DOC_COMMAND(command_general_description) static const char* explain_yourseflf = command_general_description; + + #define KV_SERIALIZE_CUSTOM_N(varialble, stored_type, from_v_to_stored, from_stored_to_v, val_name) \ + using KV_MAKE_ALIAS_NAME() [[maybe_unused]] = stored_type; \ + [[maybe_unused]] const char* VARNAME(val_tmp_name) = val_name;\ epee::serialization::selector::template serialize_custom(this_ref.varialble, stg, hparent_section, val_name, from_v_to_stored, from_stored_to_v); #define KV_SERIALIZE_EPHEMERAL_N(stored_type, from_v_to_stored, val_name) \ + using KV_MAKE_ALIAS_NAME() [[maybe_unused]] = stored_type; \ + [[maybe_unused]] const char* VARNAME(val_tmp_name) = val_name;\ epee::serialization::selector::template serialize_ephemeral(this_ref, stg, hparent_section, val_name, from_v_to_stored); @@ -81,6 +139,8 @@ public: \ #define KV_SERIALIZE_BLOB_AS_HEX_STRING_N(varialble, val_name) \ KV_SERIALIZE_CUSTOM_N(varialble, std::string, epee::transform_binbuf_to_hexstr, epee::transform_hexstr_to_binbuff, val_name) +#define KV_SERIALIZE_BLOB_AS_BASE64_STRING_N(varialble, val_name) \ + KV_SERIALIZE_CUSTOM_N(varialble, std::string, epee::transfrom_binbuf_to_base64, epee::transform_base64_to_binbuf, val_name) #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ epee::serialization::selector::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); @@ -89,19 +149,30 @@ public: \ static_assert(std::is_pod::value, "t_type must be a POD type."); \ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) +#define KV_SERIALIZE_CONTAINER_POD_AS_HEX_N(varialble, val_name) \ + KV_SERIALIZE_CUSTOM_N(varialble, std::string, epee::transform_t_pod_array_to_hex_str_array, epee::transform_hex_str_array_to_t_pod_array, val_name) + + #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ epee::serialization::selector::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); #define END_KV_SERIALIZE_MAP() return true;} -#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) -#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) -#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check -#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) -#define KV_SERIALIZE_CUSTOM(varialble, stored_type, from_v_to_stored, from_stored_to_v) KV_SERIALIZE_CUSTOM_N(varialble, stored_type, from_v_to_stored, from_stored_to_v, #varialble) -#define KV_SERIALIZE_POD_AS_HEX_STRING(varialble) KV_SERIALIZE_POD_AS_HEX_STRING_N(varialble, #varialble) -#define KV_SERIALIZE_BLOB_AS_HEX_STRING(varialble) KV_SERIALIZE_BLOB_AS_HEX_STRING_N(varialble, #varialble) +#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) +#define KV_SERIALIZE_DOC(varialble) KV_SERIALIZE_N_DOC( varialble, #varialble) +#define DOC_COMMAND(desciption_text) inline static const char* description = desciption_text; + + +#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check +#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_CONTAINER_POD_AS_HEX(varialble) KV_SERIALIZE_CONTAINER_POD_AS_HEX_N(varialble, #varialble) +#define KV_SERIALIZE_CUSTOM(varialble, stored_type, from_v_to_stored, from_stored_to_v) KV_SERIALIZE_CUSTOM_N(varialble, stored_type, from_v_to_stored, from_stored_to_v, #varialble) +#define KV_SERIALIZE_POD_AS_HEX_STRING(varialble) KV_SERIALIZE_POD_AS_HEX_STRING_N(varialble, #varialble) +#define KV_SERIALIZE_BLOB_AS_HEX_STRING(varialble) KV_SERIALIZE_BLOB_AS_HEX_STRING_N(varialble, #varialble) +#define KV_SERIALIZE_BLOB_AS_BASE64_STRING(variable) KV_SERIALIZE_BLOB_AS_BASE64_STRING_N(variable, #variable) + #define KV_CHAIN_MAP(variable_obj) epee::namespace_accessor::template serialize_map(this_ref.variable_obj, stg, hparent_section); diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index c0d46af3..cb88897f 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -315,9 +315,28 @@ namespace epee struct selector { template - static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + static bool serialize(const t_type& d, t_storage& stg, [[maybe_unused]] typename t_storage::hsection hparent_section, [[maybe_unused]] const char* pname) { - return kv_serialize(d, stg, hparent_section, pname); + //if constexpr (!t_storage::use_descriptions::value) + //{ + return kv_serialize(d, stg, hparent_section, pname); + //} + //else + // return false; + + } + //const t_type& doc_substitute = t_type(), const std::string& description = std::string() + template + static bool serialize_and_doc(t_storage& stg, typename t_storage::hsection hparent_section, const char* pname, const std::string& description = std::string(), const t_type& doc_substitute = t_type()) + { + if constexpr (t_storage::use_descriptions::value) + { + stg.set_entry_description(hparent_section, pname, description); + return kv_serialize(doc_substitute, stg, hparent_section, pname); + } + else + return false; + } template @@ -478,6 +497,5 @@ namespace epee return r; } - } } \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index a5d46e19..b6e72668 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -44,15 +44,20 @@ namespace epee /************************************************************************/ /* */ /************************************************************************/ - class portable_storage + template + class portable_storage_base { public: - typedef epee::serialization::hsection hsection; + //typedef epee::serialization::hsection hsection; + using use_descriptions = std::false_type; + typedef t_section* hsection; typedef epee::serialization::harray harray; typedef storage_entry meta_entry; - portable_storage(){} - virtual ~portable_storage(){} + portable_storage_base + (){} + virtual ~portable_storage_base + (){} hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); template bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); @@ -63,18 +68,18 @@ namespace epee //serial access for arrays of values -------------------------------------- //values template - harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); + harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); template bool get_next_value(harray hval_array, t_value& target); template - harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); + harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); template bool insert_next_value(harray hval_array, const t_value& target); //sections - harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); - bool get_next_section(harray hSecArray, hsection& h_child_section); - harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); - bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); + harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); + bool get_next_section(harray hSecArray, hsection& h_child_section); + harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); + bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); //------------------------------------------------------------------------ //delete entry (section, value or array) bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr); @@ -83,12 +88,13 @@ namespace epee bool load_from_binary(const binarybuffer& target); template bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); - bool dump_as_json(std::string& targetObj, size_t indent = 0, end_of_line_t eol = eol_crlf); + bool dump_as_json(std::string& targetObj, size_t indent = 0/*, end_of_line_t eol = eol_crlf*/); bool load_from_json(const std::string& source); + void set_entry_description(hsection hparent_section, const std::string& name, const std::string& description) {} template bool enum_entries(hsection hparent_section, cb_t cb); - private: + protected: section m_root; hsection get_root_section() {return &m_root;} storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); @@ -107,32 +113,36 @@ namespace epee }; #pragma pack(pop) }; - inline - bool portable_storage::dump_as_json(std::string& buff, size_t indent /* = 0 */, end_of_line_t eol /* = eol_crlf */) + + + template + bool portable_storage_base::dump_as_json(std::string& buff, size_t indent /* = 0 *//*, end_of_line_t eol *//* = eol_crlf */) { TRY_ENTRY(); std::stringstream ss; - epee::serialization::dump_as_json(ss, m_root, indent, eol); + epee::serialization::recursive_visitor::dump_as_(ss, m_root, indent/*, eol*/); buff = ss.str(); return true; - CATCH_ENTRY("portable_storage::dump_as_json", false) + CATCH_ENTRY("portable_storage_base::dump_as_json", false) } - inline - bool portable_storage::load_from_json(const std::string& source) + + template + bool portable_storage_base::load_from_json(const std::string& source) { TRY_ENTRY(); return json::load_from_json(source, *this); - CATCH_ENTRY("portable_storage::load_from_json", false) + CATCH_ENTRY("portable_storage_base::load_from_json", false) } + template template - bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name) + bool portable_storage_base::dump_as_xml(std::string& targetObj, const std::string& root_name) { return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format } - inline - bool portable_storage::store_to_binary(binarybuffer& target) + template + bool portable_storage_base::store_to_binary(binarybuffer& target) { TRY_ENTRY(); std::stringstream ss; @@ -144,10 +154,10 @@ namespace epee pack_entry_to_buff(ss, m_root); target = ss.str(); return true; - CATCH_ENTRY("portable_storage::store_to_binary", false) + CATCH_ENTRY("portable_storage_base::store_to_binary", false) } - inline - bool portable_storage::load_from_binary(const binarybuffer& source) + template + bool portable_storage_base::load_from_binary(const binarybuffer& source) { m_root.m_entries.clear(); if(source.size() < sizeof(storage_block_header)) @@ -172,11 +182,11 @@ namespace epee throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); buf_reader.read(m_root); return true;//TODO: - CATCH_ENTRY("portable_storage::load_from_binary", false); + CATCH_ENTRY("portable_storage_base::load_from_binary", false); } //--------------------------------------------------------------------------------------------------------------- - inline - hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) + template + typename portable_storage_base::hsection portable_storage_base::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) { TRY_ENTRY(); hparent_section = hparent_section ? hparent_section:&m_root; @@ -197,7 +207,7 @@ namespace epee return nullptr; } return &boost::get
(*pentry); - CATCH_ENTRY("portable_storage::open_section", nullptr); + CATCH_ENTRY("portable_storage_base::open_section", nullptr); } //--------------------------------------------------------------------------------------------------------------- template @@ -209,8 +219,9 @@ namespace epee void operator()(const from_type& v){convert_t(v, m_target);} }; + template template - bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section) + bool portable_storage_base::get_value(const std::string& value_name, t_value& val, hsection hparent_section) { BOOST_MPL_ASSERT(( boost::mpl::contains )); //TRY_ENTRY(); @@ -222,11 +233,11 @@ namespace epee get_value_visitor gvv(val); boost::apply_visitor(gvv, *pentry); return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); + //CATCH_ENTRY("portable_storage_base::template<>get_value", false); } //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::get_value(const std::string& value_name, storage_entry& val, hsection hparent_section) + template + bool portable_storage_base::get_value(const std::string& value_name, storage_entry& val, hsection hparent_section) { //TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; @@ -236,11 +247,12 @@ namespace epee val = *pentry; return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); + //CATCH_ENTRY("portable_storage_base::template<>get_value", false); } //--------------------------------------------------------------------------------------------------------------- + template template - bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) + bool portable_storage_base::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) { BOOST_MPL_ASSERT(( boost::mpl::contains::type, t_value> )); TRY_ENTRY(); @@ -256,11 +268,11 @@ namespace epee } *pentry = storage_entry(v); return true; - CATCH_ENTRY("portable_storage::template<>set_value", false); + CATCH_ENTRY("portable_storage_base::template<>set_value", false); } //--------------------------------------------------------------------------------------------------------------- - inline - storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection) + template + storage_entry* portable_storage_base::find_storage_entry(const std::string& pentry_name, hsection psection) { TRY_ENTRY(); CHECK_AND_ASSERT(psection, nullptr); @@ -269,27 +281,28 @@ namespace epee return nullptr; return &it->second; - CATCH_ENTRY("portable_storage::find_storage_entry", nullptr); + CATCH_ENTRY("portable_storage_base::find_storage_entry", nullptr); } //--------------------------------------------------------------------------------------------------------------- + template template - storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) + storage_entry* portable_storage_base::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) { TRY_ENTRY(); CHECK_AND_ASSERT(psection, nullptr); auto ins_res = psection->m_entries.insert(std::pair(pentry_name, entry)); return &ins_res.first->second; - CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); + CATCH_ENTRY("portable_storage_base::insert_new_entry_get_storage_entry", nullptr); } //--------------------------------------------------------------------------------------------------------------- - inline - hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection) + template + typename portable_storage_base::hsection portable_storage_base::insert_new_section(const std::string& pentry_name, hsection psection) { TRY_ENTRY(); storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section()); if(!pse) return nullptr; return &boost::get
(*pse); - CATCH_ENTRY("portable_storage::insert_new_section", nullptr); + CATCH_ENTRY("portable_storage_base::insert_new_section", nullptr); } //--------------------------------------------------------------------------------------------------------------- template @@ -308,8 +321,9 @@ namespace epee } }; //--------------------------------------------------------------------------------------------------------------- + template template - harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) + harray portable_storage_base::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) { BOOST_MPL_ASSERT(( boost::mpl::contains )); //TRY_ENTRY(); @@ -325,7 +339,7 @@ namespace epee if(!boost::apply_visitor(gfv, ar_entry)) return nullptr; return &ar_entry; - //CATCH_ENTRY("portable_storage::get_first_value", nullptr); + //CATCH_ENTRY("portable_storage_base::get_first_value", nullptr); } //--------------------------------------------------------------------------------------------------------------- template @@ -344,10 +358,10 @@ namespace epee return true; } }; - - + //--------------------------------------------------------------------------------------------------------------- + template template - bool portable_storage::get_next_value(harray hval_array, t_value& target) + bool portable_storage_base::get_next_value(harray hval_array, t_value& target) { BOOST_MPL_ASSERT(( boost::mpl::contains )); //TRY_ENTRY(); @@ -357,11 +371,12 @@ namespace epee if(!boost::apply_visitor(gnv, ar_entry)) return false; return true; - //CATCH_ENTRY("portable_storage::get_next_value", false); + //CATCH_ENTRY("portable_storage_base::get_next_value", false); } //--------------------------------------------------------------------------------------------------------------- + template template - harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) + harray portable_storage_base::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) { TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; @@ -382,11 +397,12 @@ namespace epee array_entry_t& arr_typed = boost::get >(arr); arr_typed.insert_first_val(target); return &arr; - CATCH_ENTRY("portable_storage::insert_first_value", nullptr); + CATCH_ENTRY("portable_storage_base::insert_first_value", nullptr); } //--------------------------------------------------------------------------------------------------------------- + template template - bool portable_storage::enum_entries(hsection hparent_section, cb_t cb) + bool portable_storage_base::enum_entries(hsection hparent_section, cb_t cb) { TRY_ENTRY(); if (!hparent_section) hparent_section = &m_root; @@ -396,11 +412,12 @@ namespace epee break; } return true; - CATCH_ENTRY("portable_storage::enum_entries", false); + CATCH_ENTRY("portable_storage_base::enum_entries", false); } - + //--------------------------------------------------------------------------------------------------------------- + template template - bool portable_storage::insert_next_value(harray hval_array, const t_value& target) + bool portable_storage_base::insert_next_value(harray hval_array, const t_value& target) { TRY_ENTRY(); CHECK_AND_ASSERT(hval_array, false); @@ -411,12 +428,12 @@ namespace epee array_entry_t& arr_typed = boost::get >(*hval_array); arr_typed.insert_next_value(target); return true; - CATCH_ENTRY("portable_storage::insert_next_value", false); + CATCH_ENTRY("portable_storage_base::insert_next_value", false); } //--------------------------------------------------------------------------------------------------------------- //sections - inline - harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) + template + harray portable_storage_base::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) { TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; @@ -434,11 +451,11 @@ namespace epee return nullptr; h_child_section = psec; return &ar_entry; - CATCH_ENTRY("portable_storage::get_first_section", nullptr); + CATCH_ENTRY("portable_storage_base::get_first_section", nullptr); } //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section) + template + bool portable_storage_base::get_next_section(harray hsec_array, hsection& h_child_section) { TRY_ENTRY(); CHECK_AND_ASSERT(hsec_array, false); @@ -449,11 +466,11 @@ namespace epee if(!h_child_section) return false; return true; - CATCH_ENTRY("portable_storage::get_next_section", false); + CATCH_ENTRY("portable_storage_base::get_next_section", false); } //--------------------------------------------------------------------------------------------------------------- - inline - harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) + template + harray portable_storage_base::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) { TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; @@ -474,11 +491,11 @@ namespace epee array_entry_t
& sec_array = boost::get>(ar_entry); hinserted_childsection = &sec_array.insert_first_val(section()); return &ar_entry; - CATCH_ENTRY("portable_storage::insert_first_section", nullptr); + CATCH_ENTRY("portable_storage_base::insert_first_section", nullptr); } //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection) + template + bool portable_storage_base::insert_next_section(harray hsec_array, hsection& hinserted_childsection) { TRY_ENTRY(); CHECK_AND_ASSERT(hsec_array, false); @@ -488,8 +505,9 @@ namespace epee array_entry_t
& sec_array = boost::get>(*hsec_array); hinserted_childsection = &sec_array.insert_next_value(section()); return true; - CATCH_ENTRY("portable_storage::insert_next_section", false); + CATCH_ENTRY("portable_storage_base::insert_next_section", false); } - //--------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------- + typedef portable_storage_base
portable_storage; } } diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h index 99c792e5..2f2ed5d1 100644 --- a/contrib/epee/include/storages/portable_storage_base.h +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -153,6 +153,7 @@ namespace epee /************************************************************************/ struct section { + std::map m_descriptions; std::map m_entries; }; diff --git a/contrib/epee/include/storages/portable_storage_extended_for_doc.h b/contrib/epee/include/storages/portable_storage_extended_for_doc.h new file mode 100644 index 00000000..a7b6094f --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_extended_for_doc.h @@ -0,0 +1,67 @@ +// 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. +// + + + +#pragma once + +#include "portable_storage.h" +#include "portable_storage_to_description.h" + + +namespace epee +{ + namespace serialization + { + /************************************************************************/ + /* */ + /************************************************************************/ + class portable_storage_extended_doc: public portable_storage + { + public: + using use_descriptions = std::true_type; + + void set_entry_description(hsection hparent_section, const std::string& name, const std::string& description) + { + if (!hparent_section) + hparent_section = &m_root; + hparent_section->m_descriptions[name] = description; + } + + bool dump_as_decriptions(std::string& buff, size_t indent = 0 , end_of_line_t eol = eol_crlf) + { + TRY_ENTRY(); + std::stringstream ss; + recursive_visitor::dump_as_(ss, m_root, indent); + buff = ss.str(); + return true; + CATCH_ENTRY("portable_storage_base::dump_as_json", false) + } + + }; + //--------------------------------------------------------------------------------------------------------------- + } +} diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h index 896f9eb2..318ae462 100644 --- a/contrib/epee/include/storages/portable_storage_template_helper.h +++ b/contrib/epee/include/storages/portable_storage_template_helper.h @@ -56,16 +56,16 @@ namespace epee } //----------------------------------------------------------------------------------------------------------- template - bool store_t_to_json(const t_struct& str_in, std::string& json_buff, size_t indent = 0, end_of_line_t eol = eol_crlf) + bool store_t_to_json(const t_struct& str_in, std::string& json_buff, size_t indent = 0) { portable_storage ps; str_in.store(ps); - ps.dump_as_json(json_buff, indent, eol); + ps.dump_as_json(json_buff, indent); return true; } //----------------------------------------------------------------------------------------------------------- template - std::string store_t_to_json(const t_struct& str_in, size_t indent = 0, end_of_line_t eol = eol_crlf) + std::string store_t_to_json(const t_struct& str_in, size_t indent = 0) { std::string json_buff; store_t_to_json(str_in, json_buff, indent); diff --git a/contrib/epee/include/storages/portable_storage_to_.h b/contrib/epee/include/storages/portable_storage_to_.h new file mode 100644 index 00000000..8c3bc592 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_.h @@ -0,0 +1,149 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" + +namespace epee +{ + namespace serialization + { + template + class recursive_visitor + { + public: + template + struct array_entry_store_to_json_visitor : public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + + array_entry_store_to_json_visitor(t_stream& strm, size_t indent) + : m_strm(strm) + , m_indent(indent) + {} + + template + void operator()(const array_entry_t& a) + { + + t_strategy_layout_strategy::handle_array_start(m_strm, m_indent); + if (a.m_array.size()) + { + auto last_it = --a.m_array.end(); + for (auto it = a.m_array.begin(); it != a.m_array.end(); it++) + { + dump_as_(m_strm, *it, m_indent); + if (it != last_it) + t_strategy_layout_strategy::handle_array_entry_separator(m_strm, m_indent); + } + } + t_strategy_layout_strategy::handle_array_end(m_strm, m_indent); + } + }; + + template + struct storage_entry_store_to_json_visitor : public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + + storage_entry_store_to_json_visitor(t_stream& strm, size_t indent) + : m_strm(strm) + , m_indent(indent) + {} + + //section, array_entry + template + void operator()(const visited_type& v) + { + dump_as_(m_strm, v, m_indent); + } + }; + + template + void static dump_as_(t_stream& strm, const array_entry& ae, size_t indent) + { + array_entry_store_to_json_visitor aesv(strm, indent); + boost::apply_visitor(aesv, ae); + } + + template + void static dump_as_(t_stream& strm, const storage_entry& se, size_t indent) + { + storage_entry_store_to_json_visitor sv(strm, indent); + boost::apply_visitor(sv, se); + } + + template + void static dump_as_(t_stream& strm, const t_type& v, size_t indent) + { + t_strategy_layout_strategy::handle_value(strm, v, indent); + } + + template + void static dump_as_(t_stream& strm, const section& sec, size_t indent) + { + size_t local_indent = indent + 1; + t_strategy_layout_strategy::handle_obj_begin(strm, indent); + t_strategy_layout_strategy::handle_line_break(strm, indent); + + if (sec.m_entries.size()) + { + auto it_last = --sec.m_entries.end(); + for (auto it = sec.m_entries.begin(); it != sec.m_entries.end(); it++) + { + if constexpr (t_strategy_layout_strategy::use_descriptions::value) + { + std::string descr; + auto it_descr = sec.m_descriptions.find(it->first); + if (it_descr != sec.m_descriptions.end()) + descr = it_descr->second; + + t_strategy_layout_strategy::handle_print_key(strm, it->first, descr, local_indent); + } + else + { + t_strategy_layout_strategy::handle_print_key(strm, it->first, local_indent); + } + + dump_as_(strm, it->second, local_indent); + if (it_last != it) + t_strategy_layout_strategy::handle_section_entry_separator(strm, indent); + + t_strategy_layout_strategy::handle_line_break(strm, indent); + } + } + t_strategy_layout_strategy::handle_obj_end(strm, indent); + } + }; + + } +} diff --git a/contrib/epee/include/storages/portable_storage_to_description.h b/contrib/epee/include/storages/portable_storage_to_description.h new file mode 100644 index 00000000..2d87d5d7 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_description.h @@ -0,0 +1,107 @@ +// Copyright (c) 2006-2024, 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_to_.h" + +namespace epee +{ + namespace serialization + { + class strategy_descriptin + { + public: + using use_descriptions = std::true_type; + + inline static const char* eol = get_endline(eol_crlf); + //static const end_of_line_t eol = eol_crlf; + + template + static void handle_value(t_stream& strm, const t_type& v, size_t indent) + {} + + template + static void handle_array_start(t_stream& strm, size_t indent) + { + //strm << "["; + } + + template + static void handle_array_end(t_stream& strm, size_t indent) + { + //strm << "]"; + } + + template + static void handle_obj_begin(t_stream& strm, size_t indent) + {} + + template + static void handle_obj_end(t_stream& strm, size_t indent) + {} + + template + static void handle_print_key(t_stream& strm, const std::string& key, const std::string& description, size_t indent) + { + if (description.size()) + { + const std::string indent_str = make_indent(indent); + strm << indent_str << "\"" << key << "\"" << ": " << description << eol; + } + } + + template + static void handle_print_description(t_stream& strm, const std::string& description, size_t indent) + { + } + + + template + static void handle_section_entry_separator(t_stream& strm, size_t indent) + { + //strm << ","; + } + + template + static void handle_array_entry_separator(t_stream& strm, size_t indent) + { + //strm << ","; + } + + template + static void handle_line_break(t_stream& strm, size_t indent) + { + //strm << eol; + } + }; + + } +} diff --git a/contrib/epee/include/storages/portable_storage_to_json.h b/contrib/epee/include/storages/portable_storage_to_json.h index 1e2d5d97..3292c952 100644 --- a/contrib/epee/include/storages/portable_storage_to_json.h +++ b/contrib/epee/include/storages/portable_storage_to_json.h @@ -30,174 +30,121 @@ #include "misc_language.h" #include "portable_storage_base.h" +#include "portable_storage_to_.h" namespace epee { namespace serialization { - - template - void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const std::string& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const bool& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const double& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const t_type& v, size_t indent, end_of_line_t eol = eol_crlf); - template - void dump_as_json(t_stream& strm, const section& sec, size_t indent, end_of_line_t eol = eol_crlf); - + inline const char* get_endline(end_of_line_t eol) + { + switch (eol) + { + case eol_lf: return "\n"; + case eol_cr: return "\r"; + case eol_space: return " "; + default: return "\r\n"; + } + } inline std::string make_indent(size_t indent) { - return std::string(indent*2, ' '); + return std::string(indent * 2, ' '); } - template - struct array_entry_store_to_json_visitor: public boost::static_visitor + class strategy_json { - t_stream& m_strm; - size_t m_indent; - end_of_line_t m_eol; + public: + using use_descriptions = std::false_type; - array_entry_store_to_json_visitor(t_stream& strm, size_t indent, end_of_line_t eol) - : m_strm(strm) - , m_indent(indent) - , m_eol(eol) - {} - - template - void operator()(const array_entry_t& a) + inline static const char* eol = get_endline(eol_crlf); + //static const end_of_line_t eol = eol_crlf; + template + static void handle_value(t_stream& strm, const std::string& v, size_t indent) { - m_strm << "["; - if(a.m_array.size()) - { - auto last_it = --a.m_array.end(); - for(auto it = a.m_array.begin(); it != a.m_array.end(); it++) - { - dump_as_json(m_strm, *it, m_indent, m_eol); - if(it != last_it) - m_strm << ","; - } - } - m_strm << "]"; + strm << "\"" << misc_utils::parse::transform_to_json_escape_sequence(v) << "\""; + } + template + static void handle_value(t_stream& strm, const int8_t& v, size_t indent) + { + strm << static_cast(v); + } + template + static void handle_value(t_stream& strm, const uint8_t& v, size_t indent) + { + strm << static_cast(v); + } + template + static void handle_value(t_stream& strm, const bool& v, size_t indent) + { + if (v) + strm << "true"; + else + strm << "false"; + } + template + static void handle_value(t_stream& strm, const double& v, size_t indent) + { + boost::io::ios_flags_saver ifs(strm); + strm.precision(8); + strm << std::fixed << v; + } + template + static void handle_value(t_stream& strm, const t_type& v, size_t indent) + { + strm << v; + } + + template + static void handle_array_start(t_stream& strm, size_t indent) + { + strm << "["; + } + + template + static void handle_array_end(t_stream& strm, size_t indent) + { + strm << "]"; + } + + template + static void handle_obj_begin(t_stream& strm, size_t indent) + { + strm << "{"; + } + + template + static void handle_obj_end(t_stream& strm, size_t indent) + { + strm << "}"; + } + + template + static void handle_print_key(t_stream& strm, const std::string& key, size_t indent) + { + const std::string indent_str = make_indent(indent); + strm << indent_str << "\"" << misc_utils::parse::transform_to_json_escape_sequence(key) << "\"" << ": "; + } + + + template + static void handle_section_entry_separator(t_stream& strm, size_t indent) + { + strm << ","; + } + + template + static void handle_array_entry_separator(t_stream& strm, size_t indent) + { + strm << ","; + } + + template + static void handle_line_break(t_stream& strm, size_t indent) + { + strm << eol; } }; - template - struct storage_entry_store_to_json_visitor: public boost::static_visitor - { - t_stream& m_strm; - size_t m_indent; - end_of_line_t m_eol; - - storage_entry_store_to_json_visitor(t_stream& strm, size_t indent, end_of_line_t eol) - : m_strm(strm) - , m_indent(indent) - , m_eol(eol) - {} - - //section, array_entry - template - void operator()(const visited_type& v) - { - dump_as_json(m_strm, v, m_indent, m_eol); - } - }; - - template - void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, end_of_line_t eol) - { - array_entry_store_to_json_visitor aesv(strm, indent, eol); - boost::apply_visitor(aesv, ae); - } - - template - void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, end_of_line_t eol) - { - storage_entry_store_to_json_visitor sv(strm, indent, eol); - boost::apply_visitor(sv, se); - } - - template - void dump_as_json(t_stream& strm, const std::string& v, size_t indent, end_of_line_t eol) - { - strm << "\"" << misc_utils::parse::transform_to_json_escape_sequence(v) << "\""; - } - - template - void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, end_of_line_t eol) - { - strm << static_cast(v); - } - - template - void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, end_of_line_t eol) - { - strm << static_cast(v); - } - - template - void dump_as_json(t_stream& strm, const bool& v, size_t indent, end_of_line_t eol) - { - if(v) - strm << "true"; - else - strm << "false"; - } - - template - void dump_as_json(t_stream& strm, const double& v, size_t indent, end_of_line_t eol) - { - boost::io::ios_flags_saver ifs(strm); - strm.precision(8); - strm << std::fixed << v; - } - - template - void dump_as_json(t_stream& strm, const t_type& v, size_t indent, end_of_line_t eol) - { - strm << v; - } - - template - void dump_as_json(t_stream& strm, const section& sec, size_t indent, end_of_line_t eol) - { - auto put_eol = [&]() { - switch (eol) - { - case eol_lf: strm << "\n"; break; - case eol_cr: strm << "\r"; break; - case eol_space: strm << " "; break; - default: strm << "\r\n"; break; - } - }; - - size_t local_indent = indent + 1; - strm << "{"; - put_eol(); - std::string indent_str = make_indent(local_indent); - if(sec.m_entries.size()) - { - auto it_last = --sec.m_entries.end(); - for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++) - { - strm << indent_str << "\"" << misc_utils::parse::transform_to_json_escape_sequence(it->first) << "\"" << ": "; - dump_as_json(strm, it->second, local_indent, eol); - if(it_last != it) - strm << ","; - put_eol(); - } - } - strm << make_indent(indent) << "}"; - } } } diff --git a/contrib/jwt-cpp b/contrib/jwt-cpp new file mode 160000 index 00000000..364a5572 --- /dev/null +++ b/contrib/jwt-cpp @@ -0,0 +1 @@ +Subproject commit 364a5572f4b46bb9f4304cb1c92acec8ddb2c620 diff --git a/contrib/zlib/CMakeLists.txt b/contrib/zlib/CMakeLists.txt index 696b9f52..97fd8bf4 100644 --- a/contrib/zlib/CMakeLists.txt +++ b/contrib/zlib/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 2.4.4) set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) project(zlib C) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b5ff732..200096f3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -196,10 +196,10 @@ if(BUILD_GUI) ENABLE_SHARED_PCH(Zano QTDAEMON) ENABLE_SHARED_PCH_EXECUTABLE(Zano) - QT5_USE_MODULES(Zano WebEngineWidgets WebChannel) + #QT5_USE_MODULES(Zano WebEngineWidgets WebChannel) find_package(Qt5PrintSupport REQUIRED) - target_link_libraries(Zano wallet rpc currency_core crypto common zlibstatic ethash Qt5::WebEngineWidgets Qt5::PrintSupport ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) + target_link_libraries(Zano wallet rpc currency_core crypto common zlibstatic ethash Qt5::WebChannel Qt5::WebEngineWidgets Qt5::PrintSupport ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) if (UNIX AND NOT APPLE) target_link_libraries(Zano rt) endif() diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index a7b9a5ff..ae22e874 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -25,7 +25,7 @@ namespace command_line const arg_descriptor arg_console ( "no-console", "Disable daemon console commands" ); const arg_descriptor arg_show_details ( "currency-details", "Display currency details" ); - const arg_descriptor arg_show_rpc_autodoc ( "show_rpc_autodoc", "Display rpc auto-generated documentation template" ); + const arg_descriptor arg_generate_rpc_autodoc ( "generate-rpc-autodoc", "Make auto-generated RPC API documentation documents at the given path" ); const arg_descriptor arg_disable_upnp ( "disable-upnp", "Disable UPnP (enhances local network privacy)"); const arg_descriptor arg_disable_ntp ( "disable-ntp", "Disable NTP, could enhance to time synchronization issue but increase network privacy, consider using disable-stop-if-time-out-of-sync with it"); diff --git a/src/common/command_line.h b/src/common/command_line.h index 905b1067..0b326917 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -220,7 +220,7 @@ namespace command_line extern const arg_descriptor arg_log_level; extern const arg_descriptor arg_console; extern const arg_descriptor arg_show_details; - extern const arg_descriptor arg_show_rpc_autodoc; + //extern const arg_descriptor arg_show_rpc_autodoc; extern const arg_descriptor arg_disable_upnp; extern const arg_descriptor arg_disable_ntp; extern const arg_descriptor arg_disable_stop_if_time_out_of_sync; @@ -233,4 +233,6 @@ namespace command_line extern const arg_descriptor arg_validate_predownload; extern const arg_descriptor arg_predownload_link; extern const arg_descriptor arg_deeplink; + extern const arg_descriptor arg_generate_rpc_autodoc; + } diff --git a/src/common/util.cpp b/src/common/util.cpp index f5d4baa9..1fdbe537 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -684,12 +684,8 @@ std::string get_nix_version_display_string() // got v_major, v_minor, v_revision - // allow 1.1.x and greater - - if (v_major < 1) - return false; - - if (v_major == 1 && v_minor < 1) + // allow 2.x and greater + if (v_major < 2) return false; return true; diff --git a/src/common/variant_helper.h b/src/common/variant_helper.h index 7fcf996b..9f98cbaa 100644 --- a/src/common/variant_helper.h +++ b/src/common/variant_helper.h @@ -14,6 +14,7 @@ #define VARIANT_SWITCH_END() } } +#define VARIANT_OBJ_TYPENAME local_reference_eokcmeokmeokcm.type().name() /* diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index b708283f..846e4572 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -1043,7 +1043,7 @@ namespace crypto void zero() { PUSH_GCC_WARNINGS - DISABLE_GCC_AND_CLANG_WARNING(class-memaccess) + DISABLE_GCC_WARNING(class-memaccess) size_t size_bytes = sizeof(scalar_t) * size(); memset(data(), 0, size_bytes); POP_GCC_WARNINGS @@ -1202,15 +1202,6 @@ namespace crypto void add_point(const point_t& point) { m_elements.emplace_back(point.to_public_key()); - - // faster? - /* static_assert(sizeof point.m_p3 == 5 * sizeof(item_t), "size missmatch"); - const item_t *p = (item_t*)&point.m_p3; - m_elements.emplace_back(p[0]); - m_elements.emplace_back(p[1]); - m_elements.emplace_back(p[2]); - m_elements.emplace_back(p[3]); - m_elements.emplace_back(p[4]); */ } void add_pub_key(const crypto::public_key& pk) diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index 646f2908..17cbba51 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -100,7 +100,6 @@ namespace crypto } // TODO: refactor with proper OOB handling - // TODO: @#@# add domain separation static const point_t& get_generator(bool select_H, size_t index) { if (index >= c_bpp_mn_max) diff --git a/src/currency_core/basic_pow_helpers.cpp b/src/currency_core/basic_pow_helpers.cpp index ddb87912..b7ee80ca 100644 --- a/src/currency_core/basic_pow_helpers.cpp +++ b/src/currency_core/basic_pow_helpers.cpp @@ -66,7 +66,11 @@ namespace currency init_ethash_log_if_necessary(); int epoch = ethash_height_to_epoch(height); std::shared_ptr p_context = progpow::get_global_epoch_context_full(static_cast(epoch)); - CHECK_AND_ASSERT_THROW_MES(p_context, "progpow::get_global_epoch_context_full returned null"); + if (!p_context) + { + LOG_ERROR("fatal error: get_global_epoch_context_full failed, throwing bad_alloc..."); + throw std::bad_alloc(); + } auto res_eth = progpow::hash(*p_context, static_cast(height), *(ethash::hash256*)&block_header_hash, nonce); crypto::hash result = currency::null_hash; memcpy(&result.data, &res_eth.final_hash, sizeof(res_eth.final_hash)); diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 17ed1fbd..ba9e2b32 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -297,10 +297,13 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro m_db_addr_to_alias.set_cache_size(cache_size); } + LOG_PRINT_L0("Opened DB ver " << m_db_storage_major_compatibility_version << "." << m_db_storage_minor_compatibility_version); + bool need_reinit = false; if (m_db_blocks.size() != 0) { #ifndef TESTNET + // MAINNET ONLY if ((m_db_storage_major_compatibility_version == 93 || m_db_storage_major_compatibility_version == 94) && BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION == 95) { // migrate DB to rebuild aliases container @@ -379,14 +382,21 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro // do not reinit db if moving from version 93 to version 94 LOG_PRINT_MAGENTA("DB storage does not need reinit because moving from v93 to v94", LOG_LEVEL_0); } + +#define DB_MAJ_VERSION_FOR_PER_BLOCK_GINDEX_FIX 95 + #else - // TESTNET + // TESTNET ONLY if (m_db_storage_major_compatibility_version == 95 && BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION == 96) { // do not reinit TESTNET db if moving from version 95 to version 96 LOG_PRINT_MAGENTA("DB storage does not need reinit because moving from v95 to v96", LOG_LEVEL_0); } + +#define DB_MAJ_VERSION_FOR_PER_BLOCK_GINDEX_FIX 109 + #endif + // MAINNET and TESTNET else if (m_db_storage_major_compatibility_version != BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION) { need_reinit = true; @@ -398,6 +408,46 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro need_reinit = true; LOG_PRINT_MAGENTA("DB storage needs reinit because it has minor compatibility ver " << m_db_storage_minor_compatibility_version << " that is greater than BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION: " << BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION, LOG_LEVEL_0); } + + if (!need_reinit && m_db_storage_major_compatibility_version == DB_MAJ_VERSION_FOR_PER_BLOCK_GINDEX_FIX && m_db_storage_minor_compatibility_version == 1) + { + // such version means that DB has unpopulated container m_db_per_block_gindex_incs, fix it now + LOG_PRINT_MAGENTA("DB version is " << DB_MAJ_VERSION_FOR_PER_BLOCK_GINDEX_FIX << ".1, migrating m_db_per_block_gindex_incs to ver. " << DB_MAJ_VERSION_FOR_PER_BLOCK_GINDEX_FIX << ".2...", LOG_LEVEL_0); + + // temporary set db compatibility version to zero during migration in order to trigger db reinit on the next lanunch in case the process stops in the middle + m_db.begin_transaction(); + uint64_t tmp_db_maj_version = m_db_storage_major_compatibility_version; + m_db_storage_major_compatibility_version = 0; + m_db.commit_transaction(); + + m_db.begin_transaction(); + std::unordered_map gindices; + for(size_t height = ZANO_HARDFORK_04_AFTER_HEIGHT + 1, size = m_db_blocks.size(); height < size; ++height) + { + auto block_ptr = m_db_blocks[height]; + gindices.clear(); + append_per_block_increments_for_tx(block_ptr->bl.miner_tx, gindices); + for(const crypto::hash& tx_id : block_ptr->bl.tx_hashes) + { + auto tx_ptr = m_db_transactions.get(tx_id); + if (!tx_ptr) + { + LOG_ERROR("Internal error: couldn't find a transactions with id " << tx_id << ", migration stops now and full resync is triggered in attempt to fix this."); + need_reinit = true; + break; + } + append_per_block_increments_for_tx(tx_ptr->tx, gindices); + } + push_block_to_per_block_increments(height, gindices); + } + m_db.commit_transaction(); + + // restore db maj compatibility + m_db.begin_transaction(); + m_db_storage_major_compatibility_version = tmp_db_maj_version; + m_db.commit_transaction(); + LOG_PRINT_MAGENTA("migration of m_db_per_block_gindex_incs completed successfully", LOG_LEVEL_0); + } } if (need_reinit) @@ -452,12 +502,12 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro set_lost_tx_unmixable(); m_db.commit_transaction(); - LOG_PRINT_GREEN("Blockchain initialized. (v:" << m_db_storage_major_compatibility_version << ") last block: " << m_db_blocks.size() - 1 << ENDL - << "genesis: " << get_block_hash(m_db_blocks[0]->bl) << ENDL - << "last block: " << m_db_blocks.size() - 1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " time ago" << ENDL - << "current pos difficulty: " << get_next_diff_conditional(true) << ENDL - << "current pow difficulty: " << get_next_diff_conditional(false) << ENDL - << "total transactions: " << m_db_transactions.size(), + LOG_PRINT_GREEN("Blockchain initialized, ver: " << m_db_storage_major_compatibility_version << "." << m_db_storage_minor_compatibility_version << ", last block: " << m_db_blocks.size() - 1 << ENDL + << " genesis: " << get_block_hash(m_db_blocks[0]->bl) << ENDL + << " last block: " << m_db_blocks.size() - 1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " ago" << ENDL + << " current pos difficulty: " << get_next_diff_conditional(true) << ENDL + << " current pow difficulty: " << get_next_diff_conditional(false) << ENDL + << " total transactions: " << m_db_transactions.size(), LOG_LEVEL_0); return true; @@ -1264,8 +1314,8 @@ wide_difficulty_type blockchain_storage::get_next_difficulty_for_alternative_cha //------------------------------------------------------------------ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height, bool pos) const { - CHECK_AND_ASSERT_MES((pos ? (b.miner_tx.vin.size() == 2) : (b.miner_tx.vin.size() == 1)), false, "coinbase transaction in the block has no inputs"); - CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); + CHECK_AND_ASSERT_MES((pos ? (b.miner_tx.vin.size() == 2) : (b.miner_tx.vin.size() == 1)), false, "coinbase transaction in the block has incorrect inputs number: " << b.miner_tx.vin.size()); + CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "input #0 of the coinbase transaction in the block has the wrong type : " << b.miner_tx.vin[0].type().name()); if(boost::get(b.miner_tx.vin[0]).height != height) { LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); @@ -1273,13 +1323,13 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t } if (pos) { - if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms + if (is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, height)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name()); else CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name()); } - if (m_core_runtime_config.is_hardfork_active_for_height(1, height)) + if (is_hardfork_active_for_height(ZANO_HARDFORK_01, height)) { // new rules that allow different unlock time in coinbase outputs uint64_t max_unlock_time = 0; @@ -1287,12 +1337,10 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t bool r = get_tx_max_min_unlock_time(b.miner_tx, max_unlock_time, min_unlock_time); CHECK_AND_ASSERT_MES(r && min_unlock_time >= height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW, false, - "coinbase transaction has wrong min_unlock_time: " << min_unlock_time << ", expected: " << height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + "coinbase transaction has wrong min_unlock_time: " << min_unlock_time << ", expected to be greater than or equal to " << height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); } else { - //------------------------------------------------------------------ - //bool blockchain_storage:: // pre-hard fork rules that don't allow different unlock time in coinbase outputs uint64_t max_unlock_time = 0; uint64_t min_unlock_time = 0; @@ -1304,14 +1352,7 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t } - //check outs overflow - if(!check_outs_overflow(b.miner_tx)) - { - LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); - return false; - } - - if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms + if (is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, height)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms { CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions."); CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 3"); @@ -1321,6 +1362,12 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t } else { + if(!check_bare_outs_overflow(b.miner_tx)) + { + LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); + return false; + } + CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions."); CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 0, false, "pre-HF4 coinbase shoudn't have non-empty proofs containter"); } @@ -1328,50 +1375,44 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t return true; } //------------------------------------------------------------------ -bool blockchain_storage::validate_miner_transaction(const block& b, - size_t cumulative_block_size, - uint64_t fee, - uint64_t& base_reward, - const boost::multiprecision::uint128_t& already_generated_coins) const +bool blockchain_storage::calculate_block_reward_for_next_top_block(size_t next_block_cumulative_size, uint64_t& block_reward_without_fee) const { CRITICAL_REGION_LOCAL(m_read_lock); std::vector last_blocks_sizes; get_last_n_blocks_sizes(last_blocks_sizes, CURRENCY_REWARD_BLOCKS_WINDOW); size_t blocks_size_median = misc_utils::median(last_blocks_sizes); - if (!get_block_reward(is_pos_block(b), blocks_size_median, cumulative_block_size, already_generated_coins, base_reward, get_block_height(b))) - { - LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); - return false; - } - - uint64_t block_reward = base_reward; - // before HF4: add tx fee to the block reward; after HF4: burn it - if (b.miner_tx.version < TRANSACTION_VERSION_POST_HF4) + LOG_PRINT_MAGENTA("blocks_size_median = " << blocks_size_median, LOG_LEVEL_2); + block_reward_without_fee = get_block_reward(get_top_block_height() + 1, blocks_size_median, next_block_cumulative_size); + CHECK_AND_ASSERT_MES(block_reward_without_fee != 0, false, "block size " << next_block_cumulative_size << " is bigger than allowed for this blockchain, blocks_size_median: " << blocks_size_median); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::validate_miner_transaction(const transaction& miner_tx, + uint64_t fee, + uint64_t block_reward_without_fee) const +{ + uint64_t block_reward = block_reward_without_fee; + // before HF4: add tx fee to the block reward; after HF4: burn fees, so they don't count in block_reward + if (miner_tx.version < TRANSACTION_VERSION_POST_HF4) { block_reward += fee; } - crypto::hash tx_id_for_post_hf4_era = b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4 ? get_transaction_hash(b.miner_tx) : null_hash; - if (!check_tx_balance(b.miner_tx, tx_id_for_post_hf4_era, block_reward)) + crypto::hash tx_id_for_post_hf4_era = miner_tx.version > TRANSACTION_VERSION_PRE_HF4 ? get_transaction_hash(miner_tx) : null_hash; // used in the input context for the proofs for txs ver >= 2 + if (!check_tx_balance(miner_tx, tx_id_for_post_hf4_era, block_reward)) { - LOG_ERROR("coinbase transaction balance check failed. Block reward is " << print_money_brief(block_reward) << "(" << print_money(base_reward) << "+" << print_money(fee) - << ", blocks_size_median = " << blocks_size_median - << ", cumulative_block_size = " << cumulative_block_size - << ", fee = " << fee - << ", already_generated_coins = " << already_generated_coins - << "), tx:"); - LOG_PRINT_L0(currency::obj_to_json_str(b.miner_tx)); + LOG_ERROR("coinbase transaction balance check failed. Block reward is " << print_money_brief(block_reward) << " (" << print_money(block_reward_without_fee) << "+" << print_money(fee) << "), tx:"); + LOG_PRINT_L0(currency::obj_to_json_str(miner_tx)); return false; } - if (!verify_asset_surjection_proof(b.miner_tx, tx_id_for_post_hf4_era)) + if (!verify_asset_surjection_proof(miner_tx, tx_id_for_post_hf4_era)) { LOG_ERROR("asset surjection proof verification failed for miner tx"); return false; } - LOG_PRINT_MAGENTA("Mining tx verification ok, blocks_size_median = " << blocks_size_median, LOG_LEVEL_2); return true; } //------------------------------------------------------------------ @@ -1910,7 +1951,11 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: std::stringstream ss_pow_pos_info; if (pos_block) { - ss_pow_pos_info << "PoS:\t" << abei.stake_hash << ", stake amount: " << print_money(pos_amount) << ", final_difficulty: " << pos_diff_final; + ss_pow_pos_info << "PoS:\t" << abei.stake_hash << ", stake amount: "; + if (abei.bl.miner_tx.version >= TRANSACTION_VERSION_POST_HF4) + ss_pow_pos_info << "hidden"; + else + ss_pow_pos_info << print_money_brief(pos_amount) << ", final_difficulty: " << pos_diff_final; proof = abei.stake_hash; } else @@ -2089,7 +2134,14 @@ bool blockchain_storage::is_reorganize_required(const block_extended_info& main_ main_chain_bei.this_block_tx_fee_median * main_chain_bei.bl.tx_hashes.size()) { //with the rest equal, alt block has more fees in it, prefer it + LOG_PRINT_L1("[is_reorganize_required]:TRUE, \"by order of tx_hashes.size()\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt); return true; + }else if (alt_chain_bei.this_block_tx_fee_median * alt_chain_bei.bl.tx_hashes.size() < + main_chain_bei.this_block_tx_fee_median * main_chain_bei.bl.tx_hashes.size()) + { + //with the rest equal, alt block has more fees in it, prefer it + LOG_PRINT_L1("[is_reorganize_required]:FALSE, \"by order of tx_hashes.size()\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt); + return false; } } if (!is_pos_block(main_chain_bei.bl)) @@ -2541,7 +2593,7 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU CHECK_AND_ASSERT_MES(tx_ptr->tx.vout.size() > out_ptr->out_no, false, "internal error: in global outs index, transaction out index=" << out_ptr->out_no << " is greater than transaction outputs = " << tx_ptr->tx.vout.size() << ", for tx id = " << out_ptr->tx_id); - CHECK_AND_ASSERT_MES(amount != 0 || height_upper_limit != 0, false, "height_upper_limit must be nonzero for hidden amounts (amount = 0)"); + //CHECK_AND_ASSERT_MES(amount != 0 || height_upper_limit != 0, false, "height_upper_limit must be nonzero for hidden amounts (amount = 0)"); if (height_upper_limit != 0 && tx_ptr->m_keeper_block_height > height_upper_limit) return false; @@ -2599,6 +2651,14 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU oen.amount_commitment = toz.amount_commitment; oen.concealing_point = toz.concealing_point; oen.blinded_asset_id = toz.blinded_asset_id; // TODO @#@# bad design, too much manual coping, consider redesign -- sowle + if (is_coinbase(tx_ptr->tx)) + { + oen.flags |= RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_COINBASE; + if (is_pos_coinbase(tx_ptr->tx)) + { + oen.flags |= RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_POS_COINBASE; + } + } } VARIANT_SWITCH_END(); @@ -2687,9 +2747,9 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO return true; } //------------------------------------------------------------------ -bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const +bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const { - size_t decoys_count = details.offsets.size(); + size_t decoys_count = details.global_offsets.size(); uint64_t amount = details.amount; uint64_t outs_container_size = m_db_outputs.get_item_size(details.amount); @@ -2716,7 +2776,7 @@ bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RP if (up_index_limit >= decoys_count) { std::set used; - used.insert(details.own_global_index); + //used.insert(details.own_global_index); for (uint64_t j = 0; j != decoys_count || used.size() >= up_index_limit;) { size_t g_index_initial = crypto::rand() % up_index_limit; @@ -2759,117 +2819,27 @@ bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RP } } //------------------------------------------------------------------ -bool blockchain_storage::get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const +bool blockchain_storage::get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const { - std::set used; - used.insert(details.own_global_index); - for (auto offset : details.offsets) + for (auto global_index : details.global_offsets) { - - //perfectly we would need to find transaction's output on the given height, with the given probability - //of being coinbase(coinbase outputs should be included less in decoy selection algorithm) - bool is_coinbase = (crypto::rand() % 101) > req.coinbase_percents ? false : true; - - //TODO: Consider including PoW coinbase to transactions(does it needed?) - - // convert offset to estimated height - uint64_t estimated_h = this->get_current_blockchain_size() - 1 - offset; - //make sure it's after zc hardfork - if (estimated_h < m_core_runtime_config.hard_forks.m_height_the_hardfork_n_active_after[ZANO_HARDFORK_04_ZARCANUM]) - { - LOG_ERROR("Wrong estimated offset(" << offset << "), it hits zone before zarcanum hardfork"); - return false; - } - -#define TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN 10 - //try to find output around given H - std::vector selected_global_indexes; - auto process_tx = [&](const crypto::hash& tx_id) { - - auto tx_ptr = m_db_transactions.find(tx_id); - CHECK_AND_ASSERT_THROW_MES(tx_ptr, "internal error: tx_id " << tx_id << " around estimated_h = " << estimated_h << " not found in db"); - //go through tx outputs - for (size_t i = 0; i != tx_ptr->tx.vout.size(); i++) - { - if (tx_ptr->tx.vout[i].type() != typeid(tx_out_zarcanum)) - { - continue; - } - const tx_out_zarcanum& z_out = boost::get(tx_ptr->tx.vout[i]); - - // NOTE: second part of condition (mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND && ..) might be not accurate - // since the wallet might want to request more inputs then it planning to do mixins. For now let's keep it this way and fix - // it if we see the problems about it. - if (z_out.mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || (z_out.mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND && z_out.mix_attr < details.offsets.size())) - { - continue; - } - - // skip spent outptus - if (tx_ptr->m_spent_flags[i]) - { - continue; - } - - if (used.find(tx_ptr->m_global_output_indexes[i]) != used.end()) - { - continue; - } - - // add output - // note: code that will process selected_global_indes will be revisiting transactions entries to obtain all - // needed data, that should work relatively effective because of on-top-of-db cache keep daya unserialized - selected_global_indexes.push_back(tx_ptr->m_global_output_indexes[i]); - } - - }; - - while (selected_global_indexes.size() < TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN) - { - auto block_ptr = m_db_blocks.get(estimated_h); - if (is_coinbase && is_pos_block(block_ptr->bl) ) - { - process_tx(get_transaction_hash(block_ptr->bl.miner_tx)); - } - else - { - //looking for regular output of regular transactions - for (auto tx_id : block_ptr->bl.tx_hashes) - { - process_tx(tx_id); - } - } - if(estimated_h) - estimated_h--; - else - { - //likely unusual situation when blocks enumerated all way back to genesis - //let's check if we have at least something - if (!selected_global_indexes.size()) - { - //need to regenerate offsets - return false; - } - } - } - //pick up a random output from selected_global_indes - uint64_t global_index = selected_global_indexes[crypto::rand() % selected_global_indexes.size()]; - bool res = add_out_to_get_random_outs(result_outs, details.amount, global_index, details.offsets.size(), req.use_forced_mix_outs, req.height_upper_limit); - CHECK_AND_ASSERT_THROW_MES(res, "Failed to add_out_to_get_random_outs([" << global_index << "]) at postzarcanum era"); - used.insert(global_index); + bool res = add_out_to_get_random_outs(result_outs, details.amount, global_index, this->get_core_runtime_config().hf4_minimum_mixins, false); + if (!res) + { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry{}); + oen.flags = RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_NOT_ALLOWED; + } } + CHECK_AND_ASSERT_THROW_MES(details.global_offsets.size() == result_outs.outs.size(), "details.global_offsets.size() == result_outs.outs.size() check failed"); return true; } //------------------------------------------------------------------ -bool blockchain_storage::get_random_outs_for_amounts2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res)const +bool blockchain_storage::get_random_outs_for_amounts3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res)const { CRITICAL_REGION_LOCAL(m_read_lock); LOG_PRINT_L3("[get_random_outs_for_amounts] amounts: " << req.amounts.size()); std::map amounts_to_up_index_limit_cache; - uint64_t count_zarcanum_blocks = 0; - if(is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) - count_zarcanum_blocks = this->get_current_blockchain_size() - m_core_runtime_config.hard_forks.m_height_the_hardfork_n_active_after[ZANO_HARDFORK_04_ZARCANUM]; for (size_t i = 0; i != req.amounts.size(); i++) @@ -2880,7 +2850,7 @@ bool blockchain_storage::get_random_outs_for_amounts2(const COMMAND_RPC_GET_RAND result_outs.amount = amount; bool r = false; - if (amount == 0 && count_zarcanum_blocks > 20000) + if (amount == 0) { //zarcanum era inputs r = get_target_outs_for_postzarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache); @@ -3584,7 +3554,7 @@ bool blockchain_storage::get_est_height_from_date(uint64_t date, uint64_t& res_h return true; } //------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height, bool need_global_indexes)const +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height)const { CRITICAL_REGION_LOCAL(m_read_lock); blocks_direct_container blocks_direct; @@ -3603,7 +3573,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height, bool request_coinbase_info)const +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height)const { CRITICAL_REGION_LOCAL(m_read_lock); if (!find_blockchain_supplement(qblock_ids, start_height)) @@ -3620,8 +3590,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list mis; get_transactions_direct(m_db_blocks[i]->bl.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, block " << get_block_hash(m_db_blocks[i]->bl) << " [" << i << "] contains missing transactions: " << mis); - if(request_coinbase_info) - blocks.back().third = m_db_transactions.find(get_transaction_hash(m_db_blocks[i]->bl.miner_tx)); + blocks.back().third = m_db_transactions.find(get_transaction_hash(m_db_blocks[i]->bl.miner_tx)); } return true; } @@ -3804,7 +3773,6 @@ bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx CHECK_AND_ASSERT_MES(res, false, "Internal error: multisig out not found, multisig_out_id " << multisig_out_id << "in multisig outs index"); } VARIANT_CASE_CONST(tx_out_zarcanum, toz) - // TODO: @#@# temporary comment this section and make a test for the corresponding bug if (!do_pop_output(i, 0)) return false; VARIANT_CASE_THROW_ON_OTHER(); @@ -3856,6 +3824,17 @@ uint64_t blockchain_storage::get_aliases_count() const return m_db_aliases.size(); } //------------------------------------------------------------------ +bool blockchain_storage::get_asset_history(const crypto::public_key& asset_id, std::list& result) const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + auto as_ptr = m_db_assets.find(asset_id); + if (!as_ptr) + return false; + + result = *as_ptr; + return true; +} +//------------------------------------------------------------------ bool blockchain_storage::get_asset_info(const crypto::public_key& asset_id, asset_descriptor_base& result) const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -3871,6 +3850,30 @@ bool blockchain_storage::get_asset_info(const crypto::public_key& asset_id, asse return false; } //------------------------------------------------------------------ +uint64_t blockchain_storage::get_assets(uint64_t offset, uint64_t count, std::list& assets) const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + assets.clear(); + m_db_assets.enumerate_items([&](uint64_t i, const crypto::public_key& asset_id, const std::list& asset_descriptor_history) + { + if (i < offset) + { + return true; + } + + CHECK_AND_ASSERT_THROW_MES(asset_descriptor_history.size(), "asset_descriptor_history unexpectedly have 0 size"); + assets.push_back(asset_descriptor_with_id()); + static_cast(assets.back()) = asset_descriptor_history.back().descriptor; + assets.back().asset_id = asset_id; + if (i + count > offset) + { + return false; + } + return true; + }); + return assets.size(); +} +//------------------------------------------------------------------ uint64_t blockchain_storage::get_assets_count() const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -4077,7 +4080,7 @@ bool blockchain_storage::pop_asset_info(const crypto::public_key& asset_id) return true; } //------------------------------------------------------------------ -bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& avc) +bool validate_ado_ownership(asset_op_verification_context& avc) { asset_operation_ownership_proof aoop = AUTO_VAL_INIT(aoop); bool r = get_type_in_variant_container(avc.tx.proofs, aoop); @@ -4089,32 +4092,27 @@ bool blockchain_storage::validate_ado_ownership(asset_op_verification_context& a return crypto::verify_schnorr_sig(avc.tx_id, owner_key, aoop.gss); } //------------------------------------------------------------------ -bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado) +bool blockchain_storage::validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const { CRITICAL_REGION_LOCAL(m_read_lock); - asset_op_verification_context avc = { tx, tx_id, ado }; + CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed"); + avc.asset_op_history = m_db_assets.find(avc.asset_id); + + const asset_descriptor_operation& ado = avc.ado; if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER) { - CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed"); - - avc.asset_op_history = m_db_assets.find(avc.asset_id); CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered"); - avc.amount_to_validate = ado.descriptor.current_supply; - CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "asset operation validation failed!"); - - assets_container::t_value_type local_asset_history = AUTO_VAL_INIT(local_asset_history); - local_asset_history.push_back(ado); - m_db_assets.set(avc.asset_id, local_asset_history); - LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); + CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "validate_asset_operation_amount_commitment failed!"); + if(this->is_hardfork_active(ZANO_HARDFORK_05)) + { + CHECK_AND_ASSERT_MES(validate_ado_initial(ado.descriptor), false, "validate_ado_initial failed!"); + } } else { - CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(avc.ado, &avc.asset_id_pt, &avc.asset_id), false, "get_or_calculate_asset_id failed"); - avc.asset_op_history = m_db_assets.find(avc.asset_id); - CHECK_AND_ASSERT_MES(avc.asset_op_history && avc.asset_op_history->size(), false, "asset with id " << avc.asset_id << " has not been registered"); // check ownership permission if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE /*|| ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN*/) @@ -4157,25 +4155,40 @@ bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::has bool r = validate_asset_operation_amount_commitment(avc); CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation"); } + } - assets_container::t_value_type local_asset_history = *avc.asset_op_history; - local_asset_history.push_back(ado); - m_db_assets.set(avc.asset_id, local_asset_history); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado) +{ + CRITICAL_REGION_LOCAL(m_read_lock); - switch(ado.operation_type) - { - case ASSET_DESCRIPTOR_OPERATION_UPDATE: - LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - case ASSET_DESCRIPTOR_OPERATION_EMIT: - LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN: - LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); - break; - default: - LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); - } + asset_op_verification_context avc = { tx, tx_id, ado }; + CHECK_AND_ASSERT_MES(validate_asset_operation_against_current_blochain_state(avc), false, "asset operation validation failed"); + + assets_container::t_value_type local_asset_history{}; + if (avc.asset_op_history) + local_asset_history = *avc.asset_op_history; + local_asset_history.push_back(ado); + m_db_assets.set(avc.asset_id, local_asset_history); + + switch(ado.operation_type) + { + case ASSET_DESCRIPTOR_OPERATION_REGISTER: + LOG_PRINT_MAGENTA("[ASSET_REGISTERED]: " << print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_UPDATE: + LOG_PRINT_MAGENTA("[ASSET_UPDATED]: " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_EMIT: + LOG_PRINT_MAGENTA("[ASSET_EMITTED]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); + break; + case ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN: + LOG_PRINT_MAGENTA("[ASSET_BURNT]: " << print_money_brief(avc.amount_to_validate, ado.descriptor.decimal_point) << ", " << avc.asset_id << ": " << ado.descriptor.ticker << ", \"" << ado.descriptor.full_name << "\"", LOG_LEVEL_1); + break; + default: + LOG_ERROR("Unknown operation type: " << (int)ado.operation_type); } return true; @@ -4277,16 +4290,17 @@ bool blockchain_storage::process_blockchain_tx_extra(const transaction& tx, cons bool blockchain_storage::get_outs_index_stat(outs_index_stat& outs_stat) const { CRITICAL_REGION_LOCAL(m_read_lock); - outs_stat.amount_0_001 = m_db_outputs.get_item_size(COIN / 1000); - outs_stat.amount_0_01 = m_db_outputs.get_item_size(COIN / 100); - outs_stat.amount_0_1 = m_db_outputs.get_item_size(COIN / 10); - outs_stat.amount_1 = m_db_outputs.get_item_size(COIN); - outs_stat.amount_10 = m_db_outputs.get_item_size(COIN * 10); - outs_stat.amount_100 = m_db_outputs.get_item_size(COIN * 100); - outs_stat.amount_1000 = m_db_outputs.get_item_size(COIN * 1000); - outs_stat.amount_10000 = m_db_outputs.get_item_size(COIN * 10000); - outs_stat.amount_100000 = m_db_outputs.get_item_size(COIN * 100000); - outs_stat.amount_1000000 = m_db_outputs.get_item_size(COIN * 1000000); + outs_stat.amount_0 = m_db_outputs.get_item_size(0); + outs_stat.amount_0_001 = m_db_outputs.get_item_size(COIN / 1000); + outs_stat.amount_0_01 = m_db_outputs.get_item_size(COIN / 100); + outs_stat.amount_0_1 = m_db_outputs.get_item_size(COIN / 10); + outs_stat.amount_1 = m_db_outputs.get_item_size(COIN); + outs_stat.amount_10 = m_db_outputs.get_item_size(COIN * 10); + outs_stat.amount_100 = m_db_outputs.get_item_size(COIN * 100); + outs_stat.amount_1000 = m_db_outputs.get_item_size(COIN * 1000); + outs_stat.amount_10000 = m_db_outputs.get_item_size(COIN * 10000); + outs_stat.amount_100000 = m_db_outputs.get_item_size(COIN * 100000); + outs_stat.amount_1000000 = m_db_outputs.get_item_size(COIN * 1000000); return true; } //------------------------------------------------------------------ @@ -4535,7 +4549,6 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const << " (fee: " << (is_coinbase(tx) ? "0 [coinbase]" : print_money_brief(get_tx_fee(tx))) << ")"); TIME_MEASURE_FINISH_PD_COND(need_to_profile, tx_print_log); - //@#@ del me // LOG_PRINT_L0("APPEND_TX_TIME_INNER: " << m_performance_data.tx_append_rl_wait.get_last_val() // << " | " << m_performance_data.tx_append_is_expired.get_last_val() // << " | " << m_performance_data.tx_process_extra.get_last_val() @@ -4731,7 +4744,8 @@ bool blockchain_storage::print_tx_outputs_lookup(const crypto::hash& tx_id)const continue; usage_stat[o.amount][tx_ptr->m_global_output_indexes[i]]; VARIANT_CASE_CONST(tx_out_zarcanum, toz) - //@#@ + strm_tx << "[" << i << "]: " << ENDL; + usage_stat[0][tx_ptr->m_global_output_indexes[i]]; VARIANT_SWITCH_END(); } @@ -4894,12 +4908,6 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha } VARIANT_CASE_CONST(txin_htlc, in_htlc) { - if (!is_hardfork_active(3)) // @#@ CZ, consider removing this to validate_tx_for_hardfork_specific_terms - { - LOG_ERROR("Error: Transaction with txin_htlc before hardfork 3 (before height " << m_core_runtime_config.hard_forks.get_str_height_the_hardfork_active_after(3) << ")"); - return false; - } - CHECK_AND_ASSERT_MES(in_htlc.key_offsets.size(), false, "Empty in_to_key.key_offsets for input #" << sig_index << " tx: " << tx_prefix_hash); if (!local_check_key_image(in_htlc.k_image)) return false; @@ -4994,7 +5002,7 @@ struct outputs_visitor //check tx unlock time uint64_t source_out_unlock_time = get_tx_unlock_time(source_tx, out_i); //let coinbase sources for PoS block to have locked inputs, the outputs supposed to be locked same way, except the reward - if (is_coinbase(validated_tx) && is_pos_miner_tx(validated_tx)) // @#@ consider changing to one call to is_pos_coinbase() + if (is_pos_miner_tx(validated_tx)) { CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(source_out_unlock_time), false, "source output #" << out_i << " is locked by time, not by height, which is not allowed for PoS coinbase"); if (source_out_unlock_time > m_source_max_unlock_time_for_pos_coinbase) @@ -5181,9 +5189,7 @@ bool blockchain_storage::check_ms_input(const transaction& tx, size_t in_index, LOC_CHK(tx.signatures.size() > in_index, "ms input index is out of signatures container bounds, signatures.size() = " << tx.signatures.size()); - //@#@ VARIANT_SWITCH_BEGIN(tx.signatures[in_index]); - VARIANT_CASE_CONST(void_sig, v); VARIANT_CASE_CONST(NLSAG_sig, sig) { const std::vector& input_signatures = sig.s; @@ -5228,9 +5234,9 @@ bool blockchain_storage::check_ms_input(const transaction& tx, size_t in_index, LOC_CHK(r, "failed to check extra signature for last out with TX_FLAG_SIGNATURE_MODE_SEPARATE"); } } - VARIANT_CASE_CONST(ZC_sig, s); - //@#@ - //TODO: don't forget about need_to_check_extra_sign + VARIANT_CASE_OTHER() + LOG_ERROR("Unexpected signature type: " << VARIANT_OBJ_TYPENAME); + return false; VARIANT_SWITCH_END(); @@ -5327,7 +5333,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, CRITICAL_REGION_LOCAL(m_read_lock); // we need a list this input is referring to - // and make sure that all of them are good (i.e. check 1) source tx unlock time validity; 2) mixin restrictions; 3) general gindex/ref_by_id corectness) + // and make sure that all of them are good (i.e. check: 1) source tx unlock time validity; 2) mixin restrictions; 3) general gindex/ref_by_id corectness) // get_output_keys_for_input_with_checks is used for that // std::vector dummy_output_keys; // won't be used @@ -5503,7 +5509,7 @@ std::shared_ptr blockchain_storage::find_key_imag { if (k_image == ki) { - id_result = get_transaction_hash(tx_chain_entry->tx); // ??? @#@# why not just use tx_id ? + id_result = tx_id; return tx_chain_entry; } } @@ -5519,7 +5525,6 @@ std::shared_ptr blockchain_storage::find_key_imag //--------------------------------------------------------------- bool blockchain_storage::fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short) const { - //tei.blob = tx_ptr->tx tei.id = epee::string_tools::pod_to_hex(h); if (!tei.blob_size) tei.blob_size = get_object_blobsize(tx); @@ -5536,6 +5541,9 @@ bool blockchain_storage::fill_tx_rpc_details(tx_rpc_extended_info& tei, const tr fill_tx_rpc_outputs(tei, tx, ptce); fill_tx_rpc_payload_items(tei.extra, tx.extra); fill_tx_rpc_payload_items(tei.attachments, tx.attachment); + + tei.blob = t_serializable_object_to_blob(tx); + tei.object_in_json = obj_to_json_str(tx); return true; } //------------------------------------------------------------------ @@ -5900,7 +5908,7 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti return true; } //------------------------------------------------------------------ -bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const +bool blockchain_storage::validate_pre_zarcanum_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const { uint64_t major_unlock_time = get_tx_x_detail(miner_tx); if (major_unlock_time) @@ -5927,7 +5935,7 @@ bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transactio for (uint64_t i = 0; i != miner_tx.vout.size(); i++) { uint64_t unlock_value = ut2.unlock_time_array[i]; - CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(unlock_value), false, "output #" << i << " is locked by time, not buy height, which is not allowed for PoS coinbase"); + CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(unlock_value), false, "output #" << i << " is locked by time, not by height, which is not allowed for PoS coinbase"); if (unlock_value >= source_max_unlock_time) { VARIANT_SWITCH_BEGIN(miner_tx.vout[i]); @@ -6104,8 +6112,8 @@ bool blockchain_storage::validate_pos_block(const block& b, uint64_t last_pow_h = get_last_x_block_height(false); CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to validate coinbase in PoS block, condition failed: max_related_block_height(" << max_related_block_height << ") <= last_pow_h(" << last_pow_h << ")"); //let's check that coinbase amount and unlock time - r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, source_max_unlock_time_for_pos_coinbase); - CHECK_AND_ASSERT_MES(r, false, "Failed to validate_pos_coinbase_outs_unlock_time() in miner tx, block_id = " << get_block_hash(b) + r = validate_pre_zarcanum_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, source_max_unlock_time_for_pos_coinbase); + CHECK_AND_ASSERT_MES(r, false, "Failed to validate_pre_zarcanum_pos_coinbase_outs_unlock_time() in miner tx, block_id = " << get_block_hash(b) << "source_max_unlock_time_for_pos_coinbase=" << source_max_unlock_time_for_pos_coinbase); } else @@ -6471,11 +6479,16 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt tx.signatures.clear(); tx.proofs.clear(); } - + currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc); //std::vector tx_outs_commitments; if (!m_is_in_checkpoint_zone) { - auto cleanup = [&](){ purge_block_data_from_blockchain(bl, tx_processed_count); bvc.m_verification_failed = true; }; + auto cleanup = [&](){ + bool add_res = m_tx_pool.add_tx(tx, tvc, true, true); + m_tx_pool.add_transaction_to_black_list(tx); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verification_failed = true; + }; CHECK_AND_ASSERT_MES_CUSTOM(collect_rangeproofs_data_from_tx(tx, tx_id, range_proofs_agregated), false, cleanup(), "block " << id << ", tx " << tx_id << ": collect_rangeproofs_data_from_tx failed"); @@ -6492,7 +6505,6 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt if(!check_tx_inputs(tx, tx_id)) { LOG_PRINT_L0("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs."); - currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc); if (taken_from_pool) { bool add_res = m_tx_pool.add_tx(tx, tvc, true, true); @@ -6553,15 +6565,19 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt return false; } - boost::multiprecision::uint128_t already_generated_coins = m_db_blocks.size() ? m_db_blocks.back()->already_generated_coins:0; - uint64_t base_reward = get_base_block_reward(is_pos_bl, already_generated_coins, height); + uint64_t block_reward_without_fee = 0; + if (!calculate_block_reward_for_next_top_block(cumulative_block_size, block_reward_without_fee)) + { + LOG_ERROR("calculate_block_reward_for_next_top_block filed"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verification_failed = true; + return false; + } if (!m_is_in_checkpoint_zone) { - // validate_miner_transaction will check balance proof and asset surjection proof - // and, as a side effect, it MAY recalculate base_reward, consider redisign, TODO -- sowle TIME_MEASURE_START_PD(validate_miner_transaction_time); - if (!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) // TODO @#@# base_reward will be calculated once again, consider refactoring + if (!validate_miner_transaction(bl.miner_tx, fee_summary, block_reward_without_fee)) { LOG_PRINT_L0("Block with id: " << id << " have wrong miner transaction"); @@ -6652,6 +6668,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt ////////////////////////////////////////////////////////////////////////// //etc + boost::multiprecision::uint128_t already_generated_coins = m_db_blocks.size() ? m_db_blocks.back()->already_generated_coins : 0; if (already_generated_coins < burned_coins) { LOG_ERROR("Condition failed: already_generated_coins(" << already_generated_coins << ") >= burned_coins(" << burned_coins << ")"); @@ -6659,7 +6676,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt bvc.m_verification_failed = true; return false; } - bei.already_generated_coins = already_generated_coins - burned_coins + base_reward; + bei.already_generated_coins = already_generated_coins - burned_coins + block_reward_without_fee; if (bei.bl.miner_tx.version >= TRANSACTION_VERSION_POST_HF4) { bei.already_generated_coins -= fee_summary; @@ -6714,7 +6731,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt TIME_MEASURE_FINISH_PD_MS(block_processing_time_0_ms); //print result - stringstream powpos_str_entry, timestamp_str_entry, pos_validation_str_entry; + stringstream powpos_str_entry, timestamp_str_entry, pos_validation_str_entry, block_reward_entry; if (is_pos_bl) { // PoS int64_t actual_ts = get_block_datetime(bei.bl); // signed int is intentionally used here @@ -6736,6 +6753,10 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt powpos_str_entry << "PoW:\t" << proof_hash; timestamp_str_entry << ", block ts: " << bei.bl.timestamp << " (diff: " << std::showpos << ts_diff << "s)"; } + if(bei.bl.miner_tx.version >= TRANSACTION_VERSION_POST_HF4) + block_reward_entry << "block reward: " << print_money_brief(block_reward_without_fee) << ", fee burnt: " << print_money_brief(fee_summary); + else + block_reward_entry << "block reward: " << print_money_brief(block_reward_without_fee + fee_summary) << " (" << print_money_brief(block_reward_without_fee) << " + " << print_money_brief(fee_summary) << ")"; //explanation of this code will be provided later with public announce set_lost_tx_unmixable_for_height(bei.height); @@ -6744,8 +6765,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt << ENDL << "id:\t" << id << timestamp_str_entry.str() << ENDL << powpos_str_entry.str() << ENDL << "HEIGHT " << bei.height << ", difficulty: " << current_diffic << ", cumul_diff_precise: " << bei.cumulative_diff_precise << ", cumul_diff_precise_adj: " << bei.cumulative_diff_precise_adjusted << " (+" << cumulative_diff_delta << ")" - << ENDL << "block reward: " << print_money_brief(base_reward + fee_summary) << " (" << print_money_brief(base_reward) << " + " << print_money_brief(fee_summary) - << ")" << ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", tx_count: " << bei.bl.tx_hashes.size() + << ENDL << block_reward_entry.str() << ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", tx_count: " << bei.bl.tx_hashes.size() << ", timing: " << block_processing_time_0_ms << "ms" << "(micrsec:" << block_processing_time_1 << "(" << target_calculating_time_2 << "(" << m_performance_data.target_calculating_enum_blocks.get_last_val() << "/" << m_performance_data.target_calculating_calc.get_last_val() << ")" @@ -6872,6 +6892,11 @@ bool blockchain_storage::is_hardfork_active(size_t hardfork_id) const return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, m_db_blocks.size()); // note using m_db_blocks.size() ( == top_block_height + 1 ) } //------------------------------------------------------------------ +bool blockchain_storage::is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const +{ + return m_core_runtime_config.is_hardfork_active_for_height(hardfork_id, height); +} +//------------------------------------------------------------------ bool blockchain_storage::prevalidate_block(const block& bl) { @@ -6994,7 +7019,12 @@ bool blockchain_storage::add_new_block(const block& bl, block_verification_conte if (m_event_handler) m_event_handler->on_complete_events(); return res; - } + } + catch (const std::bad_alloc&) + { + // failed memory allocation is critical; this is supposed to be handled by the called (assuming immediate stop) + throw; + } catch (const std::exception& ex) { bvc.m_verification_failed = true; @@ -7402,7 +7432,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, if (!alt_chain.empty()) { auto abg_it = alt_chain.back()->second.gindex_lookup_table.find(input_amount); - if (abg_it != alt_chain.back()->second.gindex_lookup_table.end()) + if (input_amount != 0 /* <-- TODO @#@# remove this condition after ZC outs support is implemented*/ && abg_it != alt_chain.back()->second.gindex_lookup_table.end()) { amount_touched_altchain = true; // local gindex lookup table contains last used gindex, so we can't get total number of outs diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 5bf57ad4..97f712e1 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -280,13 +280,13 @@ namespace currency bool get_short_chain_history(std::list& ids)const; bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp)const; bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset)const; - bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0, bool need_global_indexes = false)const; - bool find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0, bool request_coinbase_info = false)const; + bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0)const; + bool find_blockchain_supplement(const std::list& qblock_ids, blocks_direct_container& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count, uint64_t minimum_height = 0)const; //bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count)const; bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)const; bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const; bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const; - bool get_random_outs_for_amounts2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res)const; + bool get_random_outs_for_amounts3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res)const; bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count)const; bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs)const; bool get_alias_info(const std::string& alias, extra_alias_entry_base& info)const; @@ -299,8 +299,10 @@ namespace currency uint64_t get_aliases_count()const; uint64_t get_block_h_older_then(uint64_t timestamp) const; bool validate_tx_service_attachmens_in_services(const tx_service_attachment& a, size_t i, const transaction& tx)const; + bool get_asset_history(const crypto::public_key& asset_id, std::list& result) const; bool get_asset_info(const crypto::public_key& asset_id, asset_descriptor_base& info)const; uint64_t get_assets_count() const; + uint64_t get_assets(uint64_t offset, uint64_t count, std::list& assets) const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_htlc& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; @@ -338,7 +340,8 @@ namespace currency uint64_t get_last_timestamps_check_window_median() const; uint64_t get_last_n_blocks_timestamps_median(size_t n) const; bool prevalidate_alias_info(const transaction& tx, const extra_alias_entry& eae); - bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, const boost::multiprecision::uint128_t& already_generated_coins) const; + bool calculate_block_reward_for_next_top_block(size_t next_block_cumulative_size, uint64_t& block_reward_without_fee) const; + bool validate_miner_transaction(const transaction& miner_tx, uint64_t fee, uint64_t block_reward_without_fee) const; performnce_data& get_performnce_data()const; bool validate_instance(const std::string& path); bool is_tx_expired(const transaction& tx) const; @@ -346,6 +349,7 @@ namespace currency // returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage) bool is_hardfork_active(size_t hardfork_id) const; + bool is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const; bool fill_tx_rpc_inputs(tx_rpc_extended_info& tei, const transaction& tx) const; bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false) const; @@ -362,7 +366,7 @@ namespace currency uint64_t timestamp) const; bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash* p_last_block_hash = nullptr, uint64_t* p_last_pow_block_height = nullptr) const; - bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const; + bool validate_pre_zarcanum_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const; bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const; bool validate_pos_block(const block& b, wide_difficulty_type basic_diff, const crypto::hash& id, bool for_altchain)const; bool validate_pos_block(const block& b, @@ -374,6 +378,8 @@ namespace currency bool for_altchain, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0)const; + bool validate_asset_operation_against_current_blochain_state(asset_op_verification_context& avc) const; + void set_core_runtime_config(const core_runtime_config& pc) const; const core_runtime_config& get_core_runtime_config()const; size_t get_current_sequence_factor(bool pos)const; @@ -493,6 +499,7 @@ namespace currency bool print_tx_outputs_lookup(const crypto::hash& tx_id) const; uint64_t get_last_x_block_height(bool pos)const; bool is_tx_spendtime_unlocked(uint64_t unlock_time)const; + private: //-------------- DB containers -------------- @@ -643,8 +650,8 @@ namespace currency bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); bool add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i, uint64_t mix_count, bool use_only_forced_to_mix = false, uint64_t height_upper_limit = 0) const; - bool get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; - bool get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; + bool get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; + bool get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map& amounts_to_up_index_limit_cache) const; bool add_block_as_invalid(const block& bl, const crypto::hash& h); bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); size_t find_end_of_allowed_index(uint64_t amount)const; @@ -669,7 +676,6 @@ namespace currency bool unprocess_blockchain_tx_extra(const transaction& tx); bool process_blockchain_tx_attachments(const transaction& tx, uint64_t h, const crypto::hash& bl_id, uint64_t timestamp); bool unprocess_blockchain_tx_attachments(const transaction& tx, uint64_t h, uint64_t timestamp); - bool validate_ado_ownership(asset_op_verification_context& avc); bool pop_alias_info(const extra_alias_entry& ai); bool put_alias_info(const transaction& tx, extra_alias_entry& ai); bool pop_asset_info(const crypto::public_key& asset_id); diff --git a/src/currency_core/blockchain_storage_basic.h b/src/currency_core/blockchain_storage_basic.h index 27908fbf..65c6862b 100644 --- a/src/currency_core/blockchain_storage_basic.h +++ b/src/currency_core/blockchain_storage_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Copyright (c) 2012-2013 The Boolberry developers @@ -167,9 +167,9 @@ namespace currency uint64_t no; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(proposal_id) - KV_SERIALIZE(yes) - KV_SERIALIZE(no) + KV_SERIALIZE(proposal_id) DOC_DSCR("ID of the proposal.") DOC_EXMP("ZAP999") DOC_END + KV_SERIALIZE(yes) DOC_DSCR("Nubmer of positve votes.") DOC_EXMP(42) DOC_END + KV_SERIALIZE(no) DOC_DSCR("Number of negative votes.") DOC_EXMP(37) DOC_END END_KV_SERIALIZE_MAP() }; @@ -179,8 +179,8 @@ namespace currency std::list votes; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total_pos_blocks) - KV_SERIALIZE(votes) + KV_SERIALIZE(total_pos_blocks) DOC_DSCR("Number of blocks in a given range.") DOC_EXMP(87482) DOC_END + KV_SERIALIZE(votes) DOC_DSCR("Result of votes in a given range.") DOC_END END_KV_SERIALIZE_MAP() }; diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index b5aa7fab..80ddef17 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -589,11 +589,11 @@ namespace currency END_SERIALIZE() BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(service_id) - KV_SERIALIZE(instruction) - KV_SERIALIZE_BLOB_AS_HEX_STRING(body) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(security) - KV_SERIALIZE(flags) + KV_SERIALIZE(service_id) DOC_DSCR("Service ID, identificator that diferent one service from another") DOC_EXMP("C") DOC_END + KV_SERIALIZE(instruction) DOC_DSCR("Instruction that make sence for this particular service") DOC_EXMP("K") DOC_END + KV_SERIALIZE_BLOB_AS_HEX_STRING(body) DOC_DSCR("Hex-encoded body of the attachment") DOC_EXMP("dcfd7e055a6a3043ea3541a571a57a63e25dcc64e4a270f14fa9a58ac5dbec85dcfd7e055a6a3043ea3541a571a57a63e25dcc64e4a270f14fa9a58ac5dbec85") DOC_END + KV_SERIALIZE_CONTAINER_POD_AS_HEX(security) DOC_DSCR("Hex-encoded public key of the owner, optional") DOC_EXMP("d8f6e37f28a632c06b0b3466db1b9d2d1b36a580ee35edfd971dc1423bc412a5") DOC_END + KV_SERIALIZE(flags) DOC_DSCR("Flags that help wallet to automatically process some properties of the attachment(combination of TX_SERVICE_ATTACHMENT_ENCRYPT_BODY=1, TX_SERVICE_ATTACHMENT_DEFLATE_BODY=2, TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE=4,TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF=8 )") DOC_END END_KV_SERIALIZE_MAP() }; @@ -732,14 +732,14 @@ namespace currency END_BOOST_SERIALIZATION() BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total_max_supply) - KV_SERIALIZE(current_supply) - KV_SERIALIZE(decimal_point) - KV_SERIALIZE(ticker) - KV_SERIALIZE(full_name) - KV_SERIALIZE(meta_info) - KV_SERIALIZE_POD_AS_HEX_STRING(owner) - KV_SERIALIZE(hidden_supply) + KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for given asset, can't be changed after deployment") DOC_EXMP(1000000000000000000) DOC_END + KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for given asset") DOC_EXMP(500000000000000000) DOC_END + KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point") DOC_EXMP(12) DOC_END + KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with asset") DOC_EXMP("ZUSD") DOC_END + KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset") DOC_EXMP("Zano wrapped USD") DOC_END + KV_SERIALIZE(meta_info) DOC_DSCR("Any other information assetiaded with asset in a free form") DOC_EXMP("Stable and private") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used to validate any operations on the asset altering, could be changed in case of transfer ownership") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE(hidden_supply) DOC_DSCR("This one reserved for future use, will be documented later") DOC_END END_KV_SERIALIZE_MAP() }; @@ -757,7 +757,7 @@ namespace currency BEGIN_KV_SERIALIZE_MAP() KV_CHAIN_BASE(asset_descriptor_base) - KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset ID") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END END_KV_SERIALIZE_MAP() }; diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index 50ca76a8..a15af43c 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -10,7 +10,7 @@ #ifndef TESTNET #define CURRENCY_FORMATION_VERSION 84 #else -#define CURRENCY_FORMATION_VERSION 97 +#define CURRENCY_FORMATION_VERSION 98 #endif #define CURRENCY_GENESIS_NONCE (CURRENCY_FORMATION_VERSION + 101011010121) //bender's nightmare @@ -243,19 +243,15 @@ #define CURRENT_BLOCK_EXTENDED_INFO_ARCHIVE_VER 1 #define BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION CURRENCY_FORMATION_VERSION + 11 -#define BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION 1 +#define BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION 2 #define BC_OFFERS_CURRENT_OFFERS_SERVICE_ARCHIVE_VER CURRENCY_FORMATION_VERSION + BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION + 9 #define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin" -#ifndef TESTNET -#define WALLET_FILE_SERIALIZATION_VERSION 163 -#define WALLET_FILE_LAST_SUPPORTED_VERSION 163 -#else -#define WALLET_FILE_LAST_SUPPORTED_VERSION (CURRENCY_FORMATION_VERSION+76) -#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+76) -#endif + +#define WALLET_FILE_SERIALIZATION_VERSION 166 +#define WALLET_FILE_LAST_SUPPORTED_VERSION 165 #define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31) @@ -267,13 +263,17 @@ #define ZANO_HARDFORK_01_AFTER_HEIGHT 194624 // 2019-09-21 20:25:16 #define ZANO_HARDFORK_02_AFTER_HEIGHT 999999 // 2021-04-05 09:11:45 #define ZANO_HARDFORK_03_AFTER_HEIGHT 1082577 // 2021-06-01 23:28:10 -#define ZANO_HARDFORK_04_AFTER_HEIGHT 2555000 // 2024-03-21 10:16:46 (expected) +#define ZANO_HARDFORK_04_AFTER_HEIGHT 2555000 // 2024-03-21 11:49:55 +#define ZANO_HARDFORK_05_AFTER_HEIGHT 999999999999999999 +#define ZANO_HARDFORK_04_TIMESTAMP_ACTUAL 1711021795ull // block 2555000, 2024-03-21 11:49:55 UTC #else /////// Zarcanum Testnet ////////////////////////////// #define ZANO_HARDFORK_01_AFTER_HEIGHT 0 #define ZANO_HARDFORK_02_AFTER_HEIGHT 0 #define ZANO_HARDFORK_03_AFTER_HEIGHT 0 -#define ZANO_HARDFORK_04_AFTER_HEIGHT 2440 +#define ZANO_HARDFORK_04_AFTER_HEIGHT 200 +#define ZANO_HARDFORK_05_AFTER_HEIGHT 200 +#define ZANO_HARDFORK_04_TIMESTAMP_ACTUAL 1712785801ull // block 200, 2024-04-10 21:50:01 UTC #endif @@ -282,7 +282,8 @@ #define ZANO_HARDFORK_02 2 #define ZANO_HARDFORK_03 3 #define ZANO_HARDFORK_04_ZARCANUM 4 -#define ZANO_HARDFORKS_TOTAL 5 +#define ZANO_HARDFORK_05 5 +#define ZANO_HARDFORKS_TOTAL 6 @@ -299,4 +300,4 @@ static_assert(PREMINE_AMOUNT / WALLET_MAX_ALLOWED_OUTPUT_AMOUNT < CURRENCY_MINER #endif -#define WALLET_ASSETS_WHITELIST_VALIDATION_PUBLIC_KEY "" //TODO@#@ \ No newline at end of file +#define WALLET_ASSETS_WHITELIST_VALIDATION_PUBLIC_KEY "" //TODO@#@ diff --git a/src/currency_core/currency_core.cpp b/src/currency_core/currency_core.cpp index 876c2d17..1c15498b 100644 --- a/src/currency_core/currency_core.cpp +++ b/src/currency_core/currency_core.cpp @@ -163,6 +163,8 @@ namespace currency r = m_blockchain_storage.init(m_config_folder, vm); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + m_mempool.remove_incompatible_txs(); + r = m_miner.init(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner"); @@ -413,82 +415,102 @@ namespace currency //----------------------------------------------------------------------------------------------- bool core::handle_block_found(const block& b, block_verification_context* p_verification_result, bool need_update_miner_block_template) { - TIME_MEASURE_START_MS(time_total_ms); - block_verification_context bvc = boost::value_initialized(); - if (!p_verification_result) - p_verification_result = &bvc; - - m_miner.pause(); - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([this]() + try { - m_miner.resume(); - }); + TIME_MEASURE_START_MS(time_total_ms); + block_verification_context bvc = boost::value_initialized(); + if (!p_verification_result) + p_verification_result = &bvc; - TIME_MEASURE_START_MS(time_add_new_block_ms); - m_blockchain_storage.add_new_block(b, *p_verification_result); - TIME_MEASURE_FINISH_MS(time_add_new_block_ms); - - - //anyway - update miner template - TIME_MEASURE_START_MS(time_update_block_template_ms); - if (need_update_miner_block_template) - update_miner_block_template(); - TIME_MEASURE_FINISH_MS(time_update_block_template_ms); - - uint64_t time_pack_txs_ms = 0, time_relay_ms = 0; - - if (p_verification_result->m_verification_failed || !p_verification_result->m_added_to_main_chain) - { - LOG_PRINT2("failed_mined_blocks.log", "verification_failed: " << p_verification_result->m_verification_failed << ", added_to_main_chain: " << p_verification_result->m_added_to_main_chain << ENDL << - currency::obj_to_json_str(b), LOG_LEVEL_0); - } - CHECK_AND_ASSERT_MES(!p_verification_result->m_verification_failed, false, "mined block has failed to pass verification: id " << get_block_hash(b) << " @ height " << get_block_height(b) << " prev_id: " << b.prev_id); - - if(p_verification_result->m_added_to_main_chain) - { - time_pack_txs_ms = epee::misc_utils::get_tick_count(); - currency_connection_context exclude_context = boost::value_initialized(); - NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_size(); - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(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)) + m_miner.pause(); + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([this]() { - LOG_PRINT_L0("Block found (id " << get_block_hash(b) << " @ height " << get_block_height(b) << ") but it seems that reorganize just happened after that, do not relay this block"); - return true; + m_miner.resume(); + }); + + TIME_MEASURE_START_MS(time_add_new_block_ms); + m_blockchain_storage.add_new_block(b, *p_verification_result); + TIME_MEASURE_FINISH_MS(time_add_new_block_ms); + + + //anyway - update miner template + TIME_MEASURE_START_MS(time_update_block_template_ms); + if (need_update_miner_block_template) + update_miner_block_template(); + TIME_MEASURE_FINISH_MS(time_update_block_template_ms); + + uint64_t time_pack_txs_ms = 0, time_relay_ms = 0; + + if (p_verification_result->m_verification_failed || !p_verification_result->m_added_to_main_chain) + { + LOG_PRINT2("failed_mined_blocks.log", "verification_failed: " << p_verification_result->m_verification_failed << ", added_to_main_chain: " << p_verification_result->m_added_to_main_chain << ENDL << + currency::obj_to_json_str(b), LOG_LEVEL_0); } - if (txs.size() != b.tx_hashes.size() || missed_txs.size() != 0) + CHECK_AND_ASSERT_MES(!p_verification_result->m_verification_failed, false, "mined block has failed to pass verification: id " << get_block_hash(b) << " @ height " << get_block_height(b) << " prev_id: " << b.prev_id); + + if(p_verification_result->m_added_to_main_chain) { - std::stringstream ss; - ss << "txs:" << ENDL; - for (auto& t : txs) - ss << get_transaction_hash(t) << ENDL; - ss << "missed txs:" << ENDL; - for (auto& tx_id : missed_txs) - ss << tx_id << ENDL; - LOG_ERROR("can't find some transactions in found block: " << get_block_hash(b) << ", txs.size()=" << txs.size() - << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()=" << missed_txs.size() << ENDL << ss.str()); - return false; + time_pack_txs_ms = epee::misc_utils::get_tick_count(); + currency_connection_context exclude_context = boost::value_initialized(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_size(); + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(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_L0("Block found (id " << get_block_hash(b) << " @ height " << get_block_height(b) << ") but it seems that reorganize just happened after that, do not relay this block"); + return true; + } + if (txs.size() != b.tx_hashes.size() || missed_txs.size() != 0) + { + std::stringstream ss; + ss << "txs:" << ENDL; + for (auto& t : txs) + ss << get_transaction_hash(t) << ENDL; + ss << "missed txs:" << ENDL; + for (auto& tx_id : missed_txs) + ss << tx_id << ENDL; + LOG_ERROR("can't find some transactions in found block: " << get_block_hash(b) << ", txs.size()=" << txs.size() + << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()=" << missed_txs.size() << ENDL << ss.str()); + return false; + } + + block_to_blob(b, arg.b.block); + //pack transactions + for(auto& tx : txs) + arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + + TIME_MEASURE_FINISH_MS(time_pack_txs_ms); + + time_relay_ms = epee::misc_utils::get_tick_count(); + m_pprotocol->relay_block(arg, exclude_context); + TIME_MEASURE_FINISH_MS(time_relay_ms); } - block_to_blob(b, arg.b.block); - //pack transactions - for(auto& tx : txs) - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + TIME_MEASURE_FINISH_MS(time_total_ms); + LOG_PRINT_L2("handle_block_found timings (ms): total: " << time_total_ms << ", add new block: " << time_add_new_block_ms << ", update template: " << time_update_block_template_ms << ", pack txs: " << time_pack_txs_ms << ", relay: " << time_relay_ms); - TIME_MEASURE_FINISH_MS(time_pack_txs_ms); - - time_relay_ms = epee::misc_utils::get_tick_count(); - m_pprotocol->relay_block(arg, exclude_context); - TIME_MEASURE_FINISH_MS(time_relay_ms); + return p_verification_result->m_added_to_main_chain; + } + catch(std::bad_alloc&) + { + LOG_ERROR("bad_alloc in core::handle_block_found(), requesting immediate stop..."); + if (m_critical_error_handler) + m_critical_error_handler->on_immediate_stop_requested(); + return false; + } + catch(std::exception& e) + { + LOG_ERROR("caught exception in core::handle_block_found(): " << e.what()); + return false; + } + catch(...) + { + LOG_ERROR("caught unknown exception in core::handle_block_found()"); + return false; } - - TIME_MEASURE_FINISH_MS(time_total_ms); - LOG_PRINT_L2("handle_block_found timings (ms): total: " << time_total_ms << ", add new block: " << time_add_new_block_ms << ", update template: " << time_update_block_template_ms << ", pack txs: " << time_pack_txs_ms << ", relay: " << time_relay_ms); - - return p_verification_result->m_added_to_main_chain; } //----------------------------------------------------------------------------------------------- bool core::handle_block_found(const block& b, block_verification_context* p_verification_result /* = nullptr */) @@ -507,39 +529,59 @@ namespace currency //----------------------------------------------------------------------------------------------- bool core::add_new_block(const block& b, block_verification_context& bvc) { - uint64_t h = m_blockchain_storage.get_top_block_height(); - if (m_stop_after_height != 0 && h >= m_stop_after_height) + try { - LOG_PRINT_YELLOW("Blockchain top block height is " << h << ", the daemon will now stop as requested", LOG_LEVEL_0); - if (m_critical_error_handler) - return m_critical_error_handler->on_immediate_stop_requested(); - return false; - } - - bool r = m_blockchain_storage.add_new_block(b, bvc); - if (r && bvc.m_added_to_main_chain) - { - uint64_t h = get_block_height(b); - if (h > 0) + uint64_t h = m_blockchain_storage.get_top_block_height(); + if (m_stop_after_height != 0 && h >= m_stop_after_height) { - auto& crc = m_blockchain_storage.get_core_runtime_config(); - size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h); - size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h + 1); - if (hardfork_id_for_prev_block != hardfork_id_for_curr_block) - { - LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " has been activated after the block at height " << h, LOG_LEVEL_0); - } - } - - if (h == m_stop_after_height) - { - LOG_PRINT_YELLOW("Reached block " << h << ", the daemon will now stop as requested", LOG_LEVEL_0); + LOG_PRINT_YELLOW("Blockchain top block height is " << h << ", the daemon will now stop as requested", LOG_LEVEL_0); if (m_critical_error_handler) return m_critical_error_handler->on_immediate_stop_requested(); return false; } + + bool r = m_blockchain_storage.add_new_block(b, bvc); + if (r && bvc.m_added_to_main_chain) + { + uint64_t h = get_block_height(b); + if (h > 0) + { + auto& crc = m_blockchain_storage.get_core_runtime_config(); + size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h); + size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h + 1); + if (hardfork_id_for_prev_block != hardfork_id_for_curr_block) + { + LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " has been activated after the block at height " << h, LOG_LEVEL_0); + } + } + + if (h == m_stop_after_height) + { + LOG_PRINT_YELLOW("Reached block " << h << ", the daemon will now stop as requested", LOG_LEVEL_0); + if (m_critical_error_handler) + return m_critical_error_handler->on_immediate_stop_requested(); + return false; + } + } + return r; + } + catch(std::bad_alloc&) + { + LOG_ERROR("bad_alloc in core::add_new_block(), requesting immediate stop..."); + if (m_critical_error_handler) + m_critical_error_handler->on_immediate_stop_requested(); + return false; + } + catch(std::exception& e) + { + LOG_ERROR("caught exception in core::add_new_block(): " << e.what()); + return false; + } + catch(...) + { + LOG_ERROR("caught unknown exception in core::add_new_block()"); + return false; } - return r; } //----------------------------------------------------------------------------------------------- bool core::parse_block(const blobdata& block_blob, block& b, block_verification_context& bvc) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 8eb17e2d..5f4fa38f 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -261,6 +261,34 @@ namespace currency return diff; } //------------------------------------------------------------------ + bool add_random_derivation_hints_and_put_them_to_tx(uint8_t tx_flags, const std::set& existing_derivation_hints, std::set& new_derivation_hints, transaction& tx) + { + if (existing_derivation_hints.size() > tx.vout.size()) + { + if (tx.version < TRANSACTION_VERSION_POST_HF4 && (tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)) // for pre-HF4 consolidated txs just skip if all hints are already added + return true; + return false; + } + + size_t hints_to_be_added = tx.vout.size() - existing_derivation_hints.size(); + + size_t attempts_cnt = 0, attempts_max = 4096; + while(new_derivation_hints.size() < hints_to_be_added && ++attempts_cnt < attempts_max) + { + uint16_t hint = crypto::rand(); + if (existing_derivation_hints.count(hint) == 0) + new_derivation_hints.insert(hint); + } + + if (attempts_cnt == attempts_max) + return false; + + for(auto& el : new_derivation_hints) // iterating in sorted sequence + tx.extra.push_back(make_tx_derivation_hint_from_uint16(el)); + + return true; + } + //--------------------------------------------------------------- bool generate_tx_balance_proof(const transaction &tx, const crypto::hash& tx_id, const tx_generation_context& ogc, uint64_t block_reward_for_miner_tx, currency::zc_balance_proof& proof) { CHECK_AND_ASSERT_MES(tx.version > TRANSACTION_VERSION_PRE_HF4, false, "unsupported tx.version: " << tx.version); @@ -488,12 +516,12 @@ namespace currency // fill outputs tx_gen_context.resize(zc_ins_count, destinations.size()); // auxiliary data for each output uint64_t output_index = 0; - std::set deriv_cache; + std::set derivation_hints; for (auto& d : destinations) { finalized_tx result = AUTO_VAL_INIT(result); uint8_t tx_outs_attr = 0; - r = construct_tx_out(d, tx_gen_context.tx_key.sec, output_index, tx, deriv_cache, account_keys(), + r = construct_tx_out(d, tx_gen_context.tx_key.sec, output_index, tx, derivation_hints, account_keys(), tx_gen_context.asset_id_blinding_masks[output_index], tx_gen_context.amount_blinding_masks[output_index], tx_gen_context.blinded_asset_ids[output_index], tx_gen_context.amount_commitments[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed, output #" << output_index << ", amount: " << print_money_brief(d.amount)); @@ -504,6 +532,7 @@ namespace currency tx_gen_context.amount_commitments_sum += tx_gen_context.amount_commitments[output_index]; ++output_index; } + CHECK_AND_ASSERT_MES(add_random_derivation_hints_and_put_them_to_tx(0, std::set(), derivation_hints, tx), false, "add_random_derivation_hints_and_put_them_to_tx failed"); if (tx.attachment.size()) add_attachments_info_to_extra(tx.extra, tx.attachment); @@ -756,6 +785,7 @@ namespace currency } } //--------------------------------------------------------------- + // TODO: reverse order of arguments bool parse_amount(uint64_t& amount, const std::string& str_amount_) { std::string str_amount = str_amount_; @@ -1116,14 +1146,13 @@ namespace currency return dh; } //--------------------------------------------------------------- -// bool get_uint16_from_tx_derivation_hint(const tx_derivation_hint& dh, uint16_t& hint) -// { -// tx_derivation_hint dh; -// if (dh.msg.size() != sizeof(hint)) -// return false; -// hint = *((uint16_t*)dh.msg.data()); -// return true; -// } + bool get_uint16_from_tx_derivation_hint(const tx_derivation_hint& dh, uint16_t& hint) + { + if (dh.msg.size() != sizeof(hint)) + return false; + hint = *((uint16_t*)dh.msg.data()); + return true; + } //--------------------------------------------------------------- std::string generate_origin_for_htlc(const txout_htlc& htlc, const account_keys& acc_keys) { @@ -1151,6 +1180,12 @@ namespace currency return true; } //--------------------------------------------------------------- + bool validate_ado_initial(const asset_descriptor_base& new_ado) + { + if (new_ado.current_supply > new_ado.total_max_supply) return false; + return true; + } + //--------------------------------------------------------------- /* crypto::hash get_signature_hash_for_asset_operation(const asset_descriptor_operation& ado) { @@ -1250,11 +1285,7 @@ namespace currency out.mix_attr = tx_outs_attr; uint16_t hint = get_derivation_hint(reinterpret_cast(derivation)); - if (deriv_cache.count(hint) == 0) - { - tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint)); - deriv_cache.insert(hint); - } + deriv_cache.insert(hint); // won't be inserted if such hint already exists } tx.vout.push_back(out); @@ -1282,11 +1313,7 @@ namespace currency CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address"); uint16_t hint = get_derivation_hint(derivation); - if (deriv_cache.count(hint) == 0) - { - tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint)); - deriv_cache.insert(hint); - } + deriv_cache.insert(hint); // won't be inserted if such hint already exists } target_keys.push_back(out_eph_public_key); } @@ -1311,11 +1338,7 @@ namespace currency htlc.pkey_refund = out_eph_public_key; //add derivation hint for refund address uint16_t hint = get_derivation_hint(derivation); - if (deriv_cache.count(hint) == 0) - { - tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint)); - deriv_cache.insert(hint); - } + deriv_cache.insert(hint); // won't be inserted if such hint already exists if (htlc_dest.htlc_hash == null_hash) @@ -1905,7 +1928,22 @@ namespace currency } return n; } - + //--------------------------------------------------------------- + bool copy_all_derivation_hints_from_tx_to_container(transaction& tx, std::set& derivation_hints) + { + for(auto it = tx.extra.begin(); it != tx.extra.end(); ++it) + { + if (it->type() == typeid(tx_derivation_hint)) + { + uint16_t hint = 0; + if (!get_uint16_from_tx_derivation_hint(boost::get(*it), hint)) + return false; + if (!derivation_hints.insert(hint).second) + return false; // maybe we need to skip this? + } + } + return true; + } //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, @@ -2575,14 +2613,15 @@ namespace currency uint64_t native_coins_output_sum = 0; size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 uint64_t range_proof_start_index = 0; - std::set deriv_cache; + std::set existing_derivation_hints, new_derivation_hints; + CHECK_AND_ASSERT_MES(copy_all_derivation_hints_from_tx_to_container(tx, existing_derivation_hints), false, "move_all_derivation_hints_from_tx_to_container failed"); for(size_t destination_index = 0; destination_index < shuffled_dsts.size(); ++destination_index, ++output_index) { tx_destination_entry& dst_entr = shuffled_dsts[destination_index]; if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) dst_entr.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) - r = construct_tx_out(dst_entr, gen_context.tx_key.sec, output_index, tx, deriv_cache, sender_account_keys, + r = construct_tx_out(dst_entr, gen_context.tx_key.sec, output_index, tx, new_derivation_hints, sender_account_keys, gen_context.asset_id_blinding_masks[output_index], gen_context.amount_blinding_masks[output_index], gen_context.blinded_asset_ids[output_index], gen_context.amount_commitments[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out"); @@ -2595,6 +2634,8 @@ namespace currency native_coins_output_sum += dst_entr.amount; } + CHECK_AND_ASSERT_MES(add_random_derivation_hints_and_put_them_to_tx(flags, existing_derivation_hints, new_derivation_hints, tx), false, "add_random_derivation_hints_and_put_them_to_tx failed"); + //process offers and put there offers derived keys uint64_t att_count = 0; for (auto& o : tx.attachment) @@ -2773,6 +2814,7 @@ namespace currency return CURRENT_TRANSACTION_VERSION; } //--------------------------------------------------------------- + // TODO @#@# this function is obsolete and needs to be re-written uint64_t get_reward_from_miner_tx(const transaction& tx) { uint64_t income = 0; @@ -3076,12 +3118,12 @@ namespace currency return true; } //----------------------------------------------------------------------------------------------- - bool check_money_overflow(const transaction& tx) + bool check_bare_money_overflow(const transaction& tx) { - return check_inputs_overflow(tx) && check_outs_overflow(tx); + return check_bare_inputs_overflow(tx) && check_bare_outs_overflow(tx); } //--------------------------------------------------------------- - bool check_inputs_overflow(const transaction& tx) + bool check_bare_inputs_overflow(const transaction& tx) { uint64_t money = 0; for(const auto& in : tx.vin) @@ -3118,7 +3160,7 @@ namespace currency return true; } //--------------------------------------------------------------- - bool check_outs_overflow(const transaction& tx) + bool check_bare_outs_overflow(const transaction& tx) { uint64_t money = 0; for(const auto& o : tx.vout) @@ -4015,7 +4057,7 @@ namespace currency pei_rpc.miner_text_info = eud.buff; } - pei_rpc.base_reward = get_base_block_reward(is_pos_block(bei_chain.bl), bei_chain.already_generated_coins, bei_chain.height); + pei_rpc.base_reward = get_base_block_reward(bei_chain.height); pei_rpc.summary_reward = get_reward_from_miner_tx(bei_chain.bl.miner_tx); pei_rpc.penalty = (pei_rpc.base_reward + pei_rpc.total_fee) - pei_rpc.summary_reward; return true; @@ -4034,7 +4076,7 @@ namespace currency gindices[amount] += 1; } VARIANT_CASE_CONST(tx_out_zarcanum, o) - //@#@ + gindices[0] += 1; VARIANT_SWITCH_END(); } } @@ -4068,7 +4110,7 @@ namespace currency return CURRENCY_MAX_BLOCK_SIZE; } //----------------------------------------------------------------------------------------------- - uint64_t get_base_block_reward(bool is_pos, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t height) + uint64_t get_base_block_reward(uint64_t height) { if (!height) return PREMINE_AMOUNT; @@ -4076,9 +4118,18 @@ namespace currency return CURRENCY_BLOCK_REWARD; } //----------------------------------------------------------------------------------------------- + // Modern version, requires only necessary arguments. Returns 0 if block is too big (current_block_size > 2 * median_block_size) + uint64_t get_block_reward(uint64_t height, size_t median_block_size, size_t current_block_size) + { + uint64_t reward = 0; + get_block_reward(/* is_pos - doesn't matter */ false, median_block_size, current_block_size, /* boost::multiprecision::uint128_t -- doesn't matter*/ boost::multiprecision::uint128_t(0), reward, height); + return reward; + } + //----------------------------------------------------------------------------------------------- + // legacy version, some arguments are unnecessary now bool get_block_reward(bool is_pos, size_t median_size, size_t current_block_size, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t &reward, uint64_t height) { - uint64_t base_reward = get_base_block_reward(is_pos, already_generated_coins, height); + uint64_t base_reward = get_base_block_reward(height); //make it soft if (median_size < CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 8f858303..fd33a6b8 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -276,6 +276,7 @@ namespace currency bool check_tx_bare_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); bool check_tx_balance(const transaction& tx, const crypto::hash& tx_id, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); bool validate_asset_operation_amount_commitment(asset_op_verification_context& context); + const char* get_asset_operation_type_string(size_t asset_operation_type, bool short_name = false); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, @@ -413,9 +414,9 @@ namespace currency uint64_t get_alias_coast_from_fee(const std::string& alias, uint64_t fee_median); //const crypto::public_key get_offer_secure_key_by_index_from_tx(const transaction& tx, size_t index); - bool check_money_overflow(const transaction& tx); - bool check_outs_overflow(const transaction& tx); - bool check_inputs_overflow(const transaction& tx); + bool check_bare_money_overflow(const transaction& tx); + bool check_bare_outs_overflow(const transaction& tx); + bool check_bare_inputs_overflow(const transaction& tx); uint64_t get_block_height(const transaction& coinbase); uint64_t get_block_height(const block& b); std::vector relative_output_offsets_to_absolute(const std::vector& off); @@ -463,7 +464,7 @@ namespace currency std::string generate_origin_for_htlc(const txout_htlc& htlc, const account_keys& acc_keys); bool validate_ado_update_allowed(const asset_descriptor_base& a, const asset_descriptor_base& b); - + bool validate_ado_initial(const asset_descriptor_base& a); void normalize_asset_operation_for_hashing(asset_descriptor_operation& op); crypto::hash get_signature_hash_for_asset_operation(const asset_descriptor_operation& ado); @@ -521,8 +522,9 @@ namespace currency /************************************************************************/ size_t get_max_block_size(); size_t get_max_tx_size(); + uint64_t get_block_reward(uint64_t height, size_t median_block_size, size_t current_block_size); bool get_block_reward(bool is_pos, size_t median_size, size_t current_block_size, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t &reward, uint64_t height); - uint64_t get_base_block_reward(bool is_pos, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t height); + uint64_t get_base_block_reward(uint64_t height); bool is_payment_id_size_ok(const payment_id_t& payment_id); std::string get_account_address_as_str(const account_public_address& addr); std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id); @@ -534,6 +536,20 @@ namespace currency bool is_pos_coinbase(const transaction& tx); bool have_attachment_service_in_container(const std::vector& av, const std::string& service_id, const std::string& instruction); crypto::hash prepare_prefix_hash_for_sign(const transaction& tx, uint64_t in_index, const crypto::hash& tx_id); + + + //--------------------------------------------------------------- + template + void assets_map_to_assets_list(std::list& assets_list, const t_assets_map& assets_map) + { + for (const auto& pr : assets_map) + { + assets_list.push_back(currency::asset_descriptor_with_id()); + assets_list.back().asset_id = pr.first; + epee::misc_utils::cast_assign_a_to_b(assets_list.back(), static_cast(pr.second)); + //*static_cast(&assets_list.back()) = pr.second; + } + } //--------------------------------------------------------------- template diff --git a/src/currency_core/currency_format_utils_abstract.h b/src/currency_core/currency_format_utils_abstract.h index 11d051b9..3e5a63fe 100644 --- a/src/currency_core/currency_format_utils_abstract.h +++ b/src/currency_core/currency_format_utils_abstract.h @@ -134,7 +134,7 @@ namespace currency //--------------------------------------------------------------- // if cb returns true, it means "continue", false -- means "stop" template - bool process_type_in_variant_container(const variant_container_t& av, callback_t& cb, bool return_value_if_none_found = true) + bool process_type_in_variant_container(const variant_container_t& av, callback_t&& cb, bool return_value_if_none_found = true) { bool found = false; for (auto& ai : av) @@ -151,6 +151,27 @@ namespace currency return return_value_if_none_found; } //--------------------------------------------------------------- + // if cb returns false, stop immediately and return false + template + bool process_type_in_variant_container_and_make_sure_its_unique(const variant_container_t& av, callback_t&& cb, bool return_value_if_none_found = true) + { + bool found = false; + for (auto& ai : av) + { + if (ai.type() == typeid(specific_type_t)) + { + if (found) + return false; // already have it, type in not unique + found = true; + if (!cb(boost::get(ai))) + return false; + } + } + if (found) + return true; + return return_value_if_none_found; + } + //--------------------------------------------------------------- // callback should return true to continue iterating through the container template bool handle_2_alternative_types_in_variant_container(const container_t& container, callback_t cb) diff --git a/src/currency_core/offers_service_basics.h b/src/currency_core/offers_service_basics.h index 1dffe367..63feedb9 100644 --- a/src/currency_core/offers_service_basics.h +++ b/src/currency_core/offers_service_basics.h @@ -37,21 +37,21 @@ namespace bc_services //----------------- BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_N(offer_type, "ot") - KV_SERIALIZE_CUSTOM_N(amount_primary, std::string, bc_services::transform_amount_to_string, bc_services::transform_string_to_amount, "ap") - KV_SERIALIZE_CUSTOM_N(amount_target, std::string, bc_services::transform_amount_to_string, bc_services::transform_string_to_amount, "at") - KV_SERIALIZE_N(bonus, "b") - KV_SERIALIZE_N(target, "t") - KV_SERIALIZE_N(primary, "p") - KV_SERIALIZE_N(location_country, "lco") - KV_SERIALIZE_N(location_city, "lci") - KV_SERIALIZE_N(contacts, "cnt") - KV_SERIALIZE_N(comment, "com") - KV_SERIALIZE_N(payment_types, "pt") - KV_SERIALIZE_N(deal_option, "do") - KV_SERIALIZE_N(category, "cat") - KV_SERIALIZE_N(expiration_time, "et") - KV_SERIALIZE_N(preview_url, "url") + KV_SERIALIZE_N(offer_type, "ot") DOC_DSCR("Type of the offer: OFFER_TYPE_PRIMARY_TO_TARGET(SELL ORDER) - 0, OFFER_TYPE_TARGET_TO_PRIMARY(BUY ORDER) - 1 etc.") DOC_EXMP(0) DOC_END + KV_SERIALIZE_CUSTOM_N(amount_primary, std::string, bc_services::transform_amount_to_string, bc_services::transform_string_to_amount, "ap") DOC_DSCR("Amount of the currency") DOC_EXMP("100000") DOC_END + KV_SERIALIZE_CUSTOM_N(amount_target, std::string, bc_services::transform_amount_to_string, bc_services::transform_string_to_amount, "at") DOC_DSCR("Smount of other currency or goods") DOC_EXMP("10000000") DOC_END + KV_SERIALIZE_N(bonus, "b") DOC_DSCR("Bonus associated with the offer") DOC_EXMP("") DOC_END + KV_SERIALIZE_N(target, "t") DOC_DSCR("Target: currency / goods") DOC_EXMP("USDT") DOC_END + KV_SERIALIZE_N(primary, "p") DOC_DSCR("Currency for goods") DOC_EXMP("ZANO") DOC_END + KV_SERIALIZE_N(location_country, "lco") DOC_DSCR("Country of the offer location") DOC_EXMP("Montenegro") DOC_END + KV_SERIALIZE_N(location_city, "lci") DOC_DSCR("City of the offer location") DOC_EXMP("Kolasin") DOC_END + KV_SERIALIZE_N(contacts, "cnt") DOC_DSCR("Contacts related to the offer") DOC_EXMP("Ranko +38211111111") DOC_END + KV_SERIALIZE_N(comment, "com") DOC_DSCR("Comment associated with the offer") DOC_EXMP("Dobr dan") DOC_END + KV_SERIALIZE_N(payment_types, "pt") DOC_DSCR("Types of payment accepted for the offer") DOC_EXMP("zano") DOC_END + KV_SERIALIZE_N(deal_option, "do") DOC_DSCR("Deal option for the offer") DOC_EXMP("full amount, by parts") DOC_END + KV_SERIALIZE_N(category, "cat") DOC_DSCR("Category of the offer") DOC_EXMP("") DOC_END + KV_SERIALIZE_N(expiration_time, "et") DOC_DSCR("Expiration time of the offer") DOC_EXMP(0) DOC_END + KV_SERIALIZE_N(preview_url, "url") DOC_DSCR("URL for previewing the offer") DOC_EXMP("") DOC_END END_KV_SERIALIZE_MAP() }; @@ -69,12 +69,12 @@ namespace bc_services mutable bool stopped; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_hash) - KV_SERIALIZE_POD_AS_HEX_STRING(tx_original_hash) - KV_SERIALIZE(index_in_tx) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(fee) - KV_SERIALIZE_POD_AS_HEX_STRING(security) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_hash) DOC_DSCR("Transaction hash represented as a hexadecimal string") DOC_EXMP("cc608f59f8080e2fbfe3c8c80eb6e6a953d47cf2d6aebd345bada3a1cab99852") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(tx_original_hash) DOC_DSCR("Origin transaction hash represented as a hexadecimal string(if offer updated)") DOC_EXMP("cc608f59f8080e2fbfe3c8c80eb6e6a953d47cf2d6aebd345bada3a1cab99852") DOC_END + KV_SERIALIZE(index_in_tx) DOC_DSCR("Index of the tx_service_attachment entrie in transaction") DOC_EXMP(0) DOC_END + KV_SERIALIZE(timestamp) DOC_DSCR("Timestamp of the transaction") DOC_EXMP(1712683857) DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Fee associated with the transaction") DOC_EXMP(10000000000) DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(security) DOC_DSCR("Onwer's public key for access control") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END KV_CHAIN_BASE(offer_details) END_KV_SERIALIZE_MAP() }; @@ -163,32 +163,33 @@ namespace bc_services bool bonus; std::string category; std::string keyword; - bool fake; + //bool fake; uint64_t current_time; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(order_by) - KV_SERIALIZE(reverse) - KV_SERIALIZE(offset) - KV_SERIALIZE(limit) - KV_SERIALIZE(timestamp_start) - KV_SERIALIZE(timestamp_stop) - KV_SERIALIZE(offer_type_mask) - KV_SERIALIZE(amount_low_limit) - KV_SERIALIZE(amount_up_limit) - KV_SERIALIZE_CUSTOM(rate_low_limit, std::string, bc_services::transform_double_to_string, bc_services::transform_string_to_double) - KV_SERIALIZE_CUSTOM(rate_up_limit, std::string, bc_services::transform_double_to_string, bc_services::transform_string_to_double) - KV_SERIALIZE(payment_types) - KV_SERIALIZE(location_country) - KV_SERIALIZE(location_city) - KV_SERIALIZE(target) - KV_SERIALIZE(primary) - KV_SERIALIZE(bonus) - KV_SERIALIZE(category) - KV_SERIALIZE(keyword) - KV_SERIALIZE(fake) + KV_SERIALIZE(order_by) DOC_DSCR("Field to order the results by one on this: ORDER_BY_TIMESTAMP=0,ORDER_BY_AMOUNT_PRIMARY=1,ORDER_BY_AMOUNT_TARGET=2,ORDER_BY_AMOUNT_RATE=3,ORDER_BY_PAYMENT_TYPES=4,ORDER_BY_CONTACTS=5,ORDER_BY_LOCATION=6,ORDER_BY_NAME=7") DOC_EXMP(0) DOC_END + KV_SERIALIZE(reverse) DOC_DSCR("Flag to indicate whether the results should be sorted in reverse order") DOC_EXMP(false) DOC_END + KV_SERIALIZE(offset) DOC_DSCR("Offset for pagination") DOC_EXMP(0) DOC_END + KV_SERIALIZE(limit) DOC_DSCR("Maximum number of results to return") DOC_EXMP(100) DOC_END + KV_SERIALIZE(timestamp_start) DOC_DSCR("Start timestamp for filtering results") DOC_EXMP(0) DOC_END + KV_SERIALIZE(timestamp_stop) DOC_DSCR("Stop timestamp for filtering results") DOC_EXMP(0) DOC_END + KV_SERIALIZE(offer_type_mask) DOC_DSCR("Mask representing the types of offers to include in the results, conbination of this: OFFER_TYPE_MASK_PRIMARY_TO_TARGET 0x00000001, OFFER_TYPE_MASK_TARGET_TO_PRIMARY 0x00000002, OFFER_TYPE_MASK_GOODS_TO_PRIMARY 0x00000004, OFFER_TYPE_MASK_PRIMARY_TO_GOODS 0x00000008") DOC_EXMP(0) DOC_END + KV_SERIALIZE(amount_low_limit) DOC_DSCR("Lower limit for the amount of offers") DOC_EXMP(0) DOC_END + KV_SERIALIZE(amount_up_limit) DOC_DSCR("Upper limit for the amount of offers") DOC_EXMP(0) DOC_END + KV_SERIALIZE_CUSTOM(rate_low_limit, std::string, bc_services::transform_double_to_string, bc_services::transform_string_to_double) DOC_DSCR("Lower limit for the rate") DOC_EXMP("0.1") DOC_END + KV_SERIALIZE_CUSTOM(rate_up_limit, std::string, bc_services::transform_double_to_string, bc_services::transform_string_to_double) DOC_DSCR("Upper limit for the rate") DOC_EXMP("0.1") DOC_END + KV_SERIALIZE(payment_types) DOC_DSCR("Types of payment accepted for the offers(in a free form as it is in contract)") DOC_END + KV_SERIALIZE(location_country) DOC_DSCR("Country of the location for the offers") DOC_END + KV_SERIALIZE(location_city) DOC_DSCR("City of the location for the offers") DOC_END + KV_SERIALIZE(target) DOC_DSCR("Target entity of the offers") DOC_END + KV_SERIALIZE(primary) DOC_DSCR("Primary field for the offers") DOC_END + KV_SERIALIZE(bonus) DOC_DSCR("Bonus associated with the offers") DOC_EXMP(false) DOC_END + KV_SERIALIZE(category) DOC_DSCR("Category of the offers") DOC_END + KV_SERIALIZE(keyword) DOC_DSCR("Keyword for searching offers") DOC_EXMP("tubes") DOC_END + //KV_SERIALIZE(fake) DOC_DSCR("Flag indicating whether the offer is fake") DOC_EXMP() DOC_END END_KV_SERIALIZE_MAP() + }; diff --git a/src/currency_core/pos_mining.cpp b/src/currency_core/pos_mining.cpp index 5bc1d973..478284d8 100644 --- a/src/currency_core/pos_mining.cpp +++ b/src/currency_core/pos_mining.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Zano Project +// Copyright (c) 2022-2024 Zano Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // @@ -56,7 +56,7 @@ namespace currency bool found = false; - if (this->zarcanum /* && td.is_zc() */) + if (this->zarcanum) { crypto::mp::uint256_t lhs; crypto::mp::uint512_t rhs; @@ -70,7 +70,7 @@ namespace currency const boost::multiprecision::uint256_t d_mp = lhs / (crypto::c_zarcanum_z_coeff_mp * this->stake_amount) + 1; const boost::multiprecision::uint256_t ba = d_mp * crypto::c_zarcanum_z_coeff_mp * this->stake_amount - lhs; const boost::multiprecision::uint256_t l_div_z_D = this->z_l_div_z_D / crypto::c_zarcanum_z_coeff_mp; - LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(this->stake_amount) << /* ", gindex: " << td.m_global_output_index << */ ENDL + LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(this->stake_amount) << ENDL << "difficulty: " << this->basic_diff << ENDL << "kernel info: " << ENDL << print_stake_kernel_info(this->sk) @@ -78,9 +78,8 @@ namespace currency << "lhs: 0x" << crypto::scalar_t(lhs).to_string_as_hex_number() << " = 0x" << std::hex << d_mp << " * 2^64 * " << this->stake_amount << " - 0x" << std::hex << ba << ENDL << "rhs: 0x" << crypto::scalar_t(rhs).to_string_as_hex_number() << ENDL << "d: 0x" << std::hex << d_mp << ENDL - << "l / floor(z * D): 0x" << std::hex << l_div_z_D + << "floor(l / z * D): 0x" << std::hex << l_div_z_D , LOG_LEVEL_0); - } } else diff --git a/src/currency_core/tx_pool.cpp b/src/currency_core/tx_pool.cpp index 94a69124..33315870 100644 --- a/src/currency_core/tx_pool.cpp +++ b/src/currency_core/tx_pool.cpp @@ -104,6 +104,20 @@ namespace currency //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const transaction &tx, const crypto::hash &id, uint64_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool from_core) { + // ------------------ UNSECURE CODE FOR TESTS --------------------- + if (m_unsecure_disable_tx_validation_on_addition) + { + uint64_t tx_fee = 0; + CHECK_AND_ASSERT_MES(get_tx_fee(tx, tx_fee), false, "get_tx_fee failed"); + do_insert_transaction(tx, id, blob_size, kept_by_block, tx_fee, null_hash, 0); + tvc.m_added_to_pool = true; + tvc.m_should_be_relayed = true; + tvc.m_verification_failed = false; + tvc.m_verification_impossible = false; + return true; + } + // ---------------- END OF UNSECURE CODE FOR TESTS ------------------- + bool r = false; // defaults @@ -224,6 +238,20 @@ namespace currency } TIME_MEASURE_FINISH_PD(check_inputs_time); + if (tx.version > TRANSACTION_VERSION_PRE_HF4) + { + TIME_MEASURE_START_PD(check_post_hf4_balance); + r = check_tx_balance(tx, id); + CHECK_AND_ASSERT_MES_CUSTOM(r, false, { tvc.m_verification_failed = true; }, "post-HF4 tx: balance proof is invalid"); + TIME_MEASURE_FINISH_PD(check_post_hf4_balance); + + r = process_type_in_variant_container_and_make_sure_its_unique(tx.extra, [&](const asset_descriptor_operation& ado){ + asset_op_verification_context avc = { tx, id, ado }; + return m_blockchain.validate_asset_operation_against_current_blochain_state(avc); + }, true); + CHECK_AND_ASSERT_MES_CUSTOM(r, false, { tvc.m_verification_failed = true; }, "post-HF4 tx: asset operation is invalid"); + } + do_insert_transaction(tx, id, blob_size, kept_by_block, tx_fee, ch_inp_res ? max_used_block_id : null_hash, ch_inp_res ? max_used_block_height : 0); TIME_MEASURE_FINISH_PD(tx_processing_time); @@ -240,9 +268,11 @@ namespace currency << "/" << m_performance_data.validate_alias_time.get_last_val() << "/" << m_performance_data.check_keyimages_ws_ms_time.get_last_val() << "/" << m_performance_data.check_inputs_time.get_last_val() + << "/b"<< m_performance_data.check_post_hf4_balance.get_last_val() << "/" << m_performance_data.begin_tx_time.get_last_val() << "/" << m_performance_data.update_db_time.get_last_val() - << "/" << m_performance_data.db_commit_time.get_last_val() << ")" ); + << "/" << m_performance_data.db_commit_time.get_last_val() + << ")"); return true; } @@ -875,7 +905,7 @@ namespace currency { //not the best implementation at this time, sorry :( - if (m_db_black_tx_list.get(get_transaction_hash(txd.tx))) + if (is_tx_blacklisted(get_transaction_hash(txd.tx))) return false; //check is ring_signature already checked ? @@ -966,8 +996,8 @@ namespace currency return "(no transactions, the pool is empty)"; // sort output by receive time txs.sort([](const std::pair& lhs, const std::pair& rhs) -> bool { return lhs.second.receive_time < rhs.second.receive_time; }); - ss << "# | transaction id | size | fee | ins | outs | outs money | live_time | max used block | last failed block | kept by a block?" << ENDL; - // 1234 f99fe6d4335fc0ddd69e6880a4d95e0f6ea398de0324a6837021a61c6a31cacd 87157 0.10000111 2000 2000 112000.12345678 d0.h10.m16.s17 123456 <12345..> 123456 <12345..> YES + ss << "# | transaction id | size | fee | ins | outs | live_time | max used block | last failed block | ver | status " << ENDL; + // 1234 f99fe6d4335fc0ddd69e6880a4d95e0f6ea398de0324a6837021a61c6a31cacd 187157 0.10000111 2000 2000 d0.h10.m16.s17 1234567 <12345..> 1234567 <12345..> 2 kept_by_block BLACKLISTED size_t i = 0; for (auto& tx : txs) { @@ -975,17 +1005,17 @@ namespace currency ss << std::left << std::setw(4) << i++ << " " << tx.first << " " - << std::setw(5) << txd.blob_size << " " + << std::setw(6) << txd.blob_size << " " << std::setw(10) << print_money_brief(txd.fee) << " " << std::setw(4) << txd.tx.vin.size() << " " << std::setw(4) << txd.tx.vout.size() << " " - << std::right << std::setw(15) << print_money(get_outs_money_amount(txd.tx)) << std::left << " " << std::setw(14) << epee::misc_utils::get_time_interval_string(get_core_time() - txd.receive_time) << " " - << std::setw(6) << txd.max_used_block_height << " " + << std::setw(7) << txd.max_used_block_height << " " << std::setw(9) << print16(txd.max_used_block_id) << " " - << std::setw(6) << txd.last_failed_height << " " + << std::setw(7) << txd.last_failed_height << " " << std::setw(9) << print16(txd.last_failed_id) << " " - << (txd.kept_by_block ? "YES" : "no ") + << std::setw(3) << txd.tx.version << " " + << (txd.kept_by_block ? "kept_by_block " : "") << (is_tx_blacklisted(tx.first) ? "BLACKLISTED " : "") << ENDL; } return ss.str(); @@ -1293,6 +1323,32 @@ namespace currency return true; } //--------------------------------------------------------------------------------- + void tx_memory_pool::remove_incompatible_txs() + { + std::vector invalid_tx_ids; + + m_db_transactions.enumerate_items([&](uint64_t i, const crypto::hash& h, const tx_details &tx_entry) + { + if (!m_blockchain.validate_tx_for_hardfork_specific_terms(tx_entry.tx, h)) + invalid_tx_ids.push_back(h); + return true; + }); + + for(const auto& id : invalid_tx_ids) + { + transaction tx{}; + size_t blob_size = 0; + uint64_t fee = 0; + take_tx(id, tx, blob_size, fee); + LOG_PRINT_L0("tx " << id << " was incompatible with the hardfork rules and removed"); + } + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::is_tx_blacklisted(const crypto::hash& id) const + { + return m_db_black_tx_list.get(id) != nullptr; + } + //--------------------------------------------------------------------------------- bool tx_memory_pool::load_keyimages_cache() { CRITICAL_REGION_LOCAL(m_key_images_lock); diff --git a/src/currency_core/tx_pool.h b/src/currency_core/tx_pool.h index 4ede52a2..c97a498a 100644 --- a/src/currency_core/tx_pool.h +++ b/src/currency_core/tx_pool.h @@ -77,7 +77,8 @@ namespace currency epee::math_helper::average check_inputs_time; epee::math_helper::average begin_tx_time; epee::math_helper::average update_db_time; - epee::math_helper::average db_commit_time; + epee::math_helper::average db_commit_time; + epee::math_helper::average check_post_hf4_balance; }; typedef std::unordered_map> key_image_cache; @@ -138,6 +139,14 @@ namespace currency bool remove_stuck_transactions(); // made public to be called from coretests + void remove_incompatible_txs(); // made public to be called after the BCS is loaded and hardfork info is ready + + bool is_tx_blacklisted(const crypto::hash& id) const; + +#ifdef TX_POOL_USE_UNSECURE_TEST_FUNCTIONS + void unsecure_disable_tx_validation_on_addition(bool validation_disabled) { m_unsecure_disable_tx_validation_on_addition = validation_disabled; } +#endif + private: bool on_tx_add(crypto::hash tx_id, const transaction& tx, bool kept_by_block); bool on_tx_remove(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block); @@ -192,6 +201,7 @@ namespace currency key_image_cache m_key_images; mutable epee::critical_section m_remove_stuck_txs_lock; + bool m_unsecure_disable_tx_validation_on_addition = false; }; } diff --git a/src/currency_core/tx_semantic_validation.cpp b/src/currency_core/tx_semantic_validation.cpp index 26d95916..d358edf2 100644 --- a/src/currency_core/tx_semantic_validation.cpp +++ b/src/currency_core/tx_semantic_validation.cpp @@ -60,7 +60,7 @@ namespace currency return false; } - if (!check_money_overflow(tx)) + if (!check_bare_money_overflow(tx)) { LOG_PRINT_RED_L0("tx has money overflow, rejected for tx id= " << get_transaction_hash(tx)); return false; diff --git a/src/currency_protocol/currency_protocol_defs_print.h b/src/currency_protocol/currency_protocol_defs_print.h index 68f6fa4b..023ed305 100644 --- a/src/currency_protocol/currency_protocol_defs_print.h +++ b/src/currency_protocol/currency_protocol_defs_print.h @@ -57,7 +57,7 @@ namespace currency std::stringstream ss; ss << "\"blocks\":{" << ENDL << print_complete_block_entry_list(v.blocks) << ENDL << "}, " << ENDL; ss << "\"missed_ids\":" << ENDL; - ::epee::serialization::dump_as_json(ss, v.missed_ids, 2); + ::epee::serialization::recursive_visitor<::epee::serialization::strategy_json>::dump_as_(ss, v.missed_ids, 2); ss << ENDL << "\"current_blockchain_height\":" << v.current_blockchain_height; return ss.str(); } diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index a23e85b5..9f4c22de 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -171,6 +171,11 @@ namespace currency return true; } + if(hshd.top_id == currency::null_hash) + { + LOG_PRINT_L0("wtf"); + } + int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_size()); LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, (is_inital ? "Inital ":"Idle ") << "sync data returned unknown top block (" << hshd.top_id << "): " << m_core.get_top_block_height() << " -> " << hshd.current_height - 1 << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / DIFFICULTY_TOTAL_TARGET ) << " days) " diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index cb09bdb2..5f46b088 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -155,7 +155,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, command_line::arg_log_level); command_line::add_arg(desc_cmd_sett, command_line::arg_console); command_line::add_arg(desc_cmd_only, command_line::arg_show_details); - command_line::add_arg(desc_cmd_only, command_line::arg_show_rpc_autodoc); + command_line::add_arg(desc_cmd_only, command_line::arg_generate_rpc_autodoc); command_line::add_arg(desc_cmd_sett, command_line::arg_disable_stop_if_time_out_of_sync); command_line::add_arg(desc_cmd_sett, command_line::arg_disable_stop_on_low_free_space); command_line::add_arg(desc_cmd_sett, command_line::arg_enable_offers_service); @@ -272,21 +272,15 @@ int main(int argc, char* argv[]) if (stratum_enabled) stratum_server_ptr = std::make_shared(&ccore); - if (command_line::get_arg(vm, command_line::arg_show_rpc_autodoc)) - { - LOG_PRINT_L0("Dumping RPC auto-generated documents!"); - epee::net_utils::http::http_request_info query_info; - epee::net_utils::http::http_response_info response_info; - epee::net_utils::connection_context_base conn_context; - std::string generate_reference = std::string("RPC_COMMANDS_LIST:\n"); - bool call_found = false; - rpc_server.handle_http_request_map(query_info, response_info, conn_context, call_found, generate_reference); - std::string json_rpc_reference; - query_info.m_URI = JSON_RPC_REFERENCE_MARKER; - query_info.m_body = "{\"jsonrpc\": \"2.0\", \"method\": \"nonexisting_method\", \"params\": {}},"; - rpc_server.handle_http_request_map(query_info, response_info, conn_context, call_found, json_rpc_reference); - LOG_PRINT_L0(generate_reference << ENDL << "----------------------------------------" << ENDL << json_rpc_reference); + + if (command_line::has_arg(vm, command_line::arg_generate_rpc_autodoc)) + { + std::string path_to_generate = command_line::get_arg(vm, command_line::arg_generate_rpc_autodoc); + + if (!generate_doc_as_md_files(path_to_generate, rpc_server)) + return 1; + return 0; } bool res = false; diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 7e395584..8f2aa24a 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -47,6 +47,7 @@ public: m_cmd_binder.set_handler("print_block_info", boost::bind(&daemon_commands_handler::print_block_info, this, ph::_1), "Print block info, print_block | "); m_cmd_binder.set_handler("print_tx_prun_info", boost::bind(&daemon_commands_handler::print_tx_prun_info, this, ph::_1), "Print tx prunning info"); m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_commands_handler::print_tx, this, ph::_1), "Print transaction, print_tx "); + m_cmd_binder.set_handler("print_asset_info", boost::bind(&daemon_commands_handler::print_asset_info, this, ph::_1), "Print information about the given asset by its id"); m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_commands_handler::start_mining, this, ph::_1), "Start mining for specified address, start_mining [threads=1]"); m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_commands_handler::stop_mining, this, ph::_1), "Stop mining"); m_cmd_binder.set_handler("print_pool", boost::bind(&daemon_commands_handler::print_pool, this, ph::_1), "Print transaction pool (long format)"); @@ -688,7 +689,7 @@ private: { if (args.empty()) { - std::cout << "expected: print_tx " << std::endl; + std::cout << "usage: print_tx " << std::endl; return true; } @@ -696,55 +697,74 @@ private: crypto::hash tx_hash; if (!parse_hash256(str_hash, tx_hash)) { + LOG_PRINT_RED_L0("invalid tx hash was given"); return true; } - // std::vector tx_ids; - // tx_ids.push_back(tx_hash); - // std::list txs; - // std::list missed_ids; - // m_srv.get_payload_object().get_core().get_transactions(tx_ids, txs, missed_ids); - - currency::transaction_chain_entry tx_entry = AUTO_VAL_INIT(tx_entry); - - if (!m_srv.get_payload_object().get_core().get_blockchain_storage().get_tx_chain_entry(tx_hash, tx_entry)) + currency::tx_rpc_extended_info tx_rpc_ei{}; + currency::core& core = m_srv.get_payload_object().get_core(); + if (core.get_blockchain_storage().get_tx_rpc_details(tx_hash, tx_rpc_ei, 0, false)) { - LOG_PRINT_RED("transaction wasn't found: " << tx_hash, LOG_LEVEL_0); + // pass } - currency::block_extended_info bei = AUTO_VAL_INIT(bei); - m_srv.get_payload_object().get_core().get_blockchain_storage().get_block_extended_info_by_height(tx_entry.m_keeper_block_height, bei); - uint64_t timestamp = bei.bl.timestamp; - - const currency::transaction& tx = tx_entry.tx; - std::stringstream ss; - - ss << "------------------------------------------------------" - << ENDL << "tx_id: " << tx_hash - << ENDL << "keeper_block: " << tx_entry.m_keeper_block_height << ", timestamp (" << timestamp << ") " << epee::misc_utils::get_internet_time_str(timestamp) - << ENDL << currency::obj_to_json_str(tx) - << ENDL << "------------------------------------------------------" - << ENDL << epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(tx)) - << ENDL << "------------------------------------------------------"; - - - ss << "ATTACHMENTS: " << ENDL; - for (auto at : tx.attachment) + else if (core.get_tx_pool().get_transaction_details(tx_hash, tx_rpc_ei)) { - if (at.type() == typeid(currency::tx_service_attachment)) - { - const currency::tx_service_attachment& sa = boost::get(at); - ss << "++++++++++++++++++++++++++++++++ " << ENDL; - ss << "[SERVICE_ATTACHMENT]: ID = \'" << sa.service_id << "\', INSTRUCTION: \'" << sa.instruction << "\'" << ENDL; + // pass + } + else + { + LOG_PRINT_RED("transaction " << tx_hash << " was not found in either the blockchain or the pool", LOG_LEVEL_0); + return true; + } - if (!(sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY)) + std::stringstream ss; + ss << ENDL << + "----------------------TX-HEX--------------------------" << ENDL << + epee::string_tools::buff_to_hex_nodelimer(tx_rpc_ei.blob) << ENDL << + "------------------END-OF-TX-HEX-----------------------" << ENDL; + + ss << ENDL << + "tx " << tx_hash << " "; + + if (tx_rpc_ei.keeper_block == -1) + { + ss << "has been in the transaction pool for " << epee::misc_utils::get_time_interval_string(core.get_blockchain_storage().get_core_runtime_config().get_core_time() - tx_rpc_ei.timestamp) << + ", added " << epee::misc_utils::get_internet_time_str(tx_rpc_ei.timestamp) << ", ts: " << tx_rpc_ei.timestamp << ", blob size: " << tx_rpc_ei.blob_size << ENDL; + } + else + { + ss << "was added to block @ " << tx_rpc_ei.keeper_block << ", block time: " << epee::misc_utils::get_internet_time_str(tx_rpc_ei.timestamp) << ", ts: " << tx_rpc_ei.timestamp << ", blob size: " << tx_rpc_ei.blob_size << ENDL; + } + + ss << ENDL << + "-----------------------JSON---------------------------" << ENDL << + tx_rpc_ei.object_in_json << ENDL << + "--------------------END-OF-JSON-----------------------" << ENDL; + + currency::transaction tx{}; + CHECK_AND_ASSERT_MES(currency::parse_and_validate_tx_from_blob(tx_rpc_ei.blob, tx), true, "parse_and_validate_tx_from_blob failed"); + + if (!tx.attachment.empty()) + { + ss << "ATTACHMENTS: " << ENDL; + for (auto at : tx.attachment) + { + if (at.type() == typeid(currency::tx_service_attachment)) { - std::string body = sa.body; - if (sa.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) + const currency::tx_service_attachment& sa = boost::get(at); + ss << "++++++++++++++++++++++++++++++++ " << ENDL; + ss << "[SERVICE_ATTACHMENT]: ID = \'" << sa.service_id << "\', INSTRUCTION: \'" << sa.instruction << "\'" << ENDL; + + if (!(sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY)) { - bool r = epee::zlib_helper::unpack(sa.body, body); - CHECK_AND_ASSERT_MES(r, false, "Failed to unpack"); + std::string body = sa.body; + if (sa.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) + { + bool r = epee::zlib_helper::unpack(sa.body, body); + CHECK_AND_ASSERT_MES(r, false, "Failed to unpack"); + } + ss << "BODY: " << body << ENDL; } - ss << "BODY: " << body << ENDL; } } } @@ -753,6 +773,56 @@ private: return true; } //-------------------------------------------------------------------------------- + bool print_asset_info(const std::vector& args) + { + if (args.empty()) + { + std::cout << "usage: print_asset_info " << std::endl; + return true; + } + + const std::string& str_asset_id = args.front(); + crypto::public_key asset_id{}; + crypto::point_t asset_id_pt{}; + if (!crypto::parse_tpod_from_hex_string(str_asset_id, asset_id) || !asset_id_pt.from_public_key(asset_id)) + { + LOG_PRINT_RED_L0("invalid asset id was given"); + return true; + } + + currency::core& core = m_srv.get_payload_object().get_core(); + + std::list asset_history; + if (!core.get_blockchain_storage().get_asset_history(asset_id, asset_history)) + { + LOG_PRINT_RED_L0("asset id doesn't present in the blockchain"); + return true; + } + + std::stringstream ss; + ss << ENDL << + "history for asset " << asset_id << ":" << ENDL; + + size_t idx = 0; + for(auto& aop: asset_history) + { + ss << "[" << std::setw(2) << idx << "] operation: " << currency::get_asset_operation_type_string(aop.operation_type) << " (" << (int)aop.operation_type << ")" << ENDL << + " ticker: \"" << aop.descriptor.ticker << "\"" << ENDL << + " full name: \"" << aop.descriptor.full_name << "\"" << ENDL << + " meta info: \"" << aop.descriptor.meta_info << "\"" << ENDL << + " current supply: " << currency::print_money_brief(aop.descriptor.current_supply, aop.descriptor.decimal_point) << ENDL << + " max supply: " << currency::print_money_brief(aop.descriptor.total_max_supply, aop.descriptor.decimal_point) << ENDL << + " decimal point: " << (int)aop.descriptor.decimal_point << ENDL << + " owner * 1/8: " << aop.descriptor.owner << ENDL << + " amount cmt * 1/8: " << aop.amount_commitment << ENDL << + " hidden supply: " << (aop.descriptor.hidden_supply ? "yes" : "no") << ENDL << + ""; + } + + LOG_PRINT_L0(ss.str()); + return true; + } + //-------------------------------------------------------------------------------- bool print_tx_outputs_usage(const std::vector& args) { if (args.empty()) diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index d2cc787c..6cb817e0 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -223,7 +223,7 @@ QString MainWindow::get_default_user_dir(const QString& param) } -bool MainWindow::toggle_mining() +bool MainWindow::toggle_mining(const QString& param) { TRY_ENTRY(); m_backend.toggle_pos_mining(); @@ -238,7 +238,7 @@ QString MainWindow::get_exchange_last_top(const QString& params) CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::get_tx_pool_info() +QString MainWindow::get_tx_pool_info(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -248,7 +248,7 @@ QString MainWindow::get_tx_pool_info() CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::request_dummy() +QString MainWindow::request_dummy(const QString& param) { static int code_ = 0; TRY_ENTRY(); @@ -284,7 +284,7 @@ QString MainWindow::call_rpc(const QString& params) epee::net_utils::http::http_request_info query_info = AUTO_VAL_INIT(query_info); epee::net_utils::http::http_response_info response_info = AUTO_VAL_INIT(response_info); - currency::core_rpc_server::connection_context dummy_context = AUTO_VAL_INIT(dummy_context); + currency::core_rpc_server::connection_context dummy_context(RPC_INTERNAL_UI_CONTEXT, 0, 0, true); query_info.m_URI = "/json_rpc"; query_info.m_body = params.toStdString(); @@ -301,14 +301,34 @@ QString MainWindow::call_rpc(const QString& params) return QString::fromStdString(response_info.m_body); CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::get_default_fee() + +QString MainWindow::call_wallet_rpc(const QString& wallet_id_str, const QString& params) +{ + TRY_ENTRY(); + + if (!m_backend.is_core_initialized()) + { + epee::json_rpc::error_response rsp; + rsp.jsonrpc = "2.0"; + rsp.error.code = -1; + rsp.error.message = API_RETURN_CODE_CORE_BUSY; + return QString::fromStdString(epee::serialization::store_t_to_json(static_cast(rsp))); + } + + uint64_t wallet_id = std::stoull(wallet_id_str.toStdString()); + + return QString::fromStdString(m_backend.invoke(wallet_id, params.toStdString())); + CATCH_ENTRY_FAIL_API_RESPONCE(); +} + +QString MainWindow::get_default_fee(const QString& param) { TRY_ENTRY(); return QString(std::to_string(m_backend.get_default_fee()).c_str()); CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::get_options() +QString MainWindow::get_options(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -319,7 +339,7 @@ QString MainWindow::get_options() CATCH_ENTRY_FAIL_API_RESPONCE(); } -void MainWindow::tray_quit_requested() +void MainWindow::tray_quit_requested(const QString& param) { TRY_ENTRY(); LOG_PRINT_MAGENTA("[GUI]->[HTML] tray_quit_requested", LOG_LEVEL_0); @@ -350,10 +370,10 @@ void MainWindow::closeEvent(QCloseEvent *event) } else { + event->ignore(); //m_quit_requested = true; LOG_PRINT_L0("[GUI]->[HTML] quit_requested"); emit quit_requested("{}"); - event->ignore(); } CATCH_ENTRY2(void()); } @@ -474,7 +494,7 @@ bool MainWindow::init(const std::string& html_path) CATCH_ENTRY2(false); } -void MainWindow::on_menu_show() +void MainWindow::on_menu_show(const QString& param) { TRY_ENTRY(); qDebug() << "Context menu: show()"; @@ -546,7 +566,7 @@ void MainWindow::bool_toggle_icon(const QString& param) CATCH_ENTRY2(void()); } -QString MainWindow::get_log_file() +QString MainWindow::get_log_file(const QString& param) { TRY_ENTRY(); std::string buff; @@ -662,7 +682,7 @@ QString MainWindow::set_clipboard(const QString& param) CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } -QString MainWindow::get_clipboard() +QString MainWindow::get_clipboard(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -671,7 +691,7 @@ QString MainWindow::get_clipboard() CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } -QString MainWindow::on_request_quit() +QString MainWindow::on_request_quit(const QString& param) { TRY_ENTRY(); LOG_PRINT_MAGENTA("[HTML]->[GUI] on_request_quit", LOG_LEVEL_0); @@ -933,7 +953,7 @@ bool MainWindow::init_backend(int argc, char* argv[]) CATCH_ENTRY2(false); } -QString MainWindow::is_remnotenode_mode_preconfigured() +QString MainWindow::is_remnotenode_mode_preconfigured(const QString& param) { TRY_ENTRY(); return API_RETURN_CODE_FALSE; @@ -997,11 +1017,11 @@ bool MainWindow::update_wallet_status(const view::wallet_status_info& wsi) m_wallet_states->operator [](wsi.wallet_id) = wsi.wallet_state; std::string json_str_pub; - epee::serialization::store_t_to_json(static_cast(wsi), json_str_pub, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(static_cast(wsi), json_str_pub, 0); LOG_PRINT_L0(get_wallet_log_prefix(wsi.wallet_id) + "SENDING SIGNAL -> [update_wallet_status]:" << std::endl << json_str_pub); std::string json_str; - epee::serialization::store_t_to_json(wsi, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(wsi, json_str, 0); QMetaObject::invokeMethod(this, "update_wallet_status", Qt::QueuedConnection, Q_ARG(QString, json_str.c_str())); return true; CATCH_ENTRY2(false); @@ -1011,7 +1031,7 @@ bool MainWindow::set_options(const view::gui_options& opt) { TRY_ENTRY(); std::string json_str; - epee::serialization::store_t_to_json(opt, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(opt, json_str, 0); LOG_PRINT_L0("SENDING SIGNAL -> [set_options]:" << std::endl << json_str); QMetaObject::invokeMethod(this, "set_options", Qt::QueuedConnection, Q_ARG(QString, json_str.c_str())); return true; @@ -1022,7 +1042,7 @@ bool MainWindow::update_tor_status(const view::current_action_status& opt) { TRY_ENTRY(); std::string json_str; - epee::serialization::store_t_to_json(opt, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(opt, json_str, 0); LOG_PRINT_L0("SENDING SIGNAL -> [HANDLE_CURRENT_ACTION_STATE]:" << std::endl << json_str); QMetaObject::invokeMethod(this, "handle_current_action_state", Qt::QueuedConnection, Q_ARG(QString, json_str.c_str())); return true; @@ -1044,7 +1064,7 @@ bool MainWindow::nativeEventFilter(const QByteArray &eventType, void *message, l CATCH_ENTRY2(false); } -bool MainWindow::get_is_disabled_notifications() +bool MainWindow::get_is_disabled_notifications(const QString& param) { return m_config.disable_notifications; } @@ -1067,7 +1087,7 @@ bool MainWindow::update_wallets_info(const view::wallets_summary_info& wsi) { TRY_ENTRY(); std::string json_str; - epee::serialization::store_t_to_json(wsi, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(wsi, json_str, 0); LOG_PRINT_L0("SENDING SIGNAL -> [update_wallets_info]"<< std::endl << json_str ); QMetaObject::invokeMethod(this, "update_wallets_info", Qt::QueuedConnection, Q_ARG(QString, json_str.c_str())); @@ -1079,7 +1099,7 @@ bool MainWindow::money_transfer(const view::transfer_event_info& tei) { TRY_ENTRY(); std::string json_str; - epee::serialization::store_t_to_json(tei, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(tei, json_str, 0); LOG_PRINT_L0(get_wallet_log_prefix(tei.wallet_id) + "SENDING SIGNAL -> [money_transfer]" << std::endl << json_str); //this->money_transfer(json_str.c_str()); @@ -1134,7 +1154,7 @@ bool MainWindow::money_transfer_cancel(const view::transfer_event_info& tei) { TRY_ENTRY(); std::string json_str; - epee::serialization::store_t_to_json(tei, json_str, 0, epee::serialization::eol_lf); + epee::serialization::store_t_to_json(tei, json_str, 0); LOG_PRINT_L0(get_wallet_log_prefix(tei.wallet_id) + "SENDING SIGNAL -> [money_transfer_cancel]"); //this->money_transfer_cancel(json_str.c_str()); @@ -1149,7 +1169,7 @@ bool MainWindow::wallet_sync_progress(const view::wallet_sync_progres_param& p) TRY_ENTRY(); LOG_PRINT_L2(get_wallet_log_prefix(p.wallet_id) + "SENDING SIGNAL -> [wallet_sync_progress]" << " wallet_id: " << p.wallet_id << ": " << p.progress << "%"); //this->wallet_sync_progress(epee::serialization::store_t_to_json(p).c_str()); - QMetaObject::invokeMethod(this, "wallet_sync_progress", Qt::QueuedConnection, Q_ARG(QString, epee::serialization::store_t_to_json(p, 0, epee::serialization::eol_lf).c_str())); + QMetaObject::invokeMethod(this, "wallet_sync_progress", Qt::QueuedConnection, Q_ARG(QString, epee::serialization::store_t_to_json(p, 0).c_str())); return true; CATCH_ENTRY2(false); } @@ -1181,21 +1201,21 @@ bool MainWindow::pos_block_found(const currency::block& block_found) CATCH_ENTRY2(false); } -QString MainWindow::get_version() +QString MainWindow::get_version(const QString& param) { TRY_ENTRY(); return PROJECT_VERSION_LONG; CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::get_os_version() +QString MainWindow::get_os_version(const QString& param) { TRY_ENTRY(); return tools::get_os_version_string().c_str(); CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } -QString MainWindow::get_network_type() +QString MainWindow::get_network_type(const QString& param) { #if defined(TESTNET) return "testnet"; @@ -1211,7 +1231,7 @@ QString MainWindow::get_alias_coast(const QString& param) PREPARE_ARG_FROM_JSON(currency::struct_with_one_t_type, lvl); view::get_alias_coast_response resp; resp.error_code = m_backend.get_alias_coast(lvl.v, resp.coast); - return epee::serialization::store_t_to_json(resp, 0, epee::serialization::eol_lf).c_str(); + return epee::serialization::store_t_to_json(resp, 0).c_str(); CATCH_ENTRY_FAIL_API_RESPONCE(); } @@ -1239,7 +1259,7 @@ QString MainWindow::set_localization_strings(const QString param) resp.error_code = API_RETURN_CODE_OK; LOG_PRINT_L0("New localization set, language title: " << lr.language_title << ", strings " << lr.strings.size()); } - return epee::serialization::store_t_to_json(resp, 0, epee::serialization::eol_lf).c_str(); + return epee::serialization::store_t_to_json(resp, 0).c_str(); CATCH_ENTRY_FAIL_API_RESPONCE(); } @@ -1408,7 +1428,7 @@ void MainWindow::on_clear_events() } -QString MainWindow::store_secure_app_data(const QString& param) +QString MainWindow::store_secure_app_data(const QString& param, const QString& password) { TRY_ENTRY(); LOG_API_TIMING(); @@ -1626,7 +1646,7 @@ QString MainWindow::load_from_file(const QString& path) CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } -QString MainWindow::get_app_data() +QString MainWindow::get_app_data(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -1637,7 +1657,7 @@ QString MainWindow::get_app_data() } -QString MainWindow::have_secure_app_data() +QString MainWindow::have_secure_app_data(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -1655,7 +1675,7 @@ QString MainWindow::have_secure_app_data() CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::drop_secure_app_data() +QString MainWindow::drop_secure_app_data(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -1673,7 +1693,7 @@ QString MainWindow::drop_secure_app_data() CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::get_all_aliases() +QString MainWindow::get_all_aliases(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -1779,7 +1799,7 @@ QString MainWindow::set_enable_tor(const QString& param) // return MAKE_RESPONSE(ar); // } -QString MainWindow::webkit_launched_script() +QString MainWindow::webkit_launched_script(const QString& param) { TRY_ENTRY(); m_last_update_daemon_status_json.clear(); @@ -1795,7 +1815,7 @@ QString MainWindow::show_openfile_dialog(const QString& param) if (!epee::serialization::load_t_from_json(ofdr, param.toStdString())) { ofdres.error_code = API_RETURN_CODE_BAD_ARG; - return epee::serialization::store_t_to_json(ofdres, 0, epee::serialization::eol_lf).c_str(); + return epee::serialization::store_t_to_json(ofdres, 0).c_str(); } QString path = QFileDialog::getOpenFileName(this, ofdr.caption.c_str(), @@ -1805,7 +1825,7 @@ QString MainWindow::show_openfile_dialog(const QString& param) if (!path.length()) { ofdres.error_code = API_RETURN_CODE_CANCELED; - return epee::serialization::store_t_to_json(ofdres, 0, epee::serialization::eol_lf).c_str(); + return epee::serialization::store_t_to_json(ofdres, 0).c_str(); } ofdres.error_code = API_RETURN_CODE_OK; @@ -1828,7 +1848,7 @@ QString MainWindow::show_savefile_dialog(const QString& param) if (!path.length()) { ofdres.error_code = API_RETURN_CODE_CANCELED; - return epee::serialization::store_t_to_json(ofdres, 0, epee::serialization::eol_lf).c_str(); + return epee::serialization::store_t_to_json(ofdres, 0).c_str(); } ofdres.error_code = API_RETURN_CODE_OK; @@ -1993,7 +2013,7 @@ QString MainWindow::get_fav_offers(const QString& param) return MAKE_RESPONSE(ar); CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::is_pos_allowed() +QString MainWindow::is_pos_allowed(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -2265,7 +2285,7 @@ QString MainWindow::is_wallet_password_valid(const QString& param) CATCH_ENTRY_FAIL_API_RESPONCE(); } -QString MainWindow::is_autostart_enabled() +QString MainWindow::is_autostart_enabled(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); @@ -2319,6 +2339,16 @@ QString MainWindow::open_url_in_browser(const QString& param) CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } +QString MainWindow::setup_jwt_wallet_rpc(const QString& param) +{ + TRY_ENTRY(); + + m_backend.setup_wallet_rpc(param.toStdString()); + + return API_RETURN_CODE_OK; + CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); +} + QString MainWindow::is_valid_restore_wallet_text(const QString& param) { TRY_ENTRY(); diff --git a/src/gui/qt-daemon/application/mainwindow.h b/src/gui/qt-daemon/application/mainwindow.h index ed980f7d..074fb9f5 100644 --- a/src/gui/qt-daemon/application/mainwindow.h +++ b/src/gui/qt-daemon/application/mainwindow.h @@ -106,17 +106,18 @@ public: QString accept_cancel_contract(const QString& param); - QString get_version(); - QString get_os_version(); - QString get_network_type(); + QString on_request_quit(const QString& param); + QString get_version(const QString& param); + QString get_os_version(const QString& param); + QString get_network_type(const QString& param); QString transfer(const QString& json_transfer_object); - QString have_secure_app_data(); - QString drop_secure_app_data(); + QString have_secure_app_data(const QString& param); + QString drop_secure_app_data(const QString& param); QString get_secure_app_data(const QString& param); - QString store_secure_app_data(const QString& param); + QString store_secure_app_data(const QString& param, const QString& password); QString set_master_password(const QString& param); QString check_master_password(const QString& param); - QString get_app_data(); + QString get_app_data(const QString& param); QString store_app_data(const QString& param); QString get_default_user_dir(const QString& param); // QString get_all_offers(const QString& param); @@ -126,12 +127,11 @@ public: QString push_update_offer(const QString& param); QString get_alias_info_by_address(const QString& param); QString get_alias_info_by_name(const QString& param); - QString get_all_aliases(); + QString get_all_aliases(const QString& param); QString request_alias_registration(const QString& param); QString request_alias_update(const QString& param); QString get_alias_coast(const QString& param); QString validate_address(const QString& param); - QString on_request_quit(); QString resync_wallet(const QString& param); QString get_recent_transfers(const QString& param); QString get_mining_history(const QString& param); @@ -141,11 +141,11 @@ public: QString get_log_level(const QString& param); QString set_enable_tor(const QString& param); // QString dump_all_offers(); - QString webkit_launched_script(); + QString webkit_launched_script(const QString& param); QString get_smart_wallet_info(const QString& param); QString restore_wallet(const QString& param); QString use_whitelisting(const QString& param); - QString is_pos_allowed(); + QString is_pos_allowed(const QString& param); QString store_to_file(const QString& path, const QString& buff); QString load_from_file(const QString& path); QString is_file_exist(const QString& path); @@ -153,7 +153,7 @@ public: QString backup_wallet_keys(const QString& obj); QString reset_wallet_password(const QString& param); QString is_wallet_password_valid(const QString& param); - QString is_autostart_enabled(); + QString is_autostart_enabled(const QString& param); QString toggle_autostart(const QString& param); QString is_valid_restore_wallet_text(const QString& param); QString get_seed_phrase_info(const QString& param); @@ -161,13 +161,13 @@ public: QString print_log(const QString& param); QString set_clipboard(const QString& param); QString set_localization_strings(const QString str); - QString get_clipboard(); + QString get_clipboard(const QString& param); void message_box(const QString& msg); - bool toggle_mining(); + bool toggle_mining(const QString& param); QString get_exchange_last_top(const QString& params); - QString get_tx_pool_info(); - QString get_default_fee(); - QString get_options(); + QString get_tx_pool_info(const QString& param); + QString get_default_fee(const QString& param); + QString get_options(const QString& param); void bool_toggle_icon(const QString& param); QString add_custom_asset_id(const QString& param); QString remove_custom_asset_id(const QString& param); @@ -177,26 +177,28 @@ public: QString get_ionic_swap_proposal_info(const QString& param); QString accept_ionic_swap_proposal(const QString& param); - bool get_is_disabled_notifications(); + bool get_is_disabled_notifications(const QString& param); bool set_is_disabled_notifications(const bool& param); QString export_wallet_history(const QString& param); - QString get_log_file(); + QString get_log_file(const QString& param); //QString check_available_sources(const QString& param); QString open_url_in_browser(const QString& param); + QString setup_jwt_wallet_rpc(const QString& param); void trayIconActivated(QSystemTrayIcon::ActivationReason reason); - void tray_quit_requested(); - void on_menu_show(); - QString is_remnotenode_mode_preconfigured(); + void tray_quit_requested(const QString& param); + void on_menu_show(const QString& param); + QString is_remnotenode_mode_preconfigured(const QString& param); QString start_backend(const QString& params); QString async_call(const QString& func_name, const QString& params); QString sync_call(const QString& func_name, const QString& params); - //for test purposes onlys - QString request_dummy(); + //for test purposes only + QString request_dummy(const QString& param); QString call_rpc(const QString& params); + QString call_wallet_rpc(const QString& wallet_id, const QString& params); signals: void quit_requested(const QString str); diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index e4a76ad1..a1d86d01 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit e4a76ad1329244ec4fb9ec7825361518e95d1933 +Subproject commit a1d86d01aaeb13bb013c8b2539e01967de9b5f04 diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 22c2e457..46a39ee5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -525,7 +525,7 @@ namespace nodetool if (!tools::check_remote_client_version(rsp.payload_data.client_version)) { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong client version: " << rsp.payload_data.client_version << ", closing connection."); + LOG_PRINT_CC_YELLOW(context, "COMMAND_HANDSHAKE Failed, wrong client version: " << rsp.payload_data.client_version << ", closing connection.", LOG_LEVEL_1); return; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 7eda942c..371d0a81 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -176,7 +176,7 @@ namespace currency res.pow_sequence_factor = m_core.get_blockchain_storage().get_current_sequence_factor(false); if (req.flags&(COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY | COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS)) { - res.block_reward = currency::get_base_block_reward(true, total_coins, res.height); + res.block_reward = currency::get_base_block_reward(res.height); currency::block b = AUTO_VAL_INIT(b); m_core.get_blockchain_storage().get_top_block(b); res.last_block_total_reward = currency::get_reward_from_miner_tx(b.miner_tx); @@ -289,7 +289,7 @@ namespace currency } blockchain_storage::blocks_direct_container bs; - if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height, req.need_global_indexes)) + if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height)) { res.status = API_RETURN_CODE_FAIL; return false; @@ -326,7 +326,7 @@ namespace currency } blockchain_storage::blocks_direct_container bs; - if (!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height, req.need_global_indexes)) + if (!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, req.minimum_height)) { res.status = API_RETURN_CODE_FAIL; return false; @@ -336,21 +336,15 @@ namespace currency { res.blocks.resize(res.blocks.size()+1); res.blocks.back().block = block_to_blob(b.first->bl); - if (req.need_global_indexes) - { - CHECK_AND_ASSERT_MES(b.third.get(), false, "Internal error on handling COMMAND_RPC_GET_BLOCKS_FAST: b.third is empty, ie coinbase info is not prepared"); - res.blocks.back().coinbase_global_outs = b.third->m_global_output_indexes; - res.blocks.back().tx_global_outs.resize(b.second.size()); - } + CHECK_AND_ASSERT_MES(b.third.get(), false, "Internal error on handling COMMAND_RPC_GET_BLOCKS_FAST: b.third is empty, ie coinbase info is not prepared"); + res.blocks.back().coinbase_global_outs = b.third->m_global_output_indexes; + res.blocks.back().tx_global_outs.resize(b.second.size()); size_t i = 0; BOOST_FOREACH(auto& t, b.second) { res.blocks.back().txs.push_back(tx_to_blob(t->tx)); - if (req.need_global_indexes) - { - res.blocks.back().tx_global_outs[i].v = t->m_global_output_indexes; - } + res.blocks.back().tx_global_outs[i].v = t->m_global_output_indexes; i++; } } @@ -424,11 +418,11 @@ namespace currency return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_random_outs2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res, connection_context& cntx) + bool core_rpc_server::on_get_random_outs3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res, connection_context& cntx) { CHECK_CORE_READY(); res.status = API_RETURN_CODE_FAIL; - if (!m_core.get_blockchain_storage().get_random_outs_for_amounts2(req, res)) + if (!m_core.get_blockchain_storage().get_random_outs_for_amounts3(req, res)) { return true; } @@ -744,6 +738,18 @@ namespace currency return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx) + { + CHECK_CORE_READY(); + if (!m_core.get_blockchain_storage().get_assets(req.offset, req.count, res.assets)) + { + res.status = API_RETURN_CODE_NOT_FOUND; + return true; + } + res.status = API_RETURN_CODE_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_main_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { if (!m_core.get_blockchain_storage().get_main_block_rpc_details(req.id, res.block_details)) @@ -989,7 +995,7 @@ namespace currency res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height)); res.status = API_RETURN_CODE_OK; - + LOG_PRINT_L1("COMMAND_RPC_GETBLOCKTEMPLATE OK, response block: " << ENDL << currency::obj_to_json_str(resp.b)); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1339,6 +1345,20 @@ namespace currency res.status = API_RETURN_CODE_OK; return true; } + + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_remove_tx_from_pool(const COMMAND_RPC_REMOVE_TX_FROM_POOL::request& req, COMMAND_RPC_REMOVE_TX_FROM_POOL::response& res, connection_context& cntx) + { + for (const auto& tx_id_str : req.tx_to_remove) + { + crypto::hash tx_id = epee::transform_str_to_t_pod(tx_id_str); + currency::transaction tx; size_t dummy1 = 0; uint64_t dummy2 = 0; + m_core.get_tx_pool().take_tx(tx_id, tx, dummy1, dummy2); + } + + res.status = API_RETURN_CODE_OK; + return true; + } } diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 2c7b47a9..ad4786f2 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -50,7 +50,7 @@ namespace currency bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::response& res, connection_context& cntx); bool on_get_random_outs1(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx); - bool on_get_random_outs2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res, connection_context& cntx); + bool on_get_random_outs3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res, connection_context& cntx); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx); bool on_set_maintainers_info(const COMMAND_RPC_SET_MAINTAINERS_INFO::request& req, COMMAND_RPC_SET_MAINTAINERS_INFO::response& res, connection_context& cntx); bool on_get_tx_pool(const COMMAND_RPC_GET_TX_POOL::request& req, COMMAND_RPC_GET_TX_POOL::response& res, connection_context& cntx); @@ -75,6 +75,7 @@ namespace currency bool on_aliases_by_address(const COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& req, COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_get_alias_reward(const COMMAND_RPC_GET_ALIAS_REWARD::request& req, COMMAND_RPC_GET_ALIAS_REWARD::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_reset_transaction_pool(const COMMAND_RPC_RESET_TX_POOL::request& req, COMMAND_RPC_RESET_TX_POOL::response& res, connection_context& cntx); + bool on_remove_tx_from_pool(const COMMAND_RPC_REMOVE_TX_FROM_POOL::request& req, COMMAND_RPC_REMOVE_TX_FROM_POOL::response& res, connection_context& cntx); bool on_get_pos_mining_details(const COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, COMMAND_RPC_GET_POS_MINING_DETAILS::response& res, connection_context& cntx); bool on_get_current_core_tx_expiration_median(const COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res, connection_context& cntx); bool on_get_tx_details(const COMMAND_RPC_GET_TX_DETAILS::request& req, COMMAND_RPC_GET_TX_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); @@ -87,6 +88,7 @@ namespace currency bool on_get_pool_info(const COMMAND_RPC_GET_POOL_INFO::request& req, COMMAND_RPC_GET_POOL_INFO::response& res, connection_context& cntx); bool on_get_votes(const COMMAND_RPC_GET_VOTES::request& req, COMMAND_RPC_GET_VOTES::response& res, connection_context& cntx); bool on_get_asset_info(const COMMAND_RPC_GET_ASSET_INFO::request& req, COMMAND_RPC_GET_ASSET_INFO::response& res, connection_context& cntx); + bool on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx); bool on_get_main_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_get_alt_block_details(const COMMAND_RPC_GET_BLOCK_DETAILS::request& req, COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); @@ -112,7 +114,7 @@ namespace currency MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY) MAP_URI_AUTO_BIN2("/getrandom_outs1.bin", on_get_random_outs1, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) - MAP_URI_AUTO_BIN2("/getrandom_outs2.bin", on_get_random_outs2, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2) + MAP_URI_AUTO_BIN2("/getrandom_outs3.bin", on_get_random_outs3, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3) MAP_URI_AUTO_BIN2("/set_maintainers_info.bin", on_set_maintainers_info, COMMAND_RPC_SET_MAINTAINERS_INFO) MAP_URI_AUTO_BIN2("/get_tx_pool.bin", on_get_tx_pool, COMMAND_RPC_GET_TX_POOL) MAP_URI_AUTO_BIN2("/check_keyimages.bin", on_check_keyimages, COMMAND_RPC_CHECK_KEYIMAGES) @@ -146,16 +148,18 @@ namespace currency MAP_JON_RPC ("get_pool_info", on_get_pool_info, COMMAND_RPC_GET_POOL_INFO) MAP_JON_RPC ("getrandom_outs", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY) MAP_JON_RPC ("getrandom_outs1", on_get_random_outs1, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) - MAP_JON_RPC ("getrandom_outs2", on_get_random_outs2, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2) + MAP_JON_RPC ("getrandom_outs3", on_get_random_outs3, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3) MAP_JON_RPC ("get_votes", on_get_votes, COMMAND_RPC_GET_VOTES) //assets api - MAP_JON_RPC ("get_asset_info", on_get_asset_info, COMMAND_RPC_GET_ASSET_INFO) + MAP_JON_RPC ("get_asset_info", on_get_asset_info, COMMAND_RPC_GET_ASSET_INFO) + MAP_JON_RPC ("get_assets_list", on_get_assets_list, COMMAND_RPC_GET_ASSETS_LIST) MAP_JON_RPC_WE("get_main_block_details", on_get_main_block_details, COMMAND_RPC_GET_BLOCK_DETAILS) MAP_JON_RPC_WE("get_alt_block_details", on_get_alt_block_details, COMMAND_RPC_GET_BLOCK_DETAILS) MAP_JON_RPC ("get_alt_blocks_details", on_get_alt_blocks_details, COMMAND_RPC_GET_ALT_BLOCKS_DETAILS) // MAP_JON_RPC ("reset_transaction_pool", on_reset_transaction_pool, COMMAND_RPC_RESET_TX_POOL) + MAP_JON_RPC ("remove_tx_from_pool", on_remove_tx_from_pool, COMMAND_RPC_REMOVE_TX_FROM_POOL) MAP_JON_RPC ("get_current_core_tx_expiration_median", on_get_current_core_tx_expiration_median, COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN) // MAP_JON_RPC_WE("marketplace_global_get_offers_ex", on_get_offers_ex, COMMAND_RPC_GET_OFFERS_EX) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index ca55f2d4..f088ccc6 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,20 +1,30 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #pragma once #include "serialization/keyvalue_hexemizer.h" #include "currency_protocol/currency_protocol_defs.h" #include "currency_core/currency_basic.h" -#include "currency_core/difficulty.h" +//#include "currency_core/difficulty.h" #include "crypto/hash.h" #include "p2p/p2p_protocol_defs.h" -#include "storages/portable_storage_base.h" +//#include "storages/portable_storage_base.h" #include "currency_core/offers_service_basics.h" -#include "currency_core/basic_api_response_codes.h" +#include +#include +#include +#include +#include +#include #include "common/error_codes.h" +#include +#include +#include +#include +//#include "currency_core/basic_api_response_codes.h" + namespace currency { //----------------------------------------------- @@ -26,9 +36,9 @@ namespace currency std::string comment; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(tracking_key) - KV_SERIALIZE(comment) + KV_SERIALIZE(address) DOC_DSCR("Address of the alias.") DOC_EXMP("ZxCSpsGGeJsS8fwvQ4HktDU3qBeauoJTR6j73jAWWZxFXdF7XTbGm4YfS2kXJmAP4Rf5BVsSQ9iZ45XANXEYsrLN2L2W77dH7") DOC_END + KV_SERIALIZE(tracking_key) DOC_DSCR("View secret key of the corresponding address (optional).") DOC_EXMP("18bb94f69ed61b47b6556f3871b89dff8f9a6f4f798f706fd199b05ccf8ef20c") DOC_END + KV_SERIALIZE(comment) DOC_DSCR("Arbitrary comment (optional).") DOC_EXMP("Society is never gonna make any progress until we all learn to pretend to like each other.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -38,8 +48,8 @@ namespace currency alias_rpc_details_base details; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(alias) - KV_CHAIN_MAP(details) + KV_SERIALIZE(alias) DOC_DSCR("Alias itself, a brief shortcut for an address.") DOC_EXMP("zxdya6q6whzwqjkmtcsjpc3ku") DOC_END + KV_CHAIN_MAP(details) //DOC_DSCR("Object of alias_rpc_details_base struct.") DOC_EXMP_AUTO() DOC_END END_KV_SERIALIZE_MAP() }; @@ -51,9 +61,9 @@ namespace currency alias_rpc_details_base details; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(old_address) - KV_SERIALIZE(alias) - KV_SERIALIZE(details) + KV_SERIALIZE(old_address) DOC_DSCR("Previous address of the alias.") DOC_EXMP("ZxCSpsGGeJsS8fwvQ4HktDU3qBeauoJTR6j73jAWWZxFXdF7XTbGm4YfS2kXJmAP4Rf5BVsSQ9iZ45XANXEYsrLN2L2W77dH7") DOC_END + KV_SERIALIZE(alias) DOC_DSCR("Alias itself, a brief shortcut for an address.") DOC_EXMP("zxdya6q6whzwqjkmtcsjpc3ku") DOC_END + KV_SERIALIZE(details) DOC_DSCR("Object of alias_rpc_details_base struct.") DOC_EXMP_AUTO() DOC_END END_KV_SERIALIZE_MAP() }; @@ -62,6 +72,8 @@ namespace currency struct COMMAND_RPC_GET_POOL_INFO { + DOC_COMMAND("Obtain basic information about the transaction pool."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -75,23 +87,25 @@ namespace currency std::list aliases_que; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(error_code) - KV_SERIALIZE(aliases_que) + KV_SERIALIZE(status) DOC_DSCR("Status code, OK if succeeded.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(error_code) DOC_DSCR("Error code, if there's any error (optional).") DOC_EXMP_AUTO() DOC_END + KV_SERIALIZE(aliases_que) DOC_DSCR("List of aliases from txs that are currently in the tx pool.") DOC_EXMP_AUTO() DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_VOTES { + DOC_COMMAND("Get votes' results from the given block range."); + struct request { uint64_t h_start; uint64_t h_end; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(h_start) - KV_SERIALIZE(h_end) + KV_SERIALIZE(h_start) DOC_DSCR("Start of the block range to search in (including).") DOC_EXMP(0) DOC_END + KV_SERIALIZE(h_end) DOC_DSCR("End of the block range to serach in (excluding).") DOC_EXMP(40000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -102,9 +116,9 @@ namespace currency vote_results votes; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(error_code) - KV_SERIALIZE(votes) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(error_code) DOC_DSCR("Error code, if any.") DOC_END + KV_SERIALIZE(votes) DOC_DSCR("Found votes in the given range.") DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -114,13 +128,15 @@ namespace currency crypto::public_key asset_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("ID of an asset.") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_GET_ASSET_INFO { + DOC_COMMAND("Obtain information for the given asset by its ID."); + typedef asset_id_kv request; struct response @@ -129,14 +145,43 @@ namespace currency asset_descriptor_base asset_descriptor; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(asset_descriptor) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor of the given asset.") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_ASSETS_LIST + { + DOC_COMMAND("Return list of assets registered in Zano blockchain"); + + struct request + { + uint64_t offset = 0; + uint64_t count = 100; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(offset) DOC_DSCR("Offset for the item to start copying") DOC_EXMP(0) DOC_END + KV_SERIALIZE(count) DOC_DSCR("Number of items to recieve") DOC_EXMP(100) DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::list assets; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status code of operation, OK if success") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(assets) DOC_DSCR("List of assets registered in Zano blockchain") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_HEIGHT { + DOC_COMMAND("Return current blockchain height"); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -149,11 +194,12 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height) - KV_SERIALIZE(status) + KV_SERIALIZE(height) DOC_DSCR("Height of the blockchain (equals to top block's height + 1).") DOC_EXMP(11111) DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + template struct COMMAND_RPC_GET_BLOCKS_FAST_T @@ -161,14 +207,12 @@ namespace currency struct request { - bool need_global_indexes; uint64_t minimum_height; - 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 */ + std::list block_ids; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(need_global_indexes) - KV_SERIALIZE(minimum_height) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + KV_SERIALIZE(minimum_height) DOC_DSCR("The minimum height of the returning buch of blocks.") DOC_EXMP(0) DOC_END + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) /* TODO !!! DOC_DSCR("Current state of the local blockchain. Hashes of the most recent 10 blocks goes first, then each 2nd, then 4th, 8, 16, 32, 64 and so on, and the last one is always hash of the genesis block.") DOC_END */ END_KV_SERIALIZE_MAP() }; @@ -180,10 +224,10 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(blocks) - KV_SERIALIZE(start_height) - KV_SERIALIZE(current_height) - KV_SERIALIZE(status) + KV_SERIALIZE(blocks) DOC_DSCR("Bunch of blocks") DOC_END + KV_SERIALIZE(start_height) DOC_DSCR("Starting height of the resulting bunch of blocks.") DOC_END + KV_SERIALIZE(current_height) DOC_DSCR("Current height of the blockchain.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -194,16 +238,17 @@ namespace currency //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { + DOC_COMMAND("Retreive transactions by their IDs.") + struct request { std::list txs_hashes; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs_hashes) + KV_SERIALIZE(txs_hashes) DOC_DSCR("List of transactions' IDs.") DOC_EXMP_AUTO() DOC_END // "146791c4f5ca94bcf423557e5eb859a3a69991bd33960d52f709d88bf5d1ac6d","ec4d913a40a9ac1fbd9d33b71ef507b5c85d1f503b89096618a18b08991b5171") DOC_END END_KV_SERIALIZE_MAP() }; - struct response { std::list txs_as_hex; //transactions blobs as hex @@ -211,9 +256,9 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs_as_hex) - KV_SERIALIZE(missed_tx) - KV_SERIALIZE(status) + KV_SERIALIZE(txs_as_hex) DOC_DSCR("Transactions stored as blobs") DOC_EXMP_AUTO(1, "7d914497d91442f8f3c2268397d914497d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc2f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE(missed_tx) DOC_DSCR("Missed transactions hashes") DOC_EXMP_AUTO(1, "97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -221,12 +266,14 @@ namespace currency //----------------------------------------------- struct COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE { + DOC_COMMAND("Give an estimation of block height by the given date.") + struct request { uint64_t timestamp; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(timestamp) + KV_SERIALIZE(timestamp) DOC_DSCR("Linux timestamp for the required date.") DOC_EXMP(1711021795) DOC_END END_KV_SERIALIZE_MAP() }; @@ -236,14 +283,16 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(h) - KV_SERIALIZE(status) + KV_SERIALIZE(h) DOC_DSCR("Estimated height of a block.") DOC_EXMP(2555000) DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_GET_TX_POOL { + DOC_COMMAND("Retreives transactions from tx pool (and other information).") + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -257,20 +306,22 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) - KV_SERIALIZE(tx_expiration_ts_median) - KV_SERIALIZE(status) + KV_SERIALIZE(txs) DOC_DSCR("Transactions as blobs.") DOC_EXMP_AUTO(1, "7d914497d91442f8f3c2268397d914497d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc2f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE(tx_expiration_ts_median) DOC_DSCR("Timestamp median value of last TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW blocks.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_CHECK_KEYIMAGES { + DOC_COMMAND("Check spent status of given key images.") + struct request { std::list images; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(images) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(images) //DOC_DSCR("List of key images.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -280,20 +331,22 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(images_stat) - KV_SERIALIZE(status) + KV_SERIALIZE(images_stat) DOC_DSCR("List of spent states, where 1 means unspent and 0 means spent.") DOC_EXMP_AUTO(1, 0) DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { + DOC_COMMAND("Obtain global outputs' indexes for the given txs.") + struct request { std::list txids; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txids) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txids)// DOC_DSCR("List of transaction hashes.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -304,8 +357,8 @@ namespace currency //std::vector o_indexes; std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_global_outs) - KV_SERIALIZE(status) + KV_SERIALIZE(tx_global_outs) DOC_DSCR("List of global indexies for each output for each transaction.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -313,18 +366,19 @@ namespace currency struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_BY_AMOUNT { + DOC_COMMAND("Returns transaction ID and local output index for a given output amount and its global index.") + struct request { uint64_t amount; uint64_t i; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(i) + KV_SERIALIZE(amount) DOC_DSCR("The specific amount of output to query.") DOC_END + KV_SERIALIZE(i) DOC_DSCR("The global index of the output amount to be queried.") DOC_END END_KV_SERIALIZE_MAP() }; - struct response { std::string status; @@ -332,9 +386,9 @@ namespace currency uint64_t out_no; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - KV_SERIALIZE(out_no) - KV_SERIALIZE(status) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID where the queried output is present, if found.") DOC_END + KV_SERIALIZE(out_no) DOC_DSCR("Local output index within the transaction.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -342,16 +396,17 @@ namespace currency struct COMMAND_RPC_GET_MULTISIG_INFO { + DOC_COMMAND("Retrieve basic information about a multisig output using its unique identifier (hash)."); + struct request { crypto::hash ms_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(ms_id) + KV_SERIALIZE_POD_AS_HEX_STRING(ms_id) DOC_DSCR("The multisig output's unique identifier (hash).") DOC_END END_KV_SERIALIZE_MAP() }; - struct response { std::string status; @@ -359,15 +414,17 @@ namespace currency uint64_t out_no; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - KV_SERIALIZE(out_no) - KV_SERIALIZE(status) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID where the multisig output is present, if found.")DOC_END + KV_SERIALIZE(out_no) DOC_DSCR("Local output index within the transaction.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS { + DOC_COMMAND("Retrieve random decoy outputs for specified amounts, to be used for mixing in transactions."); + struct request { std::list amounts; @@ -375,13 +432,18 @@ namespace currency uint64_t height_upper_limit; // if nonzero, all the decoy outputs must be either older than, or the same age as this height bool use_forced_mix_outs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(decoys_count) - KV_SERIALIZE(height_upper_limit) - KV_SERIALIZE(use_forced_mix_outs) + KV_SERIALIZE(amounts) DOC_DSCR("List of amounts for which decoy outputs are requested.") DOC_END + KV_SERIALIZE(decoys_count) DOC_DSCR("Number of decoy outputs required for each amount specified.") DOC_END + KV_SERIALIZE(height_upper_limit) DOC_DSCR("Maximum blockchain height from which decoys can be taken. If nonzero, decoys must be at this height or older.") DOC_END + KV_SERIALIZE(use_forced_mix_outs) DOC_DSCR("If true, only outputs with a 'mix_attr' greater than 0 are used as decoys.") DOC_END END_KV_SERIALIZE_MAP() }; + +#define RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_COINBASE 0x0000000000000001LL +#define RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_NOT_ALLOWED 0x0000000000000002LL +#define RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_POS_COINBASE 0x0000000000000004LL + #pragma pack (push, 1) struct out_entry { @@ -397,17 +459,18 @@ namespace currency crypto::public_key concealing_point; // premultiplied by 1/8 crypto::public_key amount_commitment; // premultiplied by 1/8 crypto::public_key blinded_asset_id; // premultiplied by 1/8 + uint64_t flags; }; #pragma pack(pop) struct outs_for_amount { - uint64_t amount; + uint64_t amount = 0; std::list outs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + KV_SERIALIZE(amount) DOC_DSCR("The amount for which decoys are returned.") DOC_END + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) //DOC_DSCR("List of 'out_entry' structures, serialized as a blob.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -416,24 +479,24 @@ namespace currency std::vector outs; std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs) - KV_SERIALIZE(status) + KV_SERIALIZE(outs) DOC_DSCR("List of 'outs_for_amount' structures, each containing decoys for a specific amount.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2 + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3 { + DOC_COMMAND("Version 3 of the command to retrieve random decoy outputs for specified amounts, focusing on either pre-zarcanum or post-zarcanum zones based on the amount value."); + struct offsets_distribution { uint64_t amount; //if amount is 0 then lookup in post-zarcanum zone only, if not 0 then pre-zarcanum only - std::vector offsets; //[i] = height, estimated location where to pickup output of transaction - uint64_t own_global_index; //index to exclude from selection + std::vector global_offsets; //[i] = global_index to pick up BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(offsets) - KV_SERIALIZE(own_global_index) + KV_SERIALIZE(amount) DOC_DSCR("If set to 0, only ZC outputs are considered. If nonzero, only old bare outputs are considered.") DOC_END + KV_SERIALIZE(global_offsets) DOC_DSCR("List of global indices for picking decoys. Each index corresponds to a potential decoy output.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -445,27 +508,30 @@ namespace currency bool use_forced_mix_outs; uint64_t coinbase_percents; //from 0 to 100, estimate percents of coinbase outputs included in decoy sets BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(height_upper_limit) - KV_SERIALIZE(use_forced_mix_outs) - KV_SERIALIZE(coinbase_percents) + KV_SERIALIZE(amounts) DOC_DSCR("List of amount distributions specifying where to look for decoys, based on old bare outputs or ZC outputs.") DOC_END + KV_SERIALIZE(height_upper_limit) DOC_DSCR("Maximum blockchain height from which decoys can be taken. If nonzero, decoys must be at this height or older.") DOC_END + KV_SERIALIZE(use_forced_mix_outs) DOC_DSCR("If true, only outputs with a 'mix_attr' greater than 0 are used as decoys.") DOC_END + KV_SERIALIZE(coinbase_percents) DOC_DSCR("Specifies the estimated percentage of coinbase outputs to be included in the decoy sets, ranging from 0 to 100.") DOC_END END_KV_SERIALIZE_MAP() }; typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response response; }; - - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY + //----------------------------------------------- + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY { + DOC_COMMAND("Retrieve random decoy outputs for specified amounts (legacy format)."); + struct request { std::list amounts; uint64_t outs_count; bool use_forced_mix_outs; + BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(outs_count) - KV_SERIALIZE(use_forced_mix_outs) + KV_SERIALIZE(amounts) DOC_DSCR("List of amounts for which decoy outputs are requested.") DOC_END + KV_SERIALIZE(outs_count) DOC_DSCR("Number of decoy outputs requested for each amount.") DOC_END + KV_SERIALIZE(use_forced_mix_outs) DOC_DSCR("If true, only outputs with a 'mix_attr' greater than 0 are used as decoys.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -483,8 +549,8 @@ namespace currency std::list outs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + KV_SERIALIZE(amount) DOC_DSCR("The amount for which decoys are returned.") DOC_END + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) //DOC_DSCR("List of 'out_entry' structures serialized as a blob, representing the decoy outputs for the specified amount.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -492,9 +558,10 @@ namespace currency { std::vector outs; std::string status; + BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs) - KV_SERIALIZE(status) + KV_SERIALIZE(outs) DOC_DSCR("List of 'outs_for_amount' structures, each containing decoys for a specific amount.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -510,7 +577,7 @@ namespace currency { std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -518,7 +585,9 @@ namespace currency //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX { - struct request + DOC_COMMAND("Broadcasts a raw transaction encoded in hexadecimal format to the network."); + + struct request { std::string tx_as_hex; @@ -526,8 +595,8 @@ namespace currency explicit request(const transaction &); BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_as_hex) - END_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_as_hex) DOC_DSCR("The transaction data as a hexadecimal string, ready for network broadcast.") DOC_END + END_KV_SERIALIZE_MAP() }; @@ -536,11 +605,12 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_FORCE_RELAY_RAW_TXS { @@ -549,7 +619,7 @@ namespace currency std::vector txs_as_hex; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs_as_hex) + KV_SERIALIZE(txs_as_hex) DOC_DSCR("List of transactions as a hexadecimal strings.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -559,21 +629,23 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_START_MINING { + DOC_COMMAND("Initiates PoW mining process on a node using the specified miner address and the number of CPU threads."); + struct request { std::string miner_address; uint64_t threads_count; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(miner_address) - KV_SERIALIZE(threads_count) + KV_SERIALIZE(miner_address) DOC_DSCR("The address where the mining rewards will be deposited.") DOC_END + KV_SERIALIZE(threads_count) DOC_DSCR("The number of CPU threads to use for mining.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -582,13 +654,14 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct outs_index_stat { + uint64_t amount_0; uint64_t amount_0_001; uint64_t amount_0_01; uint64_t amount_0_1; @@ -601,6 +674,7 @@ namespace currency uint64_t amount_1000000; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount_0) KV_SERIALIZE(amount_0_001) KV_SERIALIZE(amount_0_01) KV_SERIALIZE(amount_0_1) @@ -769,11 +843,14 @@ namespace currency struct COMMAND_RPC_GET_INFO { + DOC_COMMAND("Retrieves various information about the blockchain node. The user must specify their needs via a 'flags' field in the request by combining necessary flags using binary OR. Some values are always calculated and provided, others only if the corresponding flag is specified."); + struct request { uint64_t flags; + BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(flags) + KV_SERIALIZE(flags) DOC_DSCR("Combination of flags to request specific data elements that are computationally expensive to calculate.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -841,83 +918,92 @@ namespace currency uint64_t offers_count; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(height) - KV_SERIALIZE(pos_difficulty) - KV_SERIALIZE(pow_difficulty) - 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(synchronized_connections_count) - KV_SERIALIZE(net_time_delta_median) - KV_SERIALIZE(white_peerlist_size) - KV_SERIALIZE(grey_peerlist_size) - KV_SERIALIZE(current_blocks_median) - KV_SERIALIZE(current_network_hashrate_50) - KV_SERIALIZE(current_network_hashrate_350) - KV_SERIALIZE(alias_count) - KV_SERIALIZE(daemon_network_state) - KV_SERIALIZE(synchronization_start_height) - KV_SERIALIZE(max_net_seen_height) - KV_SERIALIZE(transactions_cnt_per_day) - KV_SERIALIZE(transactions_volume_per_day) - KV_SERIALIZE(mi) - KV_SERIALIZE(pos_sequence_factor) - KV_SERIALIZE(pow_sequence_factor) - KV_SERIALIZE(last_pow_timestamp) - KV_SERIALIZE(last_pos_timestamp) - KV_SERIALIZE(seconds_for_10_blocks) - KV_SERIALIZE(seconds_for_30_blocks) - KV_SERIALIZE(total_coins) - KV_SERIALIZE(block_reward) - KV_SERIALIZE(last_block_total_reward) - KV_SERIALIZE(pos_diff_total_coins_rate) - KV_SERIALIZE(pos_block_ts_shift_vs_actual) - KV_SERIALIZE(expiration_median_timestamp) - KV_SERIALIZE(pos_allowed) - KV_SERIALIZE(outs_stat) - KV_SERIALIZE(performance_data) - KV_SERIALIZE(tx_pool_performance_data) - KV_SERIALIZE(last_block_size) - KV_SERIALIZE(current_max_allowed_block_size) - KV_SERIALIZE(tx_count_in_last_block) - KV_SERIALIZE(default_fee) - KV_SERIALIZE(minimum_fee) - KV_SERIALIZE(last_block_timestamp) - KV_SERIALIZE(last_block_hash) - KV_SERIALIZE(is_hardfok_active) - KV_SERIALIZE(offers_count) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + // Always calculated and provided fields + KV_SERIALIZE(height) DOC_DSCR("The current size of the blockchain, equal to the height of the top block plus one.") DOC_END + KV_SERIALIZE(pos_allowed) DOC_DSCR("Boolean value indicating whether PoS mining is currently allowed based on network rules and state.") DOC_END + KV_SERIALIZE(pos_difficulty) DOC_DSCR("Current difficulty for Proof of Stake mining.") DOC_END + KV_SERIALIZE(pow_difficulty) DOC_DSCR("Current difficulty for Proof of Work mining.") DOC_END + KV_SERIALIZE(tx_count) DOC_DSCR("Total number of transactions in the blockchain.") DOC_END + KV_SERIALIZE(tx_pool_size) DOC_DSCR("Number of transactions currently in the pool.") DOC_END + KV_SERIALIZE(alt_blocks_count) DOC_DSCR("Number of alternative blocks on the blockchain.") DOC_END + KV_SERIALIZE(outgoing_connections_count) DOC_DSCR("Number of outgoing P2P connections to other nodes.") DOC_END + KV_SERIALIZE(incoming_connections_count) DOC_DSCR("Number of incoming P2P connections established by other nodes.") DOC_END + KV_SERIALIZE(synchronized_connections_count) DOC_DSCR("Number of P2P connections to nodes that have a fully synchronized blockchain.") DOC_END + KV_SERIALIZE(white_peerlist_size) DOC_DSCR("Size of the white peer list, which includes addresses of reliable nodes.") DOC_END + KV_SERIALIZE(grey_peerlist_size) DOC_DSCR("Size of the grey peer list, which includes addresses of nodes with less consistent availability.") DOC_END + KV_SERIALIZE(current_blocks_median) // TODO + KV_SERIALIZE(alias_count) DOC_DSCR("The total number of unique aliases registered on the blockchain. Aliases are alternate, human-readable names associated with addresses.") DOC_END + KV_SERIALIZE(current_max_allowed_block_size) DOC_DSCR("Current maximum allowed cummulative block size in bytes.") DOC_END + KV_SERIALIZE(is_hardfok_active) DOC_DSCR("A list of boolean values indicating whether each corresponding hardfork is active. For example, a list 'true, true, false' indicates that the first hardfork is activated, while the second is not. Hardfork #0 is always active as it is a stub.") DOC_END + KV_SERIALIZE(daemon_network_state) DOC_DSCR("Current network state of the daemon, which could be connecting, synchronizing, online, loading core, internal error, unloading core, or downloading database.") DOC_END + KV_SERIALIZE(synchronization_start_height) DOC_DSCR("Blockchain height at which the current synchronization process started. Indicates the starting point for catching up to the network's latest state.") DOC_END + KV_SERIALIZE(max_net_seen_height) DOC_DSCR("Maximum blockchain height observed in the network by this node.") DOC_END + KV_SERIALIZE(default_fee) DOC_DSCR("Default fee for transactions.") DOC_END + KV_SERIALIZE(minimum_fee) DOC_DSCR("Minimum fee for transactions.") DOC_END + KV_SERIALIZE(mi) DOC_DSCR("The most recent mainterner's info.") DOC_END + + // Fields dependent on flags for their inclusion + KV_SERIALIZE(net_time_delta_median) DOC_DSCR("A value of 0 indicates no time synchronization issues, while a value of 1 indicates the presence of time sync issues. Only available if the COMMAND_RPC_GET_INFO_FLAG_NET_TIME_DELTA_MEDIAN flag is set.") DOC_END + KV_SERIALIZE(current_network_hashrate_50)DOC_DSCR("The PoW hash rate calculated over the last 50 blocks of any type. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_CURRENT_NETWORK_HASHRATE_50 flag is set.") DOC_END + KV_SERIALIZE(current_network_hashrate_350) DOC_DSCR("The PoW hash rate calculated over the last 350 blocks of any type. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_CURRENT_NETWORK_HASHRATE_350 flag is set.") DOC_END + KV_SERIALIZE(seconds_for_10_blocks) DOC_DSCR("The time period in seconds between the most recent block and the 10th block older. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_SECONDS_FOR_10_BLOCKS flag is set.") DOC_END + KV_SERIALIZE(seconds_for_30_blocks) DOC_DSCR("The time period in seconds between the most recent block and the 30th block older. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_SECONDS_FOR_30_BLOCKS flag is set.") DOC_END + KV_SERIALIZE(transactions_cnt_per_day) DOC_DSCR("The number of non-mining transactions recorded over the last 24 hours. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_TRANSACTIONS_DAILY_STAT flag is set.") DOC_END + KV_SERIALIZE(transactions_volume_per_day)DOC_DSCR("The total sum of input amounts from all non-mining transactions over the last 24 hours. Only old bare inputs with explicit amounts are considered. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_TRANSACTIONS_DAILY_STAT flag is set.") DOC_END + KV_SERIALIZE(last_pos_timestamp) DOC_DSCR("The timestamp of the most recent PoS block. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_LAST_POS_TIMESTAMP flag is set.") DOC_END + KV_SERIALIZE(last_pow_timestamp) DOC_DSCR("The timestamp of the most recent PoW block. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_LAST_POW_TIMESTAMP flag is set.") DOC_END + KV_SERIALIZE(total_coins) DOC_DSCR("The total amount of all emitted coins in the system. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(last_block_size) DOC_DSCR("The size of the last block in bytes. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_LAST_BLOCK_SIZE flag is set.") DOC_END + KV_SERIALIZE(tx_count_in_last_block) DOC_DSCR("The number of non-mining transactions in the last block. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_TX_COUNT_IN_LAST_BLOCK flag is set.") DOC_END + KV_SERIALIZE(pos_sequence_factor) DOC_DSCR("The current PoS sequence factor, representing the number of consecutive PoS blocks. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_POS_SEQUENCE_FACTOR flag is set.") DOC_END + KV_SERIALIZE(pow_sequence_factor) DOC_DSCR("The current PoW sequence factor, representing the number of consecutive PoW blocks. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_POW_SEQUENCE_FACTOR flag is set.") DOC_END + KV_SERIALIZE(block_reward) DOC_DSCR("The base block reward that is effective for the next block. Calculated only if either COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY or COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(last_block_total_reward) DOC_DSCR("Reward for the last block, including base reward and transaction fees. Calculated only if either COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY or COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(pos_diff_total_coins_rate) DOC_DSCR("PoS difficulty divided by the total amount of all coins in the system minus a premined amount (17,517,203). Calculated only if either COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY or COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(last_block_timestamp) DOC_DSCR("Timestamp of the last block. Calculated only if either COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY or COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(last_block_hash) DOC_DSCR("Hash of the last block. Calculated only if either COMMAND_RPC_GET_INFO_FLAG_POS_DIFFICULTY or COMMAND_RPC_GET_INFO_FLAG_TOTAL_COINS flag is set.") DOC_END + KV_SERIALIZE(pos_block_ts_shift_vs_actual) DOC_DSCR("The difference between the timestamp used in the last PoS block for mining purposes and its actual timestamp as stored in the miner's transaction extra data. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_POS_BLOCK_TS_SHIFT_VS_ACTUAL flag is set.") DOC_END + KV_SERIALIZE(outs_stat) DOC_DSCR("Statistics for the number of outputs that have a specific amount. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_OUTS_STAT flag is set.") DOC_END + KV_SERIALIZE(performance_data) DOC_DSCR("Detailed technical performance data intended for developers. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_PERFORMANCE flag is set.") DOC_END + KV_SERIALIZE(tx_pool_performance_data) DOC_DSCR("Detailed technical performance data intended for developers. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_PERFORMANCE flag is set.") DOC_END + KV_SERIALIZE(offers_count) DOC_DSCR("Current number of offers in the offers service. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_PERFORMANCE flag is set.") DOC_END + KV_SERIALIZE(expiration_median_timestamp)DOC_DSCR("Median of timestamps of the last N blocks, used to determine the expiration status of transactions. This information is only provided if the COMMAND_RPC_GET_INFO_FLAG_EXPIRATIONS_MEDIAN flag is set.") DOC_END END_KV_SERIALIZE_MAP() }; }; //----------------------------------------------- struct COMMAND_RPC_STOP_MINING { + DOC_COMMAND("Stop PoW mining process on CPU.") + struct request { - BEGIN_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP() }; - struct response { std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- - // struct COMMAND_RPC_GETBLOCKCOUNT { - typedef std::list request; + DOC_COMMAND("Returns the total number of blocks in the blockchain (the height of the top block plus one)."); + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; struct response { @@ -925,39 +1011,45 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(count) - KV_SERIALIZE(status) + KV_SERIALIZE(count) DOC_DSCR("The total number of blocks in the blockchain, equivalent to the top block's height plus one.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; - }; + //----------------------------------------------- + struct COMMAND_RPC_GETBLOCKHASH { + DOC_COMMAND("Returns block hash by the given height."); + typedef std::vector request; typedef std::string response; }; + //----------------------------------------------- struct COMMAND_RPC_GETBLOCKTEMPLATE { + DOC_COMMAND("Generates a block template for mining, intended for both PoW and PoS types of blocks based on the provided parameters."); + struct request { blobdata explicit_transaction; std::string extra_text; std::string wallet_address; - std::string stakeholder_address; // address for stake return (PoS blocks) - pos_entry pe; // for PoS blocks - bool pos_block; // is pos block + std::string stakeholder_address; // address for stake return (PoS blocks) + pos_entry pe; // for PoS blocks + bool pos_block; // is pos block BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_BLOB_AS_HEX_STRING(explicit_transaction) - KV_SERIALIZE(extra_text) - KV_SERIALIZE(wallet_address) - KV_SERIALIZE(stakeholder_address); - KV_SERIALIZE(pe) - KV_SERIALIZE(pos_block) + KV_SERIALIZE_BLOB_AS_HEX_STRING(explicit_transaction) DOC_DSCR("A transaction blob that must be explicitly included in the block.") DOC_END + KV_SERIALIZE(extra_text) DOC_DSCR("Arbitrary data added to the extra field of the miner transaction.") DOC_END + KV_SERIALIZE(wallet_address) DOC_DSCR("Address where mining rewards will be deposited.") DOC_END + KV_SERIALIZE(stakeholder_address) DOC_DSCR("Address where the stake is returned for PoS blocks (usually the same as 'wallet_address').") DOC_END + KV_SERIALIZE(pe) DOC_DSCR("PoS entry details, relevant only for PoS block generation.") DOC_END + KV_SERIALIZE(pos_block) DOC_DSCR("Flag indicating whether the block is a PoS block.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -975,22 +1067,26 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(difficulty) - KV_SERIALIZE(height) - KV_SERIALIZE_POD_AS_HEX_STRING(seed) - KV_SERIALIZE(blocktemplate_blob) - KV_SERIALIZE(prev_hash) - KV_SERIALIZE(miner_tx_tgc) - KV_SERIALIZE(block_reward_without_fee) - KV_SERIALIZE(block_reward) - KV_SERIALIZE(txs_fee) - KV_SERIALIZE(status) + KV_SERIALIZE(difficulty) DOC_DSCR("The mining difficulty targeted by the block template.") DOC_END + KV_SERIALIZE(height) DOC_DSCR("The height of the block template in the blockchain.") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(seed) DOC_DSCR("Seed value for the ProgPoWZ mining algorithm's epoch.") DOC_END + KV_SERIALIZE(blocktemplate_blob) DOC_DSCR("Serialized block template blob.") DOC_END + KV_SERIALIZE(prev_hash) DOC_DSCR("Hash of the previous block in the chain.") DOC_END + KV_SERIALIZE(miner_tx_tgc) DOC_DSCR("Miner transaction generation context. Intended for PoS blocks and Zarcanum.") DOC_END + KV_SERIALIZE(block_reward_without_fee) DOC_DSCR("Base block reward excluding any transaction fees.") DOC_END + KV_SERIALIZE(block_reward) DOC_DSCR("Total block reward, including transaction fees if they are given to the miner (legacy), or the base reward if fees are burnt (current state).") DOC_END + KV_SERIALIZE(txs_fee) DOC_DSCR("Total fees from transactions included in the block.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_SUBMITBLOCK { + DOC_COMMAND("Adds new block to the blockchain. Request should contain one string with hex-encoded block blob."); + typedef std::vector request; struct response @@ -998,21 +1094,25 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_SUBMITBLOCK2 { + DOC_COMMAND("Adds new block to the blockchain."); + struct request { std::string b; //hex encoded block blob std::list explicit_txs; //hex encoded tx blobs BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_BLOB_AS_HEX_STRING(b) - KV_SERIALIZE(explicit_txs) + KV_SERIALIZE_BLOB_AS_HEX_STRING(b) DOC_DSCR("Hex-encoded serialized block.") DOC_END + KV_SERIALIZE(explicit_txs) DOC_DSCR("List of hex-encoded transactions to be explicitly included in the block.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1021,11 +1121,13 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct block_header_response { uint8_t major_version; @@ -1041,23 +1143,24 @@ namespace currency uint64_t reward; BEGIN_KV_SERIALIZE_MAP() - 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(reward) + KV_SERIALIZE(major_version) DOC_DSCR("Major version of the block.") DOC_END + KV_SERIALIZE(minor_version) DOC_DSCR("Minor version of the block.") DOC_END + KV_SERIALIZE(timestamp) DOC_DSCR("Timestamp of the block creation.") DOC_END + KV_SERIALIZE(prev_hash) DOC_DSCR("Hash of the previous block in the chain.") DOC_END + KV_SERIALIZE(nonce) DOC_DSCR("Nonce used for generating the block to meet the network difficulty.") DOC_END + KV_SERIALIZE(orphan_status) DOC_DSCR("Indicates if the block is an orphan (true) or a normal block (false).") DOC_END + KV_SERIALIZE(height) DOC_DSCR("Height of the block in the blockchain.") DOC_END + KV_SERIALIZE(depth) DOC_DSCR("Depth of the block in the blockchain. Depth 0 indicates the most recent block.") DOC_END + KV_SERIALIZE(hash) DOC_DSCR("Hash of the block.") DOC_END + KV_SERIALIZE(difficulty) DOC_DSCR("Network difficulty target that the block met.") DOC_END + KV_SERIALIZE(reward) DOC_DSCR("Total mining reward of the block including transaction fees (if applicable).") DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { - typedef std::list request; + DOC_COMMAND("Returns the block header information of the most recent block."); + typedef std::list request; // TODO @#@# fix this struct response { @@ -1066,20 +1169,24 @@ namespace currency BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH { + DOC_COMMAND("Retrieves the block header information for a given block hash."); + struct request { std::string hash; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hash) + KV_SERIALIZE(hash) DOC_DSCR("The hash of the block for which the header information is being requested.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1089,21 +1196,24 @@ namespace currency block_header_response block_header; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) + KV_SERIALIZE(block_header) DOC_DSCR("Detailed header information of the block.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; - }; + //----------------------------------------------- + struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { + DOC_COMMAND("Retrieves the block header information for a given block height."); + struct request { uint64_t height; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height) + KV_SERIALIZE(height) DOC_DSCR("The height of the block for which the header information is being requested.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1113,20 +1223,23 @@ namespace currency block_header_response block_header; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) + KV_SERIALIZE(block_header) DOC_DSCR("Detailed header information of the block.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; - }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALIAS_DETAILS { + DOC_COMMAND("Retrieves information about a specific address alias."); + struct request { std::string alias; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(alias) + KV_SERIALIZE(alias) DOC_DSCR("The alias name for which details are being requested.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1136,19 +1249,23 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(alias_details) - KV_SERIALIZE(status) + KV_SERIALIZE(alias_details) DOC_DSCR("Contains the detailed information about the specified alias, including the associated wallet address, tracking key, comment etc..") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALIAS_REWARD { + DOC_COMMAND("Retrieves the cost of registering an alias on the blockchain."); + struct request { std::string alias; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(alias) + KV_SERIALIZE(alias) DOC_DSCR("The alias name for which the registration cost is being queried.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1158,15 +1275,18 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(reward) - KV_SERIALIZE(status) + KV_SERIALIZE(reward) DOC_DSCR("The registration cost for the specified alias.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_GET_ALL_ALIASES { + DOC_COMMAND("Retrieves all registered aliases along with associated information."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1179,22 +1299,26 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(aliases) - KV_SERIALIZE(status) + KV_SERIALIZE(aliases) DOC_DSCR("List of alias_rpc_details objects, each containing information about an individual alias.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALIASES { + DOC_COMMAND("Retrieves a specified portion of all registered aliases, allowing pagination through large sets of aliases."); + struct request { - uint64_t offset; - uint64_t count; + uint64_t offset; // The starting point from which aliases are to be retrieved. + uint64_t count; // The number of aliases to retrieve. BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(offset) - KV_SERIALIZE(count) + KV_SERIALIZE(offset) DOC_DSCR("The offset in the list of all aliases from which to start retrieving.") DOC_END + KV_SERIALIZE(count) DOC_DSCR("The number of aliases to retrieve from the specified offset.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1204,33 +1328,37 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(aliases) - KV_SERIALIZE(status) + KV_SERIALIZE(aliases) DOC_DSCR("List of alias_rpc_details objects, each containing information about an individual alias retrieved based on the request parameters.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALIASES_BY_ADDRESS { + DOC_COMMAND("Retrieves all aliases registered for a given address."); typedef std::string request; struct response { - //std::string alias; std::vector alias_info_list; std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(alias_info_list) - KV_SERIALIZE(status) + KV_SERIALIZE(alias_info_list) DOC_DSCR("List of alias_rpc_details objects, each containing detailed information about each alias registered to the specified address.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_RESET_TX_POOL { + DOC_COMMAND("Clears transaction pool."); struct request { @@ -1243,13 +1371,42 @@ namespace currency std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + + struct COMMAND_RPC_REMOVE_TX_FROM_POOL + { + DOC_COMMAND("Removes specified transactions from the transaction pool, typically to clear out transactions that are no longer valid or needed."); + + struct request + { + std::list tx_to_remove; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_to_remove) DOC_DSCR("List of transaction IDs that are to be removed from the transaction pool.") DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_POS_MINING_DETAILS - { + { + DOC_COMMAND("Retrieves basic information regarding PoS mining, including current PoS conditions and constraints."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1267,17 +1424,19 @@ namespace currency bool pos_sequence_factor_is_good; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(sm) - KV_SERIALIZE(pos_basic_difficulty) - KV_SERIALIZE(starter_timestamp) - KV_SERIALIZE(pos_mining_allowed) - KV_SERIALIZE(pos_sequence_factor_is_good) - KV_SERIALIZE(status) - KV_SERIALIZE_VAL_POD_AS_BLOB(last_block_hash) + KV_SERIALIZE_VAL_POD_AS_BLOB(sm) //DOC_DSCR("Stake modifier object used in PoS mining calculations.") DOC_END + KV_SERIALIZE(pos_basic_difficulty) DOC_DSCR("Current PoS difficulty.") DOC_END + KV_SERIALIZE(starter_timestamp) DOC_DSCR("Timestamp from which timestamps are evaluated for meeting PoS win condition.") DOC_END + KV_SERIALIZE(pos_mining_allowed) DOC_DSCR("Indicates whether PoS mining is currently allowed, which may be restricted under certain blockchain conditions or in testnets.") DOC_END + KV_SERIALIZE(pos_sequence_factor_is_good) DOC_DSCR("Indicates whether the PoS sequence factor is at a level that allows for continued PoS mining, requiring a PoW block to reset if too high.") DOC_END + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE_VAL_POD_AS_BLOB(last_block_hash) //DOC_DSCR("Hash of the most recent block in the blockchain.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct tx_out_rpc_entry { uint64_t amount; @@ -1286,11 +1445,11 @@ namespace currency bool is_spent; uint64_t global_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(pub_keys) - KV_SERIALIZE(minimum_sigs) - KV_SERIALIZE(is_spent) - KV_SERIALIZE(global_index) + KV_SERIALIZE(amount) DOC_DSCR("The output's amount, 0 for ZC outputs.") DOC_END + KV_SERIALIZE(pub_keys) DOC_DSCR("List of public keys associated with the output.") DOC_END + KV_SERIALIZE(minimum_sigs) DOC_DSCR("Minimum number of signatures required to spend the output, for multisig outputs only.") DOC_END + KV_SERIALIZE(is_spent) DOC_DSCR("Indicates whether the output has been spent.") DOC_END + KV_SERIALIZE(global_index) DOC_DSCR("Global index of the output for this specific amount.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1303,12 +1462,12 @@ namespace currency std::vector global_indexes; std::vector etc_options; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(htlc_origin) - KV_SERIALIZE(kimage_or_ms_id) - KV_SERIALIZE(global_indexes) - KV_SERIALIZE(multisig_count) - KV_SERIALIZE(etc_options) + KV_SERIALIZE(amount) DOC_DSCR("The amount of coins being transacted.") DOC_END + KV_SERIALIZE(htlc_origin) DOC_DSCR("Origin hash for HTLC (Hash Time Locked Contract).") DOC_END + KV_SERIALIZE(kimage_or_ms_id) DOC_DSCR("Contains either the key image for the input or the multisig output ID, depending on the input type.") DOC_END + KV_SERIALIZE(global_indexes) DOC_DSCR("List of global indexes indicating the outputs referenced by this input, where only one is actually being spent.") DOC_END + KV_SERIALIZE(multisig_count) DOC_DSCR("Number of multisig signatures used, relevant only for multisig outputs.") DOC_END + KV_SERIALIZE(etc_options) DOC_DSCR("Auxiliary options associated with the input, containing additional configuration or data.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1318,9 +1477,9 @@ namespace currency std::string short_view; std::string details_view; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(type) - KV_SERIALIZE(short_view) - KV_SERIALIZE(details_view) + KV_SERIALIZE(type) DOC_DSCR("Type of the extra entry in the transaction.") DOC_END + KV_SERIALIZE(short_view) DOC_DSCR("A concise representation of the extra entry.") DOC_END + KV_SERIALIZE(details_view) DOC_DSCR("A detailed representation of the extra entry.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1342,19 +1501,19 @@ namespace currency std::string object_in_json; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(blob) - KV_SERIALIZE(blob_size) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(keeper_block) - KV_SERIALIZE(fee) - KV_SERIALIZE(amount) - KV_SERIALIZE(id) - KV_SERIALIZE(pub_key) - KV_SERIALIZE(outs) - KV_SERIALIZE(ins) - KV_SERIALIZE(extra) - KV_SERIALIZE(attachments) - KV_SERIALIZE(object_in_json) + KV_SERIALIZE_BLOB_AS_BASE64_STRING(blob) DOC_DSCR("Serialized form of the transaction, encoded in Base64.") DOC_END + KV_SERIALIZE(blob_size) DOC_DSCR("Size of the serialized transaction in bytes.") DOC_END + KV_SERIALIZE(timestamp) DOC_DSCR("Timestamp when the transaction was created.") DOC_END + KV_SERIALIZE(keeper_block) DOC_DSCR("Block height where the transaction is confirmed, or -1 if it is unconfirmed.") DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Transaction fee in the smallest currency unit.") DOC_END + KV_SERIALIZE(amount) DOC_DSCR("Total output amount of the transaction (legacy, for pre-Zarcanum txs).") DOC_END + KV_SERIALIZE(id) DOC_DSCR("Hash of the transaction.") DOC_END + KV_SERIALIZE(pub_key) DOC_DSCR("Public key associated with the transaction.") DOC_END + KV_SERIALIZE(outs) DOC_DSCR("Outputs of the transaction.") DOC_END + KV_SERIALIZE(ins) DOC_DSCR("Inputs of the transaction.") DOC_END + KV_SERIALIZE(extra) DOC_DSCR("Extra data associated with the transaction.") DOC_END + KV_SERIALIZE(attachments) DOC_DSCR("Additional attachments to the transaction.") DOC_END + KV_SERIALIZE_BLOB_AS_BASE64_STRING(object_in_json) DOC_DSCR("Serialized transaction represented in JSON, encoded in Base64.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1366,10 +1525,10 @@ namespace currency uint64_t sz; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) - KV_SERIALIZE(fee) - KV_SERIALIZE(total_amount) - KV_SERIALIZE(sz) + KV_SERIALIZE(id) DOC_DSCR("Hash of the transaction.") DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Transaction fee in the smallest currency unit.") DOC_END + KV_SERIALIZE(total_amount) DOC_DSCR("Total amount transferred in the transaction (legacy, for pre-Zarcanum txs).") DOC_END + KV_SERIALIZE(sz) DOC_DSCR("Size of the transaction in bytes.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1400,41 +1559,43 @@ namespace currency std::list transactions_details; std::string miner_text_info; std::string object_in_json; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(blob) - KV_SERIALIZE(height) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(actual_timestamp) - KV_SERIALIZE(block_cumulative_size) - KV_SERIALIZE(total_txs_size) - KV_SERIALIZE(block_tself_size) - KV_SERIALIZE(base_reward) - KV_SERIALIZE(summary_reward) - KV_SERIALIZE(total_fee) - KV_SERIALIZE(penalty) - KV_SERIALIZE(id) - KV_SERIALIZE(prev_id) - KV_SERIALIZE(pow_seed) - KV_SERIALIZE(cumulative_diff_adjusted) - KV_SERIALIZE(cumulative_diff_precise) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(already_generated_coins) - KV_SERIALIZE(this_block_fee_median) - KV_SERIALIZE(effective_fee_median) - KV_SERIALIZE(transactions_details) - KV_SERIALIZE(type) - KV_SERIALIZE(is_orphan) - KV_SERIALIZE(miner_text_info) - KV_SERIALIZE(object_in_json) + KV_SERIALIZE(blob) DOC_DSCR("Serialized form of the block.") DOC_END + KV_SERIALIZE(height) DOC_DSCR("Height of the block in the blockchain.") DOC_END + KV_SERIALIZE(timestamp) DOC_DSCR("Timestamp when the block was created, in PoS blocks used for mining.") DOC_END + KV_SERIALIZE(actual_timestamp) DOC_DSCR("Actual timestamp encoded in the block's extra data for PoS blocks.") DOC_END + KV_SERIALIZE(block_cumulative_size) DOC_DSCR("Cumulative size of the block including all transactions.") DOC_END + KV_SERIALIZE(total_txs_size) DOC_DSCR("Total size of all transactions included in the block.") DOC_END + KV_SERIALIZE(block_tself_size) // TODO ? + KV_SERIALIZE(base_reward) DOC_DSCR("Base mining reward for the block.") DOC_END + KV_SERIALIZE(summary_reward) DOC_DSCR("Total reward for the block, including base reward and transaction fees (legacy).") DOC_END + KV_SERIALIZE(total_fee) DOC_DSCR("Total transaction fees included in the block.") DOC_END + KV_SERIALIZE(penalty) DOC_DSCR("Penalty applied to the reward if the block is larger than median but not large enough to be rejected.") DOC_END + KV_SERIALIZE(id) DOC_DSCR("Unique identifier of the block.") DOC_END + KV_SERIALIZE(prev_id) DOC_DSCR("Hash of the previous block in the chain.") DOC_END + KV_SERIALIZE(pow_seed) // TODO + KV_SERIALIZE(cumulative_diff_adjusted) DOC_DSCR("Adjusted cumulative difficulty of the blockchain up to this block.") DOC_END + KV_SERIALIZE(cumulative_diff_precise) DOC_DSCR("Precise cumulative difficulty of the blockchain up to this block.") DOC_END + KV_SERIALIZE(difficulty) DOC_DSCR("Mining difficulty of the block.") DOC_END + KV_SERIALIZE(already_generated_coins) DOC_DSCR("Total amount of coins generated in the blockchain up to this block.") DOC_END + KV_SERIALIZE(this_block_fee_median) DOC_DSCR("Median transaction fee of the transactions within this block.") DOC_END + KV_SERIALIZE(effective_fee_median) // TODO + KV_SERIALIZE(transactions_details) DOC_DSCR("Detailed information about each transaction included in the block.") DOC_END + KV_SERIALIZE(type) DOC_DSCR("Type of the block.") DOC_END + KV_SERIALIZE(is_orphan) DOC_DSCR("Indicates whether the block is an orphan.") DOC_END + KV_SERIALIZE(miner_text_info) DOC_DSCR("Additional textual information provided by the miner of the block.") DOC_END + KV_SERIALIZE(object_in_json) DOC_DSCR("Serialized representation of the block in JSON format.") DOC_END END_KV_SERIALIZE_MAP() }; - + //----------------------------------------------- + struct COMMAND_RPC_GET_BLOCKS_DETAILS { + DOC_COMMAND("Retrieves detailed information about a sequence of blocks starting from a specific height."); + struct request { uint64_t height_start; @@ -1442,9 +1603,9 @@ namespace currency bool ignore_transactions; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height_start) - KV_SERIALIZE(count) - KV_SERIALIZE(ignore_transactions) + KV_SERIALIZE(height_start) DOC_DSCR("The starting block height from which block details are retrieved.") DOC_END + KV_SERIALIZE(count) DOC_DSCR("The number of blocks to retrieve from the starting height.") DOC_END + KV_SERIALIZE(ignore_transactions) // TODO END_KV_SERIALIZE_MAP() }; @@ -1454,22 +1615,26 @@ namespace currency std::list blocks; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(blocks) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(blocks) DOC_DSCR("List of blocks with detailed information, starting from the specified height.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALT_BLOCKS_DETAILS { + DOC_COMMAND("Retrieves details of alternative blocks in the blockchain, allowing for pagination through large datasets."); + struct request { uint64_t offset; uint64_t count; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(offset) - KV_SERIALIZE(count) + KV_SERIALIZE(offset) DOC_DSCR("The offset in the list of alternative blocks from which to start retrieval.") DOC_END + KV_SERIALIZE(count) DOC_DSCR("The number of alternative blocks to retrieve from the specified offset.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1479,21 +1644,24 @@ namespace currency std::vector blocks; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(blocks) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(blocks) DOC_DSCR("List of alternative blocks with detailed information, retrieved based on the specified parameters.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_GET_BLOCK_DETAILS { + DOC_COMMAND("Retrieves detailed information about a specific block identified by its hash."); + struct request { crypto::hash id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(id) + KV_SERIALIZE_POD_AS_HEX_STRING(id) DOC_DSCR("The hash ID of the block for which detailed information is being requested.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1503,21 +1671,24 @@ namespace currency block_rpc_extended_info block_details; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(block_details) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(block_details) DOC_DSCR("Detailed information about the block retrieved based on the provided hash ID.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_GET_POOL_TXS_DETAILS { + DOC_COMMAND("Retrieves detailed information about specific transactions in the transaction pool, identified by their IDs."); + struct request { std::list ids; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(ids) + KV_SERIALIZE(ids) DOC_DSCR("List of transaction IDs.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1527,21 +1698,24 @@ namespace currency std::list txs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(txs) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(txs) DOC_DSCR("List of transactions with detailed information.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_GET_POOL_TXS_BRIEF_DETAILS { + DOC_COMMAND("Retrieves brief details about specific transactions in the transaction pool, identified by their IDs."); + struct request { std::list ids; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(ids) + KV_SERIALIZE(ids) DOC_DSCR("List of transaction IDs.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1551,14 +1725,18 @@ namespace currency std::list txs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(txs) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(txs) DOC_DSCR("List of transactions with detailed information.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_ALL_POOL_TX_LIST { + DOC_COMMAND("Retrieves a list of all transaction IDs currently in the transaction pool."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1571,13 +1749,15 @@ namespace currency std::list ids; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(ids) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(ids) DOC_DSCR("List of all transaction IDs currently in the transaction pool.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + // TODO looks like it is never used, a typo? struct COMMAND_RPC_GET_GLOBAL_INDEX_INFO { struct request @@ -1605,14 +1785,18 @@ namespace currency }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_TX_DETAILS { + DOC_COMMAND("Retrieves detailed information about a specific transaction."); + struct request { std::string tx_hash; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_hash) DOC_DSCR("The hash of the transaction for which detailed information is being requested.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1622,12 +1806,13 @@ namespace currency tx_rpc_extended_info tx_info; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(tx_info) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(tx_info) DOC_DSCR("Detailed information about the transaction.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct search_entry { @@ -1636,12 +1821,14 @@ namespace currency struct COMMAND_RPC_SERARCH_BY_ID { + DOC_COMMAND("Searches for a given ID across various entity types such as blocks, transactions, key images, multisig outputs, and alternative blocks, useful when the entity type is unknown or unspecified."); + struct request { std::string id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) + KV_SERIALIZE(id) DOC_DSCR("The identifier used to search across various types of entities.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1651,20 +1838,23 @@ namespace currency std::list types_found; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(types_found) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(types_found) DOC_DSCR("List of entity types where the identifier was found.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_RPC_GET_OFFERS_EX { + DOC_COMMAND("Fetch from daemon offers listed in the marketplace with given filters"); + struct request { bc_services::core_offers_filter filter; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(filter) + KV_SERIALIZE(filter) DOC_DSCR("Filter options.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1675,15 +1865,19 @@ namespace currency uint64_t total_offers; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(offers) - KV_SERIALIZE(total_offers) + KV_SERIALIZE(status) DOC_DSCR("Status of the operation.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(offers) DOC_DSCR("List of offers related to the operation.") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(total_offers) DOC_DSCR("Total number of offers.") DOC_EXMP(1) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN { + DOC_COMMAND("Retrieves the current core transaction expiration median."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1696,15 +1890,18 @@ namespace currency uint64_t expiration_median; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(expiration_median) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + KV_SERIALIZE(expiration_median) DOC_DSCR("The median timestamp from the last N blocks, used to determine if transactions are expired based on their timestamp.") DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- struct COMMAND_VALIDATE_SIGNATURE { + DOC_COMMAND("Validates a Schnorr signature for arbitrary data. The public key for verification is provided directly or retrieved using an associated alias."); + struct request { std::string buff; //base64 encoded data @@ -1713,23 +1910,24 @@ namespace currency std::string alias; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(buff) - KV_SERIALIZE_POD_AS_HEX_STRING(sig) - KV_SERIALIZE_POD_AS_HEX_STRING(pkey) - KV_SERIALIZE(alias) + KV_SERIALIZE(buff) DOC_DSCR("Base64 encoded data for which the signature is to be validated.") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(sig) DOC_DSCR("Schnorr signature to validate, encoded as a hexadecimal string.") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(pkey) DOC_DSCR("Public key used for signature verification, encoded as a hexadecimal string. If null or not set, the public key is retrieved using the provided alias.") DOC_END + KV_SERIALIZE(alias) DOC_DSCR("Alias to retrieve the associated public spend key if no explicit public key is provided for verification.") DOC_END END_KV_SERIALIZE_MAP() }; - struct response { std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Status of the call.") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; + //----------------------------------------------- + struct void_struct { BEGIN_KV_SERIALIZE_MAP() diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp index b4ca7448..6203eee9 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/simplewallet/password_container.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -63,6 +63,11 @@ namespace tools } bool password_container::read_password(const std::string& prompt_text) + { + return read_input(prompt_text, '*'); + } + + bool password_container::read_input(const std::string& prompt_text, char char_to_replace_user_input /* = '\0' */) { clear(); @@ -70,7 +75,7 @@ namespace tools if (is_cin_tty()) { std::cout << prompt_text; - r = read_from_tty(); + r = read_from_tty(char_to_replace_user_input); } else { @@ -122,7 +127,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool password_container::read_from_tty(char char_to_replace_user_input) { const char BACKSPACE = 8; @@ -162,7 +167,7 @@ namespace tools else { m_password.push_back(ch); - std::cout << '*'; + std::cout << (char_to_replace_user_input != '\0' ? char_to_replace_user_input : ch); } } @@ -198,7 +203,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool password_container::read_from_tty(char char_to_replace_user_input) { const char BACKSPACE = 127; @@ -227,7 +232,7 @@ namespace tools else { m_password.push_back(ch); - std::cout << '*'; + std::cout << (char_to_replace_user_input != '\0' ? char_to_replace_user_input : ch); } } diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h index 49a3412a..5bca195b 100644 --- a/src/simplewallet/password_container.h +++ b/src/simplewallet/password_container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -24,13 +24,15 @@ namespace tools void clear(); bool empty() const { return m_empty; } const std::string& password() const { return m_password; } + const std::string& get_input() const { return m_password; } // TODO: refactor this void password(std::string&& val) { m_password = std::move(val); m_empty = false; } bool read_password(); bool read_password(const std::string& prompt_text); + bool read_input(const std::string& prompt_text, char char_to_replace_user_input = '\0'); private: bool read_from_file(); - bool read_from_tty(); + bool read_from_tty(char char_to_replace_user_input); private: bool m_empty; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 1a1f1e8e..c953138f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -118,29 +118,32 @@ namespace ph = boost::placeholders; #define CONFIRM_WITH_PASSWORD() if(!check_password_for_operation()) return true; +#define DEFAULT_WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 3 +#define DEFAULT_WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 10 + namespace { - const command_line::arg_descriptor arg_wallet_file ("wallet-file", "Use wallet ", ""); - const command_line::arg_descriptor arg_generate_new_wallet ("generate-new-wallet", "Generate new wallet and save it to or
.wallet by default", ""); - const command_line::arg_descriptor arg_generate_new_auditable_wallet ("generate-new-auditable-wallet", "Generate new auditable wallet and store it to ", ""); - const command_line::arg_descriptor arg_daemon_address ("daemon-address", "Use daemon instance at :", ""); - const command_line::arg_descriptor arg_daemon_host ("daemon-host", "Use daemon instance at host instead of localhost", ""); - const command_line::arg_descriptor arg_password ("password", "Wallet password"); - const command_line::arg_descriptor arg_dont_refresh ( "no-refresh", "Do not refresh after load"); - const command_line::arg_descriptor arg_dont_set_date ( "no-set-creation-date", "Do not set wallet creation date", false); - const command_line::arg_descriptor arg_daemon_port ("daemon-port", "Use daemon instance at port instead of default", 0); - //const command_line::arg_descriptor arg_log_level ("set-log", ""); - const command_line::arg_descriptor arg_do_pos_mining ( "do-pos-mining", "Do PoS mining", false); - const command_line::arg_descriptor arg_pos_mining_reward_address ( "pos-mining-reward-address", "Block reward will be sent to the giving address if specified", "" ); - const command_line::arg_descriptor arg_restore_wallet ( "restore-wallet", "Restore wallet from seed phrase or tracking seed and save it to ", "" ); - const command_line::arg_descriptor arg_offline_mode ( "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)"); - const command_line::arg_descriptor arg_scan_for_wallet ( "scan-for-wallet", ""); - const command_line::arg_descriptor arg_addr_to_compare ( "addr-to-compare", ""); - const command_line::arg_descriptor arg_disable_tor_relay ( "disable-tor-relay", "Disable TOR relay", false); - const command_line::arg_descriptor arg_set_timeout("set-timeout", "Set timeout for the wallet"); - const command_line::arg_descriptor arg_voting_config_file("voting-config-file", "Set voting config instead of getting if from daemon", ""); - const command_line::arg_descriptor arg_no_password_confirmations("no-password-confirmation", "Enable/Disable password confirmation for transactions", false); + const command_line::arg_descriptor arg_wallet_file ("wallet-file", "Use wallet ", ""); + const command_line::arg_descriptor arg_generate_new_wallet ("generate-new-wallet", "Generate new wallet and save it to or
.wallet by default", ""); + const command_line::arg_descriptor arg_generate_new_auditable_wallet ("generate-new-auditable-wallet", "Generate new auditable wallet and store it to ", ""); + const command_line::arg_descriptor arg_daemon_address ("daemon-address", "Use daemon instance at :", ""); + const command_line::arg_descriptor arg_daemon_host ("daemon-host", "Use daemon instance at host instead of localhost", ""); + const command_line::arg_descriptor arg_password ("password", "Wallet password"); + const command_line::arg_descriptor arg_dont_refresh ( "no-refresh", "Do not refresh after load"); + const command_line::arg_descriptor arg_dont_set_date ( "no-set-creation-date", "Do not set wallet creation date", false); + const command_line::arg_descriptor arg_daemon_port ("daemon-port", "Use daemon instance at port instead of default", 0); + const command_line::arg_descriptor arg_do_pos_mining ( "do-pos-mining", "Do PoS mining", false); + const command_line::arg_descriptor arg_pos_mining_reward_address ( "pos-mining-reward-address", "Block reward will be sent to the giving address if specified", "" ); + const command_line::arg_descriptor arg_pos_mining_defrag ( "pos-mining-defrag", ",,|disable Generate defragmentation tx for small outputs each time a PoS block is found. Disabled by default. If empty string given, the default params used: " STR(DEFAULT_WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX) "," STR(DEFAULT_WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX) ",1.0", "disable" ); + const command_line::arg_descriptor arg_restore_wallet ( "restore-wallet", "Restore wallet from seed phrase or tracking seed and save it to ", "" ); + const command_line::arg_descriptor arg_offline_mode ( "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)"); + const command_line::arg_descriptor arg_scan_for_wallet ( "scan-for-wallet", ""); + const command_line::arg_descriptor arg_addr_to_compare ( "addr-to-compare", ""); + const command_line::arg_descriptor arg_disable_tor_relay ( "disable-tor-relay", "Disable TOR relay", false); + const command_line::arg_descriptor arg_set_timeout("set-timeout", "Set timeout for the wallet"); + const command_line::arg_descriptor arg_voting_config_file("voting-config-file", "Set voting config instead of getting if from daemon", ""); + const command_line::arg_descriptor arg_no_password_confirmations("no-password-confirmation", "Enable/Disable password confirmation for transactions", false); const command_line::arg_descriptor< std::vector > arg_command ("command", ""); @@ -287,7 +290,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, ph::_1), "start_mining - Start mining in daemon"); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, ph::_1), "Stop mining in daemon"); m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, ph::_1), "Resynchronize transactions and balance"); - m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, ph::_1), "Show current wallet balance"); + m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, ph::_1), "[force_all] Show current wallet balance, with 'force_all' param it displays all assets without filtering against whitelists"); m_cmd_binder.set_handler("show_staking_history", boost::bind(&simple_wallet::show_staking_history, this, ph::_1), "show_staking_history [2] - Show staking transfers, if option provided - number of days for history to display"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, ph::_1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); m_cmd_binder.set_handler("incoming_counts", boost::bind(&simple_wallet::show_incoming_transfers_counts, this, ph::_1), "incoming_transfers counts"); @@ -312,6 +315,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("scan_transfers_for_ki", boost::bind(&simple_wallet::scan_transfers_for_ki, this,ph::_1), "Rescan transfers for key image"); m_cmd_binder.set_handler("print_utxo_distribution", boost::bind(&simple_wallet::print_utxo_distribution, this,ph::_1), "Prints utxo distribution"); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this,ph::_1), "sweep_below
[payment_id] - Tries to transfers all coins with amount below the given limit to the given address"); + m_cmd_binder.set_handler("sweep_bare_outs", boost::bind(&simple_wallet::sweep_bare_outs, this,ph::_1), "sweep_bare_outs - Transfers all bare unspent outputs to itself. Uses several txs if necessary."); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this,ph::_1), "Show current wallet public address"); m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::integrated_address, this,ph::_1), "integrated_address [|& args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) +bool simple_wallet::show_balance(const std::vector& args /* = std::vector()*/) { - success_msg_writer() << m_wallet->get_balance_str(); + if (args.size() == 1 && args[0] == "raw") + { + success_msg_writer() << m_wallet->get_balance_str_raw(); + } + else + { + success_msg_writer() << m_wallet->get_balance_str(); + } return true; } //---------------------------------------------------------------------------------------------------- @@ -2113,11 +2123,11 @@ bool simple_wallet::emit_asset(const std::vector &args) tx_destination_entry td = AUTO_VAL_INIT(td); td.addr.push_back(m_wallet->get_account().get_public_address()); td.amount = amount; - td.asset_id = asset_id; + td.asset_id = currency::null_pkey; std::vector destinations; destinations.push_back(td); currency::transaction result_tx = AUTO_VAL_INIT(result_tx); - m_wallet->emmit_asset(asset_id, destinations, result_tx); + m_wallet->emit_asset(asset_id, destinations, result_tx); success_msg_writer(true) << "Emitted " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL << "Asset ID: " << asset_id << ENDL @@ -2508,8 +2518,129 @@ bool simple_wallet::sweep_below(const std::vector &args) SIMPLE_WALLET_CATCH_TRY_ENTRY(); return true; } +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_bare_outs(const std::vector &args) +{ + CONFIRM_WITH_PASSWORD(); + SIMPLE_WALLET_BEGIN_TRY_ENTRY(); + bool r = false; + if (args.size() > 1) + { + fail_msg_writer() << "Invalid number of agruments given"; + return true; + } + currency::account_public_address target_address = m_wallet->get_account().get_public_address(); + currency::payment_id_t integrated_payment_id{}; + if (args.size() == 1) + { + if (!m_wallet->get_transfer_address(args[0], target_address, integrated_payment_id)) + { + fail_msg_writer() << "Unable to parse address from " << args[1]; + return true; + } + if (!integrated_payment_id.empty()) + { + fail_msg_writer() << "Payment id is not supported. Please, don't use integrated address with this command."; + return true; + } + } + + if (!m_wallet->has_bare_unspent_outputs()) + { + success_msg_writer(true) << "This wallet doesn't have bare unspent outputs.\nNothing to do. Everything looks good."; + return true; + } + + std::vector groups; + m_wallet->get_bare_unspent_outputs_stats(groups); + if (groups.empty()) + { + uint64_t unlocked_balance = 0; + uint64_t balance = m_wallet->balance(unlocked_balance); + if (balance < COIN) + success_msg_writer(false) << "Looks like it's not enough coins to perform this operation. Transferring " << print_money_brief(TX_MINIMUM_FEE) << " ZANO or more to this wallet may help."; + else if (unlocked_balance < COIN) + success_msg_writer(false) << "Not enough spendable outputs to perform this operation. Please, try again later."; + else + { + success_msg_writer(false) << "This operation couldn't be performed for some reason. Please, copy simplewallet's log file and ask for support. Nothing was done."; + LOG_PRINT_L0("strange situation: balance: " << print_money_brief(balance) << ", unlocked_balance: " << print_money_brief(unlocked_balance) << " but get_bare_unspent_outputs_stats returned empty result"); + } + return true; + } + + size_t i = 0, total_bare_outs = 0; + uint64_t total_amount = 0; + std::stringstream details_ss; + for(auto &g : groups) + { + details_ss << std::setw(2) << i << ": "; + for (auto& tid: g.tids) + { + tools::transfer_details td{}; + CHECK_AND_ASSERT_THROW_MES(m_wallet->get_transfer_info_by_index(tid, td), "get_transfer_info_by_index failed with index " << tid); + details_ss << tid << " (" << print_money_brief(td.m_amount) << "), "; + total_amount += td.m_amount; + } + + if (g.additional_tid) + details_ss << "additional tid: " << g.tids.back() << "( " << print_money_brief(g.additional_tid_amount) << ")"; + else + details_ss.seekp(-2, std::ios_base::end); + + details_ss << ENDL; + ++i; + total_bare_outs += g.tids.size(); + } + + LOG_PRINT_L1("bare UTXO:" << ENDL << details_ss.str()); + + success_msg_writer(true) << "This wallet contains " << total_bare_outs << " bare outputs with total amount of " << print_money_brief(total_amount) << + ". They can be converted in " << groups.size() << " transaction" << (groups.size() > 1 ? "s" : "") << ", with total fee = " << print_money_brief(TX_DEFAULT_FEE * i) << "."; + if (target_address != m_wallet->get_account().get_public_address()) + message_writer(epee::log_space::console_color_yellow, false) << print_money_brief(total_amount) << " coins will be sent to address " << get_account_address_as_str(target_address); + + tools::password_container reader; + if (!reader.read_input("Would you like to continue? (y/yes/n/no):\n") || (reader.get_input() != "y" && reader.get_input() != "yes")) + { + success_msg_writer(false) << "Operatation terminated as requested by user."; + return true; + } + + size_t total_tx_sent = 0; + uint64_t total_fee_spent = 0; + uint64_t total_amount_sent = 0; + auto on_tx_sent_callback = [&](size_t batch_index, const currency::transaction& tx, uint64_t amount, uint64_t fee, bool sent_ok, const std::string& err) { + auto mw = success_msg_writer(false); + mw << std::setw(2) << batch_index << ": transaction "; + if (!sent_ok) + { + mw << "failed (" << err << ")"; + return; + } + mw << get_transaction_hash(tx) << ", fee: " << print_money_brief(fee) << ", amount: " << print_money_brief(amount); + ++total_tx_sent; + total_fee_spent += fee; + total_amount_sent += amount; + }; + + if (!m_wallet->sweep_bare_unspent_outputs(target_address, groups, on_tx_sent_callback)) + { + auto mw = fail_msg_writer(); + mw << "Operatation failed."; + if (total_tx_sent > 0) + mw << " However, " << total_tx_sent << " transaction" << (total_tx_sent == 1 ? " was" : "s were") << " successfully sent, " << print_money_brief(total_amount_sent) << " coins transferred, and " << print_money_brief(total_fee_spent) << " was spent for fees."; + } + else + { + success_msg_writer(true) << "Operatation succeeded. " << ENDL << total_tx_sent << " transaction" << (total_tx_sent == 1 ? " was" : "s were") << " successfully sent, " << print_money_brief(total_amount_sent) << " coins transferred, and " << print_money_brief(total_fee_spent) << " was spent for fees."; + } + + SIMPLE_WALLET_CATCH_TRY_ENTRY(); + return true; +} //---------------------------------------------------------------------------------------------------- uint64_t get_tick_count__() @@ -2651,7 +2782,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_general, command_line::arg_help); command_line::add_arg(desc_general, command_line::arg_version); - po::options_description desc_params("Wallet options"); + po::options_description desc_params("Wallet options", 160); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_generate_new_wallet); command_line::add_arg(desc_params, arg_generate_new_auditable_wallet); @@ -2665,6 +2796,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_dont_set_date); command_line::add_arg(desc_params, arg_do_pos_mining); command_line::add_arg(desc_params, arg_pos_mining_reward_address); + command_line::add_arg(desc_params, arg_pos_mining_defrag); command_line::add_arg(desc_params, arg_restore_wallet); command_line::add_arg(desc_params, arg_offline_mode); command_line::add_arg(desc_params, command_line::arg_log_file); @@ -2675,9 +2807,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_set_timeout); command_line::add_arg(desc_params, arg_voting_config_file); command_line::add_arg(desc_params, arg_no_password_confirmations); - - - + command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc); tools::wallet_rpc_server::init_options(desc_params); @@ -2721,7 +2851,7 @@ int main(int argc, char* argv[]) std::string log_dir; log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str(), LOG_LEVEL_4); - message_writer(epee::log_space::console_color_white, true) << CURRENCY_NAME << " wallet v" << PROJECT_VERSION_LONG; + message_writer(epee::log_space::console_color_white, true, std::string(), LOG_LEVEL_0) << CURRENCY_NAME << " simplewallet v" << PROJECT_VERSION_LONG; if (command_line::has_arg(vm, command_line::arg_log_level)) { @@ -2732,6 +2862,16 @@ int main(int argc, char* argv[]) message_writer(epee::log_space::console_color_white, true) << "Log level changed: " << old_log_level << " -> " << new_log_level; } + if (command_line::has_arg(vm, command_line::arg_generate_rpc_autodoc)) + { + tools::wallet_rpc_server wallet_rpc_server(std::shared_ptr(new tools::wallet2())); + std::string path_to_generate = command_line::get_arg(vm, command_line::arg_generate_rpc_autodoc); + + if (!generate_doc_as_md_files(path_to_generate, wallet_rpc_server)) + return 1; + return 0; + } + if (command_line::has_arg(vm, arg_scan_for_wallet)) { @@ -2812,6 +2952,22 @@ int main(int argc, char* argv[]) break; } //try to sync it + struct wallet_rpc_local_callback : public tools::i_wallet2_callback + { + std::shared_ptr m_wlt_ptr; + wallet_rpc_local_callback(std::shared_ptr ptr): m_wlt_ptr(ptr) + {} + virtual void on_mw_get_wallets(std::vector& wallets) + { + wallets.push_back(tools::wallet_public::wallet_entry_info()); + wallets.back().wallet_id = 0; + tools::get_wallet_info(*m_wlt_ptr, wallets.back().wi); + } + + }; + + std::shared_ptr callback(new wallet_rpc_local_callback(wallet_ptr)); + while (true) { try @@ -2824,6 +2980,7 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; wal.set_use_assets_whitelisting(true); + wal.callback(callback); if (!offline_mode) wal.refresh(); @@ -2850,6 +3007,31 @@ int main(int argc, char* argv[]) LOG_PRINT_YELLOW("PoS reward will be sent to another address: " << arg_pos_mining_reward_address_str, LOG_LEVEL_0); } } + + if (command_line::has_arg(vm, arg_pos_mining_defrag)) + { + std::string arg_pos_mining_defrag_str = command_line::get_arg(vm, arg_pos_mining_defrag); + if (arg_pos_mining_defrag_str.empty()) + { + // enable with default params + wal.set_defragmentation_tx_settings(true, DEFAULT_WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX, DEFAULT_WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX, COIN); + } + else + { + if (arg_pos_mining_defrag_str != "disable") + { + std::vector params; + boost::split(params, arg_pos_mining_defrag_str, boost::is_any_of(",;"), boost::token_compress_on); + CHECK_AND_ASSERT_MES(params.size() == 3, EXIT_FAILURE, "incorrect number of params given: " << arg_pos_mining_defrag_str); + int64_t outs_min = 0, outs_max = 0; + uint64_t max_amount = 0; + CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[0], outs_min) && outs_min > 0 && outs_min < 256, EXIT_FAILURE, "incorrect param: " << params[0]); + CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[1], outs_max) && outs_max > 0 && outs_max < 256, EXIT_FAILURE, "incorrect param: " << params[1]); + CHECK_AND_ASSERT_MES(currency::parse_amount(max_amount, params[2]), EXIT_FAILURE, "incorrect param: " << params[2]); + wal.set_defragmentation_tx_settings(true, outs_min, outs_max, max_amount); + } + } + } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 81958f18..0b0843d0 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -87,6 +87,7 @@ namespace currency bool sign_transfer(const std::vector &args); bool submit_transfer(const std::vector &args); bool sweep_below(const std::vector &args); + bool sweep_bare_outs(const std::vector &args); bool tor_enable(const std::vector &args); bool tor_disable(const std::vector &args); bool deploy_new_asset(const std::vector &args); diff --git a/src/version.h.in b/src/version.h.in index 5094f5be..6f6c9bad 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 273 +#define PROJECT_VERSION_BUILD_NO 309 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" diff --git a/src/wallet/core_default_rpc_proxy.cpp b/src/wallet/core_default_rpc_proxy.cpp index 92cf11bc..4bd94188 100644 --- a/src/wallet/core_default_rpc_proxy.cpp +++ b/src/wallet/core_default_rpc_proxy.cpp @@ -39,7 +39,6 @@ namespace tools currency::COMMAND_RPC_GET_BLOCKS_FAST::request req; req.block_ids = rqt.block_ids; req.minimum_height = rqt.minimum_height; - req.need_global_indexes = rqt.need_global_indexes; currency::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); bool r = call_COMMAND_RPC_GET_BLOCKS_FAST(req, res); rsp.status = res.status; @@ -52,6 +51,27 @@ namespace tools return r; } //------------------------------------------------------------------------------------------------------------------------------ + bool default_http_core_proxy::call_COMMAND_RPC_INVOKE(const std::string& uri, const std::string& body, int& response_code, std::string& response_body) + { + return call_request([&]() { +#ifdef MOBILE_WALLET_BUILD + LOG_PRINT_L0("[INVOKE_PROXY] ---> " << uri) +#endif + + const epee::net_utils::http::http_response_info* response = nullptr; + bool res = m_http_client.invoke(uri, "POST", body, &response); + if (response) + { + response_body = response->m_body; + response_code = response->m_response_code; + } +#ifdef MOBILE_WALLET_BUILD + LOG_PRINT_L0("[INVOKE_PROXY] <---" << uri) +#endif + return res; + }); + } + //------------------------------------------------------------------------------------------------------------------------------ bool default_http_core_proxy::call_COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE(const currency::COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::request& rqt, currency::COMMAND_RPC_GET_EST_HEIGHT_FROM_DATE::response& rsp) { return invoke_http_json_rpc_update_is_disconnect("get_est_height_from_date", rqt, rsp); @@ -77,9 +97,9 @@ namespace tools return invoke_http_bin_remote_command2_update_is_disconnect("/getrandom_outs1.bin", req, res); } //------------------------------------------------------------------------------------------------------------------------------ - bool default_http_core_proxy::call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res) + bool default_http_core_proxy::call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res) { - return invoke_http_bin_remote_command2_update_is_disconnect("/getrandom_outs2.bin", req, res); + return invoke_http_bin_remote_command2_update_is_disconnect("/getrandom_outs3.bin", req, res); } //------------------------------------------------------------------------------------------------------------------------------ bool default_http_core_proxy::call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& req, currency::COMMAND_RPC_SEND_RAW_TX::response& res) diff --git a/src/wallet/core_default_rpc_proxy.h b/src/wallet/core_default_rpc_proxy.h index 2d28c071..7eb94397 100644 --- a/src/wallet/core_default_rpc_proxy.h +++ b/src/wallet/core_default_rpc_proxy.h @@ -28,7 +28,7 @@ namespace tools bool set_connection_addr(const std::string& url) override; - void set_connectivity(unsigned int connection_timeout, size_t repeats_count); + void set_connectivity(unsigned int connection_timeout, size_t repeats_count) override; bool call_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES(const currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& rqt, currency::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& rsp) override; bool call_COMMAND_RPC_GET_BLOCKS_FAST(const currency::COMMAND_RPC_GET_BLOCKS_FAST::request& rqt, currency::COMMAND_RPC_GET_BLOCKS_FAST::response& rsp) override; bool call_COMMAND_RPC_GET_BLOCKS_DIRECT(const currency::COMMAND_RPC_GET_BLOCKS_DIRECT::request& rqt, currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& rsp) override; @@ -37,7 +37,7 @@ namespace tools bool call_COMMAND_RPC_GET_TX_POOL(const currency::COMMAND_RPC_GET_TX_POOL::request& rqt, currency::COMMAND_RPC_GET_TX_POOL::response& rsp) override; bool call_COMMAND_RPC_GET_ALIASES_BY_ADDRESS(const currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& rqt, currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& rsp) override; bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp) override; - bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& rsp) override; + bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& rsp) override; bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& rqt, currency::COMMAND_RPC_SEND_RAW_TX::response& rsp) override; bool call_COMMAND_RPC_FORCE_RELAY_RAW_TXS(const currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& rqt, currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& rsp) override; bool call_COMMAND_RPC_GET_ALL_ALIASES(currency::COMMAND_RPC_GET_ALL_ALIASES::response& rsp) override; @@ -53,6 +53,7 @@ namespace tools bool call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN(const currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res) override; bool call_COMMAND_RPC_GET_POOL_INFO(const currency::COMMAND_RPC_GET_POOL_INFO::request& req, currency::COMMAND_RPC_GET_POOL_INFO::response& res) override; bool call_COMMAND_RPC_GET_ASSET_INFO(const currency::COMMAND_RPC_GET_ASSET_INFO::request& req, currency::COMMAND_RPC_GET_ASSET_INFO::response& res) override; + bool call_COMMAND_RPC_INVOKE(const std::string& uri, const std::string& body, int& response_code, std::string& response_body) override; bool check_connection() override; bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id) override; diff --git a/src/wallet/core_fast_rpc_proxy.h b/src/wallet/core_fast_rpc_proxy.h index 3d212676..bf8ac62e 100644 --- a/src/wallet/core_fast_rpc_proxy.h +++ b/src/wallet/core_fast_rpc_proxy.h @@ -58,9 +58,9 @@ namespace tools return m_rpc.on_get_random_outs1(req, res, m_cntxt_stub); } //------------------------------------------------------------------------------------------------------------------------------ - bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res) override + bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res) override { - return m_rpc.on_get_random_outs2(req, res, m_cntxt_stub); + return m_rpc.on_get_random_outs3(req, res, m_cntxt_stub); } //------------------------------------------------------------------------------------------------------------------------------ bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& req, currency::COMMAND_RPC_SEND_RAW_TX::response& res) override @@ -143,6 +143,22 @@ namespace tools return m_rpc.on_get_asset_info(req, res, m_cntxt_stub); } //------------------------------------------------------------------------------------------------------------------------------ + virtual bool call_COMMAND_RPC_INVOKE(const std::string& uri, const std::string& body, int& response_code, std::string& response_body) override + { + epee::net_utils::http::http_request_info query_info = AUTO_VAL_INIT(query_info); + query_info.m_URI = uri; + query_info.m_http_method = epee::net_utils::http::http_method_get; + query_info.m_body = body; + + epee::net_utils::http::http_response_info response = AUTO_VAL_INIT(response); + epee::net_utils::connection_context_base conn_context = AUTO_VAL_INIT(conn_context); + + bool res = m_rpc.handle_http_request(query_info, response, conn_context); + response_body = response.m_body; + response_code = response.m_response_code; + return res; + } + //------------------------------------------------------------------------------------------------------------------------------ virtual bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id) override { return tools::get_transfer_address(adr_str, addr, payment_id, this); diff --git a/src/wallet/core_rpc_proxy.h b/src/wallet/core_rpc_proxy.h index effec340..5a4a25b9 100644 --- a/src/wallet/core_rpc_proxy.h +++ b/src/wallet/core_rpc_proxy.h @@ -36,7 +36,7 @@ namespace tools virtual bool call_COMMAND_RPC_GET_TX_POOL(const currency::COMMAND_RPC_GET_TX_POOL::request& rqt, currency::COMMAND_RPC_GET_TX_POOL::response& rsp){ return false; } virtual bool call_COMMAND_RPC_GET_ALIASES_BY_ADDRESS(const currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& rqt, currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& rsp){ return false; } virtual bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp){ return false; } - virtual bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& rsp) { return false; } + virtual bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& rsp) { return false; } virtual bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& rqt, currency::COMMAND_RPC_SEND_RAW_TX::response& rsp){ return false; } virtual bool call_COMMAND_RPC_FORCE_RELAY_RAW_TXS(const currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& rqt, currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& rsp){ return false; } virtual bool call_COMMAND_RPC_GET_ALL_ALIASES(currency::COMMAND_RPC_GET_ALL_ALIASES::response& rsp){ return false; } @@ -53,6 +53,8 @@ namespace tools virtual bool call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN(const currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res){ return false; } virtual bool call_COMMAND_RPC_GET_POOL_INFO(const currency::COMMAND_RPC_GET_POOL_INFO::request& req, currency::COMMAND_RPC_GET_POOL_INFO::response& res) { return false; } virtual bool call_COMMAND_RPC_GET_ASSET_INFO(const currency::COMMAND_RPC_GET_ASSET_INFO::request& req, currency::COMMAND_RPC_GET_ASSET_INFO::response& res) { return false; } + + virtual bool call_COMMAND_RPC_INVOKE(const std::string& uri, const std::string& body, int& response_code, std::string& response_body) { return false; } i_core_proxy() { diff --git a/src/wallet/decoy_selection.cpp b/src/wallet/decoy_selection.cpp index a75f333d..e9d24446 100644 --- a/src/wallet/decoy_selection.cpp +++ b/src/wallet/decoy_selection.cpp @@ -24,9 +24,10 @@ uint64_t scaler::scale(uint64_t h) void decoy_selection_generator::init(uint64_t max_h) { + load_distribution(g_default_distribution, max_h); m_is_initialized = true; - + m_max = max_h; // distribution INCLUDE m_max, count = m_max + 1 } bool decoy_selection_generator::load_distribution_from_file(const char* path) { @@ -66,6 +67,60 @@ std::vector decoy_selection_generator::generate_distribution(uint64_t return res; } + +std::vector decoy_selection_generator::generate_unique_reversed_distribution(uint64_t count) +{ + std::set set_to_extend; + generate_unique_reversed_distribution(count, set_to_extend); + return std::vector(set_to_extend.begin(), set_to_extend.end()); +} + +std::vector decoy_selection_generator::generate_unique_reversed_distribution(uint64_t count, uint64_t preincluded_item) +{ + std::set set_to_extend; + set_to_extend.insert(preincluded_item); + generate_unique_reversed_distribution(count, set_to_extend); + return std::vector(set_to_extend.begin(), set_to_extend.end()); +} + +#define DECOY_SELECTION_GENERATOR_MAX_ITERATIONS 1000000 + +void decoy_selection_generator::generate_unique_reversed_distribution(uint64_t count, std::set& set_to_extend) +{ + if (count + set_to_extend.size() > m_max) + { + throw std::runtime_error(std::string("generate_distribution_set with unexpected count=") + std::to_string(count) + ", set_to_extend.size() = " + std::to_string(set_to_extend.size()) + ", m_max: " + std::to_string(m_max)); + } + + size_t attempt_count = 0; + while (set_to_extend.size() != count) + { + attempt_count++; + if (attempt_count > DECOY_SELECTION_GENERATOR_MAX_ITERATIONS) + { + throw std::runtime_error("generate_distribution_set: attempt_count hit DECOY_SELECTION_GENERATOR_MAX_ITERATIONS"); + } + + uint64_t r = 0; + crypto::generate_random_bytes(sizeof(r), &r); + double r_ = map_uint_to_double(r); + auto it = m_distribution_mapping.upper_bound(r_); + if (it == m_distribution_mapping.end()) + { + throw(std::runtime_error(std::string("_r not found in m_distribution_mapping: ") + std::to_string(r_))); + } + uint64_t h = it->second; + if (it != m_distribution_mapping.begin()) + { + uint64_t h_0 = (--it)->second; + crypto::generate_random_bytes(sizeof(r), &r); + h = h_0 + r % (h - h_0) + 1; + } + //scale from nominal to max_h + set_to_extend.insert(m_max - h); + } +} + uint64_t get_distance(const std::vector entries, size_t i) { if (i == 0) @@ -79,7 +134,8 @@ bool decoy_selection_generator::load_distribution(const std::vector derived_distribution; scaler scl; - scl.config_scale(original_distribution.back().h, max_h); + uint64_t adjustment_value = original_distribution[0].h; + scl.config_scale(original_distribution.back().h - adjustment_value, max_h); uint64_t last_scaled_h = 0; std::list last_scaled_array; @@ -87,7 +143,7 @@ bool decoy_selection_generator::load_distribution(const std::vector generate_distribution(uint64_t count); + std::vector generate_unique_reversed_distribution(uint64_t count, uint64_t preincluded_item); + std::vector generate_unique_reversed_distribution(uint64_t count); + void generate_unique_reversed_distribution(uint64_t count, std::set& set_to_extend); bool is_initialized() { return m_is_initialized; } private: bool load_distribution(const std::vector& entries, uint64_t max_h); bool m_is_initialized = false; + uint64_t m_max = 0; std::map m_distribution_mapping; }; diff --git a/src/wallet/view_iface.h b/src/wallet/view_iface.h index d315a041..dce4b5ee 100644 --- a/src/wallet/view_iface.h +++ b/src/wallet/view_iface.h @@ -201,11 +201,13 @@ public: std::list balances; uint64_t minied_total; + bool has_bare_unspent_outputs; BEGIN_KV_SERIALIZE_MAP() KV_CHAIN_BASE(wallet_status_info_base) KV_SERIALIZE(balances) KV_SERIALIZE(minied_total) + KV_SERIALIZE(has_bare_unspent_outputs) END_KV_SERIALIZE_MAP() }; @@ -729,12 +731,14 @@ public: struct gui_options { - bool use_debug_mode; - bool disable_price_fetch; + bool use_debug_mode = false; + bool disable_price_fetch = false; + int32_t rpc_port = 0; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(use_debug_mode) KV_SERIALIZE(disable_price_fetch) + KV_SERIALIZE(rpc_port) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b2af9d58..ecda7231 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -56,12 +56,12 @@ using namespace currency; #define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB -#define WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO @#@# change to default decoy set number -#define WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 3 // TODO: @#@# consider descreasing to mimic normal tx -#define WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO: @#@# consider descreasing to mimic normal tx - #define WALLET_TX_MAX_ALLOWED_FEE (COIN * 100) +#define WALLET_FETCH_RANDOM_OUTS_SIZE 200 + + + #undef LOG_DEFAULT_CHANNEL #define LOG_DEFAULT_CHANNEL "wallet" ENABLE_CHANNEL_BY_DEFAULT("wallet") @@ -77,9 +77,11 @@ namespace tools , m_log_prefix("???") , m_watch_only(false) , m_required_decoys_count(CURRENCY_DEFAULT_DECOY_SET_SIZE) - , m_min_utxo_count_for_defragmentation_tx(WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX) - , m_max_utxo_count_for_defragmentation_tx(WALLET_MAX_UTXO_COUNT_FOR_DEFRAGMENTATION_TX) - , m_decoys_count_for_defragmentation_tx(WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX) + , m_defragmentation_tx_enabled(false) + , m_max_allowed_output_amount_for_defragmentation_tx(CURRENCY_BLOCK_REWARD) + , m_min_utxo_count_for_defragmentation_tx(0) + , m_max_utxo_count_for_defragmentation_tx(0) + , m_decoys_count_for_defragmentation_tx(SIZE_MAX) , m_use_deffered_global_outputs(false) #ifdef DISABLE_TOR , m_disable_tor_relay(true) @@ -168,15 +170,22 @@ bool wallet2::set_core_proxy(const std::shared_ptr& proxy) return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::set_pos_utxo_count_limits_for_defragmentation_tx(uint64_t min_outs, uint64_t max_outs) +void wallet2::set_defragmentation_tx_settings(bool enabled, uint64_t min_outs, uint64_t max_outs, uint64_t max_allowed_amount, size_t decoys_count) { - m_min_utxo_count_for_defragmentation_tx = min_outs; - m_max_utxo_count_for_defragmentation_tx = max_outs; -} -//---------------------------------------------------------------------------------------------------- -void wallet2::set_pos_decoys_count_for_defragmentation_tx(size_t decoys_count) -{ - m_decoys_count_for_defragmentation_tx = decoys_count; + m_defragmentation_tx_enabled = enabled; + m_min_utxo_count_for_defragmentation_tx = min_outs; + m_max_utxo_count_for_defragmentation_tx = max_outs; + m_max_allowed_output_amount_for_defragmentation_tx = max_allowed_amount; + m_decoys_count_for_defragmentation_tx = decoys_count; + if (enabled) + { + WLT_LOG_L0("Defragmentation tx creation is enabled, settings: min outs: " << min_outs << ", max outs: " << max_outs << ", max amount: " << print_money_brief(max_allowed_amount) << + ", decoys: " << (decoys_count != SIZE_MAX ? epee::string_tools::num_to_string_fast(decoys_count) : std::string("default"))); + } + else + { + WLT_LOG_L0("Defragmentation tx creation is disabled"); + } } //---------------------------------------------------------------------------------------------------- std::shared_ptr wallet2::get_core_proxy() @@ -343,31 +352,6 @@ const currency::txout_htlc& out_get_htlc(const currency::tx_out_v& out_t) return boost::get(boost::get(out_t).target); } -uint8_t wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) -{ - if (out_t.type() == typeid(currency::tx_out_bare)) - { - if (boost::get(out_t).target.type() == typeid(currency::txout_to_key)) - { - return boost::get(boost::get(out_t).target).mix_attr; - } - else - { - THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr"); - } - } - else if (out_t.type() == typeid(currency::tx_out_zarcanum)) - { - return boost::get(out_t).mix_attr; - } - else - { - THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr"); - } - THROW_WALLET_CMN_ERR_EX("Unexpected out type im wallet: " << out_t.type().name()); - return false; -} - const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out_t, std::list& htlc_info_list) { if (out_t.type() == typeid(tx_out_bare)) @@ -415,16 +399,17 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(m_own_asset_descriptors.count(asset_id) == 0, "asset with asset_id " << asset_id << " has already been registered in the wallet as own asset"); wallet_own_asset_context& asset_context = m_own_asset_descriptors[asset_id]; - asset_context.asset_descriptor = ado.descriptor; + epee::misc_utils::cast_assign_a_to_b(asset_context, ado.descriptor); + //*static_cast(&asset_context) = ado.descriptor; std::stringstream ss; ss << "New Asset Registered:" << ENDL << "asset id: " << asset_id - << ENDL << "Name: " << asset_context.asset_descriptor.full_name - << ENDL << "Ticker: " << asset_context.asset_descriptor.ticker - << ENDL << "Total Max Supply: " << print_asset_money(asset_context.asset_descriptor.total_max_supply, asset_context.asset_descriptor.decimal_point) - << ENDL << "Current Supply: " << print_asset_money(asset_context.asset_descriptor.current_supply, asset_context.asset_descriptor.decimal_point) - << ENDL << "Decimal Point: " << asset_context.asset_descriptor.decimal_point; + << ENDL << "Name: " << asset_context.full_name + << ENDL << "Ticker: " << asset_context.ticker + << ENDL << "Total Max Supply: " << print_asset_money(asset_context.total_max_supply, asset_context.decimal_point) + << ENDL << "Current Supply: " << print_asset_money(asset_context.current_supply, asset_context.decimal_point) + << ENDL << "Decimal Point: " << (int)asset_context.decimal_point; add_rollback_event(ptc.height, asset_register_event{ asset_id }); @@ -439,7 +424,8 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op break; //asset had been updated add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); - it->second.asset_descriptor = ado.descriptor; + epee::misc_utils::cast_assign_a_to_b(it->second, ado.descriptor); + } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_UPDATE ) { @@ -451,7 +437,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op // ownership of the asset acquired wallet_own_asset_context& asset_context = m_own_asset_descriptors[asset_id]; - asset_context.asset_descriptor = ado.descriptor; + epee::misc_utils::cast_assign_a_to_b(asset_context, ado.descriptor); std::stringstream ss; ss << "Asset ownership acquired:" @@ -460,7 +446,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op << ENDL << "Ticker: " << ado.descriptor.ticker << ENDL << "Total Max Supply: " << print_asset_money(ado.descriptor.total_max_supply, ado.descriptor.decimal_point) << ENDL << "Current Supply: " << print_asset_money(ado.descriptor.current_supply, ado.descriptor.decimal_point) - << ENDL << "Decimal Point: " << ado.descriptor.decimal_point; + << ENDL << "Decimal Point: " << (int)ado.descriptor.decimal_point; add_rollback_event(ptc.height, asset_register_event{ asset_id }); @@ -491,7 +477,7 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op << ENDL << "Ticker: " << ado.descriptor.ticker << ENDL << "Total Max Supply: " << print_asset_money(ado.descriptor.total_max_supply, ado.descriptor.decimal_point) << ENDL << "Current Supply: " << print_asset_money(ado.descriptor.current_supply, ado.descriptor.decimal_point) - << ENDL << "Decimal Point: " << ado.descriptor.decimal_point; + << ENDL << "Decimal Point: " << (int)ado.descriptor.decimal_point; add_rollback_event(ptc.height, asset_register_event{ asset_id }); WLT_LOG_MAGENTA(ss.str(), LOG_LEVEL_0); @@ -502,7 +488,8 @@ void wallet2::process_ado_in_new_transaction(const currency::asset_descriptor_op { //just an update of the asset add_rollback_event(ptc.height, asset_update_event{ it->first, it->second }); - it->second.asset_descriptor = ado.descriptor; + epee::misc_utils::cast_assign_a_to_b(it->second, ado.descriptor); + } } } @@ -514,6 +501,64 @@ void wallet2::add_rollback_event(uint64_t h, const wallet_event_t& ev) m_rollback_events.emplace_back(h, ev); } //---------------------------------------------------------------------------------------------------- +#define M_LAST_ZC_GLOBAL_INDEXS_MAX_SIZE 30 +void wallet2::add_to_last_zc_global_indexs(uint64_t h, uint64_t last_zc_output_index) +{ + if (m_last_zc_global_indexs.size()) + { + if (h > m_last_zc_global_indexs.begin()->first) + { + //new block added on top of last one, simply add new record + m_last_zc_global_indexs.push_front(std::make_pair(h, last_zc_output_index)); + } + else if (h < m_last_zc_global_indexs.begin()->first) + { + //looks like reorganize, pop all records before + while (m_last_zc_global_indexs.size() && m_last_zc_global_indexs.begin()->first >= h) + { + m_last_zc_global_indexs.erase(m_last_zc_global_indexs.begin()); + } + m_last_zc_global_indexs.push_front(std::make_pair(h, last_zc_output_index)); + } + else + { + //@#@ +#ifdef _DEBUG + if (m_last_zc_global_indexs.begin()->second > last_zc_output_index) + { + LOG_ERROR("!!!!!!!!!!!!!!!!!"); + } +#endif + //equals, same h but new last_zc_output_index, just update it, should be always bigger then prev + WLT_THROW_IF_FALSE_WITH_CODE(m_last_zc_global_indexs.begin()->second <= last_zc_output_index, + "condition m_last_zc_global_indexs.begin()->second " << m_last_zc_global_indexs.begin()->second << " <= last_zc_output_index " << last_zc_output_index << " failed", API_RETURN_CODE_INTERNAL_ERROR); + m_last_zc_global_indexs.begin()->second = last_zc_output_index; + } + } + else + { + //new block added on top of last one, simply add new record + m_last_zc_global_indexs.push_front(std::make_pair(h, last_zc_output_index)); + } + + if (m_last_zc_global_indexs.size() > M_LAST_ZC_GLOBAL_INDEXS_MAX_SIZE) + m_last_zc_global_indexs.pop_back(); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::get_actual_zc_global_index() +{ + WLT_THROW_IF_FALSE_WITH_CODE(m_last_zc_global_indexs.size(), "m_last_zc_global_indexs is empty", API_RETURN_CODE_INTERNAL_ERROR); + for (auto it = m_last_zc_global_indexs.begin(); it != m_last_zc_global_indexs.end(); it++) + { + if (it->first <= m_last_known_daemon_height - WALLET_DEFAULT_TX_SPENDABLE_AGE) + { + return it->second; + } + } + WLT_THROW_IF_FALSE_WITH_CODE(false, "doesn't have anything that match expected height = " << m_last_known_daemon_height - WALLET_DEFAULT_TX_SPENDABLE_AGE, API_RETURN_CODE_INTERNAL_ERROR); + throw std::runtime_error(""); //mostly to suppress compiler warning +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) { //check for transaction spends @@ -526,8 +571,14 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t //PoW block don't have change, so all outs supposed to be marked as "mined" ptc.is_derived_from_coinbase = !ptc.is_pos_coinbase; ptc.height = height; - - + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pglobal_indexes, "pglobal_indexes not set"); + if (this->is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)) + { + if (pglobal_indexes->size()) + { + add_to_last_zc_global_indexs(ptc.height, pglobal_indexes->back()); + } + } for(auto& in : tx.vin) { @@ -723,15 +774,16 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t } } - if (is_auditable() && (out_type_to_key || out_type_zc) && - out_get_mixin_attr(out_v) != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) + uint8_t mix_attr = CURRENCY_TO_KEY_OUT_RELAXED; + [[maybe_unused]] bool mix_attr_r = get_mix_attr_from_tx_out_v(out_v, mix_attr); + if (is_auditable() && (out_type_to_key || out_type_zc) && mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) { std::stringstream ss; ss << "output #" << o << " from tx " << ptc.tx_hash(); if (!out.is_native_coin()) ss << " asset_id: " << out.asset_id; ss << " with amount " << print_money_brief(out.amount) - << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)out_get_mixin_attr(out_v) << ". Output is IGNORED."; + << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)mix_attr << ". Output is IGNORED."; WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0); if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_red, ss.str()); @@ -1101,7 +1153,8 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept //--------------------------------------------------------------------------------- uint64_t wallet2::get_current_tx_version() { - return currency::get_tx_version(this->get_top_block_height(), this->m_core_runtime_config.hard_forks); + uint64_t tx_expected_block_height = get_top_block_height() + 1; + return currency::get_tx_version(tx_expected_block_height, this->m_core_runtime_config.hard_forks); } //--------------------------------------------------------------------------------- void wallet2::finish_contract(const crypto::hash& contract_id, const std::string& release_type, currency::transaction* p_release_tx /* = nullptr */) @@ -1917,8 +1970,6 @@ void wallet2::pull_blocks(size_t& blocks_added, std::atomic& stop) currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response res = AUTO_VAL_INIT(res); req.minimum_height = get_wallet_minimum_height(); - if (is_auditable()) - req.need_global_indexes = true; if (req.minimum_height > m_height_of_start_sync) m_height_of_start_sync = req.minimum_height; @@ -1971,12 +2022,14 @@ void wallet2::handle_pulled_blocks(size_t& blocks_added, std::atomic& stop currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& res) { size_t current_index = res.start_height; + m_last_known_daemon_height = res.current_height; bool been_matched_block = false; if (res.start_height == 0 && get_blockchain_current_size() == 1 && !res.blocks.empty()) { const currency::block& genesis = res.blocks.front().block_ptr->bl; THROW_IF_TRUE_WALLET_EX(get_block_height(genesis) != 0, error::wallet_internal_error, "first block expected to be genesis"); - process_genesis_if_needed(genesis); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(res.blocks.front().coinbase_ptr, "Unexpected empty coinbase"); + process_genesis_if_needed(genesis, &(res.blocks.front().coinbase_ptr->m_global_output_indexes)); res.blocks.pop_front(); ++current_index; been_matched_block = true; @@ -2181,6 +2234,190 @@ bool wallet2::has_related_alias_entry_unconfirmed(const currency::transaction& t return false; } //---------------------------------------------------------------------------------------------------- +bool wallet2::has_bare_unspent_outputs() const +{ + if (m_account.get_createtime() > ZANO_HARDFORK_04_TIMESTAMP_ACTUAL) + return false; + + [[maybe_unused]] uint64_t bal = 0; + if (!m_has_bare_unspent_outputs.has_value()) + bal = balance(); + + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(m_has_bare_unspent_outputs.has_value(), "m_has_bare_unspent_outputs has no value after balance()"); + + return m_has_bare_unspent_outputs.value(); +} +//---------------------------------------------------------------------------------------------------- +#define MAX_INPUTS_FOR_SIMPLE_TX_EURISTIC 20 +bool wallet2::get_bare_unspent_outputs_stats(std::vector& tids_grouped_by_txs) const +{ + tids_grouped_by_txs.clear(); + + // 1/3. Populate a list of bare unspent outputs + std::unordered_map> buo_ids; // tx hash -> Bare Unspent Outs list + for(size_t tid = 0; tid != m_transfers.size(); ++tid) + { + const auto& td = m_transfers[tid]; + if (!td.is_zc() && td.is_spendable()) + { + buo_ids[td.tx_hash()].push_back(tid); + } + } + + if (buo_ids.empty()) + return true; + + // 2/3. Split them into groups + tids_grouped_by_txs.emplace_back(); + for(auto& buo_el : buo_ids) + { + if (tids_grouped_by_txs.back().tids.size() + buo_el.second.size() > MAX_INPUTS_FOR_SIMPLE_TX_EURISTIC) + tids_grouped_by_txs.emplace_back(); + + for(auto& tid : buo_el.second) + { + if (tids_grouped_by_txs.back().tids.size() >= MAX_INPUTS_FOR_SIMPLE_TX_EURISTIC) + tids_grouped_by_txs.emplace_back(); + tids_grouped_by_txs.back().tids.push_back((uint64_t)tid); + tids_grouped_by_txs.back().total_amount += m_transfers[tid].m_amount; + } + } + + + // 3/3. Iterate through groups and check whether total amount is big enough to cover min fee. + // Add additional zc output if not. + std::multimap usable_zc_outs_tids; // grouped by amount + bool usable_zc_outs_tids_precalculated = false; + auto precalculate_usable_zc_outs_if_needed = [&](){ + if (usable_zc_outs_tids_precalculated) + return; + size_t decoys = is_auditable() ? 0 : m_core_runtime_config.hf4_minimum_mixins; + for(size_t tid = 0; tid != m_transfers.size(); ++tid) + { + auto& td = m_transfers[tid]; + if (td.is_zc() && td.is_native_coin() && is_transfer_ready_to_go(td, decoys)) + usable_zc_outs_tids.insert(std::make_pair(td.m_amount, tid)); + } + usable_zc_outs_tids_precalculated = true; + }; + + std::unordered_set used_zc_outs; + for(auto it = tids_grouped_by_txs.begin(); it != tids_grouped_by_txs.end(); ) + { + auto& group = *it; + if (group.total_amount < TX_MINIMUM_FEE) + { + precalculate_usable_zc_outs_if_needed(); + + uint64_t min_required_amount = TX_MINIMUM_FEE - group.total_amount; + auto jt = usable_zc_outs_tids.lower_bound(min_required_amount); + bool found = false; + while(jt != usable_zc_outs_tids.end()) + { + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(jt->first >= min_required_amount, "jt->first=" << jt->first << ", min_required_amount=" << min_required_amount); + if (used_zc_outs.count(jt->second) == 0) + { + group.tids.push_back((uint64_t)jt->second); + used_zc_outs.insert(jt->second); + group.additional_tid = true; + group.additional_tid_amount = jt->first; + found = true; + break; + } + ++jt; + } + + if (!found) + { + // no usable outs for required amount, remove this group and go to the next + it = tids_grouped_by_txs.erase(it); + continue; + } + } + + ++it; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::sweep_bare_unspent_outputs(const currency::account_public_address& target_address, const std::vector& tids_grouped_by_txs, + std::function on_tx_sent) +{ + if (m_watch_only) + return false; + + size_t decoys_count = is_auditable() ? 0 : CURRENCY_DEFAULT_DECOY_SET_SIZE; + + bool send_to_network = true; + + size_t batch_index = 0; + for(const batch_of_bare_unspent_outs& group : tids_grouped_by_txs) + { + currency::finalized_tx ftx{}; + currency::finalize_tx_param ftp{}; + ftp.pevents_dispatcher = &m_debug_events_dispatcher; + ftp.tx_version = this->get_current_tx_version(); + + if (!prepare_tx_sources(decoys_count, /*use_all_decoys_if_found_less_than_required*/ true, ftp.sources, group.tids)) + { + on_tx_sent(batch_index, transaction{}, 0, 0, false, "sources for tx couldn't be prepared"); + LOG_PRINT_L0("prepare_tx_sources failed, batch_index = " << batch_index); + return false; + } + uint64_t fee = TX_DEFAULT_FEE; + std::vector destinations{tx_destination_entry(group.total_amount + group.additional_tid_amount - fee, target_address)}; + assets_selection_context needed_money_map{std::make_pair(native_coin_asset_id, selection_for_amount{group.total_amount + group.additional_tid_amount, group.total_amount + group.additional_tid_amount})}; + try + { + prepare_tx_destinations(needed_money_map, get_current_split_strategy(), tx_dust_policy{}, destinations, 0 /* tx_flags */, ftp.prepared_destinations); + } + catch(...) + { + on_tx_sent(batch_index, transaction{}, 0, 0, false, "destinations for tx couldn't be prepared"); + LOG_PRINT_L0("prepare_tx_destinations failed, batch_index = " << batch_index); + return false; + } + + mark_transfers_as_spent(ftp.selected_transfers, std::string("sweep bare UTXO, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(ftx.tx))); + try + { + finalize_transaction(ftp, ftx, send_to_network); + on_tx_sent(batch_index, ftx.tx, group.total_amount + group.additional_tid_amount, fee, true, std::string()); + } + catch(std::exception& e) + { + clear_transfers_from_flag(ftp.selected_transfers, WALLET_TRANSFER_DETAIL_FLAG_SPENT, std::string("exception on sweep bare UTXO, tx: ") + epee::string_tools::pod_to_hex(get_transaction_hash(ftx.tx))); + on_tx_sent(batch_index, transaction{}, 0, 0, false, e.what()); + return false; + } + + ++batch_index; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::sweep_bare_unspent_outputs(const currency::account_public_address& target_address, const std::vector& tids_grouped_by_txs, + size_t& total_txs_sent, uint64_t& total_amount_sent, uint64_t& total_fee_spent, uint64_t& total_bare_outs_sent) +{ + total_txs_sent = 0; + total_amount_sent = 0; + total_fee_spent = 0; + total_bare_outs_sent = 0; + auto on_tx_sent_callback = [&](size_t batch_index, const currency::transaction& tx, uint64_t amount, uint64_t fee, bool sent_ok, const std::string& err) { + if (sent_ok) + { + total_bare_outs_sent += count_type_in_variant_container(tx.vin); + ++total_txs_sent; + total_fee_spent += fee; + total_amount_sent += amount; + } + }; + + return sweep_bare_unspent_outputs(target_address, tids_grouped_by_txs, on_tx_sent_callback); +} +//---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk) { return get_directly_spent_transfer_index_by_input_in_tracking_wallet(intk.amount, intk.key_offsets); @@ -2499,20 +2736,37 @@ void wallet2::scan_tx_pool(bool& has_related_alias_in_unconfirmed) //---------------------------------------------------------------------------------------------------- bool wallet2::on_idle() { - scan_unconfirmed_outdate_tx(); + scan_not_compliant_unconfirmed_txs(); return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::scan_unconfirmed_outdate_tx() +bool wallet2::scan_not_compliant_unconfirmed_txs() { uint64_t tx_expiration_ts_median = get_tx_expiration_median(); uint64_t time_limit = m_core_runtime_config.get_core_time() - CURRENCY_MEMPOOL_TX_LIVETIME; for (auto it = m_unconfirmed_txs.begin(); it != m_unconfirmed_txs.end(); ) { - bool tx_outdated = it->second.timestamp < time_limit; - if (tx_outdated || is_tx_expired(it->second.tx, tx_expiration_ts_median)) + bool remove_this_tx = false; + std::stringstream reason_ss; + if (it->second.timestamp < time_limit) { - WLT_LOG_BLUE("removing unconfirmed tx " << it->second.tx_hash << ", reason: " << (tx_outdated ? "outdated" : "expired") << ", tx_expiration_ts_median=" << tx_expiration_ts_median, LOG_LEVEL_0); + remove_this_tx = true; + reason_ss << "outdated, "; + } + if (is_tx_expired(it->second.tx, tx_expiration_ts_median)) + { + remove_this_tx = true; + reason_ss << "expired, "; + } + if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM) && it->second.tx.version < TRANSACTION_VERSION_POST_HF4) + { + remove_this_tx = true; + reason_ss << "not compliant with HF4, "; + } + + if (remove_this_tx) + { + WLT_LOG_BLUE("removing unconfirmed tx " << it->second.tx_hash << ", reason: " << reason_ss.str() << "tx_expiration_ts_median=" << tx_expiration_ts_median, LOG_LEVEL_0); //lookup all used transfer and update flags for (auto i : it->second.selected_indicies) { @@ -2524,15 +2778,18 @@ bool wallet2::scan_unconfirmed_outdate_tx() if (!m_transfers[i].m_spent_height) { uint32_t flags_before = m_transfers[i].m_flags; - m_transfers[i].m_flags &= ~(WALLET_TRANSFER_DETAIL_FLAG_SPENT); + m_transfers[i].m_flags &= ~(WALLET_TRANSFER_DETAIL_FLAG_SPENT); // TODO: consider removing other blocking flags (e.g. for escrow tx) -- sowle WLT_LOG_BLUE("mark transfer #" << i << " as unspent, flags: " << flags_before << " -> " << m_transfers[i].m_flags << ", reason: removing unconfirmed tx " << it->second.tx_hash, LOG_LEVEL_0); } } //fire some event m_wcallback->on_transfer_canceled(it->second); m_unconfirmed_txs.erase(it++); - }else + } + else + { it++; + } } //scan marked as spent but don't have height (unconfirmed, and actually not unconfirmed) @@ -2542,16 +2799,11 @@ bool wallet2::scan_unconfirmed_outdate_tx() if (!it->second.has_outgoing_entries()) continue; - for (auto& in : it->second.tx.vin) + for (auto& in_v : it->second.tx.vin) { - if (in.type() == typeid(txin_to_key)) - { - ki_in_unconfirmed.insert(boost::get(in).k_image); - } - else if (in.type() == typeid(txin_zc_input)) - { - ki_in_unconfirmed.insert(boost::get(in).k_image); - } + crypto::key_image ki{}; + if (get_key_image_from_txin_v(in_v, ki)) + ki_in_unconfirmed.insert(ki); } } @@ -2827,6 +3079,12 @@ void wallet2::detach_blockchain(uint64_t including_height) ++it; } + //detach in m_last_zc_global_indexs + while (m_last_zc_global_indexs.size() && including_height <= m_last_zc_global_indexs.begin()->first ) + { + m_last_zc_global_indexs.erase(m_last_zc_global_indexs.begin()); + } + //asset descriptors handle_rollback_events(including_height); @@ -3410,6 +3668,7 @@ uint64_t wallet2::balance(const crypto::public_key& asset_id) const bool wallet2::balance(std::unordered_map& balances, uint64_t& mined) const { mined = 0; + m_has_bare_unspent_outputs = false; for(auto& td : m_transfers) { @@ -3430,6 +3689,9 @@ bool wallet2::balance(std::unordered_map& balances, u { if (m_whitelisted_assets.find(own_asset.first) == m_whitelisted_assets.end()) { - custom_assets_local[own_asset.first] = own_asset.second.asset_descriptor; + custom_assets_local[own_asset.first] = own_asset.second; } } @@ -3600,7 +3862,7 @@ bool wallet2::add_custom_asset_id(const crypto::public_key& asset_id, asset_desc //---------------------------------------------------------------------------------------------------- bool wallet2::delete_custom_asset_id(const crypto::public_key& asset_id) { - auto it = m_custom_assets.find(asset_id); + const auto it = m_custom_assets.find(asset_id); if (it != m_custom_assets.end()) { m_custom_assets.erase(it); @@ -3609,6 +3871,21 @@ bool wallet2::delete_custom_asset_id(const crypto::public_key& asset_id) return true; } //---------------------------------------------------------------------------------------------------- +const std::unordered_map& wallet2::get_local_whitelist() const +{ + return m_custom_assets; +} +//---------------------------------------------------------------------------------------------------- +const std::unordered_map& wallet2::get_global_whitelist() const +{ + return m_whitelisted_assets; +} +//---------------------------------------------------------------------------------------------------- +const std::unordered_map& wallet2::get_own_assets() const +{ + return m_own_asset_descriptors; +} + //---------------------------------------------------------------------------------------------------- bool wallet2::load_whitelisted_tokens() const { if(!m_use_assets_whitelisting) @@ -3622,9 +3899,10 @@ bool wallet2::load_whitelisted_tokens() const for (auto it = aw.assets.begin(); it != aw.assets.end(); it++) { m_whitelisted_assets[it->asset_id] = static_cast(*it); - } + } + return true; } - return true; + return false; } //---------------------------------------------------------------------------------------------------- bool wallet2::load_whitelisted_tokens_if_not_loaded() const @@ -3646,6 +3924,9 @@ void wallet2::get_transfers(transfer_container& incoming_transfers) const //---------------------------------------------------------------------------------------------------- bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::transaction& tx) { + if (!m_defragmentation_tx_enabled) + return false; + construct_tx_param ctp = get_default_construct_tx_param(); ctp.create_utxo_defragmentation_tx = true; finalized_tx ftp{}; @@ -3741,6 +4022,59 @@ std::string wallet2::get_balance_str() const return ss.str(); } //---------------------------------------------------------------------------------------------------- +std::string wallet2::get_balance_str_raw() const +{ + // balance unlocked / [balance total] ticker asset id + // 1391306.970000000000 / 1391306.970000000000 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 1391306.97 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 106.971 / 206.4 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + + static const char* header = " balance unlocked / [balance total] asset id"; + std::stringstream ss; + ss << header << ENDL; + + uint64_t dummy = 0; + std::unordered_map balances_map; + this->balance(balances_map, dummy); + + for(const auto& entry : balances_map) + { + ss << " " << std::left << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.unlocked, 12); + if(entry.second.total == entry.second.unlocked) + ss << " "; + else + ss << " / " << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.total, 12); + ss << " " << std::setw(8) << std::left << entry.first << ENDL; + } + + //print whitelist + ss << "WHITELIST: " << ENDL; + + + for(const auto& entry : m_whitelisted_assets) + { + ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; + } + + // print whitelist + ss << "CUSTOM LIST: " << ENDL; + + + for(const auto& entry : m_custom_assets) + { + ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; + } + + ss << "OWN DESCRIPTORS LIST: " << ENDL; + + for(const auto& entry : m_own_asset_descriptors) + { + ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; + } + + return ss.str(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::get_payments(const std::string& payment_id, std::list& payments, uint64_t min_height) const { auto range = m_payments.equal_range(payment_id); @@ -4212,6 +4546,11 @@ bool wallet2::is_in_hardfork_zone(uint64_t hardfork_index) const return m_core_runtime_config.is_hardfork_active_for_height(hardfork_index, get_blockchain_current_size()); } //---------------------------------------------------------------------------------------------------- +bool wallet2::proxy_to_daemon(const std::string& uri, const std::string& body, int& response_code, std::string& response_body) +{ + return m_core_proxy->call_COMMAND_RPC_INVOKE(uri, body, response_code, response_body); +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, uint64_t full_block_reward, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc, currency::block& b) const { bool r = false; @@ -4254,7 +4593,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, uint64_t ful if (m_required_decoys_count > 0 && !is_auditable()) { COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request decoys_req = AUTO_VAL_INIT(decoys_req); - decoys_req.height_upper_limit = 0; // TODO @#@# maybe use m_last_pow_block_h like Zarcanum? + decoys_req.height_upper_limit = std::min(m_last_pow_block_h, m_last_known_daemon_height > m_core_runtime_config.min_coinstake_age ? m_last_known_daemon_height - m_core_runtime_config.min_coinstake_age : m_last_pow_block_h); decoys_req.use_forced_mix_outs = false; decoys_req.decoys_count = m_required_decoys_count + 1; // one more to be able to skip a decoy in case it hits the real output decoys_req.amounts.push_back(pe.amount); // request one batch of decoys @@ -4706,6 +5045,7 @@ void wallet2::get_unconfirmed_transfers(std::vector& destinations, currency::transaction& result_tx) +void wallet2::emit_asset(const crypto::public_key asset_id, std::vector& destinations, currency::transaction& result_tx) { auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id); @@ -4945,6 +5285,12 @@ void wallet2::emmit_asset(const crypto::public_key asset_id, std::vectorsecond.control_key; + for(auto& dst : ctp.dsts) + { + if (dst.asset_id == asset_id) + dst.asset_id = null_pkey; // emit operation requires null_pkey for emitting asset outputs, fix it ad-hoc here + } + finalized_tx ft = AUTO_VAL_INIT(ft); this->transfer(ctp, ft, true, nullptr); result_tx = ft.tx; @@ -5600,7 +5946,7 @@ bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal construct_tx_param ctp = get_default_construct_tx_param(); - ctp.fake_outputs_count = proposal_detais.mixins; + //ctp.fake_outputs_count = proposal_detais.mixins; ctp.fee = proposal_detais.fee_paid_by_a; ctp.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE; ctp.mark_tx_as_complete = false; @@ -5745,10 +6091,10 @@ bool wallet2::get_ionic_swap_proposal_info(const wallet_public::ionic_swap_propo } amounts_provided_by_a[in_asset_id] += amount; - if (proposal_info.mixins == 0 || proposal_info.mixins > mx) - { - proposal_info.mixins = mx; - } + //if (proposal_info.mixins == 0 || proposal_info.mixins > mx) + //{ + // proposal_info.mixins = mx; + //} } @@ -5910,8 +6256,8 @@ bool wallet2::decrypt_buffer(const std::string& buff, std::string& res_buff) //---------------------------------------------------------------------------------------------------- bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector& sources, std::vector& selected_indicies, uint64_t& found_money) { - //prepare_free_transfers_cache(fake_outputs_count); - //free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id]; + if (!m_defragmentation_tx_enabled) + return false; std::stringstream ss; if (epee::log_space::log_singletone::get_log_detalisation_level() >= LOG_LEVEL_2) @@ -5919,10 +6265,11 @@ bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector CURRENCY_BLOCK_REWARD) + if (!td.is_native_coin() || td.m_amount > m_max_allowed_output_amount_for_defragmentation_tx) continue; - if (is_transfer_ready_to_go(td, m_decoys_count_for_defragmentation_tx)) + uint64_t fake_outs_count_for_td = m_decoys_count_for_defragmentation_tx == SIZE_MAX ? (td.is_zc() ? m_core_runtime_config.hf4_minimum_mixins : CURRENCY_DEFAULT_DECOY_SET_SIZE) : m_decoys_count_for_defragmentation_tx; + if (is_transfer_ready_to_go(td, fake_outs_count_for_td)) { found_money += td.m_amount; selected_indicies.push_back(i); @@ -5931,7 +6278,7 @@ bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector& sources, std::vector& selected_indicies) { - bool r = select_transfers(needed_money_map, fake_outputs_count, dust_threshold, selected_indicies); - if (!r) - return r; - return prepare_tx_sources(fake_outputs_count, sources, selected_indicies); + try + { + select_transfers(needed_money_map, fake_outputs_count, dust_threshold, selected_indicies); // always returns true, TODO consider refactoring -- sowle + return prepare_tx_sources(fake_outputs_count, sources, selected_indicies); + } + catch(...) + { + // if smth went wrong -- invalidate transfers cache to trigger its regeneration on the next use + // it is necessary because it may be in invalid state (some items might be erased within select_indices_for_transfer() or expand_selection_with_zc_input()) + m_found_free_amounts.clear(); + throw; + } } //---------------------------------------------------------------------------------------------------- void wallet2::prefetch_global_indicies_if_needed(const std::vector& selected_indicies) { - std::list> txs; - std::list indices_that_requested_global_indicies; + //std::list> txs; + //std::list indices_that_requested_global_indicies; for (uint64_t i : selected_indicies) { - if (m_transfers[i].m_global_output_index == WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED) - { - indices_that_requested_global_indicies.push_back(i); - txs.push_back(m_transfers[i].m_ptx_wallet_info->m_tx); - } + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(m_transfers[i].m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED, + "m_transfers[" << i << "].m_global_output_index is WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED"); + //indices_that_requested_global_indicies.push_back(i); + //txs.push_back(m_transfers[i].m_ptx_wallet_info->m_tx); + //} } + /* std::vector > outputs_for_all_txs; fetch_tx_global_indixes(txs, outputs_for_all_txs); WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(txs.size() == outputs_for_all_txs.size(), "missmatch sizes txs.size() == outputs_for_all_txs.size()"); @@ -5975,10 +6331,15 @@ void wallet2::prefetch_global_indicies_if_needed(const std::vector& se transfer_details& td = m_transfers[*it_indices]; td.m_global_output_index = (*it_ooutputs)[td.m_internal_output_index]; it_ooutputs++; it_indices++; - } + }*/ } //---------------------------------------------------------------------------------------------------- -bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vector& sources, const std::vector& selected_indicies) +bool wallet2::prepare_tx_sources(size_t fake_outputs_count, std::vector& sources, const std::vector& selected_indicies) +{ + return prepare_tx_sources(fake_outputs_count, false, sources, selected_indicies); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, bool use_all_decoys_if_found_less_than_required, std::vector& sources, const std::vector& selected_indicies) { typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; typedef currency::tx_source_entry::output_entry tx_output_entry; @@ -5991,30 +6352,23 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vector= zarcanum_start_from) - { - //in Zarcanum era - const uint64_t test_scale_size = current_size - 1 - zarcanum_start_from; - zarcanum_decoy_set_generator.init(test_scale_size - 1); - } bool need_to_request = fake_outputs_count != 0; - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request req = AUTO_VAL_INIT(req); req.height_upper_limit = m_last_pow_block_h; // request decoys to be either older than, or the same age as stake output's height req.use_forced_mix_outs = false; // TODO: add this feature to UI later //req.decoys_count = fake_outputs_count + 1; // one more to be able to skip a decoy in case it hits the real output for (uint64_t i: selected_indicies) { - req.amounts.push_back(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution()); - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& rdisttib = req.amounts.back(); + req.amounts.push_back(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution()); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::offsets_distribution& rdisttib = req.amounts.back(); auto it = m_transfers.begin() + i; WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->m_ptx_wallet_info->m_tx.vout.size() > it->m_internal_output_index, "m_internal_output_index = " << it->m_internal_output_index << " is greater or equal to outputs count = " << it->m_ptx_wallet_info->m_tx.vout.size()); - rdisttib.own_global_index = it->m_global_output_index; + //rdisttib.own_global_index = it->m_global_output_index; //check if we have Zarcanum era output of pre-Zarcanum if (it->is_zc()) { @@ -6023,15 +6377,14 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vectorm_global_output_index); need_to_request = true; } else { //for prezarcanum era use flat distribution rdisttib.amount = it->m_amount; - rdisttib.offsets.resize(fake_outputs_count, 0); + rdisttib.global_offsets.resize(fake_outputs_count + 1, 0); } } if (need_to_request) @@ -6040,8 +6393,8 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vectorcall_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(req, daemon_resp); - THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs2.bin"); + bool r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3(req, daemon_resp); + THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs3.bin"); if (daemon_resp.status == API_RETURN_CODE_FAIL) { if (attempt_count < 10) @@ -6064,19 +6417,20 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vector scanty_outs; THROW_IF_FALSE_WALLET_EX(daemon_resp.outs.size() == req.amounts.size(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); - //for (COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs : daemon_resp.outs) - for(size_t i = 0; i != daemon_resp.outs.size(); i++) + + if (!use_all_decoys_if_found_less_than_required) { - if (daemon_resp.outs[i].outs.size() != req.amounts[i].offsets.size()) - { - scanty_outs.push_back(daemon_resp.outs[i]); - } + // make sure we have received the requested number of decoys + for(size_t i = 0; i != daemon_resp.outs.size(); i++) + if (req.amounts[i].amount != 0 && daemon_resp.outs[i].outs.size() != req.amounts[i].global_offsets.size()) + scanty_outs.push_back(daemon_resp.outs[i]); + THROW_IF_FALSE_WALLET_EX(scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); } - THROW_IF_FALSE_WALLET_EX(scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); } } //lets prefetch m_global_output_index for selected_indicies + //this days doesn't prefetch, only validated that prefetch is not needed prefetch_global_indicies_if_needed(selected_indicies); //prepare inputs @@ -6100,6 +6454,16 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vector= fake_outputs_count) break; @@ -6172,6 +6536,78 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vector +typename t_obj_container::value_type extract_random_from_container(t_obj_container& container) +{ + auto it = container.begin(); + std::advance(it, (crypto::rand() % container.size())); + typename t_obj_container::value_type obj = *it; + container.erase(it); + return obj; +} +//---------------------------------------------------------------------------------------------------------------- +void wallet2::select_decoys(currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_entry, uint64_t own_g_index) +{ + THROW_IF_FALSE_WALLET_INT_ERR_EX(amount_entry.amount == 0, "Amount is not 0 in zc decoys entry"); + typedef currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + + //TODO: This strategy would be a subject for continuous refactoring + + //first take all real transactions if ther are some + std::list local_outs; + std::list coinbases; + + while (amount_entry.outs.size() && local_outs.size() != m_core_runtime_config.hf4_minimum_mixins) + { + out_entry entry = extract_random_from_container(amount_entry.outs); + + // + if (entry.global_amount_index == own_g_index) + { + continue; + } + + //skip auditable + if ((entry.flags & (RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_NOT_ALLOWED))) + { + continue; + } + if (entry.flags & (RANDOM_OUTPUTS_FOR_AMOUNTS_FLAGS_COINBASE)) + { + coinbases.push_back(entry); + continue; + } + + + local_outs.push_back(entry); + } + + //extend with coin base outs if needed + while (coinbases.size() && local_outs.size() != m_core_runtime_config.hf4_minimum_mixins) + { + out_entry entry = extract_random_from_container(coinbases); + local_outs.push_back(entry); + } + + THROW_IF_FALSE_WALLET_INT_ERR_EX(local_outs.size() == m_core_runtime_config.hf4_minimum_mixins, "Amount is not 0 in zc decoys entry"); + amount_entry.outs = local_outs; +} +//---------------------------------------------------------------------------------------------------------------- +void wallet2::build_distribution_for_input(std::vector& offsets, uint64_t own_index) +{ + decoy_selection_generator zarcanum_decoy_set_generator; + zarcanum_decoy_set_generator.init(get_actual_zc_global_index()); + + THROW_IF_FALSE_WALLET_INT_ERR_EX(zarcanum_decoy_set_generator.is_initialized(), "zarcanum_decoy_set_generator are not initialized"); + if (m_core_runtime_config.hf4_minimum_mixins) + { + uint64_t actual_zc_index = get_actual_zc_global_index(); + offsets = zarcanum_decoy_set_generator.generate_unique_reversed_distribution(actual_zc_index - 1 > WALLET_FETCH_RANDOM_OUTS_SIZE ? WALLET_FETCH_RANDOM_OUTS_SIZE : actual_zc_index - 1, own_index); + } +} //---------------------------------------------------------------------------------------------------------------- bool wallet2::prepare_tx_sources(crypto::hash multisig_id, std::vector& sources, uint64_t& found_money) { @@ -6586,8 +7022,6 @@ bool wallet2::expand_selection_with_zc_input(assets_selection_context& needed_mo //---------------------------------------------------------------------------------------------------- bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes) { - bool res = true; - // for (auto& item : needed_money_map) { if(item.second.needed_amount == 0) @@ -6614,8 +7048,7 @@ bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money } } - - return res; + return true; } //---------------------------------------------------------------------------------------------------- uint64_t wallet2::select_indices_for_transfer(std::vector& selected_indexes, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count_) @@ -6654,7 +7087,7 @@ uint64_t wallet2::select_indices_for_transfer(std::vector& selected_in return found_money; } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count) +bool wallet2::is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count) const { if (is_transfer_able_to_go(td, fake_outputs_count) && is_transfer_unlocked(td)) { @@ -6663,7 +7096,7 @@ bool wallet2::is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_ return false; } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count) +bool wallet2::is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count) const { if (!td.is_spendable()) return false; @@ -6703,7 +7136,7 @@ bool wallet2::prepare_free_transfers_cache(uint64_t fake_outputs_count) if (td.m_zc_info_ptr) { //zarcanum out, redefine fake_outputs_count - fake_outputs_count_local = this->is_auditable() ? 0 : CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE; + fake_outputs_count_local = this->is_auditable() ? 0 : m_core_runtime_config.hf4_minimum_mixins; } if (is_transfer_able_to_go(td, fake_outputs_count_local)) { @@ -6731,7 +7164,7 @@ void wallet2::add_transfer_to_transfers_cache(uint64_t amount, uint64_t index, c m_found_free_amounts[asset_id][amount].insert(index); } //---------------------------------------------------------------------------------------------------- -bool wallet2::select_transfers(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust, std::vector& selected_indicies) +bool wallet2::select_transfers(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t /*dust_threshold*/, std::vector& selected_indicies) { prepare_free_transfers_cache(fake_outputs_count); return select_indices_for_transfer(needed_money_map, fake_outputs_count, selected_indicies); @@ -6808,7 +7241,7 @@ bool wallet2::is_connected_to_net() return (res.synchronized_connections_count) ? true : false; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_genesis_if_needed(const currency::block& genesis) +void wallet2::process_genesis_if_needed(const currency::block& genesis, const std::vector* pglobal_indexes) { if (!m_transfers.empty() || !m_key_images.empty()) return; @@ -6826,7 +7259,7 @@ void wallet2::process_genesis_if_needed(const currency::block& genesis) m_last_bc_timestamp = genesis.timestamp; WLT_LOG_L2("Processing genesis block: " << genesis_hash); - process_new_transaction(genesis.miner_tx, 0, genesis, nullptr); + process_new_transaction(genesis.miner_tx, 0, genesis, pglobal_indexes); } //---------------------------------------------------------------------------------------------------- void wallet2::set_genesis(const crypto::hash& genesis_hash) @@ -6969,7 +7402,7 @@ void wallet2::prepare_tx_destinations(uint64_t needed_money, const crypto::public_key& asset_id, std::vector& final_destinations) { - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(found_money >= needed_money, "needed_money==" << needed_money << " < found_money==" << found_money); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(found_money >= needed_money, "found_money = " << print_money_brief(found_money) << " is less than needed_money = " << print_money_brief(needed_money) << ", assed_id: " << asset_id); if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)) { @@ -7121,10 +7554,6 @@ void wallet2::finalize_transaction(currency::finalize_tx_param& ftp, currency::f // broadcasting tx without secret key storing is forbidden to avoid lost key issues WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(!broadcast_tx || store_tx_secret_key, "finalize_tx is requested to broadcast a tx without storing the key"); - //overide mixins count for hardfork 4 outputs - if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)) - ftp.tx_outs_attr = m_core_runtime_config.hf4_minimum_mixins; - bool r = currency::construct_tx(m_account.get_keys(), ftp, result); //TIME_MEASURE_FINISH_MS(construct_tx_time); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3097a9c5..e220959c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -46,6 +46,7 @@ #include "currency_core/pos_mining.h" #include "view_iface.h" #include "wallet2_base.h" +#include "decoy_selection.h" #define WALLET_DEFAULT_TX_SPENDABLE_AGE CURRENCY_HF4_MANDATORY_MIN_COINAGE #define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1 @@ -147,6 +148,7 @@ namespace tools std::unordered_map m_pending_key_images; // (out_pk -> ki) pairs of change outputs to be added in watch-only wallet without spend sec key uint64_t m_last_pow_block_h = 0; std::list> m_rollback_events; + std::list > m_last_zc_global_indexs; // , biggest height comes in front //variables that not being serialized std::atomic m_last_bc_timestamp = 0; @@ -154,8 +156,10 @@ namespace tools std::atomic m_last_sync_percent = 0; mutable uint64_t m_current_wallet_file_size = 0; bool m_use_assets_whitelisting = true; + mutable std::optional m_has_bare_unspent_outputs; // recalculated each time the balance() is called - + // variables that should be part of state data object but should not be stored during serialization + mutable std::atomic m_whitelist_updated = false; //=============================================================== template inline void serialize(t_archive &a, const unsigned int ver) @@ -218,6 +222,16 @@ namespace tools a & m_rollback_events; a & m_whitelisted_assets; a & m_use_assets_whitelisting; + if (ver <= 165) + { + uint64_t last_zc_global_index = 0; + a& last_zc_global_index; + m_last_zc_global_indexs.push_back(std::make_pair(uint64_t(0), last_zc_global_index)); + } + else + { + a& m_last_zc_global_indexs; + } } }; @@ -227,6 +241,7 @@ namespace tools wallet2(const wallet2&) = delete; public: wallet2(); + virtual ~wallet2() {} static std::string transfer_flags_to_str(uint32_t flags); @@ -337,6 +352,13 @@ namespace tools mutable crypto::hash tx_hash_ = currency::null_hash; }; + struct batch_of_bare_unspent_outs + { + std::vector tids; + uint64_t total_amount = 0; + bool additional_tid = false; // additional zc transfer if total_amount < min fee + uint64_t additional_tid_amount = 0; + }; @@ -372,6 +394,12 @@ namespace tools void set_do_rise_transfer(bool do_rise) { m_do_rise_transfer = do_rise; } bool has_related_alias_entry_unconfirmed(const currency::transaction& tx); + bool has_bare_unspent_outputs() const; + bool get_bare_unspent_outputs_stats(std::vector& buo_txs) const; + bool sweep_bare_unspent_outputs(const currency::account_public_address& target_address, const std::vector& tids_grouped_by_txs, + std::function on_tx_sent); + bool sweep_bare_unspent_outputs(const currency::account_public_address& target_address, const std::vector& tids_grouped_by_txs, + size_t& total_txs_sent, uint64_t& total_amount_sent, uint64_t& total_fee, uint64_t& total_bare_outs_sent); void handle_unconfirmed_tx(process_transaction_context& ptc); void scan_tx_pool(bool& has_related_alias_in_unconfirmed); void refresh(); @@ -389,16 +417,14 @@ namespace tools bool check_available_sources(std::list& amounts); void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id); - void emmit_asset(const crypto::public_key asset_id, std::vector& destinations, currency::transaction& result_tx); + void emit_asset(const crypto::public_key asset_id, std::vector& destinations, currency::transaction& result_tx); void update_asset(const crypto::public_key asset_id, const currency::asset_descriptor_base new_descriptor, currency::transaction& result_tx); void burn_asset(const crypto::public_key asset_id, uint64_t amount_to_burn, currency::transaction& result_tx); void transfer_asset_ownership(const crypto::public_key asset_id, const crypto::public_key& new_owner, currency::transaction& result_tx); bool daemon_get_asset_info(const crypto::public_key& asset_id, currency::asset_descriptor_base& adb); - const std::unordered_map& get_own_assets() const { return m_own_asset_descriptors; } bool set_core_proxy(const std::shared_ptr& proxy); - void set_pos_utxo_count_limits_for_defragmentation_tx(uint64_t min_outs, uint64_t max_outs); // don't create UTXO defrag. tx if there are less than 'min_outs' outs; don't put more than 'max_outs' outs - void set_pos_decoys_count_for_defragmentation_tx(size_t decoys_count); + void set_defragmentation_tx_settings(bool enabled, uint64_t min_outs, uint64_t max_outs, uint64_t max_allowed_amount = CURRENCY_BLOCK_REWARD, size_t decoys_count = SIZE_MAX); void set_pos_required_decoys_count(size_t v) { m_required_decoys_count = v; } void set_minimum_height(uint64_t h); std::shared_ptr get_core_proxy(); @@ -536,6 +562,7 @@ namespace tools void get_transfers(transfer_container& incoming_transfers) const; std::string get_transfers_str(bool include_spent = true, bool include_unspent = true, bool show_only_unknown = false, const std::string& filter_asset_ticker = std::string{}) const; std::string get_balance_str() const; + std::string get_balance_str_raw() const; // Returns all payments by given id in unspecified order void get_payments(const std::string& payment_id, std::list& payments, uint64_t min_height = 0) const; @@ -572,8 +599,8 @@ namespace tools wallet2_base_state::serialize(a, ver); } - bool is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count); - bool is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count); + bool is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count) const; + bool is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count) const; uint64_t select_indices_for_transfer(std::vector& ind, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count); bool select_indices_for_transfer(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes); @@ -656,6 +683,10 @@ namespace tools bool add_custom_asset_id(const crypto::public_key& asset_id, currency::asset_descriptor_base& asset_descriptor); bool delete_custom_asset_id(const crypto::public_key& asset_id); + const std::unordered_map& get_local_whitelist() const; + const std::unordered_map& get_global_whitelist() const; + const std::unordered_map& get_own_assets() const; + bool load_whitelisted_tokens_if_not_loaded() const; bool load_whitelisted_tokens() const; @@ -692,6 +723,8 @@ namespace tools bool encrypt_buffer(const std::string& buff, std::string& res_buff); bool decrypt_buffer(const std::string& buff, std::string& res_buff); bool is_in_hardfork_zone(uint64_t hardfork_index) const; + //performance inefficient call, suitable only for rare ocasions or super lazy developers + bool proxy_to_daemon(const std::string& uri, const std::string& body, int& response_code, std::string& response_body); construct_tx_param get_default_construct_tx_param(); @@ -744,20 +777,22 @@ private: void handle_money(const currency::block& b, const process_transaction_context& tx_process_context); void load_wti_from_process_transaction_context(wallet_public::wallet_transfer_info& wti, const process_transaction_context& tx_process_context); bool process_payment_id_for_wti(wallet_public::wallet_transfer_info& wti, const process_transaction_context& tx_process_context); - + void add_to_last_zc_global_indexs(uint64_t h, uint64_t last_zc_output_index); + uint64_t get_actual_zc_global_index(); void handle_pulled_blocks(size_t& blocks_added, std::atomic& stop, currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& blocks); std::string get_alias_for_address(const std::string& addr); std::vector get_aliases_for_address(const std::string& addr); bool is_connected_to_net(); bool is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const; - bool scan_unconfirmed_outdate_tx(); + bool scan_not_compliant_unconfirmed_txs(); const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash); void rise_on_transfer2(const wallet_public::wallet_transfer_info& wti); - void process_genesis_if_needed(const currency::block& genesis); + void process_genesis_if_needed(const currency::block& genesis, const std::vector* pglobal_indexes); bool build_escrow_proposal(bc_services::contract_private_details& ecrow_details, uint64_t fee, uint64_t unlock_time, currency::tx_service_attachment& att, std::vector& selected_indicies); bool prepare_tx_sources(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust_threshold, std::vector& sources, std::vector& selected_indicies); bool prepare_tx_sources(size_t fake_outputs_count, std::vector& sources, const std::vector& selected_indicies); + bool prepare_tx_sources(size_t fake_outputs_count, bool use_all_decoys_if_found_less_than_required, std::vector& sources, const std::vector& selected_indicies); bool prepare_tx_sources(crypto::hash multisig_id, std::vector& sources, uint64_t& found_money); bool prepare_tx_sources_htlc(crypto::hash htlc_tx_id, const std::string& origin, std::vector& sources, uint64_t& found_money); bool prepare_tx_sources_for_defragmentation_tx(std::vector& sources, std::vector& selected_indicies, uint64_t& found_money); @@ -850,6 +885,8 @@ private: void remove_transfer_from_amount_gindex_map(uint64_t tid); uint64_t get_alias_cost(const std::string& alias); detail::split_strategy_id_t get_current_split_strategy(); + void build_distribution_for_input(std::vector& offsets, uint64_t own_index); + void select_decoys(currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount & amount_entry, uint64_t own_g_index); static void wti_to_csv_entry(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index); static void wti_to_txt_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index); @@ -877,15 +914,18 @@ private: bool m_do_rise_transfer; + + bool m_defragmentation_tx_enabled; + uint64_t m_max_allowed_output_amount_for_defragmentation_tx; uint64_t m_min_utxo_count_for_defragmentation_tx; uint64_t m_max_utxo_count_for_defragmentation_tx; size_t m_decoys_count_for_defragmentation_tx; + size_t m_required_decoys_count; pending_ki_file_container_t m_pending_key_images_file_container; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value std::atomic m_stop; - mutable std::atomic m_whitelist_updated = false; std::shared_ptr m_core_proxy; std::shared_ptr m_wcallback; @@ -903,6 +943,8 @@ private: std::string m_votes_config_path; tools::wallet_public::wallet_vote_config m_votes_config; + uint64_t m_last_known_daemon_height = 0; + //this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions friend class test_generator; }; // class wallet2 @@ -1064,7 +1106,6 @@ namespace tools ts_middle -= ts_middle % POS_SCAN_STEP; uint64_t ts_window = std::min(ts_middle - ts_from, ts_to - ts_middle); - size_t pos_entry_index = 0; for (size_t transfer_index = 0; transfer_index != m_transfers.size(); transfer_index++) { auto& tr = m_transfers[transfer_index]; @@ -1132,7 +1173,6 @@ namespace tools next_turn(); } - ++pos_entry_index; } cxt.status = API_RETURN_CODE_NOT_FOUND; return false; diff --git a/src/wallet/wallet2_base.h b/src/wallet/wallet2_base.h index d9c9589c..c2ec860b 100644 --- a/src/wallet/wallet2_base.h +++ b/src/wallet/wallet2_base.h @@ -251,19 +251,18 @@ namespace tools crypto::public_key asset_id = currency::null_pkey; BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(asset_id) - END_BOOST_SERIALIZATION() + END_BOOST_SERIALIZATION() }; - struct wallet_own_asset_context + struct wallet_own_asset_context: public currency::asset_descriptor_base { - currency::asset_descriptor_base asset_descriptor; bool thirdparty_custody = false; BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(asset_descriptor) + BOOST_SERIALIZE_BASE_CLASS(currency::asset_descriptor_base) BOOST_SERIALIZE(thirdparty_custody) - END_BOOST_SERIALIZATION() + END_BOOST_SERIALIZATION() }; struct asset_update_event diff --git a/src/wallet/wallet_helpers.h b/src/wallet/wallet_helpers.h index 12a75d7a..50da63a0 100644 --- a/src/wallet/wallet_helpers.h +++ b/src/wallet/wallet_helpers.h @@ -20,6 +20,7 @@ namespace tools wi.path = epee::string_encoding::wstring_to_utf8(w.get_wallet_path()); wi.is_auditable = w.is_auditable(); wi.is_watch_only = w.is_watch_only(); + wi.has_bare_unspent_outputs = w.has_bare_unspent_outputs(); return true; } @@ -37,21 +38,40 @@ namespace tools result.hash_sum_matched = false; result.syntax_correct = acc.restore_from_tracking_seed(seed_phrase); if (result.syntax_correct) + { result.tracking = true; + result.address = acc.get_public_address_str(); + } } else { result.syntax_correct = currency::account_base::is_seed_password_protected(seed_phrase, result.require_password); - if (result.syntax_correct && result.require_password) + if (result.syntax_correct ) { - if (seed_password.size()) + if (result.require_password) { - currency::account_base acc; - result.hash_sum_matched = acc.restore_from_seed_phrase(seed_phrase, seed_password); + if (seed_password.size()) + { + currency::account_base acc; + result.hash_sum_matched = acc.restore_from_seed_phrase(seed_phrase, seed_password); + if (result.hash_sum_matched) + { + result.address = acc.get_public_address_str(); + } + } + else + { + result.hash_sum_matched = false; + } } else { - result.hash_sum_matched = false; + currency::account_base acc; + result.syntax_correct = acc.restore_from_seed_phrase(seed_phrase, ""); + if (result.syntax_correct) + { + result.address = acc.get_public_address_str(); + } } } } diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 6610bb7d..663fb080 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -14,8 +14,12 @@ #include "currency_core/bc_escrow_service.h" #include "rpc/core_rpc_server_commands_defs.h" + + const uint64_t WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED = std::numeric_limits::max(); +const boost::uuids::uuid RPC_INTERNAL_UI_CONTEXT = {0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 2, 1, 0, 0}; //Bender's nightmare + namespace tools { namespace wallet_public @@ -119,9 +123,9 @@ namespace wallet_public crypto::public_key asset_id = currency::native_coin_asset_id; // not blinded, not premultiplied by 1/8 BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(is_income) - KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) + KV_SERIALIZE(amount) DOC_DSCR("Amount of asset the had been transfered") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE(is_income) DOC_DSCR("Indicates if transfer was income our outgoing") DOC_EXMP(false) DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id") DOC_EXMP("cc608f59f8080e2fbfe3c8c80eb6e6a953d47cf2d6aebd345bada3a1cab99852") DOC_END END_KV_SERIALIZE_MAP() BEGIN_BOOST_SERIALIZATION() @@ -134,17 +138,17 @@ namespace wallet_public struct wallet_transfer_info { - uint64_t timestamp; - crypto::hash tx_hash; - uint64_t height; //if height == 0 then tx is unconfirmed - uint64_t unlock_time; - uint32_t tx_blob_size; + uint64_t timestamp = 0; + crypto::hash tx_hash = currency::null_hash; + uint64_t height = 0; //if height == 0 then tx is unconfirmed + uint64_t unlock_time = 0; + uint32_t tx_blob_size = 0; std::string payment_id; std::string comment; - bool is_service; - bool is_mixing; - bool is_mining; - uint64_t tx_type; + bool is_service = false; + bool is_mixing = false; + bool is_mining = false; + uint64_t tx_type = 0; employed_tx_entries employed_entries; std::vector service_entries; std::vector remote_addresses; //optional @@ -153,38 +157,40 @@ namespace wallet_public std::vector subtransfers; //not included in streaming serialization - uint64_t fee; - bool show_sender; + uint64_t fee = 0; + bool show_sender = false; std::vector contract; - uint16_t extra_flags; - uint64_t transfer_internal_index; + uint16_t extra_flags = 0; + uint64_t transfer_internal_index = 0; //not included in kv serialization map currency::transaction tx; std::vector selected_indicies; std::list marketplace_entries; + + BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_hash) - KV_SERIALIZE(height) - KV_SERIALIZE(unlock_time) - KV_SERIALIZE(tx_blob_size) - KV_SERIALIZE_BLOB_AS_HEX_STRING(payment_id) - KV_SERIALIZE(comment) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(employed_entries) - KV_SERIALIZE(fee) - KV_SERIALIZE(is_service) - KV_SERIALIZE(is_mixing) - KV_SERIALIZE(is_mining) - KV_SERIALIZE(tx_type) - KV_SERIALIZE(show_sender) - KV_SERIALIZE(contract) - KV_SERIALIZE(service_entries) - KV_SERIALIZE(transfer_internal_index) - KV_SERIALIZE(remote_addresses) - KV_SERIALIZE(remote_aliases) - KV_SERIALIZE(subtransfers) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_hash) DOC_DSCR("Transaction ID(hash)") DOC_EXMP("5509650e12c8f901e6731a2bfaf3abfd64409e3e1366d3d94cd11db8beddb0c3") DOC_END + KV_SERIALIZE(height) DOC_DSCR("Height of the block that included transaction(0 i transaction is unconfirmed)") DOC_EXMP(0) DOC_END + KV_SERIALIZE(unlock_time) DOC_DSCR("Unlock time of this transfer (if present)") DOC_EXMP(0) DOC_END + KV_SERIALIZE(tx_blob_size) DOC_DSCR("Size of transaction in bytes") DOC_EXMP(0) DOC_END + KV_SERIALIZE_BLOB_AS_HEX_STRING(payment_id) DOC_DSCR("HEX-encoded payment id blob, if it was present") DOC_EXMP("00000000ff00ff00") DOC_END + KV_SERIALIZE(comment) DOC_DSCR("Some human-readable comment") DOC_EXMP("Comment here") DOC_END + KV_SERIALIZE(timestamp) DOC_DSCR("Timestamp of the block that included transaction in blockchain, 0 for unconfirmed") DOC_EXMP(1712590951) DOC_END + KV_SERIALIZE(employed_entries) DOC_DSCR("Mark entries from transaction that was connected to this wallet") DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Transaction fee") DOC_EXMP(10000000000) DOC_END + KV_SERIALIZE(is_service) DOC_DSCR("Tells if this transaction is used as utility by one of Zano services(contracts, ionic swaps, etc)") DOC_EXMP(false) DOC_END + KV_SERIALIZE(is_mixing) DOC_DSCR("Tells if this transaction using mixins or not(auditble wallets normally don't use mixins)") DOC_EXMP(false) DOC_END + KV_SERIALIZE(is_mining) DOC_DSCR("Tells if this transaction is coinbase transaction(ie generated by PoW mining or by PoS staking)") DOC_EXMP(false) DOC_END + KV_SERIALIZE(tx_type) DOC_DSCR("Could be one of this: GUI_TX_TYPE_NORMAL=0, GUI_TX_TYPE_PUSH_OFFER=1, GUI_TX_TYPE_UPDATE_OFFER=2, GUI_TX_TYPE_CANCEL_OFFER=3, GUI_TX_TYPE_NEW_ALIAS=4,GUI_TX_TYPE_UPDATE_ALIAS=5,GUI_TX_TYPE_COIN_BASE=6,GUI_TX_TYPE_ESCROW_PROPOSAL=7,GUI_TX_TYPE_ESCROW_TRANSFER=8,GUI_TX_TYPE_ESCROW_RELEASE_NORMAL=9,GUI_TX_TYPE_ESCROW_RELEASE_BURN=10,GUI_TX_TYPE_ESCROW_CANCEL_PROPOSAL=11,GUI_TX_TYPE_ESCROW_RELEASE_CANCEL=12,GUI_TX_TYPE_HTLC_DEPOSIT=13,GUI_TX_TYPE_HTLC_REDEEM=14") DOC_EXMP(0) DOC_END + KV_SERIALIZE(show_sender) DOC_DSCR("If sender is included in tx") DOC_EXMP(false) DOC_END + KV_SERIALIZE(contract) DOC_DSCR("Escrow contract if it's part of transaction") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(service_entries) DOC_DSCR("Additional entries that might be stored in transaction but not part of it's consensus") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(transfer_internal_index) DOC_DSCR("Index of this entry in the wallet's array of transaction's history") DOC_EXMP(12) DOC_END + KV_SERIALIZE(remote_addresses) DOC_DSCR("Remote addresses of this transfer(destination if it's outgoing transfer or sender if it's incoming transaction)") DOC_EXMP_AUTO(1, "ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE(remote_aliases) DOC_DSCR("Aliases for remot addresses, of discovered") DOC_EXMP_AUTO(1, "roger") DOC_END + KV_SERIALIZE(subtransfers) DOC_DSCR("Essential part of transfer entry: amounts that been transfered in this transaction grouped by asset id") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() BEGIN_BOOST_SERIALIZATION() @@ -279,8 +285,8 @@ namespace wallet_public //bool is_income = false; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_EPHEMERAL_N(uint64_t, wallet_transfer_info_to_amount, "amount") - KV_SERIALIZE_EPHEMERAL_N(bool, wallet_transfer_info_to_is_income, "is_income") + KV_SERIALIZE_EPHEMERAL_N(uint64_t, wallet_transfer_info_to_amount, "amount") DOC_DSCR("Native coins amount") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE_EPHEMERAL_N(bool, wallet_transfer_info_to_is_income, "is_income") DOC_DSCR("If trnasfer entrie is income (taken from native subtransfer)") DOC_EXMP(false) DOC_END //KV_SERIALIZE(amount) KV_CHAIN_BASE(wallet_transfer_info) END_KV_SERIALIZE_MAP() @@ -308,10 +314,10 @@ namespace wallet_public //v2 BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total) - KV_SERIALIZE(unlocked) - KV_SERIALIZE(awaiting_in) - KV_SERIALIZE(awaiting_out) + KV_SERIALIZE(total) DOC_DSCR("Total coins available(including locked)") DOC_EXMP(100000000000000) DOC_END + KV_SERIALIZE(unlocked) DOC_DSCR("Unlocked coins available(the ones that could be used right now)") DOC_EXMP(50000000000000) DOC_END + KV_SERIALIZE(awaiting_in) DOC_DSCR("Unconfirmed amount for receive") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE(awaiting_out) DOC_DSCR("Unconfirmed amount for send") DOC_EXMP(2000000000000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -320,7 +326,7 @@ namespace wallet_public currency::asset_descriptor_with_id asset_info; //v2 BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(asset_info) + KV_SERIALIZE(asset_info) DOC_DSCR("Asset info details") DOC_END KV_CHAIN_BASE(asset_balance_entry_base) END_KV_SERIALIZE_MAP() }; @@ -342,9 +348,9 @@ namespace wallet_public uint64_t t; uint64_t h; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(a) - KV_SERIALIZE(t) - KV_SERIALIZE(h) + KV_SERIALIZE(a) DOC_DSCR("Mined amount(block reward)") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE(t) DOC_DSCR("Timestamp") DOC_EXMP(1712683857) DOC_END + KV_SERIALIZE(h) DOC_DSCR("height") DOC_EXMP(102000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -353,7 +359,7 @@ namespace wallet_public { std::vector mined_entries; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(mined_entries) + KV_SERIALIZE(mined_entries) DOC_DSCR("Mined blocks entries.") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; @@ -364,8 +370,8 @@ namespace wallet_public std::string seed_password; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(seed_phrase) - KV_SERIALIZE(seed_password) + KV_SERIALIZE(seed_phrase) DOC_DSCR("Mnemonic seed phrase used for wallet recovery or generation.") DOC_EXMP("girlfriend unlike mutter tightly social silent expect constant bid nowhere reach flower bite salt lightning conversation dog rush quietly become usually midnight each secret offer class") DOC_END + KV_SERIALIZE(seed_password) DOC_DSCR("Password used to encrypt or decrypt the mnemonic seed phrase, if applicable.") DOC_EXMP("0101010103") DOC_END END_KV_SERIALIZE_MAP() }; @@ -375,17 +381,21 @@ namespace wallet_public bool require_password; bool hash_sum_matched; bool tracking; + std::string address; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(syntax_correct) - KV_SERIALIZE(require_password) - KV_SERIALIZE(hash_sum_matched) - KV_SERIALIZE(tracking) + KV_SERIALIZE(syntax_correct) DOC_DSCR("Indicates whether the syntax is correct.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(require_password) DOC_DSCR("Indicates whether a password is required.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(hash_sum_matched) DOC_DSCR("Indicates whether the hash sum matches.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(tracking) DOC_DSCR("Indicates whether tracking is enabled.") DOC_EXMP(false) DOC_END + KV_SERIALIZE(address) DOC_DSCR("Return address of the seed phrase.") DOC_EXMP("ZxDNaMeZjwCjnHuU5gUNyrP1pM3U5vckbakzzV6dEHyDYeCpW8XGLBFTshcaY8LkG9RQn7FsQx8w2JeJzJwPwuDm2NfixPAXf") DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_GET_BALANCE { + DOC_COMMAND("Return the balances across all whitelisted assets of the wallet"); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -399,15 +409,17 @@ namespace wallet_public std::list balances; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(balance) - KV_SERIALIZE(unlocked_balance) - KV_SERIALIZE(balances) + KV_SERIALIZE(balance) DOC_DSCR("Native coins total amount") DOC_EXMP(10000000000) DOC_END + KV_SERIALIZE(unlocked_balance) DOC_DSCR("Native coins total unlocked amount") DOC_EXMP(11000000000) DOC_END + KV_SERIALIZE(balances) DOC_DSCR("Balances groupped by it's asset_id") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_ADDRESS { + DOC_COMMAND("Obtains wallet's public address"); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -419,7 +431,7 @@ namespace wallet_public std::string address; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) + KV_SERIALIZE(address) DOC_DSCR("string; standard public address of the wallet.") DOC_EXMP("ZxDNaMeZjwCjnHuU5gUNyrP1pM3U5vckbakzzV6dEHyDYeCpW8XGLBFTshcaY8LkG9RQn7FsQx8w2JeJzJwPwuDm2NfixPAXf") DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -428,6 +440,8 @@ namespace wallet_public struct COMMAND_RPC_GET_WALLET_INFO { + DOC_COMMAND("Returns wallet helpful wallet information"); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -441,29 +455,33 @@ namespace wallet_public uint64_t transfers_count; uint64_t transfer_entries_count; bool is_whatch_only; + bool has_bare_unspent_outputs; std::vector utxo_distribution; uint64_t current_height; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(path) - KV_SERIALIZE(transfers_count) - KV_SERIALIZE(transfer_entries_count) - KV_SERIALIZE(is_whatch_only) - KV_SERIALIZE(utxo_distribution) - KV_SERIALIZE(current_height) + KV_SERIALIZE(address) DOC_DSCR("string; standard public address of the wallet.") DOC_EXMP("ZxDNaMeZjwCjnHuU5gUNyrP1pM3U5vckbakzzV6dEHyDYeCpW8XGLBFTshcaY8LkG9RQn7FsQx8w2JeJzJwPwuDm2NfixPAXf") DOC_END + KV_SERIALIZE(path) DOC_DSCR("Path to wallet file location") DOC_EXMP("/some/path/to/wallet/file.zan") DOC_END + KV_SERIALIZE(transfers_count) DOC_DSCR("Represent number of transactions that happened to this wallet(basically tx history)") DOC_EXMP(11) DOC_END + KV_SERIALIZE(transfer_entries_count) DOC_DSCR("Represent number of internal entries count(each entry represent tx output that have been addressed to this wallet)") DOC_EXMP(24) DOC_END + KV_SERIALIZE(is_whatch_only) DOC_DSCR("Shows if the wallet is watch-only") DOC_EXMP(false) DOC_END + KV_SERIALIZE(has_bare_unspent_outputs) DOC_DSCR("Shows if the wallet still has UTXO from pre-zarcanum era") DOC_EXMP(false) DOC_END + KV_SERIALIZE(utxo_distribution) DOC_DSCR("UTXO distribution for this particular wallet: disabled right now") DOC_EXMP_AUTO(1, "1") DOC_END + KV_SERIALIZE(current_height) DOC_DSCR("Current wallet/daemon height") DOC_EXMP(112132) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_WALLET_RESTORE_INFO { + DOC_COMMAND("Return wallet seed, which could be password-protected(seed secured with passphrase) or open(unsecured seed). If no password provided it returns open (unsecured) seed. "); + struct request { std::string seed_password; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(seed_password) + KV_SERIALIZE(seed_password) DOC_DSCR("Password to secure wallet's seed") DOC_EXMP("010101012") DOC_END END_KV_SERIALIZE_MAP() }; @@ -472,13 +490,15 @@ namespace wallet_public std::string seed_phrase; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(seed_phrase) + KV_SERIALIZE(seed_phrase) DOC_DSCR("Wallet's seed(secured with password if it was provided in argument)") DOC_EXMP("girlfriend unlike offer mutter tightly social silent expect constant bid nowhere reach flower bite salt becomeconversation dog rush quietly become usually lightning midnight each secret class") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_SEED_PHRASE_INFO { + DOC_COMMAND("This call is used to validate seed phrase and to fetch additional information about it"); + typedef seed_info_param request; typedef seed_phrase_info response; }; @@ -493,16 +513,19 @@ namespace wallet_public BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(transfers_count) - KV_SERIALIZE(transfer_entries_count) - KV_SERIALIZE(balance) - KV_SERIALIZE(unlocked_balance) - KV_SERIALIZE(curent_height) + KV_SERIALIZE(transfers_count) DOC_DSCR("Number of transfers in wallet") DOC_EXMP(1) DOC_END + KV_SERIALIZE(transfer_entries_count) DOC_DSCR("Number of UTXO entries in wallet") DOC_EXMP(3) DOC_END + KV_SERIALIZE(balance) DOC_DSCR("Current balance of native coins") DOC_EXMP(100000000000) DOC_END + KV_SERIALIZE(unlocked_balance) DOC_DSCR("Unlocked balance oof native coins") DOC_EXMP(90000000000) DOC_END + KV_SERIALIZE(curent_height) DOC_DSCR("Current sync height of the wallet") DOC_EXMP(121212) DOC_END END_KV_SERIALIZE_MAP() + }; struct COMMAND_RPC_GET_MINING_HISTORY { + DOC_COMMAND("Returns wallet statistic on mining. As an argument 'v' it receive timestamp from which history is reviewed"); + typedef currency::struct_with_one_t_type request; typedef wallet_public::mining_history response; }; @@ -513,6 +536,8 @@ namespace wallet_public struct COMMAND_RPC_GET_RECENT_TXS_AND_INFO2 { + DOC_COMMAND("Returns wallet history of transactions V2(post-zarcanum version)"); + struct request { @@ -535,12 +560,12 @@ namespace wallet_public std::string order; // "FROM_BEGIN_TO_END" or "FROM_END_TO_BEGIN" BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(offset) - KV_SERIALIZE(count) - KV_SERIALIZE(update_provision_info) - KV_SERIALIZE(exclude_mining_txs) - KV_SERIALIZE(exclude_unconfirmed) - KV_SERIALIZE(order) + KV_SERIALIZE(offset) DOC_DSCR("Offset from what index to start fetching transfers entries(if filters are used then last_item_index could be used from previous call)") DOC_EXMP(0) DOC_END + KV_SERIALIZE(count) DOC_DSCR("How many items to fetch, if items fetched is less then count, then it enumeration is over") DOC_EXMP(100) DOC_END + KV_SERIALIZE(update_provision_info) DOC_DSCR("If update pi is required, could be false only if need to optimize performance(appliable for a veru big wallets)") DOC_EXMP(true) DOC_END + KV_SERIALIZE(exclude_mining_txs) DOC_DSCR("Exclude mining/staking transactions from results(last_item_index should be used for subsequential calls)") DOC_EXMP(false) DOC_END + KV_SERIALIZE(exclude_unconfirmed) DOC_DSCR("Do not include uncomfirmed transactions in results (it also not included is offset is non zero)") DOC_EXMP(false) DOC_END + KV_SERIALIZE(order) DOC_DSCR("Order: \"FROM_BEGIN_TO_END\" or \"FROM_END_TO_BEGIN\"") DOC_EXMP("FROM_END_TO_BEGIN") DOC_END END_KV_SERIALIZE_MAP() }; @@ -552,10 +577,10 @@ namespace wallet_public uint64_t last_item_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(pi) - KV_SERIALIZE(transfers) - KV_SERIALIZE(total_transfers) - KV_SERIALIZE(last_item_index) + KV_SERIALIZE(pi) DOC_DSCR("Details on wallet balance etc") DOC_END + KV_SERIALIZE(transfers) DOC_DSCR("Transfers") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(total_transfers) DOC_DSCR("Total transfers") DOC_EXMP(1) DOC_END + KV_SERIALIZE(last_item_index) DOC_DSCR("Last item index") DOC_EXMP(1) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -563,6 +588,8 @@ namespace wallet_public struct COMMAND_RPC_GET_RECENT_TXS_AND_INFO { + DOC_COMMAND("Returns wallet history of transactions"); + typedef COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::request request; struct response @@ -573,27 +600,26 @@ namespace wallet_public uint64_t last_item_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(pi) - KV_SERIALIZE(transfers) - KV_SERIALIZE(total_transfers) - KV_SERIALIZE(last_item_index) + KV_SERIALIZE(pi) DOC_DSCR("Additiona details about balance state") DOC_END + KV_SERIALIZE(transfers) DOC_DSCR("Transfers history array") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(total_transfers) DOC_DSCR("Total number of transfers in the tx history") DOC_EXMP(1) DOC_END + KV_SERIALIZE(last_item_index) DOC_DSCR("Index of last returned item(might be needed if filters are used)") DOC_EXMP(1) DOC_END END_KV_SERIALIZE_MAP() }; }; - - - struct COMMAND_RPC_REGISTER_ALIAS { + DOC_COMMAND("Register an alias for the address"); + struct request { currency::alias_rpc_details al; crypto::secret_key authority_key; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(al) - KV_SERIALIZE_POD_AS_HEX_STRING(authority_key) + KV_SERIALIZE(al) DOC_DSCR("Alias details") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(authority_key) DOC_DSCR("Key for registering aliases shorter than 6 letters (team)") DOC_END END_KV_SERIALIZE_MAP() }; @@ -602,7 +628,7 @@ namespace wallet_public crypto::hash tx_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("If success - transactions that performs registration(alias becomes available after few confirmations)") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -614,14 +640,16 @@ namespace wallet_public std::string address; crypto::public_key asset_id = currency::native_coin_asset_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(address) - KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) + KV_SERIALIZE(amount) DOC_DSCR("Amount to transfer to destination") DOC_EXMP(10000000000000) DOC_END + KV_SERIALIZE(address) DOC_DSCR("Destination address") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id to transfer") DOC_EXMP("cc608f59f8080e2fbfe3c8c80eb6e6a953d47cf2d6aebd345bada3a1cab99852") DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_TRANSFER { + DOC_COMMAND("Make new payment transaction from the wallet"); + struct request { std::list destinations; @@ -636,15 +664,15 @@ namespace wallet_public bool service_entries_permanent; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(destinations) - KV_SERIALIZE(fee) - KV_SERIALIZE(mixin) - KV_SERIALIZE(payment_id) - KV_SERIALIZE(comment) - KV_SERIALIZE(push_payer) - KV_SERIALIZE(hide_receiver) - KV_SERIALIZE(service_entries) - KV_SERIALIZE(service_entries_permanent) + KV_SERIALIZE(destinations) DOC_DSCR("List of destinations") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Fee to be paid on behalf of sender's wallet(paid in native coins)") DOC_EXMP_AUTO(10000000000) DOC_END + KV_SERIALIZE(mixin) DOC_DSCR("Specifies number of mixins(decoys) that would be used to create input, actual for pre-zarcanum outputs, for post-zarcanum outputs instead of this option, number that is defined by network hard rules(15+)") DOC_EXMP(15) DOC_END + KV_SERIALIZE(payment_id) DOC_DSCR("Hex-encoded payment_id, that normally used for user database by exchanges") DOC_EXMP_AUTO("1dfe5a88ff9effb3") DOC_END + KV_SERIALIZE(comment) DOC_DSCR("Text comment that is displayed in UI") DOC_EXMP_AUTO("Thanks for the coffe") DOC_END + KV_SERIALIZE(push_payer) DOC_DSCR("Reveal information about sender of this transaction, basically add sender address to transaction in encrypted way, so only receiver can see who sent transaction") DOC_EXMP(false) DOC_END + KV_SERIALIZE(hide_receiver) DOC_DSCR("This add to transaction information about remote address(destination), might be needed when the wallet restored from seed phrase and fully resynched, if this option were true, then sender won't be able to see remote address for sent transactions anymore.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(service_entries) DOC_DSCR("Service entries that might be used by different apps that works on top of Zano network, not part of consensus") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(service_entries_permanent) DOC_DSCR("Point to wallet that service_entries should be placed to 'extra' section of transaction(which won't be pruned after checkpoints)") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; @@ -664,6 +692,8 @@ namespace wallet_public struct COMMAND_RPC_STORE { + DOC_COMMAND("Store wallet's data to file"); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -675,7 +705,7 @@ namespace wallet_public uint64_t wallet_file_size; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(wallet_file_size) + KV_SERIALIZE(wallet_file_size) DOC_DSCR("Resulting file size in bytes") DOC_EXMP(232243) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -689,24 +719,27 @@ namespace wallet_public uint64_t unlock_time; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(amount) - KV_SERIALIZE(block_height) - KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) DOC_DSCR("Payment id that related to this payment") DOC_EXMP("1dfe5a88ff9effb3") DOC_END + KV_SERIALIZE(tx_hash) DOC_DSCR("Transaction ID that is holding this payment") DOC_EXMP("01220e8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a93") DOC_END + KV_SERIALIZE(amount) DOC_DSCR("Amount of native coins transfered") DOC_EXMP(100000000000) DOC_END + KV_SERIALIZE(block_height) DOC_DSCR("Block height that holds transaction") DOC_EXMP(12321) DOC_END + KV_SERIALIZE(unlock_time) DOC_DSCR("Timestamp/blocknumber after which this money would become availabe, recommended don't count transfers that has this field not 0") DOC_EXMP(0) DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_GET_PAYMENTS { + DOC_COMMAND("Gets list of incoming transfers by a given payment ID"); + + struct request { std::string payment_id; // hex-encoded bool allow_locked_transactions; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - KV_SERIALIZE(allow_locked_transactions) + KV_SERIALIZE(payment_id) DOC_DSCR("Payment id that is used to identify transfers") DOC_EXMP("1dfe5a88ff9effb3") DOC_END + KV_SERIALIZE(allow_locked_transactions) DOC_DSCR("Says to wallet if locked transfers should be included or not (false is strongly recomennded)") DOC_EXMP(false) DOC_END END_KV_SERIALIZE_MAP() }; @@ -715,13 +748,16 @@ namespace wallet_public std::list payments; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payments) + KV_SERIALIZE(payments) DOC_DSCR("Array of payments that connected to given payment_id") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_GET_BULK_PAYMENTS { + DOC_COMMAND("Gets list of incoming transfers by a given multiple payment_ids"); + + struct request { std::vector payment_ids; @@ -729,30 +765,25 @@ namespace wallet_public bool allow_locked_transactions; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_ids) - KV_SERIALIZE(min_block_height) - KV_SERIALIZE(allow_locked_transactions) + KV_SERIALIZE(payment_ids) DOC_DSCR("Payment ids that is used to identify transfers") DOC_EXMP_AUTO(2, "1dfe5a88ff9effb3") DOC_END + KV_SERIALIZE(min_block_height) DOC_DSCR("Minimal block height to consider") DOC_EXMP(0) DOC_END + KV_SERIALIZE(allow_locked_transactions) DOC_DSCR("Says to wallet if locked transfers should be included or not (false is strongly recomennded)") DOC_EXMP(false) DOC_END END_KV_SERIALIZE_MAP() }; - struct response - { - std::list payments; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payments) - END_KV_SERIALIZE_MAP() - }; + typedef COMMAND_RPC_GET_PAYMENTS::response response; }; struct COMMAND_RPC_MAKE_INTEGRATED_ADDRESS { + DOC_COMMAND("Generate integrated address"); + struct request { std::string payment_id; // hex-encoded BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) + KV_SERIALIZE(payment_id) DOC_DSCR("Hex-encoded Payment ID to be associated with the this address. If empty then wallet would generate new payment id using system random library") DOC_EXMP("1dfe5a88ff9effb3") DOC_END END_KV_SERIALIZE_MAP() }; @@ -762,20 +793,22 @@ namespace wallet_public std::string payment_id; // hex-encoded BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(integrated_address) - KV_SERIALIZE(payment_id) + KV_SERIALIZE(integrated_address) DOC_DSCR("Integrated address combining a standard address and payment ID, if applicable.") DOC_EXMP("iZ2EMyPD7g28hgBfboZeCENaYrHBYZ1bLFi5cgWvn4WJLaxfgs4kqG6cJi9ai2zrXWSCpsvRXit14gKjeijx6YPCLJEv6Fx4rVm1hdAGQFis") DOC_END + KV_SERIALIZE(payment_id) DOC_DSCR("Payment ID associated with the this address.") DOC_EXMP("1dfe5a88ff9effb3") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS { + DOC_COMMAND("Decode integrated address"); + struct request { std::string integrated_address; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(integrated_address) + KV_SERIALIZE(integrated_address) DOC_DSCR("Integrated address combining a standard address and payment ID, if applicable.") DOC_EXMP("iZ2EMyPD7g28hgBfboZeCENaYrHBYZ1bLFi5cgWvn4WJLaxfgs4kqG6cJi9ai2zrXWSCpsvRXit14gKjeijx6YPCLJEv6Fx4rVm1hdAGQFis") DOC_END END_KV_SERIALIZE_MAP() }; @@ -785,14 +818,16 @@ namespace wallet_public std::string payment_id; // hex-encoded BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(standard_address) - KV_SERIALIZE(payment_id) + KV_SERIALIZE(standard_address) DOC_DSCR("Standart address.") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE(payment_id) DOC_DSCR("Hex-encoded payment id") DOC_EXMP("1dfe5a88ff9effb3") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_SWEEP_BELOW { + DOC_COMMAND("Tries to transfer all coins with amount below the given limit to the given address"); + struct request { uint64_t mixin; @@ -802,11 +837,11 @@ namespace wallet_public uint64_t fee; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(mixin) - KV_SERIALIZE(address) - KV_SERIALIZE(amount) - KV_SERIALIZE(payment_id_hex) - KV_SERIALIZE(fee) + KV_SERIALIZE(mixin) DOC_DSCR("Number of outputs from the blockchain to mix with when sending a transaction to improve privacy.") DOC_EXMP(15) DOC_END + KV_SERIALIZE(address) DOC_DSCR("Public address for sending or receiving native coins.") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE(amount) DOC_DSCR("Threshold amount of native coins to sweep.") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE(payment_id_hex) DOC_DSCR("Payment ID associated with the transaction in hexadecimal format.") DOC_EXMP("1dfe5a88ff9effb3") DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Transaction fee required for processing the transaction.") DOC_EXMP(10000000000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -820,24 +855,80 @@ namespace wallet_public uint64_t amount_swept; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_unsigned_hex) - KV_SERIALIZE(outs_total) - KV_SERIALIZE(amount_total) - KV_SERIALIZE(outs_swept) - KV_SERIALIZE(amount_swept) + KV_SERIALIZE(tx_hash) DOC_DSCR("Transaction ID (hash) format.") DOC_EXMP("01220e8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a93") DOC_END + KV_SERIALIZE(tx_unsigned_hex) DOC_DSCR("Unsigned transaction data in hexadecimal format.") DOC_EXMP("8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a736") DOC_END + KV_SERIALIZE(outs_total) DOC_DSCR("Total number of outputs in the transaction.") DOC_EXMP(10) DOC_END + KV_SERIALIZE(amount_total) DOC_DSCR("Total amount of native coins involved in the transaction.") DOC_EXMP(100000000000) DOC_END + KV_SERIALIZE(outs_swept) DOC_DSCR("Number of outputs swept in the transaction.") DOC_EXMP(112) DOC_END + KV_SERIALIZE(amount_swept) DOC_DSCR("Amount of native coins swept in the transaction.") DOC_EXMP(101000000000) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_BARE_OUTS_STATS + { + DOC_COMMAND("Return information about wallet's pre-zarcanum era outputs. Those outputs should be converted to post-zarcanum varian with trnasfering it sooner or later. (Only outputs that have been created in Zarcanum era can participaet in staking)"); + + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t total_bare_outs; + uint64_t total_amount; + uint64_t expected_total_fee; + uint64_t txs_count; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(total_bare_outs) DOC_DSCR("Total number of inspent bare outputs in the wallet.") DOC_EXMP(112) DOC_END + KV_SERIALIZE(total_amount) DOC_DSCR("Total amount of native coins involved in bare outputs.") DOC_EXMP(12000000000000) DOC_END + KV_SERIALIZE(expected_total_fee) DOC_DSCR("Expected total transaction fee required for processing the transaction.") DOC_EXMP(10000000000) DOC_END + KV_SERIALIZE(txs_count) DOC_DSCR("Total number of transactions needed to convert all bare outputs .") DOC_EXMP(2) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SWEEP_BARE_OUTS + { + DOC_COMMAND("Execute transactions needed be convert all bare(pre-zarcanum) outputs to post-zarcanum outputs. (Only outputs that have been created in Zarcanum era can participaet in staking)"); + + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t bare_outs_swept; + uint64_t amount_swept; + uint64_t fee_spent; + uint64_t txs_sent; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(bare_outs_swept) DOC_DSCR("Number of bare outputs swept in the transactions.") DOC_EXMP(112) DOC_END + KV_SERIALIZE(amount_swept) DOC_DSCR("Amount of native coins swept in the transactions.") DOC_EXMP(12000000000000) DOC_END + KV_SERIALIZE(fee_spent) DOC_DSCR("Total fee spent on the transactions.") DOC_EXMP(10000000000) DOC_END + KV_SERIALIZE(txs_sent) DOC_DSCR("Total number of transactions sent.") DOC_EXMP(2) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_SIGN_TRANSFER { + DOC_COMMAND("Sign transaction with the wallet's keys"); + struct request { std::string tx_unsigned_hex; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_unsigned_hex) + KV_SERIALIZE(tx_unsigned_hex) DOC_DSCR("Unsigned transaction hex-encoded blob.") DOC_EXMP("8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a736") DOC_END END_KV_SERIALIZE_MAP() }; @@ -847,22 +938,22 @@ namespace wallet_public std::string tx_hash; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_signed_hex) - KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_signed_hex) DOC_DSCR("Signed transaction hex-encoded blob.") DOC_EXMP("8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a736") DOC_END + KV_SERIALIZE(tx_hash) DOC_DSCR("Signed transaction hash.") DOC_EXMP("01220e8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a93") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_SUBMIT_TRANSFER { + DOC_COMMAND("Relay signed transaction over the network"); + struct request { - //std::string tx_unsigned_hex; std::string tx_signed_hex; BEGIN_KV_SERIALIZE_MAP() - //KV_SERIALIZE(tx_unsigned_hex) - KV_SERIALIZE(tx_signed_hex) + KV_SERIALIZE(tx_signed_hex) DOC_DSCR("Signed transaction hex-encoded blob.") DOC_EXMP("8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a7368304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a9334f158a736") DOC_END END_KV_SERIALIZE_MAP() }; @@ -871,7 +962,7 @@ namespace wallet_public std::string tx_hash; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_hash) DOC_DSCR("Signed transaction hash.") DOC_EXMP("01220e8304d46b940a86e383d55ca5887b34f158a7365bbcdd17c5a305814a93") DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -1109,10 +1200,18 @@ namespace wallet_public }; //-------------------- - typedef currency::COMMAND_RPC_GET_OFFERS_EX COMMAND_MARKETPLACE_GET_MY_OFFERS; + struct COMMAND_MARKETPLACE_GET_MY_OFFERS + { + DOC_COMMAND("Fetch wallet's offers listed in the marketplace with given filters"); + + typedef currency::COMMAND_RPC_GET_OFFERS_EX::request request; + typedef currency::COMMAND_RPC_GET_OFFERS_EX::response response; + }; struct COMMAND_MARKETPLACE_PUSH_OFFER { + DOC_COMMAND("Creates new offer and publish it on the blockchain"); + struct request { bc_services::offer_details_ex od; @@ -1129,14 +1228,16 @@ namespace wallet_public uint64_t tx_blob_size; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_blob_size) + KV_SERIALIZE(tx_hash) DOC_DSCR("Transaction hash") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(tx_blob_size) DOC_DSCR("Size of the transaction blob") DOC_EXMP(1234) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_MARKETPLACE_PUSH_UPDATE_OFFER { + DOC_COMMAND("Updates existing offer that this wallet created, and publish updated version on the blockchain"); + struct request { crypto::hash tx_id; @@ -1144,8 +1245,8 @@ namespace wallet_public bc_services::offer_details_ex od; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - KV_SERIALIZE(no) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID represented as a hexadecimal string") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(no) DOC_DSCR("Number of offer entrie inside transacton(likely 0)") DOC_EXMP(0) DOC_END KV_SERIALIZE(od) END_KV_SERIALIZE_MAP() }; @@ -1157,14 +1258,16 @@ namespace wallet_public uint64_t tx_blob_size; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_blob_size) + KV_SERIALIZE(tx_hash) DOC_DSCR("Transaction hash") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(tx_blob_size) DOC_DSCR("Size of the transaction blob") DOC_EXMP(1232) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_MARKETPLACE_CANCEL_OFFER { + DOC_COMMAND("Cancel existing offer that this wallet created(it actually create transaction that says that existing order got canceled)"); + struct request { crypto::hash tx_id; @@ -1172,9 +1275,9 @@ namespace wallet_public uint64_t fee; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - KV_SERIALIZE(no) - KV_SERIALIZE(fee) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID represented as a hexadecimal string") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(no) DOC_DSCR("Number of offer entrie inside transacton(likely 0)") DOC_EXMP(0) DOC_END + KV_SERIALIZE(fee) DOC_DSCR("Fee for operation") DOC_EXMP(10000000000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -1185,36 +1288,33 @@ namespace wallet_public uint64_t tx_blob_size; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_blob_size) + KV_SERIALIZE(tx_hash) DOC_DSCR("Transaction hash") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(tx_blob_size) DOC_DSCR("Size of the transaction blob") DOC_EXMP(1232) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_SEARCH_FOR_TRANSACTIONS { + DOC_COMMAND("Search for transacrions in the wallet by few parameters"); struct request { crypto::hash tx_id; bool in; bool out; - //bool pending; - //bool failed; bool pool; bool filter_by_height; uint64_t min_height; uint64_t max_height; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) - KV_SERIALIZE(in) - KV_SERIALIZE(out) - //KV_SERIALIZE(pending) - //KV_SERIALIZE(failed) - KV_SERIALIZE(pool) - KV_SERIALIZE(filter_by_height) - KV_SERIALIZE(min_height) - KV_SERIALIZE(max_height) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Transaction ID represented as a hexadecimal string.") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE(in) DOC_DSCR("Search over incoming transactions.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(out) DOC_DSCR("Search over outgoing transactions.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(pool) DOC_DSCR("Search over pool transactions.") DOC_EXMP(false) DOC_END + KV_SERIALIZE(filter_by_height) DOC_DSCR("Do filter transactions by height or not.") DOC_EXMP(true) DOC_END + KV_SERIALIZE(min_height) DOC_DSCR("Minimum height for filtering transactions.") DOC_EXMP(11000) DOC_END + KV_SERIALIZE(max_height) DOC_DSCR("Maximum height for filtering transactions.") DOC_EXMP(20000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -1222,38 +1322,32 @@ namespace wallet_public { std::list in; std::list out; - //std::list pending; - //std::list failed; std::list pool; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(in) - KV_SERIALIZE(out) - //KV_SERIALIZE(pending) - //KV_SERIALIZE(failed) - KV_SERIALIZE(pool) + KV_SERIALIZE(in) DOC_DSCR("List of incoming transactions.") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(out) DOC_DSCR("List of outgoing transactions.") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(pool) DOC_DSCR("List of pool transactions.") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_RPC_SEARCH_FOR_TRANSACTIONS_LEGACY - { + { + DOC_COMMAND("Search for transacrions in the wallet by few parameters(legacy version)"); + typedef COMMAND_RPC_SEARCH_FOR_TRANSACTIONS::request request; struct response { std::list in; std::list out; - //std::list pending; - //std::list failed; std::list pool; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(in) - KV_SERIALIZE(out) - //KV_SERIALIZE(pending) - //KV_SERIALIZE(failed) - KV_SERIALIZE(pool) + KV_SERIALIZE(in) DOC_DSCR("List of incoming transactions.") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(out) DOC_DSCR("List of outgoing transactions.") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(pool) DOC_DSCR("List of pool transactions.") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -1421,17 +1515,15 @@ namespace wallet_public { std::vector to_finalizer; //assets that addressed to receiver of proposal (aka Bob, aka "finalizer") and funded by side that creating proposal (aka Alice, aka "initiator") std::vector to_initiator; //assets addressed to initiator of proposal (aka Alice, aka "initiator") and expected to be funded by the side that receiving proposal (aka Bob, aka "finalizer") - uint64_t mixins; + //uint64_t mixins; uint64_t fee_paid_by_a; //uint64_t expiration_time; BEGIN_KV_SERIALIZE_MAP() - - KV_SERIALIZE(to_finalizer) - KV_SERIALIZE(to_initiator) - KV_SERIALIZE(mixins) - KV_SERIALIZE(fee_paid_by_a) - //KV_SERIALIZE(expiration_time) + KV_SERIALIZE(to_finalizer) DOC_DSCR("Assets sent to the finalizer") DOC_EXMP_AUTO(1, asset_funds{ epee::transform_str_to_t_pod("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc"), 1000000000000}) DOC_END + KV_SERIALIZE(to_initiator) DOC_DSCR("Assets sent to the initiator") DOC_EXMP_AUTO(1, asset_funds{ currency::native_coin_asset_id, 10000000000}) DOC_END + //KV_SERIALIZE(mixins) DOC_DSCR("Number of mixins used") DOC_EXMP() DOC_END + KV_SERIALIZE(fee_paid_by_a) DOC_DSCR("Fee paid by party A(initiator)") DOC_EXMP(10000000000) DOC_END END_KV_SERIALIZE_MAP() }; @@ -1475,13 +1567,16 @@ namespace wallet_public struct COMMAND_IONIC_SWAP_GENERATE_PROPOSAL { + DOC_COMMAND("Generates ionic swap proposal according to details provided in request, result present as hex-encoded blob, that should be passed to recepient to validate this proposal and executing on it") + struct request { ionic_swap_proposal_info proposal; std::string destination_address; + BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(proposal) - KV_SERIALIZE(destination_address) + KV_SERIALIZE(proposal) DOC_DSCR("Proposal details") DOC_END + KV_SERIALIZE(destination_address) DOC_DSCR("Destination address") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1491,19 +1586,22 @@ namespace wallet_public std::string hex_raw_proposal; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hex_raw_proposal) + KV_SERIALIZE(hex_raw_proposal) DOC_DSCR("Hex-encoded proposal raw data(encrypted with common shared key). Includes half-created transaction template and some extra information that would be needed counterparty to finialize and sign transaction") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO { + DOC_COMMAND("Reads hex-encoded ionic swap proposal info, generated by other user and addressed to this wallet") + + struct request { std::string hex_raw_proposal; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hex_raw_proposal) + KV_SERIALIZE(hex_raw_proposal) DOC_DSCR("Hex-encoded proposal raw data(encrypted with common shared key). Includes half-created transaction template and some extra information that would be needed counterparty to finialize and sign transaction") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1513,19 +1611,22 @@ namespace wallet_public ionic_swap_proposal_info proposal; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(proposal) + KV_SERIALIZE(proposal) DOC_DSCR("Proposal details") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL { + DOC_COMMAND("This essential command actually execute proposal that was sent by counter party, by completing and signing transaction template that was in proposal, and sending it to the network.") + + struct request { std::string hex_raw_proposal; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hex_raw_proposal) + KV_SERIALIZE(hex_raw_proposal) DOC_DSCR("Hex-encoded proposal raw data(encrypted with common shared key). Includes half-created transaction template and some extra information that would be needed counterparty to finialize and sign transaction") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1535,7 +1636,7 @@ namespace wallet_public crypto::hash result_tx_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(result_tx_id) + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx_id) DOC_DSCR("Result transaction id") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; }; @@ -1550,15 +1651,17 @@ namespace wallet_public std::string path; bool is_auditable; bool is_watch_only; + bool has_bare_unspent_outputs; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(balances) - KV_SERIALIZE(mined_total) - KV_SERIALIZE(address) - KV_SERIALIZE(view_sec_key) - KV_SERIALIZE(path) - KV_SERIALIZE(is_auditable) - KV_SERIALIZE(is_watch_only) + KV_SERIALIZE(balances) DOC_DSCR("Balances hold by this wallet") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(mined_total) DOC_DSCR("Total amount mined") DOC_EXMP(1000000000000) DOC_END + KV_SERIALIZE(address) DOC_DSCR("Address") DOC_EXMP("ZxBvJDuQjMG9R2j4WnYUhBYNrwZPwuyXrC7FHdVmWqaESgowDvgfWtiXeNGu8Px9B24pkmjsA39fzSSiEQG1ekB225ZnrMTBp") DOC_END + KV_SERIALIZE(view_sec_key) DOC_DSCR("View secret key") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE(path) DOC_DSCR("Path to wallet file") DOC_EXMP("/some/path/to/wallet/file.zan") DOC_END + KV_SERIALIZE(is_auditable) DOC_DSCR("Flag indicating whether the wallet is auditable") DOC_EXMP(false) DOC_END + KV_SERIALIZE(is_watch_only) DOC_DSCR("Flag indicating whether the wallet is watch-only") DOC_EXMP(false) DOC_END + KV_SERIALIZE(has_bare_unspent_outputs) DOC_DSCR("Flag indicating whether the wallet has bare unspent outputs(pre-zarcanum outputs)") DOC_EXMP(false) DOC_END END_KV_SERIALIZE_MAP() }; @@ -1587,13 +1690,15 @@ namespace wallet_public BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(wi) - KV_SERIALIZE(wallet_id) + KV_SERIALIZE(wallet_id) DOC_DSCR("Wallet ID") DOC_EXMP(2) DOC_END END_KV_SERIALIZE_MAP() }; struct COMMAND_MW_GET_WALLETS { + DOC_COMMAND("Get loaded wallets list, userful for multi-wallet API") + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1605,19 +1710,21 @@ namespace wallet_public std::vector wallets; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(wallets) + KV_SERIALIZE(wallets) DOC_DSCR("Array of wallets") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_MW_SELECT_WALLET { + DOC_COMMAND("Select curent active wallet, after that all wallet RPC call would be addressed to this wallet") + struct request { uint64_t wallet_id; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(wallet_id) + KV_SERIALIZE(wallet_id) DOC_DSCR("Wallet id") DOC_EXMP_AUTO(2) DOC_END END_KV_SERIALIZE_MAP() }; @@ -1626,19 +1733,21 @@ namespace wallet_public { std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE(status) DOC_DSCR("Result (OK if success)") DOC_EXMP(API_RETURN_CODE_OK) DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_SIGN_MESSAGE { + DOC_COMMAND("Trivially sign base64 encoded data message using wallet spend key") + struct request { std::string buff; //base64 encoded data BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(buff) + KV_SERIALIZE(buff) DOC_DSCR("base64 encoded data message to be signed") DOC_EXMP("ZGNjc2Ztc2xrZm12O2xrZm12OydlbGtmdm0nbGtmbXY=") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1649,20 +1758,22 @@ namespace wallet_public crypto::public_key pkey = currency::null_pkey; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_POD_AS_HEX_STRING(sig) - KV_SERIALIZE_POD_AS_HEX_STRING(pkey) + KV_SERIALIZE_POD_AS_HEX_STRING(sig) DOC_DSCR("Signature represented as a hexadecimal string") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(pkey) DOC_DSCR("Wallet's public key represented as a hexadecimal string") DOC_EXMP("97d91442f8f3c22683585eaa60b53757d49bf046a96269cef45c1bc9ff7300cc") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_ENCRYPT_DATA { + DOC_COMMAND("Trivially encrypt base64 encoded data message with chacha using wallet spend key") + struct request { std::string buff; //base64 encoded data BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(buff) + KV_SERIALIZE(buff) DOC_DSCR("base64 encoded data message to be encrypted") DOC_EXMP("ZGNjc2Ztc2xrZm12O2xrZm12OydlbGtmdm0nbGtmbXY=") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1670,19 +1781,20 @@ namespace wallet_public { std::string res_buff; //base64 encoded encrypted data BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(res_buff) + KV_SERIALIZE(res_buff) DOC_DSCR("base64 encoded resulted data message") DOC_EXMP("ZGNjc2Ztc2xrZm12O2xrZm12OydlbGtmdm0nbGtmbXY=") DOC_END END_KV_SERIALIZE_MAP() }; }; struct COMMAND_DECRYPT_DATA { + DOC_COMMAND("Trivially decrypt base64 encoded data message with chacha using wallet spend key") struct request { std::string buff; //base64 encoded encrypted data BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(buff) + KV_SERIALIZE(buff) DOC_DSCR("base64 encoded data message to be decrypted") DOC_EXMP("ZGNjc2Ztc2xrZm12O2xrZm12OydlbGtmdm0nbGtmbXY=") DOC_END END_KV_SERIALIZE_MAP() }; @@ -1692,11 +1804,37 @@ namespace wallet_public std::string res_buff; //base64 encoded data BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(res_buff) + KV_SERIALIZE(res_buff) DOC_DSCR("base64 encoded resulted data message") DOC_EXMP("ZGNjc2Ztc2xrZm12O2xrZm12OydlbGtmdm0nbGtmbXY=") DOC_END END_KV_SERIALIZE_MAP() }; }; + struct COMMAND_PROXY_TO_DAEMON + { + DOC_COMMAND("Proxy call to daemon(node), might be not effective in some cases, so need to be carefull with use of it") + + struct request + { + std::string uri; + std::string base64_body; //base64 encoded body + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(uri) DOC_DSCR("URI for daemon API") DOC_EXMP("/json_rpc") DOC_END + KV_SERIALIZE(base64_body) DOC_DSCR("Base64 encoded request body") DOC_EXMP("ewogICAgImpzb25ycGMiOiAiMi4wIiwKICAgICJpZCI6IDAsCiAgICAibWV0aG9kIjogImdldF9taW5pbmdfaGlzdG9yeSIKfQ==") DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string base64_body; //base64 encoded response body + int32_t response_code; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(base64_body) DOC_DSCR("Base64 encoded daemon response body") DOC_EXMP("ewogICJpZCI6IDAsCiAgImpzb25ycGMiOiAiMi4wIiwKICAicmVzdWx0IjogewogICAgInBheW1lbnRzIjogWwogICAgICB7CiAgICAgICAgInBheW1lbnRfaWQiOiAiMDAwMDAwMDBmZjAwZmYwMCIsCiAgICAgICAgImFtb3VudCI6IDEwMDAwMDAwMCwKICAgICAgICAiYmxvY2tfaGVpZ2h0IjogMjAyNTU2LAogICAgICAgICJ0eF9oYXNoIjogIjAxMjIwZTgzMDRkNDZiOTQwYTg2ZTM4M2Q1NWNhNTg4N2IzNGYxNThhNzM2NWJiY2RkMTdjNWEzMDU4MTRhOTMiLAogICAgICAgICJ1bmxvY2tfdGltZSI6IDAKICAgICAgfSwKICAgICAgewogICAgICAgICJwYXltZW50X2lkIjogIjAwMDAwMDAwZmYwMGZmMDEiLAogICAgICAgICJhbW91bnQiOiAxMDAwMDAwMDAsCiAgICAgICAgImJsb2NrX2hlaWdodCI6IDIwMjU1NiwKICAgICAgICAidHhfaGFzaCI6ICIwYjVlYjk2ODVjMGMxMWRiNzdlMmNkZDk4NzljOGQzYjgxNTUyM2M2ZTRiZjAzZGNlZTYyYzU4M2I3ZTFmNzcyIiwKICAgICAgICAidW5sb2NrX3RpbWUiOiAwCiAgICAgIH0KICAgIF0KICB9Cn0=") DOC_END + KV_SERIALIZE(response_code) DOC_DSCR("Response code") DOC_EXMP(200) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; struct assets_whitelist { @@ -1736,5 +1874,167 @@ namespace wallet_public return lhs.amount == rhs.amount && lhs.asset_id == rhs.asset_id; } + + + struct COMMAND_ASSETS_WHITELIST_GET + { + DOC_COMMAND("Get whitelisted assets for this wallet, assets descriptors present in any of the lists in results would be present in balance() call results(if those assets are part of the wallet transfers). Assets that are not included in those lists won't be included in balance even if the wallet own inputs with such assets.") + + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::list local_whitelist; + std::list global_whitelist; + std::list own_assets; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(local_whitelist) DOC_DSCR("Local whitelist, assets that hase been added to this wallet file manually(!)") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(global_whitelist) DOC_DSCR("Global whitelist, well-known assets with adoption, mantained by the team and community") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(own_assets) DOC_DSCR("Own assets, the ones that is under control of this wallet") DOC_EXMP_AUTO(1) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_ASSETS_WHITELIST_ADD + { + DOC_COMMAND("Add given asset id to local whitelist. This whitelist is stored with the wallet file and reset in case of wallet resync or restoring wallet from seed phrase.") + + struct request + { + crypto::public_key asset_id = currency::null_pkey; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id that needed to be added to local whitelist, asset_id must exist in the network") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + currency::asset_descriptor_base asset_descriptor; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status of the asset") DOC_EXMP("OK") DOC_END + KV_SERIALIZE(asset_descriptor) DOC_DSCR("Details of the asset, recieved from node") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_ASSETS_WHITELIST_REMOVE + { + DOC_COMMAND("Remove given asset id from local whitelist. This whitelist is stored with the wallet file and reset in case of wallet resync or restoring wallet from seed phrase.") + + struct request + { + crypto::public_key asset_id = currency::null_pkey; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id to be removed from local whitelist") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Command result (OK if success)") DOC_EXMP(API_RETURN_CODE_OK) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + + + + struct COMMAND_ASSETS_DEPLOY + { + DOC_COMMAND("Deploy new asset in the system."); + + struct request + { + std::list destinations; + currency::asset_descriptor_base asset_descriptor; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in destinations should be set to 0000000000000000000000000000000000000000000000000000000000000000") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor that holds all information about asset - ticker, emission, description etc") DOC_END + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + crypto::hash result_tx; + crypto::public_key new_asset_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(new_asset_id) DOC_DSCR("Issued asset id") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_ASSETS_EMIT + { + DOC_COMMAND("Emmit new coins of the the asset, that is controlled by this wallet."); + + struct request + { + crypto::public_key asset_id; + std::list destinations; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to emit more coins") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in destinations should be set to 0000000000000000000000000000000000000000000000000000000000000000") DOC_EXMP_AUTO(1) DOC_END + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + crypto::hash result_tx; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_ASSETS_UPDATE + { + DOC_COMMAND("Update asset descriptor(you can change only owner so far)"); + + struct request + { + crypto::public_key asset_id; + currency::asset_descriptor_base asset_descriptor; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to update") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor that holds all information about asset that need to be updated (only owner could be updated)") DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + crypto::hash result_tx; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset registration command, asset would be registered as soon as transaction got confirmed") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + } // namespace wallet_rpc } // namespace tools diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a602ecba..10d2a399 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -17,6 +17,19 @@ using namespace epee; #include "wallet_rpc_server_error_codes.h" #include "wallet_helpers.h" #include "wrap_service.h" +PUSH_VS_WARNINGS +DISABLE_VS_WARNINGS(4244) +#include "jwt-cpp/jwt.h" +POP_VS_WARNINGS +#include "crypto/bitcoin/sha256_helper.h" + +#define JWT_TOKEN_EXPIRATION_MAXIMUM (60 * 60) +#define JWT_TOKEN_CLAIM_NAME_BODY_HASH "body_hash" +#define JWT_TOKEN_CLAIM_NAME_SALT "salt" +#define JWT_TOKEN_CLAIM_NAME_EXPIRATION "exp" +#define JWT_TOKEN_OVERWHELM_LIMIT 100000 // if there are more records in m_jwt_used_salts then we consider it as an attack + + #define GET_WALLET() wallet_rpc_locker w(m_pwallet_provider); @@ -25,13 +38,13 @@ using namespace epee; catch (const tools::error::daemon_busy& e) \ { \ er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; \ - er.message = e.what(); \ + er.message = std::string("WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY") + e.what(); \ return false; \ } \ catch (const tools::error::not_enough_money& e) \ { \ - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; \ - er.message = e.error_code(); \ + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY; \ + er.message = std::string("WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY") + e.error_code(); \ return false; \ } \ catch (const tools::error::wallet_error& e) \ @@ -43,7 +56,7 @@ using namespace epee; catch (const std::exception& e) \ { \ er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; \ - er.message = e.what(); \ + er.message = std::string("WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR") + e.what(); \ return false; \ } \ catch (...) \ @@ -53,6 +66,10 @@ using namespace epee; return false; \ } + +void exception_handler() +{} + namespace tools { //----------------------------------------------------------------------------------- @@ -60,6 +77,7 @@ namespace tools const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip ("rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"); const command_line::arg_descriptor wallet_rpc_server::arg_miner_text_info ( "miner-text-info", "Wallet password"); const command_line::arg_descriptor wallet_rpc_server::arg_deaf_mode ( "deaf", "Put wallet into 'deaf' mode make it ignore any rpc commands(usable for safe PoS mining)"); + const command_line::arg_descriptor wallet_rpc_server::arg_jwt_secret("jwt-secret", "Enables JWT auth over secret string provided"); void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { @@ -67,6 +85,7 @@ namespace tools command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_miner_text_info); command_line::add_arg(desc, arg_deaf_mode); + command_line::add_arg(desc, arg_jwt_secret); } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::wallet_rpc_server(std::shared_ptr wptr): @@ -184,11 +203,81 @@ namespace tools m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); + + + if(command_line::has_arg(vm, arg_jwt_secret)) + { + m_jwt_secret = command_line::get_arg(vm, arg_jwt_secret); + } return epee::http_server_impl_base::init(m_port, m_bind_ip); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context) + { + + auto it = std::find_if(query_info.m_header_info.m_etc_fields.begin(), query_info.m_header_info.m_etc_fields.end(), [](const auto& element) + { return element.first == ZANO_ACCESS_TOKEN; }); + if(it == query_info.m_header_info.m_etc_fields.end()) + return false; + + try + { + if(m_jwt_used_salts.get_set().size() > JWT_TOKEN_OVERWHELM_LIMIT) + { + throw std::runtime_error("Salt is overwhelmed"); + } + + auto decoded = jwt::decode(it->second, [](const std::string& str) + { return jwt::base::decode(jwt::base::pad(str)); }); + + + auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::hs256 { m_jwt_secret }); + + verifier.verify(decoded); + std::string body_hash = decoded.get_payload_claim(JWT_TOKEN_CLAIM_NAME_BODY_HASH).as_string(); + std::string salt = decoded.get_payload_claim(JWT_TOKEN_CLAIM_NAME_SALT).as_string(); + crypto::hash jwt_claim_sha256 = currency::null_hash; + epee::string_tools::hex_to_pod(body_hash, jwt_claim_sha256); + crypto::hash sha256 = crypto::sha256_hash(query_info.m_body.data(), query_info.m_body.size()); + if (sha256 != jwt_claim_sha256) + { + throw std::runtime_error("Body hash missmatch"); + } + if(m_jwt_used_salts.get_set().find(salt) != m_jwt_used_salts.get_set().end()) + { + throw std::runtime_error("Salt reused"); + } + + uint64_t ticks_now = epee::misc_utils::get_tick_count(); + m_jwt_used_salts.add(salt, ticks_now + JWT_TOKEN_EXPIRATION_MAXIMUM); + m_jwt_used_salts.remove_if_expiration_less_than(ticks_now); + + LOG_PRINT_L0("JWT token OK"); + return true; + } + catch(const std::exception& e) + { + LOG_ERROR("Invalid JWT token: " << e.what()); + return false; + } + + + return false; + + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context) { + if (m_jwt_secret.size() && m_conn_context.m_connection_id != RPC_INTERNAL_UI_CONTEXT) + { + if (!auth_http_request(query_info, response, m_conn_context)) + { + response.m_response_code = 401; + response.m_response_comment = "Unauthorized"; + return true; + } + } + response.m_response_code = 200; response.m_response_comment = "Ok"; std::string reference_stub; @@ -199,7 +288,7 @@ namespace tools response.m_response_comment = "Internal Server Error"; return true; } - if (!handle_http_request_map(query_info, response, m_conn_context, call_found, reference_stub) && response.m_response_code == 200) + if (!handle_http_request_map(query_info, response, m_conn_context, call_found) && response.m_response_code == 200) { response.m_response_code = 500; response.m_response_comment = "Internal Server Error"; @@ -214,6 +303,16 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::set_jwt_secret(const std::string& jwt) + { + m_jwt_secret = jwt; + } + //------------------------------------------------------------------------------------------------------------------------------ + const std::string& wallet_rpc_server::get_jwt_secret() + { + return m_jwt_secret; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); @@ -256,6 +355,7 @@ namespace tools res.utxo_distribution.push_back(currency::print_money_brief(ent.first) + ":" + std::to_string(ent.second)); res.current_height = w.get_wallet()->get_top_block_height(); + res.has_bare_unspent_outputs = w.get_wallet()->has_bare_unspent_outputs(); return true; WALLET_RPC_CATCH_TRY_ENTRY(); } @@ -325,7 +425,7 @@ namespace tools return true; WALLET_RPC_CATCH_TRY_ENTRY(); } - //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer(const wallet_public::COMMAND_RPC_TRANSFER::request& req, wallet_public::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); @@ -357,6 +457,7 @@ namespace tools } bool wrap = false; std::vector& dsts = ctp.dsts; + for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { currency::tx_destination_entry de; @@ -657,6 +758,78 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_bare_outs_stats(const wallet_public::COMMAND_RPC_GET_BARE_OUTS_STATS ::request& req, wallet_public::COMMAND_RPC_GET_BARE_OUTS_STATS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + + if (w.get_wallet()->is_watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "operation cannot be performed in watch-only wallet"; + return false; + } + + std::vector groups; + if (!w.get_wallet()->get_bare_unspent_outputs_stats(groups)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "get_bare_unspent_outputs_stats failed"; + return false; + } + + res.total_amount = 0; + res.total_bare_outs = 0; + res.expected_total_fee = 0; + res.txs_count = 0; + + for(auto &g : groups) + { + for (auto& tid: g.tids) + { + tools::transfer_details td{}; + CHECK_AND_ASSERT_THROW_MES(w.get_wallet()->get_transfer_info_by_index(tid, td), "get_transfer_info_by_index failed with index " << tid); + res.total_amount += td.m_amount; + } + ++res.txs_count; + res.total_bare_outs += g.tids.size(); + res.expected_total_fee += TX_DEFAULT_FEE; + } + + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sweep_bare_outs(const wallet_public::COMMAND_RPC_SWEEP_BARE_OUTS::request& req, wallet_public::COMMAND_RPC_SWEEP_BARE_OUTS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + + if (w.get_wallet()->is_watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "operation cannot be performed in watch-only wallet"; + return false; + } + + std::vector groups; + if (!w.get_wallet()->get_bare_unspent_outputs_stats(groups)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "get_bare_unspent_outputs_stats failed"; + return false; + } + + res.amount_swept = 0; + res.bare_outs_swept = 0; + res.fee_spent = 0; + + size_t txs_sent = 0; + w.get_wallet()->sweep_bare_unspent_outputs(w.get_wallet()->get_account().get_public_address(), groups, txs_sent, res.amount_swept, res.fee_spent, res.bare_outs_swept); + res.txs_sent = txs_sent; + + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sign_transfer(const wallet_public::COMMAND_SIGN_TRANSFER::request& req, wallet_public::COMMAND_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); @@ -1018,6 +1191,106 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_whitelist_get(const wallet_public::COMMAND_ASSETS_WHITELIST_GET::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_GET::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + + currency::assets_map_to_assets_list(res.local_whitelist, w.get_wallet()->get_local_whitelist()); + currency::assets_map_to_assets_list(res.global_whitelist, w.get_wallet()->get_global_whitelist()); + currency::assets_map_to_assets_list(res.own_assets, w.get_wallet()->get_own_assets()); + + const auto global_whitelist = w.get_wallet()->get_global_whitelist(); + + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_whitelist_add(const wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + if(!w.get_wallet()->add_custom_asset_id(req.asset_id, res.asset_descriptor)) + { + res.status = API_RETURN_CODE_NOT_FOUND; + } + else + { + res.status = API_RETURN_CODE_OK; + } + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_whitelist_remove(const wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + if(!w.get_wallet()->delete_custom_asset_id(req.asset_id)) + { + res.status = API_RETURN_CODE_NOT_FOUND; + } + else + { + res.status = API_RETURN_CODE_OK; + } + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::rpc_destinations_to_currency_destination(const std::list& rpc_destinations, std::vector& currency_destinations) + { + GET_WALLET(); + std::vector& dsts = currency_destinations; + for (auto it = rpc_destinations.begin(); it != rpc_destinations.end(); it++) + { + currency::tx_destination_entry de; + de.addr.resize(1); + std::string embedded_payment_id; + //check if address looks like wrapped address + WLT_THROW_IF_FALSE_WITH_CODE(!currency::is_address_like_wrapped(it->address), "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS", "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS"); + WLT_THROW_IF_FALSE_WITH_CODE(!w.get_wallet()->get_transfer_address(it->address, de.addr.back(), embedded_payment_id), "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS", "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS"); + WLT_THROW_IF_FALSE_WITH_CODE(embedded_payment_id.size() == 0, "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS", "WALLET_RPC_ERROR_CODE_WRONG_ADDRESS"); + de.amount = it->amount; + de.asset_id = it->asset_id; + dsts.push_back(de); + } + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::transaction result_tx; + std::vector currency_destinations; + rpc_destinations_to_currency_destination(req.destinations, currency_destinations); + w.get_wallet()->deploy_new_asset(req.asset_descriptor, currency_destinations, result_tx, res.new_asset_id); + res.result_tx = currency::get_transaction_hash(result_tx); + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::transaction result_tx; + std::vector currency_destinations; + rpc_destinations_to_currency_destination(req.destinations, currency_destinations); + + w.get_wallet()->emit_asset(req.asset_id, currency_destinations, result_tx); + res.result_tx = currency::get_transaction_hash(result_tx); + return true; + + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::transaction result_tx; + w.get_wallet()->update_asset(req.asset_id, req.asset_descriptor, result_tx); + res.result_tx = currency::get_transaction_hash(result_tx); + return true; + + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); @@ -1068,6 +1341,18 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_proxy_to_daemon(const wallet_public::COMMAND_PROXY_TO_DAEMON::request& req, wallet_public::COMMAND_PROXY_TO_DAEMON::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + std::string buff = epee::string_encoding::base64_decode(req.base64_body); + + w.get_wallet()->proxy_to_daemon(req.uri, buff, res.response_code, res.base64_body); + + res.base64_body = epee::string_encoding::base64_encode(res.base64_body); + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index f95ae920..86a0039a 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -12,11 +12,11 @@ #include "wallet_public_structs_defs.h" #include "wallet2.h" #include "common/command_line.h" + +#define ZANO_ACCESS_TOKEN "Zano-Access-Token" + namespace tools { - - - struct i_wallet_provider { virtual void lock() {}; @@ -82,12 +82,15 @@ namespace tools const static command_line::arg_descriptor arg_rpc_bind_ip; const static command_line::arg_descriptor arg_miner_text_info; const static command_line::arg_descriptor arg_deaf_mode; + const static command_line::arg_descriptor arg_jwt_secret; static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); bool run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address); bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context); + void set_jwt_secret(const std::string& jwt); + const std::string& get_jwt_secret(); BEGIN_URI_MAP2_VIRTUAL() BEGIN_JSON_RPC_MAP("/json_rpc") @@ -103,6 +106,8 @@ namespace tools MAP_JON_RPC_WE("make_integrated_address", on_make_integrated_address, wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS) MAP_JON_RPC_WE("split_integrated_address", on_split_integrated_address, wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS) MAP_JON_RPC_WE("sweep_below", on_sweep_below, wallet_public::COMMAND_SWEEP_BELOW) + MAP_JON_RPC_WE("get_bare_outs_stats", on_get_bare_outs_stats, wallet_public::COMMAND_RPC_GET_BARE_OUTS_STATS) + MAP_JON_RPC_WE("sweep_bare_outs", on_sweep_bare_outs, wallet_public::COMMAND_RPC_SWEEP_BARE_OUTS) MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_public::COMMAND_SIGN_TRANSFER) MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_public::COMMAND_SUBMIT_TRANSFER) MAP_JON_RPC_WE("search_for_transactions", on_search_for_transactions, wallet_public::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS_LEGACY) @@ -112,39 +117,52 @@ namespace tools MAP_JON_RPC_WE("get_mining_history", on_get_mining_history, wallet_public::COMMAND_RPC_GET_MINING_HISTORY) MAP_JON_RPC_WE("register_alias", on_register_alias, wallet_public::COMMAND_RPC_REGISTER_ALIAS) //contracts API - MAP_JON_RPC_WE("contracts_send_proposal", on_contracts_send_proposal, wallet_public::COMMAND_CONTRACTS_SEND_PROPOSAL) - MAP_JON_RPC_WE("contracts_accept_proposal", on_contracts_accept_proposal, wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL) - MAP_JON_RPC_WE("contracts_get_all", on_contracts_get_all, wallet_public::COMMAND_CONTRACTS_GET_ALL) - MAP_JON_RPC_WE("contracts_release", on_contracts_release, wallet_public::COMMAND_CONTRACTS_RELEASE) - MAP_JON_RPC_WE("contracts_request_cancel", on_contracts_request_cancel, wallet_public::COMMAND_CONTRACTS_REQUEST_CANCEL) - MAP_JON_RPC_WE("contracts_accept_cancel", on_contracts_accept_cancel, wallet_public::COMMAND_CONTRACTS_ACCEPT_CANCEL) + //MAP_JON_RPC_WE("contracts_send_proposal", on_contracts_send_proposal, wallet_public::COMMAND_CONTRACTS_SEND_PROPOSAL) + //MAP_JON_RPC_WE("contracts_accept_proposal", on_contracts_accept_proposal, wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL) + //MAP_JON_RPC_WE("contracts_get_all", on_contracts_get_all, wallet_public::COMMAND_CONTRACTS_GET_ALL) + //MAP_JON_RPC_WE("contracts_release", on_contracts_release, wallet_public::COMMAND_CONTRACTS_RELEASE) + //MAP_JON_RPC_WE("contracts_request_cancel", on_contracts_request_cancel, wallet_public::COMMAND_CONTRACTS_REQUEST_CANCEL) + //MAP_JON_RPC_WE("contracts_accept_cancel", on_contracts_accept_cancel, wallet_public::COMMAND_CONTRACTS_ACCEPT_CANCEL) //marketplace API MAP_JON_RPC_WE("marketplace_get_offers_ex", on_marketplace_get_my_offers, wallet_public::COMMAND_MARKETPLACE_GET_MY_OFFERS) MAP_JON_RPC_WE("marketplace_push_offer", on_marketplace_push_offer, wallet_public::COMMAND_MARKETPLACE_PUSH_OFFER) MAP_JON_RPC_WE("marketplace_push_update_offer", on_marketplace_push_update_offer, wallet_public::COMMAND_MARKETPLACE_PUSH_UPDATE_OFFER) MAP_JON_RPC_WE("marketplace_cancel_offer", on_marketplace_cancel_offer, wallet_public::COMMAND_MARKETPLACE_CANCEL_OFFER) //HTLC API - MAP_JON_RPC_WE("atomics_create_htlc_proposal", on_create_htlc_proposal, wallet_public::COMMAND_CREATE_HTLC_PROPOSAL) - MAP_JON_RPC_WE("atomics_get_list_of_active_htlc", on_get_list_of_active_htlc, wallet_public::COMMAND_GET_LIST_OF_ACTIVE_HTLC) - MAP_JON_RPC_WE("atomics_redeem_htlc", on_redeem_htlc, wallet_public::COMMAND_REDEEM_HTLC) - MAP_JON_RPC_WE("atomics_check_htlc_redeemed", on_check_htlc_redeemed, wallet_public::COMMAND_CHECK_HTLC_REDEEMED) + //MAP_JON_RPC_WE("atomics_create_htlc_proposal", on_create_htlc_proposal, wallet_public::COMMAND_CREATE_HTLC_PROPOSAL) + //MAP_JON_RPC_WE("atomics_get_list_of_active_htlc", on_get_list_of_active_htlc, wallet_public::COMMAND_GET_LIST_OF_ACTIVE_HTLC) + //MAP_JON_RPC_WE("atomics_redeem_htlc", on_redeem_htlc, wallet_public::COMMAND_REDEEM_HTLC) + //MAP_JON_RPC_WE("atomics_check_htlc_redeemed", on_check_htlc_redeemed, wallet_public::COMMAND_CHECK_HTLC_REDEEMED) //IONIC_SWAPS API - MAP_JON_RPC_WE("ionic_swap_generate_proposal", on_ionic_swap_generate_proposal, wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL) - MAP_JON_RPC_WE("ionic_swap_get_proposal_info", on_ionic_swap_get_proposal_info, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO) - MAP_JON_RPC_WE("ionic_swap_accept_proposal", on_ionic_swap_accept_proposal, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL) + MAP_JON_RPC_WE("ionic_swap_generate_proposal", on_ionic_swap_generate_proposal, wallet_public::COMMAND_IONIC_SWAP_GENERATE_PROPOSAL) + MAP_JON_RPC_WE("ionic_swap_get_proposal_info", on_ionic_swap_get_proposal_info, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO) + MAP_JON_RPC_WE("ionic_swap_accept_proposal", on_ionic_swap_accept_proposal, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL) + + // Assets API + MAP_JON_RPC_WE("assets_whitelist_get", on_assets_whitelist_get, wallet_public::COMMAND_ASSETS_WHITELIST_GET) + MAP_JON_RPC_WE("assets_whitelist_add", on_assets_whitelist_add, wallet_public::COMMAND_ASSETS_WHITELIST_ADD) + MAP_JON_RPC_WE("assets_whitelist_remove", on_assets_whitelist_remove, wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE) + + MAP_JON_RPC_WE("deploy_asset", on_assets_deploy, wallet_public::COMMAND_ASSETS_DEPLOY) + MAP_JON_RPC_WE("emit_asset", on_assets_emit, wallet_public::COMMAND_ASSETS_EMIT) + MAP_JON_RPC_WE("update_asset", on_assets_update, wallet_public::COMMAND_ASSETS_UPDATE) //MULTIWALLET APIs - MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS) - MAP_JON_RPC_WE("mw_select_wallet", on_mw_select_wallet, wallet_public::COMMAND_MW_SELECT_WALLET) + MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS) + MAP_JON_RPC_WE("mw_select_wallet", on_mw_select_wallet, wallet_public::COMMAND_MW_SELECT_WALLET) //basic crypto operations - MAP_JON_RPC_WE("sign_message", on_sign_message, wallet_public::COMMAND_SIGN_MESSAGE) - MAP_JON_RPC_WE("encrypt_data", on_encrypt_data, wallet_public::COMMAND_ENCRYPT_DATA) - MAP_JON_RPC_WE("decrypt_data", on_decrypt_data, wallet_public::COMMAND_DECRYPT_DATA) - END_JSON_RPC_MAP() + MAP_JON_RPC_WE("sign_message", on_sign_message, wallet_public::COMMAND_SIGN_MESSAGE) + MAP_JON_RPC_WE("encrypt_data", on_encrypt_data, wallet_public::COMMAND_ENCRYPT_DATA) + MAP_JON_RPC_WE("decrypt_data", on_decrypt_data, wallet_public::COMMAND_DECRYPT_DATA) + + //utility call + MAP_JON_RPC_WE("proxy_to_daemon", on_proxy_to_daemon, wallet_public::COMMAND_PROXY_TO_DAEMON) + END_JSON_RPC_MAP() END_URI_MAP2() + bool auth_http_request(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response, connection_context& m_conn_context); //json_rpc bool on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getaddress(const wallet_public::COMMAND_RPC_GET_ADDRESS::request& req, wallet_public::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); @@ -160,6 +178,8 @@ namespace tools bool on_make_integrated_address(const wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_public::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_split_integrated_address(const wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_public::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_sweep_below(const wallet_public::COMMAND_SWEEP_BELOW::request& req, wallet_public::COMMAND_SWEEP_BELOW::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_get_bare_outs_stats(const wallet_public::COMMAND_RPC_GET_BARE_OUTS_STATS ::request& req, wallet_public::COMMAND_RPC_GET_BARE_OUTS_STATS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_sweep_bare_outs(const wallet_public::COMMAND_RPC_SWEEP_BARE_OUTS::request& req, wallet_public::COMMAND_RPC_SWEEP_BARE_OUTS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_sign_transfer(const wallet_public::COMMAND_SIGN_TRANSFER::request& req, wallet_public::COMMAND_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_submit_transfer(const wallet_public::COMMAND_SUBMIT_TRANSFER::request& req, wallet_public::COMMAND_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_search_for_transactions(const wallet_public::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS_LEGACY::request& req, wallet_public::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS_LEGACY::response& res, epee::json_rpc::error& er, connection_context& cntx); @@ -189,6 +209,15 @@ namespace tools bool on_ionic_swap_get_proposal_info(const wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::request& req, wallet_public::COMMAND_IONIC_SWAP_GET_PROPOSAL_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_ionic_swap_accept_proposal(const wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::request& req, wallet_public::COMMAND_IONIC_SWAP_ACCEPT_PROPOSAL::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_assets_whitelist_get(const wallet_public::COMMAND_ASSETS_WHITELIST_GET::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_GET::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_assets_whitelist_add(const wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_assets_whitelist_remove(const wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::request& req, wallet_public::COMMAND_ASSETS_WHITELIST_REMOVE::response& res, epee::json_rpc::error& er, connection_context& cntx); + + bool on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_assets_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_assets_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx); + + bool on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx); @@ -196,6 +225,7 @@ namespace tools bool on_encrypt_data(const wallet_public::COMMAND_ENCRYPT_DATA::request& req, wallet_public::COMMAND_ENCRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_proxy_to_daemon(const wallet_public::COMMAND_PROXY_TO_DAEMON::request& req, wallet_public::COMMAND_PROXY_TO_DAEMON::response& res, epee::json_rpc::error& er, connection_context& cntx); //std::shared_ptr get_wallet(); @@ -203,6 +233,7 @@ namespace tools //bool reset_active_wallet(std::shared_ptr w); bool handle_command_line(const boost::program_options::variables_map& vm); + void rpc_destinations_to_currency_destination(const std::list& rpc_destinations, std::vector& currency_destinations); private: std::shared_ptr m_pwallet_provider_sh_ptr; @@ -212,6 +243,8 @@ namespace tools bool m_do_mint; bool m_deaf; uint64_t m_last_wallet_store_height; + std::string m_jwt_secret; + epee::misc_utils::expirating_set m_jwt_used_salts; }; } // namespace tools diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index f9c0a2a0..d24302e0 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -13,3 +13,4 @@ #define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4 #define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5 #define WALLET_RPC_ERROR_CODE_WRONG_ARGUMENT -6 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -7 diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index c1edabff..02287be5 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -48,7 +48,7 @@ #define TX_POOL_SCAN_INTERVAL 1 #endif -#define HTTP_PROXY_TIMEOUT 2000 +#define HTTP_PROXY_TIMEOUT 4000 #define HTTP_PROXY_ATTEMPTS_COUNT 1 const command_line::arg_descriptor arg_alloc_win_console ( "alloc-win-console", "Allocates debug console with GUI", false ); @@ -546,13 +546,12 @@ bool wallets_manager::init_local_daemon() CHECK_AND_ASSERT_AND_SET_GUI(res, "Failed to initialize core rpc server."); LOG_PRINT_GREEN("Core rpc server initialized OK on port: " << m_rpc_server.get_binded_port(), LOG_LEVEL_0); + m_ui_opt.rpc_port = m_rpc_server.get_binded_port(); + + //chain calls to rpc server m_prpc_chain_handler = &m_wallet_rpc_server; //disable this for main net until we get full support of authentication with network -#ifdef TESTNET - m_rpc_server.set_rpc_chain_handler(this); -#endif - LOG_PRINT_L0("Starting core rpc server..."); //dsi.text_state = "Starting core rpc server"; @@ -572,6 +571,25 @@ bool wallets_manager::init_local_daemon() return true; } +std::string wallets_manager::setup_wallet_rpc(const std::string& jwt_secret) +{ +#ifndef MOBILE_WALLET_BUILD + if (!jwt_secret.size()) + { + //disabling wallet RPC + m_rpc_server.set_rpc_chain_handler(nullptr); + return WALLET_RPC_STATUS_OK; + } + + //we don't override command line JWT secret + //if(!m_wallet_rpc_server.get_jwt_secret().size() ) + m_wallet_rpc_server.set_jwt_secret(jwt_secret); + + m_rpc_server.set_rpc_chain_handler(this); +#endif + return WALLET_RPC_STATUS_OK; +} + bool wallets_manager::deinit_local_daemon() { #ifndef MOBILE_WALLET_BUILD @@ -1025,7 +1043,6 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st std::shared_ptr w(new tools::wallet2()); w->set_use_deffered_global_outputs(m_use_deffered_global_outputs); - w->set_use_assets_whitelisting(true); owr.wallet_id = m_wallet_id_counter++; w->callback(std::shared_ptr(new i_wallet_to_i_backend_adapter(this, owr.wallet_id))); @@ -1057,10 +1074,11 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st w->get_recent_transfers_history(owr.recent_history.history, 0, txs_to_return, owr.recent_history.total_history_items, owr.recent_history.last_item_index, exclude_mining_txs); //w->get_unconfirmed_transfers(owr.recent_history.unconfirmed); w->get_unconfirmed_transfers(owr.recent_history.history, exclude_mining_txs); + w->set_use_assets_whitelisting(true); owr.wallet_local_bc_size = w->get_blockchain_current_size(); //workaround for missed fee - //owr.seed = w->get_account().get_seed_phrase(); + owr.seed = w->get_account().get_seed_phrase(""); break; } catch (const tools::error::file_not_found& /**/) @@ -1170,7 +1188,7 @@ std::string wallets_manager::generate_wallet(const std::wstring& path, const std { w->generate(path, password, false); w->set_minimum_height(m_last_daemon_height-1); - //owr.seed = w->get_account().get_seed_phrase(); + owr.seed = w->get_account().get_seed_phrase(""); } catch (const tools::error::file_exists&) { @@ -1279,7 +1297,7 @@ std::string wallets_manager::restore_wallet(const std::wstring& path, const std: { bool is_tracking = currency::account_base::is_seed_tracking(seed_phrase); w->restore(path, password, seed_phrase, is_tracking, seed_password); - //owr.seed = w->get_account().get_seed_phrase(); + owr.seed = w->get_account().get_seed_phrase(""); } catch (const tools::error::file_exists&) { @@ -1645,11 +1663,10 @@ std::string wallets_manager::invoke(uint64_t wallet_id, std::string params) epee::net_utils::http::http_request_info query_info = AUTO_VAL_INIT(query_info); epee::net_utils::http::http_response_info response_info = AUTO_VAL_INIT(response_info); epee::net_utils::connection_context_base stub_conn_context = AUTO_VAL_INIT(stub_conn_context); - std::string reference_stub; bool call_found = false; query_info.m_URI = "/json_rpc"; query_info.m_body = params; - wo.rpc_wrapper->handle_http_request_map(query_info, response_info, stub_conn_context, call_found, reference_stub); + wo.rpc_wrapper->handle_http_request_map(query_info, response_info, stub_conn_context, call_found); return response_info.m_body; } @@ -1880,6 +1897,7 @@ void wallets_manager::prepare_wallet_status_info(wallet_vs_options& wo, view::wa wsi.wallet_id = wo.wallet_id; wsi.is_alias_operations_available = !wo.has_related_alias_in_unconfirmed; wo.w->get()->balance(wsi.balances, wsi.minied_total); + wsi.has_bare_unspent_outputs = wo.w->get()->has_bare_unspent_outputs(); } std::string wallets_manager::check_available_sources(uint64_t wallet_id, std::list& amounts) { @@ -2103,10 +2121,10 @@ bool wallets_manager::on_mw_select_wallet(const tools::wallet_public::COMMAND_MW return true; } - void wallets_manager::lock() { #ifndef MOBILE_WALLET_BUILD + m_select_wallet_rpc_lock.lock(); { SHARED_CRITICAL_REGION_LOCAL(m_wallets_lock); auto it = m_wallets.find(m_rpc_selected_wallet_id); @@ -2123,6 +2141,7 @@ void wallets_manager::unlock() { #ifndef MOBILE_WALLET_BUILD m_current_wallet_locked_object.reset(); + m_select_wallet_rpc_lock.unlock(); #endif } std::shared_ptr wallets_manager::get_wallet() diff --git a/src/wallet/wallets_manager.h b/src/wallet/wallets_manager.h index a19e8138..014ea3ef 100644 --- a/src/wallet/wallets_manager.h +++ b/src/wallet/wallets_manager.h @@ -146,6 +146,8 @@ public: std::string get_fav_offers(const std::list& hashes, const bc_services::core_offers_filter& filter, std::list& offers); std::string get_tx_pool_info(currency::COMMAND_RPC_GET_POOL_INFO::response& res); std::string export_wallet_history(const view::export_wallet_info& ewi); + std::string setup_wallet_rpc(const std::string& jwt_secret); + #ifndef MOBILE_WALLET_BUILD currency::core_rpc_server& get_rpc_server() { return m_rpc_server; } #endif @@ -193,12 +195,12 @@ private: bool do_exception_safe_call(guarded_code_t guarded_code, error_prefix_maker_t error_prefix_maker, std::string& api_return_code_result); //----- i_backend_wallet_callback ------ - virtual void on_new_block(size_t wallet_id, uint64_t height, const currency::block& block); - virtual void on_transfer2(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti, const std::list& balances, uint64_t total_mined); - virtual void on_pos_block_found(size_t wallet_id, const currency::block& /*block*/); - virtual void on_sync_progress(size_t wallet_id, const uint64_t& /*percents*/); - virtual void on_transfer_canceled(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti); - virtual void on_tor_status_change(size_t wallet_id, const std::string& state); + virtual void on_new_block(size_t wallet_id, uint64_t height, const currency::block& block) override; + virtual void on_transfer2(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti, const std::list& balances, uint64_t total_mined) override; + virtual void on_pos_block_found(size_t wallet_id, const currency::block& /*block*/) override; + virtual void on_sync_progress(size_t wallet_id, const uint64_t& /*percents*/) override; + virtual void on_transfer_canceled(size_t wallet_id, const tools::wallet_public::wallet_transfer_info& wti) override; + virtual void on_tor_status_change(size_t wallet_id, const std::string& state) override; virtual void on_mw_get_wallets(std::vector& wallets) override; virtual bool on_mw_select_wallet(uint64_t wallet_id) override; @@ -232,6 +234,8 @@ private: std::mutex m_stop_singal_sent_mutex; std::condition_variable m_stop_singal_sent_mutex_cv; + std::mutex m_select_wallet_rpc_lock; + view::i_view m_view_stub; view::i_view* m_pview; std::shared_ptr m_rpc_proxy; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 71b544c1..323dbbd8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,11 +29,11 @@ add_executable(net_load_tests_srv net_load_tests/srv.cpp) add_dependencies(coretests version) target_link_libraries(coretests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(functional_tests rpc wallet currency_core crypto common zlibstatic ethash libminiupnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(functional_tests rpc wallet currency_core crypto common zlibstatic ethash libminiupnpc-static ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(hash-tests crypto ethash) target_link_libraries(hash-target-tests crypto currency_core ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(performance_tests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(unit_tests wallet currency_core common crypto gtest_main zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(performance_tests rpc wallet currency_core common crypto zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(unit_tests wallet currency_core common crypto gtest_main zlibstatic ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index e9ec72c1..0aff096f 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -69,8 +69,8 @@ bool block_template_against_txs_size::c1(currency::core& c, size_t ev_index, con uint64_t top_block_height = bcs.get_top_block_height(); uint64_t blocksize_limit = bcs.get_current_comulative_blocksize_limit(); - uint64_t base_block_reward_pow = get_base_block_reward(false, bcs.total_coins(), top_block_height + 1); - uint64_t base_block_reward_pos = get_base_block_reward(true, bcs.total_coins(), top_block_height + 1); + uint64_t base_block_reward_pow = get_base_block_reward(top_block_height + 1); + uint64_t base_block_reward_pos = base_block_reward_pow; g_block_txs_fee = TESTS_DEFAULT_FEE; // passing an argument to custom_fill_block_template_func via global variable (not perfect but works well) @@ -91,13 +91,16 @@ bool block_template_against_txs_size::c1(currency::core& c, size_t ev_index, con CHECK_AND_ASSERT_MES(r, false, "create_block_template failed, txs_total_size = " << txs_total_size); CHECK_AND_ASSERT_MES(height == top_block_height + 1, false, "Incorrect height: " << height << ", expected: " << top_block_height + 1 << ", txs_total_size = " << txs_total_size); - uint64_t base_reward = 0; + uint64_t block_reward_without_fee = 0; size_t cumulative_block_size = txs_total_size; size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); if (coinbase_blob_size > CURRENCY_COINBASE_BLOB_RESERVED_SIZE) cumulative_block_size += coinbase_blob_size; - r = bcs.validate_miner_transaction(b, cumulative_block_size, g_block_txs_fee, base_reward, bcs.total_coins()); + + r = bcs.calculate_block_reward_for_next_top_block(cumulative_block_size, block_reward_without_fee); + CHECK_AND_ASSERT_MES(r, false, "calculate_block_reward_for_next_top_block failed"); + r = bcs.validate_miner_transaction(b.miner_tx, g_block_txs_fee, block_reward_without_fee); CHECK_AND_ASSERT_MES(r, false, "validate_miner_transaction failed, txs_total_size = " << txs_total_size); uint64_t generated_coins = get_outs_money_amount(b.miner_tx) - (is_pos != 0 ? boost::get(b.miner_tx.vout.back()).amount : 0) - g_block_txs_fee / 2; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index f03bf1a2..21005216 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,11 +1,9 @@ -// Copyright (c) 2014-2022 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on pseudorandom number generator manupulations for tests - #include "chaingen.h" #include @@ -553,12 +551,16 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain, //skip genesis currency::block_direct_data_entry bdde = AUTO_VAL_INIT(bdde); std::shared_ptr bptr(new block_extended_info()); - bptr->bl = b->b; + bptr->bl = b->b; bdde.block_ptr = bptr; + std::shared_ptr coinbase_tx_ptr(new transaction_chain_entry()); + coinbase_tx_ptr->m_global_output_indexes = get_tx_gindex_from_map(currency::get_transaction_hash(b->b.miner_tx), txs_outs); + bdde.coinbase_ptr = coinbase_tx_ptr; for (auto& tx : b->m_transactions) { std::shared_ptr tx_ptr(new transaction_chain_entry()); tx_ptr->tx = tx; + tx_ptr->m_global_output_indexes = get_tx_gindex_from_map(currency::get_transaction_hash(tx), txs_outs); bdde.txs_ptr.push_back(tx_ptr); } @@ -826,7 +828,7 @@ uint64_t test_generator::get_base_reward_for_next_block(const crypto::hash& head auto it = m_blocks_info.find(head_id); if (it == m_blocks_info.end()) return 0; - return get_base_block_reward(!pow, it->second.already_generated_coins, get_block_height(it->second.b)); + return get_base_block_reward(get_block_height(it->second.b)); } bool test_generator::find_nounce(currency::block& blk, std::vector& blocks, wide_difficulty_type dif, uint64_t height) const @@ -2317,15 +2319,25 @@ bool check_ring_signature_at_gen_time(const std::vector& event bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c) { - std::shared_ptr ptce = c.get_blockchain_storage().get_tx_chain_entry(tx_id); - if (!ptce) - return false; + transaction tx_local; + const transaction* ptx = &tx_local; - for (size_t i = 0; i < ptce->tx.vin.size(); ++i) + std::shared_ptr ptce = c.get_blockchain_storage().get_tx_chain_entry(tx_id); + if (ptce) { - auto& input = ptce->tx.vin[i]; + ptx = &ptce->tx; + } + else + { + if (!c.get_tx_pool().get_transaction(tx_id, tx_local)) + return false; + } + + for (size_t i = 0; i < ptx->vin.size(); ++i) + { + auto& input = ptx->vin[i]; const std::vector& key_offsets = get_key_offsets_from_txin_v(input); - CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << key_offsets.size() - 1 << ", expected is " << mixin); + CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " ring size is " << key_offsets.size() << ", mixin count is " << key_offsets.size() - 1 << ", expected mixin count is " << mixin); } return true; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 690bdef5..087e0c3a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2022 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -9,7 +9,8 @@ #include #include -#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on pseudorandom number generator manupulations for tests +#define USE_INSECURE_RANDOM_RPNG_ROUTINES // turns on pseudorandom number generator manupulations for tests +#define TX_POOL_USE_UNSECURE_TEST_FUNCTIONS // turns on special tests functions of tx pool #include "currency_core/currency_basic.h" #include "currency_core/currency_core.h" @@ -752,6 +753,17 @@ bool shuffle_source_entries(std::vector& sources); // one output will be created for each destination entry and one additional output to add up to old coinbase total amount bool replace_coinbase_in_genesis_block(const std::vector& destinations, test_generator& generator, std::vector& events, currency::block& genesis_block); +template +const std::vector& get_tx_gindex_from_map(const crypto::hash& tx_id, const t_map& id_to_vector) +{ + auto it_global_indexes = id_to_vector.find(tx_id); + if (it_global_indexes == id_to_vector.end()) + { + throw std::runtime_error("TX ID NOT FOUND"); + } + return it_global_indexes->second; +} + //-------------------------------------------------------------------------- template auto do_check_tx_verification_context(const currency::tx_verification_context& tvc, bool tx_added, size_t event_index, const currency::transaction& tx, t_test_class& validator, int) @@ -994,6 +1006,7 @@ namespace crypto { } } + inline uint64_t get_sources_total_amount(const std::vector& s) { uint64_t result = 0; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 31d317f7..71bb8fe1 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -977,6 +977,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(multisig_out_make_and_spent_in_altchain); GENERATE_AND_PLAY(multisig_unconfirmed_transfer_and_multiple_scan_pool_calls); GENERATE_AND_PLAY(multisig_out_spent_in_altchain_case_b4); + GENERATE_AND_PLAY(multisig_n_participants_seq_signing); GENERATE_AND_PLAY(ref_by_id_basics); GENERATE_AND_PLAY(ref_by_id_mixed_inputs_types); @@ -1089,6 +1090,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(wallet_rpc_exchange_suite, "3,4"); GENERATE_AND_PLAY(wallet_chain_switch_with_spending_the_same_ki); GENERATE_AND_PLAY(wallet_sending_to_integrated_address); + GENERATE_AND_PLAY_HF(block_template_blacklist_test, "4-*"); + // GENERATE_AND_PLAY(emission_test); // simulate 1 year of blockchain, too long run (1 y ~= 1 hr), by demand only // LOG_ERROR2("print_reward_change_first_blocks.log", currency::print_reward_change_first_blocks(525601).str()); // outputs first 1 year of blocks' rewards (simplier) @@ -1244,7 +1247,10 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(hard_fork_2_incorrect_alias_update); // HF4 - // GENERATE_AND_PLAY_HF(hard_fork_4_consolidated_txs, "4"); TODO, doesn't work atm -- sowle + GENERATE_AND_PLAY_HF(hard_fork_4_consolidated_txs, "3-*"); + GENERATE_AND_PLAY_HF(hardfork_4_wallet_transfer_with_mandatory_mixins, "3-*"); + GENERATE_AND_PLAY(hardfork_4_wallet_sweep_bare_outs); + GENERATE_AND_PLAY_HF(hardfork_4_pop_tx_from_global_index, "4-*"); // atomics GENERATE_AND_PLAY(atomic_simple_test); diff --git a/tests/core_tests/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index 68565f8d..d0b88650 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -708,7 +708,7 @@ bool gen_no_attchments_in_coinbase::init_config_set_cp(currency::core& c, size_t // different checkpoints due to different block versions for different hardforks -> different hashes if (crc.is_hardfork_active_for_height(ZANO_HARDFORK_03, 11) && !crc.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, 11)) { - m_checkpoints.add_checkpoint(12, "4e6055dda442e04b2feb70bc7245584742604e8515b8d2e1c3d46c26f758d59f"); + m_checkpoints.add_checkpoint(12, "70fbbd33d88ccaa26f9fe64d102bcff2572494009339de9fab7158a40633fbb5"); } else { diff --git a/tests/core_tests/hard_fork_2.cpp b/tests/core_tests/hard_fork_2.cpp index b4700f0e..173bb2b9 100644 --- a/tests/core_tests/hard_fork_2.cpp +++ b/tests/core_tests/hard_fork_2.cpp @@ -1217,23 +1217,28 @@ bool hard_fork_2_alias_update_using_old_tx::c1(currency::core& c, s // // Send tx and print it's blob // this code was used to generate old-style tx in develop branch, commit 36eabb916b95c7db06bdfb5d34d9820bd94b82df + // UPD: 2024-03-15 tx blob was updated as of commit f538fc3a7612e7ce86556a1cd2602400f6b4e9aa // - transaction tx_upd = AUTO_VAL_INIT(tx_upd); - alice_wlt->request_alias_update(ai_upd, tx_upd, TESTS_DEFAULT_FEE, 0); - std::string tx_upd_hex = epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(tx_upd)); - LOG_PRINT_L0("tx upd: " << ENDL << tx_upd_hex); + { + transaction tx_upd = AUTO_VAL_INIT(tx_upd); + alice_wlt->request_alias_update(ai_upd, tx_upd, TESTS_DEFAULT_FEE, 0); + std::string tx_upd_hex = epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(tx_upd)); + LOG_PRINT_L0("tx upd: " << ENDL << tx_upd_hex); + } */ // use prepared tx blob - std::string tx_upd_hex("01010180988be49903011a0100000000000000ff593921b61d52e818058ef881764383fe9fb0cf4512da460daf477e3a216144000180d0dbc3f402037c2e68e9c60914d369dc53fcc91522dcec284e1b7c1604ccc3d8fcf67e2da0790003140a616c696365616c696365efdcf084d7af59e0d68e8bda3ff4514a02d812ccfd6a09f69811c5a6a1b035c9b1b2395fcd84283d02e2fc4f37395ac9d8f01ff3f1ad1b94a26aaf1c76bb39d00c48656c6c6f206d696e657221000118faf7b79730fd6c1ec5c918891e264e959749a0d8eebc7997b503bd185ef100fd9e0150c175e0f048ee729137ceed035e9145d7857eae9ab786dd319fe6520b16b0cc0d6a8766cd098fe5a42875243789cc3b4bf66ee0c12ff6ef2fc179d4cef50b0288360101f3012451b07e58e742c020d68e8ff6db2905fa3aad78806ba80b16f3c861ee09f79817ea1ec36b0e30c7be5412daf59bcbc34410461a0d126de4a2dd897ecf0200"); + std::string tx_upd_hex("01010180988be49903011a01000000000000007bdeaff6ac11597e9fe397349dd2ecfcd6880cda9bbedaa67f759a5b95ba2ed9000180d0dbc3f4020335db05c510daddcb82257be70789650007ad2d567092ec1eb2a38a93ef8fa0070004140a616c696365616c696365efdcf084d7af59e0d68e8bda3ff4514a02d812ccfd6a09f69811c5a6a1b035c9b1b2395fcd84283d02e2fc4f37395ac9d8f01ff3f1ad1b94a26aaf1c76bb39d00c48656c6c6f206d696e6572210001c2537b8203e9c9981b87a3c63cc0c63e4ccc14ddd0309fdcee94664e4b3bc307ee438e90bb5720b7ac7cbec38fcac9c7aa5b9df02abd2fa1a58c580f9b3eaf0516116e8d89a67440aad0922a7e5dd88870e6f0af792a7af201ee21effd90cc6c7f1700000b02f37101016ed0997a3fe0c6bc47ffde14599a77697ba95c5c1489b65e5e54854146079f06569313d921f19ed2848cdccc8bad7f75fb46fc1608912365f569b7690703010b00"); std::string tx_upd_blob; r = epee::string_tools::parse_hexstr_to_binbuff(tx_upd_hex, tx_upd_blob); CHECK_AND_ASSERT_MES(r, false, "parse_hexstr_to_binbuff failed"); - //r = t_unserializable_object_from_blob(tx_upd, tx_upd_blob); - //CHECK_AND_ASSERT_MES(r, false, "t_unserializable_object_from_blob failed"); + crypto::hash tx_upd_hash{}; + transaction tx_upd{}; + CHECK_AND_ASSERT_MES(parse_and_validate_tx_from_blob(tx_upd_blob, tx_upd, tx_upd_hash), false, "parse_tx_form_blob failed"); + //LOG_PRINT_L0("tx_upd: " << tx_upd_hash << ", json:" << ENDL << obj_to_json_str(tx_upd)); tx_verification_context tvc = AUTO_VAL_INIT(tvc); - r = c.handle_incoming_tx(tx_upd_blob, tvc, false); + r = c.handle_incoming_tx(tx_upd, tvc, false, tx_upd_hash); CHECK_AND_ASSERT_MES(r, false, "handle_incoming_tx failed"); diff --git a/tests/core_tests/hard_fork_4.cpp b/tests/core_tests/hard_fork_4.cpp index 2c4a7fd3..396ac2ec 100644 --- a/tests/core_tests/hard_fork_4.cpp +++ b/tests/core_tests/hard_fork_4.cpp @@ -89,6 +89,10 @@ bool hard_fork_4_consolidated_txs::generate(std::vector& event ADD_CUSTOM_EVENT(events, tx_0b); MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list({tx_0a, tx_0b})); + size_t dhc = count_type_in_variant_container(tx_0b.extra); + CHECK_AND_ASSERT_MES(dhc == tx_0b.vout.size(), false, "unexpected derivation hints count: " << dhc); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // check Alice's balance @@ -100,7 +104,7 @@ bool hard_fork_4_consolidated_txs::generate(std::vector& event CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice", alice_amount, 0, alice_amount, 0, 0), false, ""); uint64_t miner_amount = MK_TEST_COINS(60); - uint64_t bob_amount = miner_amount + alice_amount - TX_DEFAULT_FEE; + m_bob_amount = miner_amount + alice_amount - TX_DEFAULT_FEE; // Consolidated tx (TX_FLAG_SIGNATURE_MODE_SEPARATE). @@ -119,17 +123,33 @@ bool hard_fork_4_consolidated_txs::generate(std::vector& event std::vector destinations; if (miner_change != 0) destinations.push_back(tx_destination_entry(miner_change, miner_acc.get_public_address())); - destinations.push_back(tx_destination_entry(bob_amount, bob_acc.get_public_address())); + destinations.push_back(tx_destination_entry(m_bob_amount, bob_acc.get_public_address())); add_flags_to_all_destination_entries(tx_destination_entry_flags::tdef_explicit_native_asset_id, destinations); r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_extra, empty_attachment, tx_1, get_tx_version_from_events(events), one_time_secret_key, 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, TX_DEFAULT_FEE, gen_context); CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + dhc = count_type_in_variant_container(tx_1.extra); + CHECK_AND_ASSERT_MES(dhc == destinations.size(), false, "unexpected derivation hints count: " << dhc); + // partially completed tx_1 shouldn't be accepted - //DO_CALLBACK(events, "mark_invalid_tx"); - ADD_CUSTOM_EVENT(events, tx_1); - MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner_acc, tx_1); + + // now we added a balance check to tx_memory_pool::add_tx() for post-HF4 txs, so the behaviour is the same -- partially completed consolidated tx won't be added to the pool -- sowle + // (subject to change in future) + + //if (m_post_hf4_zarcanum) + //{ + // ADD_CUSTOM_EVENT(events, tx_1); + // DO_CALLBACK(events, "mark_invalid_block"); + // MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner_acc, tx_1); + // DO_CALLBACK(events, "clear_tx_pool"); + //} + //else + //{ + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + //} } @@ -147,23 +167,25 @@ bool hard_fork_4_consolidated_txs::generate(std::vector& event 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, 0 /* note zero fee here */, gen_context); CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + size_t dhc_2 = count_type_in_variant_container(tx_1.extra); + CHECK_AND_ASSERT_MES(dhc_2 == dhc, false, "unexpected derivation hints count: " << dhc_2); + ADD_CUSTOM_EVENT(events, tx_1); } MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1); - //std::shared_ptr bob_wlt; - //r = generator.init_test_wallet(bob_acc, get_block_hash(blk_0), bob_wlt); - //CHECK_AND_ASSERT_MES(r, false, "init_test_wallet failed"); - //r = generator.refresh_test_wallet(events, bob_wlt.get(), get_block_hash(blk_2), 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2); - //CHECK_AND_ASSERT_MES(r, false, "refresh_test_wallet failed"); - //CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", bob_amount, 0, 0, 0, 0), false, ""); - + DO_CALLBACK(events, "c1"); return true; } bool hard_fork_4_consolidated_txs::c1(currency::core& c, size_t ev_index, const std::vector& events) { + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + bob_wlt->refresh(); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", m_bob_amount, 0, 0, 0, 0), false, ""); + return true; } @@ -220,4 +242,257 @@ bool hardfork_4_explicit_native_ids_in_outs::c1(currency::core& c, size_t ev_ind { return true; } -*/ \ No newline at end of file +*/ + +//------------------------------------------------------------------------------ + +hardfork_4_wallet_transfer_with_mandatory_mixins::hardfork_4_wallet_transfer_with_mandatory_mixins() +{ + REGISTER_CALLBACK_METHOD(hardfork_4_wallet_transfer_with_mandatory_mixins, c1); + REGISTER_CALLBACK_METHOD(hardfork_4_wallet_transfer_with_mandatory_mixins, configure_core); +} + +bool hardfork_4_wallet_transfer_with_mandatory_mixins::configure_core(currency::core& c, size_t ev_index, const std::vector& events) +{ + test_chain_unit_enchanced::configure_core(c, ev_index, events); // call default + currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config(); + pc.hf4_minimum_mixins = CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE; + c.get_blockchain_storage().set_core_runtime_config(pc); + return true; +} + +bool hardfork_4_wallet_transfer_with_mandatory_mixins::generate(std::vector& events) const +{ + /* Test outline: make sure that after HF4 a normal transfer with CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE decoys goes normal. + * (It should also work prior to HF4.) + */ + + bool r = false; + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // necessary for the test to be run by GENERATE_AND_PLAY_HF + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + MAKE_TX(events, tx_1, miner_acc, alice_acc, MK_TEST_COINS(10), blk_0r); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_1); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool hardfork_4_wallet_transfer_with_mandatory_mixins::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + alice_wlt->refresh(); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(10), 0, 0, 0, 0), false, ""); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + alice_wlt->refresh(); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(10), 0, MK_TEST_COINS(10), 0, 0), false, ""); + + alice_wlt->transfer(MK_TEST_COINS(9), m_accounts[BOB_ACC_IDX].get_public_address()); + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(9), 0, 0, 0, 0), false, ""); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", 0, 0, 0, 0, 0), false, ""); + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(9), 0, MK_TEST_COINS(9), 0, 0), false, ""); + + return true; +} + +//------------------------------------------------------------------------------ + +hardfork_4_wallet_sweep_bare_outs::hardfork_4_wallet_sweep_bare_outs() +{ + REGISTER_CALLBACK_METHOD(hardfork_4_wallet_sweep_bare_outs, c1); + + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 10); +} + +bool hardfork_4_wallet_sweep_bare_outs::generate(std::vector& events) const +{ + // Test idea: make sure wallet2::sweep_bare_outs works well even if there's not enough outputs to mix + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + + // rebuild genesis miner tx + std::vector destinations; + destinations.emplace_back(MK_TEST_COINS(23), alice_acc.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(23), bob_acc.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(55), bob_acc.get_public_address()); // this output is unique and doesn't have decoys + for (size_t i = 0; i < 10; ++i) + destinations.emplace_back(MK_TEST_COINS(23), miner_acc.get_public_address()); // decoys (later Alice will spend her output using mixins) + destinations.emplace_back(COIN, miner_acc.get_public_address()); // leftover amount will be also send to the last destination + CHECK_AND_ASSERT_MES(replace_coinbase_in_genesis_block(destinations, generator, events, blk_0), false, ""); + + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool hardfork_4_wallet_sweep_bare_outs::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(23), UINT64_MAX, MK_TEST_COINS(23), 0, 0), false, ""); + + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(23 + 55), UINT64_MAX, MK_TEST_COINS(23 + 55), 0, 0), false, ""); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + // 1. Try to sweep bare out for Alice (enough decoys to mix with) + std::vector tids_grouped_by_txs; + CHECK_AND_ASSERT_MES(alice_wlt->get_bare_unspent_outputs_stats(tids_grouped_by_txs), false, ""); + size_t total_txs_sent = 0; + uint64_t total_amount_sent = 0; + uint64_t total_fee_spent = 0; + uint64_t total_bare_outs_sent = 0; + CHECK_AND_ASSERT_MES(alice_wlt->sweep_bare_unspent_outputs(m_accounts[ALICE_ACC_IDX].get_public_address(), tids_grouped_by_txs, total_txs_sent, total_amount_sent, total_fee_spent, total_bare_outs_sent), false, ""); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_txs_sent, 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_amount_sent, MK_TEST_COINS(23)); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_fee_spent, TESTS_DEFAULT_FEE); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_bare_outs_sent, 1); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + std::list txs; + c.get_pool_transactions(txs); + auto& tx = txs.back(); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(CURRENCY_DEFAULT_DECOY_SET_SIZE, get_transaction_hash(tx), c), false, ""); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(23) - TESTS_DEFAULT_FEE, UINT64_MAX, MK_TEST_COINS(23) - TESTS_DEFAULT_FEE, 0, 0), false, ""); + + + // 2. Try to sweep bare out for Bob (not enough decoys to mix with) + tids_grouped_by_txs.clear(); + CHECK_AND_ASSERT_MES(bob_wlt->get_bare_unspent_outputs_stats(tids_grouped_by_txs), false, ""); + CHECK_AND_ASSERT_MES(bob_wlt->sweep_bare_unspent_outputs(m_accounts[BOB_ACC_IDX].get_public_address(), tids_grouped_by_txs, total_txs_sent, total_amount_sent, total_fee_spent, total_bare_outs_sent), false, ""); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_txs_sent, 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_amount_sent, MK_TEST_COINS(23 + 55)); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_fee_spent, TESTS_DEFAULT_FEE); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_bare_outs_sent, 2); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + txs.clear(); + c.get_pool_transactions(txs); + auto& tx2 = txs.back(); + CHECK_V_EQ_EXPECTED_AND_ASSERT(tx2.vin.size(), 2); + + const txin_to_key &input_with_enough_decoys = boost::get(tx2.vin[0]).amount == MK_TEST_COINS(23) ? boost::get(tx2.vin[0]) : boost::get(tx2.vin[1]); + const txin_to_key &input_with_not_enough_decoys = boost::get(tx2.vin[0]).amount == MK_TEST_COINS(23) ? boost::get(tx2.vin[1]) : boost::get(tx2.vin[0]); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(input_with_enough_decoys.key_offsets.size(), CURRENCY_DEFAULT_DECOY_SET_SIZE + 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(input_with_not_enough_decoys.key_offsets.size(), 1); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(23 + 55) - TESTS_DEFAULT_FEE, UINT64_MAX, MK_TEST_COINS(23 + 55) - TESTS_DEFAULT_FEE, 0, 0), false, ""); + + return true; +} + +//------------------------------------------------------------------------------ + +hardfork_4_pop_tx_from_global_index::hardfork_4_pop_tx_from_global_index() +{ + REGISTER_CALLBACK_METHOD(hardfork_4_pop_tx_from_global_index, c1); +} + +bool hardfork_4_pop_tx_from_global_index::generate(std::vector& events) const +{ + // Test idea: make sure that pop_transaction_from_global_index works for tx_out_zarcanum as well (m_db_outputs is consistent after pop_transaction_from_global_index() call) + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + MAKE_NEXT_BLOCK(events, blk_1a, blk_0r, miner_acc); // blk_1a will be the alt chain + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_1a)); // make sure now it's the main chain + + MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc); + + MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); // this should trigger chain switching + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_2)); // make sure it did + + // during switching to the alternative chain pop_block_from_blockchain() -> ... -> pop_transaction_from_global_index() will be called + // but abort_transaction() will not, meaning m_db_outputs will be in incorrect state, if pop_transaction_from_global_index() hasn't properly pop all outs + // this will be checked later in c1 + + DO_CALLBACK(events, "c1"); + return true; +} + +bool hardfork_4_pop_tx_from_global_index::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + auto& bcs = c.get_blockchain_storage(); + bool r = false; + + //currency::outs_index_stat outs_stat{}; + //bcs.get_outs_index_stat(outs_stat); // 24 - bad, 22 - good + + COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_BY_AMOUNT::response res; + COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_BY_AMOUNT::request req; + req.amount = 0; + req.i = 22; + CHECK_AND_ASSERT_MES(!bcs.get_global_index_details(req, res), false, "gindex 22 exists which is unexpected"); + req.i = 21; + CHECK_AND_ASSERT_MES(bcs.get_global_index_details(req, res), false, "gindex 21 does not exist which is unexpected"); + + return true; +} diff --git a/tests/core_tests/hard_fork_4.h b/tests/core_tests/hard_fork_4.h index bf12419f..5e9014b8 100644 --- a/tests/core_tests/hard_fork_4.h +++ b/tests/core_tests/hard_fork_4.h @@ -13,6 +13,7 @@ struct hard_fork_4_consolidated_txs : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); mutable bool m_post_hf4_zarcanum = false; + mutable uint64_t m_bob_amount = 0; }; @@ -24,3 +25,27 @@ struct hardfork_4_explicit_native_ids_in_outs : public wallet_test mutable uint64_t m_alice_initial_balance = 0; }; + + +struct hardfork_4_wallet_transfer_with_mandatory_mixins : public wallet_test +{ + hardfork_4_wallet_transfer_with_mandatory_mixins(); + bool configure_core(currency::core& c, size_t ev_index, const std::vector& events); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; + + +struct hardfork_4_wallet_sweep_bare_outs : public wallet_test +{ + hardfork_4_wallet_sweep_bare_outs(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; + +struct hardfork_4_pop_tx_from_global_index : public wallet_test +{ + hardfork_4_pop_tx_from_global_index(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; diff --git a/tests/core_tests/ionic_swap_tests.cpp b/tests/core_tests/ionic_swap_tests.cpp index c08634a1..9942b2f5 100644 --- a/tests/core_tests/ionic_swap_tests.cpp +++ b/tests/core_tests/ionic_swap_tests.cpp @@ -178,7 +178,6 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve // Alice wants to trade with Bob, to exchange 10.0 TCT to 0.5 ZANO view::ionic_swap_proposal_info proposal_details = AUTO_VAL_INIT(proposal_details); proposal_details.fee_paid_by_a = TESTS_DEFAULT_FEE; - proposal_details.mixins = c.get_blockchain_storage().get_core_runtime_config().hf4_minimum_mixins; proposal_details.to_finalizer.push_back(view::asset_funds{ asset_id , assets_to_exchange }); proposal_details.to_initiator.push_back(view::asset_funds{ currency::native_coin_asset_id , native_coins_to_exchange }); @@ -192,7 +191,6 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve if (proposal_decoded_info.to_finalizer != proposal_details.to_finalizer || proposal_decoded_info.to_initiator != proposal_details.to_initiator || proposal_decoded_info.fee_paid_by_a != proposal_details.fee_paid_by_a - || proposal_decoded_info.mixins != proposal_details.mixins ) { CHECK_AND_ASSERT_MES(false, false, "proposal actual and proposals decoded mismatch"); @@ -233,7 +231,6 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve //now Alice want to trade with Bob, to send 0.5 ZANO and get 10.0 TCT in exchange view::ionic_swap_proposal_info proposal_details = AUTO_VAL_INIT(proposal_details); proposal_details.fee_paid_by_a = TESTS_DEFAULT_FEE; - proposal_details.mixins = c.get_blockchain_storage().get_core_runtime_config().hf4_minimum_mixins; proposal_details.to_finalizer.push_back(view::asset_funds{ currency::native_coin_asset_id , native_coins_to_exchange }); proposal_details.to_initiator.push_back(view::asset_funds{ asset_id , assets_to_exchange }); @@ -247,7 +244,6 @@ bool ionic_swap_basic_test::c1(currency::core& c, size_t ev_index, const std::ve if (proposal_decoded_info.to_finalizer != proposal_details.to_finalizer || proposal_decoded_info.to_initiator != proposal_details.to_initiator || proposal_decoded_info.fee_paid_by_a != proposal_details.fee_paid_by_a - || proposal_decoded_info.mixins != proposal_details.mixins ) { CHECK_AND_ASSERT_MES(false, false, "proposal actual and proposals decoded mismatch"); @@ -387,7 +383,6 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const proposal_details.to_initiator.push_back(view::asset_funds{ asset_id, adb.total_max_supply }); proposal_details.to_finalizer.push_back(view::asset_funds{ native_coin_asset_id, MK_TEST_COINS(20) }); proposal_details.fee_paid_by_a = MK_TEST_COINS(1); - proposal_details.mixins = c.get_blockchain_storage().get_core_runtime_config().hf4_minimum_mixins; tools::wallet_public::ionic_swap_proposal proposal{}; alice_wlt->create_ionic_swap_proposal(proposal_details, m_accounts[BOB_ACC_IDX].get_public_address(), proposal); @@ -397,8 +392,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const CHECK_AND_ASSERT_MES( proposal_decoded_info.to_finalizer == proposal_details.to_finalizer && proposal_decoded_info.to_initiator == proposal_details.to_initiator && - proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a && - proposal_decoded_info.mixins == proposal_details.mixins, + proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a, false, "actual and decoded proposal mismatch \nproposal_decoded_info: " << epee::serialization::store_t_to_json(proposal_decoded_info) << "\nproposal_details" << epee::serialization::store_t_to_json(proposal_details)); @@ -429,7 +423,6 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const proposal_details.to_initiator.push_back(view::asset_funds{ asset_id, adb.total_max_supply }); proposal_details.to_finalizer.push_back(view::asset_funds{ native_coin_asset_id, MK_TEST_COINS(20) }); proposal_details.fee_paid_by_a = MK_TEST_COINS(1); - proposal_details.mixins = c.get_blockchain_storage().get_core_runtime_config().hf4_minimum_mixins; proposal = tools::wallet_public::ionic_swap_proposal{}; carol_wlt->create_ionic_swap_proposal(proposal_details, m_accounts[ALICE_ACC_IDX].get_public_address(), proposal); @@ -439,8 +432,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const CHECK_AND_ASSERT_MES( proposal_decoded_info.to_finalizer == proposal_details.to_finalizer && proposal_decoded_info.to_initiator == proposal_details.to_initiator && - proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a && - proposal_decoded_info.mixins == proposal_details.mixins, + proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a, false, "actual and decoded proposal mismatch"); currency::transaction tx_is2{}; diff --git a/tests/core_tests/multiassets_test.cpp b/tests/core_tests/multiassets_test.cpp index 28dda0e5..240b3bb3 100644 --- a/tests/core_tests/multiassets_test.cpp +++ b/tests/core_tests/multiassets_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -142,9 +142,9 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(asset_info2.meta_info == asset_info.meta_info, false, "Failed to find needed asset in result balances"); - //test emmit function + //test emit function //use same destinations as we used before - miner_wlt->emmit_asset(asset_id, destinations, tx); + miner_wlt->emit_asset(asset_id, destinations, tx); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); @@ -186,7 +186,20 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); asset_info.meta_info = "{\"some2\": \"info2\"}"; + r = false; + try + { + miner_wlt->update_asset(asset_id, asset_info, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "Test failed, broken ownership passed"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); miner_wlt->update_asset(asset_id, asset_info, tx); + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 2); CHECK_AND_ASSERT_MES(!r, false, "Test failed, broken ownership passed"); c.get_tx_pool().purge_transactions(); @@ -202,25 +215,54 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); asset_info.ticker = "XXX"; + r = false; + try + { + miner_wlt->update_asset(asset_id, asset_info, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "update_asset succeeded, but this shouldn't happened"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); miner_wlt->update_asset(asset_id, asset_info, tx); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); c.get_tx_pool().purge_transactions(); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not added + // check update_asset() with modified 'full_name' r = c.get_blockchain_storage().get_asset_info(asset_id, asset_info); CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); asset_info.full_name = "XXX"; + r = false; + try + { + miner_wlt->update_asset(asset_id, asset_info, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "update_asset succeeded, but this shouldn't happened"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); miner_wlt->update_asset(asset_id, asset_info, tx); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); c.get_tx_pool().purge_transactions(); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not added miner_wlt->refresh(); @@ -229,13 +271,27 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(r, false, "Failed to get_asset_info"); asset_info.decimal_point = 3; + r = false; + try + { + miner_wlt->update_asset(asset_id, asset_info, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "update_asset succeeded, but this shouldn't happened"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); miner_wlt->update_asset(asset_id, asset_info, tx); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); c.get_tx_pool().purge_transactions(); miner_wlt->refresh(); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not added // check update_asset() with modified 'owner' @@ -252,20 +308,35 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v //miner_wlt->refresh(); - // check emmit_asset() with modified 'current_supply' + // check emit_asset() with modified 'current_supply' miner_wlt->get_debug_events_dispatcher().SUBSCIRBE_DEBUG_EVENT([&](const wde_construct_tx_handle_asset_descriptor_operation_before_seal& o) { o.pado->descriptor.current_supply += 1000000; }); - //test emmit function but re-adjust current_supply to wrong amount - miner_wlt->emmit_asset(asset_id, destinations, tx); + //test emit function but re-adjust current_supply to wrong amount + r = false; + try + { + miner_wlt->emit_asset(asset_id, destinations, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "emit_asset succeeded, but this shouldn't happened"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); + miner_wlt->emit_asset(asset_id, destinations, tx); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); c.get_tx_pool().purge_transactions(); miner_wlt->refresh(); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not added + //------------------- tests that trying to break stuff ------------------- //test burn that burns more than tx has miner_wlt->get_debug_events_dispatcher().UNSUBSCRIBE_ALL(); @@ -276,11 +347,24 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v }); + r = false; + try + { + miner_wlt->burn_asset(asset_id, 10000000000000, tx); + } + catch(tools::error::tx_rejected&) + { + r = true; + } + CHECK_AND_ASSERT_MES(r, false, "burn_asset succeeded, but this shouldn't happened"); + + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(true); miner_wlt->burn_asset(asset_id, 10000000000000, tx); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); r = mine_next_pow_block_in_playtime(miner_wlt->get_account().get_public_address(), c); CHECK_AND_ASSERT_MES(!r, false, "block with a bad tx was unexpectedly mined"); CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); // make sure tx was not confirmed + c.get_tx_pool().unsecure_disable_tx_validation_on_addition(false); c.get_tx_pool().purge_transactions(); miner_wlt->refresh(); miner_wlt->get_debug_events_dispatcher().UNSUBSCRIBE_ALL(); @@ -305,7 +389,7 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v uint64_t balance_alice_asset = alice_wlt->balance(asset_id); uint64_t balance_miner_asset = miner_wlt->balance(asset_id); - alice_wlt->emmit_asset(asset_id, destinations, tx); + alice_wlt->emit_asset(asset_id, destinations, tx); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); @@ -315,7 +399,7 @@ bool multiassets_basic_test::c1(currency::core& c, size_t ev_index, const std::v CHECK_AND_ASSERT_MES(miner_wlt->balance(asset_id) == balance_miner_asset + destinations[0].amount, false, "Miner balance wrong"); CHECK_AND_ASSERT_MES(alice_wlt->balance(asset_id) == balance_alice_asset + destinations[1].amount, false, "Alice balance wrong"); - //TODO: attempt to emmmit from old key, attempt to emmit from more then max supply + //TODO: attempt to emmmit from old key, attempt to emit from more then max supply return true; } diff --git a/tests/core_tests/multisig_wallet_tests.cpp b/tests/core_tests/multisig_wallet_tests.cpp index fd327b76..2801b1fc 100644 --- a/tests/core_tests/multisig_wallet_tests.cpp +++ b/tests/core_tests/multisig_wallet_tests.cpp @@ -23,7 +23,7 @@ using namespace currency; #define TMP_LOG_RESTORE #endif -void exception_handler(){} +static void exception_handler(){} //============================================================================================================================== // helper routine: creates multisig-spending tx using a wallet and keys of other ms-participants, then sends it to the core proxy @@ -1638,7 +1638,7 @@ multisig_and_checkpoints::multisig_and_checkpoints() bool multisig_and_checkpoints::set_cp(currency::core& c, size_t ev_index, const std::vector& events) { currency::checkpoints checkpoints; - checkpoints.add_checkpoint(15, "6f9194c144afd73077478e7f04e947c50160b5673558e6f696a4f662a3ca261e"); + checkpoints.add_checkpoint(15, "fda3e645fbfd0f4852aa68e6ad021c9005c9faf2d7ba6b1b3c8e24efb9c0e8d0"); c.set_checkpoints(std::move(checkpoints)); return true; diff --git a/tests/core_tests/pos_validation.cpp b/tests/core_tests/pos_validation.cpp index 0106b050..4f444e69 100644 --- a/tests/core_tests/pos_validation.cpp +++ b/tests/core_tests/pos_validation.cpp @@ -1085,7 +1085,7 @@ bool pos_minting_tx_packing::c1(currency::core& c, size_t ev_index, const std::v m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked ), false, ""); - alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(m_pos_mint_packing_size + 1, m_pos_mint_packing_size + 1); // +1 because previous implementation () had an error with this limit + alice_wlt->set_defragmentation_tx_settings(true, m_pos_mint_packing_size + 1, m_pos_mint_packing_size + 1); // +1 because previous implementation () had an error with this limit // no coinbase tx outputs should be packed r = alice_wlt->try_mint_pos(); diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 7382060b..37acebec 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -939,7 +939,25 @@ bool gen_crypted_attachments::check_crypted_tx(currency::core& c, size_t ev_inde std::vector at; bool r = currency::decrypt_payload_items(true, *ptx_from_bc, bob_acc.get_keys(), at); CHECK_EQ(r, true); - CHECK_EQ(at.size(), 8); // custom attachments: 1) tx_payer, 2) tx_comment, 3) std::string; system attachments: 4) tx_crypto_checksum; system extra: 5) tx pub key, 6) extra_attachment_info, 7) etc_tx_flags16_t + if (at.size() != 16) + { + std::stringstream ss; + for(auto& el : at) + ss << " " << el.type().name() << ENDL; + LOG_PRINT_RED("at.size() = " << at.size() << " : " << ENDL << ss.str(), LOG_LEVEL_0); + + // expected items: + // public_key + // etc_tx_flags16_t + // tx_derivation_hint x 10 + // extra_attachment_info + // tx_payer + // tx_comment + // tx_crypto_checksum + // + // total: 16 + CHECK_AND_ASSERT_MES(false, false, "unexpected number of decrypted items"); + } currency::tx_payer decrypted_pr = AUTO_VAL_INIT(decrypted_pr); r = get_type_in_variant_container(at, decrypted_pr); diff --git a/tests/core_tests/wallet_test_core_proxy.cpp b/tests/core_tests/wallet_test_core_proxy.cpp index e303bc8c..db0fd0fc 100644 --- a/tests/core_tests/wallet_test_core_proxy.cpp +++ b/tests/core_tests/wallet_test_core_proxy.cpp @@ -99,20 +99,30 @@ bool wallet_test_core_proxy::call_COMMAND_RPC_GET_BLOCKS_FAST(const currency::CO { auto b = m_blocks[i]; currency::block_complete_entry bce = AUTO_VAL_INIT(bce); - for (auto tx : b->m_transactions) + bce.tx_global_outs.resize(b->m_transactions.size()); + bce.coinbase_global_outs = get_tx_gindex(currency::get_transaction_hash(b->b.miner_tx)); + for (size_t j = 0; j != b->m_transactions.size(); j++) + { + const auto& tx = b->m_transactions[j]; bce.txs.push_back(tx_to_blob(tx)); + bce.tx_global_outs[j].v = get_tx_gindex(currency::get_transaction_hash(tx)); + } bce.block = block_to_blob(b->b); rsp.blocks.push_back(bce); } rsp.current_height = m_blocks.size(); return true; } + +const std::vector& wallet_test_core_proxy::get_tx_gindex(const crypto::hash& tx_id) +{ + return get_tx_gindex_from_map(tx_id, m_txs_outs); +} bool wallet_test_core_proxy::call_COMMAND_RPC_GET_BLOCKS_DIRECT(const currency::COMMAND_RPC_GET_BLOCKS_DIRECT::request& rqt, currency::COMMAND_RPC_GET_BLOCKS_DIRECT::response& rsp) { currency::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); req.block_ids = rqt.block_ids; req.minimum_height = rqt.minimum_height; - req.need_global_indexes = rqt.need_global_indexes; currency::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); bool r = this->call_COMMAND_RPC_GET_BLOCKS_FAST(req, res); rsp.status = res.status; diff --git a/tests/core_tests/wallet_test_core_proxy.h b/tests/core_tests/wallet_test_core_proxy.h index 322a7bb3..052a4f70 100644 --- a/tests/core_tests/wallet_test_core_proxy.h +++ b/tests/core_tests/wallet_test_core_proxy.h @@ -26,6 +26,9 @@ struct wallet_test_core_proxy : public tools::i_core_proxy virtual bool call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN(const currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res) override; virtual bool get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id) override; + const std::vector& get_tx_gindex(const crypto::hash& tx_id); + + test_generator::tx_global_indexes m_txs_outs; test_generator::blockchain_vector m_blocks; test_generator::outputs_index m_oi; diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index 373ac3a0..bfadc1fb 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -3440,8 +3440,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde // 1. Try to defragment the same UTXO that is used for staking // (Bob has two: one UTXO is for staking, other is being defragmented) - bob_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 10); - bob_wlt->set_pos_decoys_count_for_defragmentation_tx(0); + bob_wlt->set_defragmentation_tx_settings(true, 1, 10, CURRENCY_BLOCK_REWARD, 0); bob_wlt->try_mint_pos(); CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 3, false, "Incorrect blockchain height:" << c.get_current_blockchain_size()); @@ -3453,8 +3452,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde // 2. Try to mine a PoS block and defragment some of UTXO - alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(2, 2); - alice_wlt->set_pos_decoys_count_for_defragmentation_tx(0); + alice_wlt->set_defragmentation_tx_settings(true, 2, 2, CURRENCY_BLOCK_REWARD, 0); alice_wlt->try_mint_pos(); CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 4, false, "Incorrect blockchain height:" << c.get_current_blockchain_size()); @@ -3467,8 +3465,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde // 3. Try to mine a PoS block and defragment with huge decoy set. Make sure block is mined successfully without a defragmentation tx // Alice has one UTXO - alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 1); - alice_wlt->set_pos_decoys_count_for_defragmentation_tx(80); + alice_wlt->set_defragmentation_tx_settings(true, 1, 1, CURRENCY_BLOCK_REWARD, 80); alice_wlt->try_mint_pos(); CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 5, false, "Incorrect blockchain height:" << c.get_current_blockchain_size()); @@ -3480,8 +3477,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde // 4. Finally mine a PoS and defragment the last one unlocked UTXO - alice_wlt->set_pos_utxo_count_limits_for_defragmentation_tx(1, 1); - alice_wlt->set_pos_decoys_count_for_defragmentation_tx(0); + alice_wlt->set_defragmentation_tx_settings(true, 1, 1, CURRENCY_BLOCK_REWARD, 0); alice_wlt->try_mint_pos(); CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 + 6, false, "Incorrect blockchain height:" << c.get_current_blockchain_size()); @@ -3809,3 +3805,95 @@ bool wallet_and_sweep_below::c1(currency::core& c, size_t ev_index, const std::v return true; } + + +//------------------------------------------------------------------------------ +block_template_blacklist_test::block_template_blacklist_test() +{ + REGISTER_CALLBACK_METHOD(block_template_blacklist_test, c1); +} + +bool block_template_blacklist_test::generate(std::vector& events) const +{ + // Test idea: basic check for wallet2::sweep_below() functionality + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base preminer_acc; + preminer_acc.generate(); + preminer_acc.set_createtime(ts); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, ts); + DO_CALLBACK(events, "configure_core"); + + MAKE_NEXT_BLOCK(events, blk_1, blk_0, preminer_acc); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, 3 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW - 1); + + DO_CALLBACK(events, "c1"); + return true; +} + +bool block_template_blacklist_test::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + + miner_wlt->refresh(); + miner_wlt->transfer(COIN / 10, alice_wlt->get_account().get_public_address()); + miner_wlt->transfer(COIN / 10, alice_wlt->get_account().get_public_address()); + + //take first transaction and corrupt it intentionalyy + std::list txs; + c.get_tx_pool().get_transactions(txs); + CHECK_AND_ASSERT_MES(txs.size() == 2, false, "wrong tx count"); + + + txs.resize(1); + currency::transaction broken_tx; + uint64_t blob_size = 0; + uint64_t fee = 0; + r = c.get_tx_pool().take_tx(currency::get_transaction_hash(*txs.begin()), broken_tx, blob_size, fee); + CHECK_AND_ASSERT_MES(r, false, "failed to take from pool"); + + + broken_tx.signatures.resize(broken_tx.signatures.size() - 1); + //manually add completely broken tx to pool + c.get_tx_pool().do_insert_transaction(broken_tx, get_transaction_hash(broken_tx), currency::get_object_blobsize(broken_tx), false, get_tx_fee(broken_tx), c.get_block_id_by_height(0), 0); + + CHECK_AND_ASSERT_MES(c.get_tx_pool().get_transactions_count() == 2, false, "wrong tx count"); + + + currency::create_block_template_params cbtp = AUTO_VAL_INIT(cbtp); + cbtp.miner_address = miner_wlt->get_account().get_public_address(); + + { + currency::create_block_template_response cbtr = AUTO_VAL_INIT(cbtr); + r = c.get_block_template(cbtp, cbtr); + + CHECK_AND_ASSERT_MES(r, false, "failed to create block template"); + CHECK_AND_ASSERT_MES(cbtr.b.tx_hashes.size() == 2, false, "failed to create block template"); + } + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "Unexpectedly created block"); + + //now let's check if broken tx actually added to next blocktemplate + + { + currency::create_block_template_response cbtr = AUTO_VAL_INIT(cbtr); + r = c.get_block_template(cbtp, cbtr); + + CHECK_AND_ASSERT_MES(r, false, "failed to create block template"); + CHECK_AND_ASSERT_MES(cbtr.b.tx_hashes.size() == 1, false, "failed to create block template"); + + } + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "Unexpectedly failed to create block"); + + + return true; +} diff --git a/tests/core_tests/wallet_tests.h b/tests/core_tests/wallet_tests.h index a5aad448..cad3871e 100644 --- a/tests/core_tests/wallet_tests.h +++ b/tests/core_tests/wallet_tests.h @@ -294,3 +294,11 @@ struct wallet_and_sweep_below : public wallet_test bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; + + +struct block_template_blacklist_test : public wallet_test +{ + block_template_blacklist_test(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; \ No newline at end of file diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index ddd5443b..5b6e758e 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -9,6 +9,10 @@ # make it prominent in the GUI. option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) +if(POLICY CMP0148) + cmake_policy(SET CMP0148 OLD) +endif() + # When other libraries are using a shared version of runtime libraries, # Google Test also has to use one. option( @@ -40,7 +44,6 @@ endif() # ${gtest_BINARY_DIR}. # Language "C" is required for find_package(Threads). project(gtest CXX C) -cmake_minimum_required(VERSION 2.6.2) if (COMMAND set_up_hermetic_build) set_up_hermetic_build() diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index acd1f406..58fbd85f 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -27,12 +27,26 @@ #include "wallet/plain_wallet_api.h" #include "wallet/view_iface.h" +PUSH_VS_WARNINGS +DISABLE_VS_WARNINGS(4244) +#include "jwt-cpp/jwt.h" +POP_VS_WARNINGS + void test_plain_wallet() { - std::string res = plain_wallet::init("195.201.107.230", "33336", "E:\\tmp\\", 0); + //std::string res = plain_wallet::init("195.201.107.230", "33336", "E:\\tmp\\", 0); + std::string res = plain_wallet::init("127.0.0.1", "12111", "C:\\Users\\roky\\home22\\", 0); + + std::string res___ = plain_wallet::get_wallet_files(); + + uint64_t instance_id = 0; - res = plain_wallet::open("test.zan", "111"); + res = plain_wallet::open("test_restored_2.zan", "111"); + //res = plain_wallet::restore("", + // "test_restored_2.zan", "111", ""); + + while(true) { epee::misc_utils::sleep_no_w(2000); @@ -43,10 +57,23 @@ void test_plain_wallet() break; } + std::string invoke_body = "{\"method\":\"store\",\"params\":{}}"; + std::string res1 = plain_wallet::sync_call("invoke", instance_id, invoke_body); + + invoke_body = "{\"method\":\"get_recent_txs_and_info\",\"params\":{\"offset\":0,\"count\":30,\"update_provision_info\":true}}"; + std::string res2 = plain_wallet::sync_call("invoke", instance_id, invoke_body); + + invoke_body = "{\"method\":\"get_recent_txs_and_info2\",\"params\":{\"offset\":0,\"count\":30,\"update_provision_info\":true}}"; + res2 = plain_wallet::sync_call("invoke", instance_id, invoke_body); + + + invoke_body = "{\"method\":\"getbalance\",\"params\":{}}"; + std::string res3 = plain_wallet::sync_call("invoke", instance_id, invoke_body); + + + invoke_body = "{\r\n \"method\": \"transfer\",\r\n \"params\": {\r\n \"destinations\": [\r\n {\r\n \"amount\": \"1000000000000\",\r\n \"address\": \"ZxD9oVwGwW6ULix9Pqttnr7JDpaoLvDVA1KJ9eA9KRxPMRZT5X7WwtU94XH1Z6q6XTMxNbHmbV2xfZ429XxV6fST2DxEg4BQV\",\r\n \"asset_id\": \"cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6\"\r\n }\r\n ],\r\n \"fee\": 10000000000,\r\n \"mixin\": 10,\r\n \"payment_id\": \"\",\r\n \"comment\": \"\",\r\n \"push_payer\": false,\r\n \"hide_receiver\": true\r\n }\r\n}"; + std::string res4 = plain_wallet::sync_call("invoke", instance_id, invoke_body); - std::string invoke_body = "{\"method\":\"get_recent_txs_and_info\",\"params\":{\"offset\":0,\"count\":30,\"update_provision_info\":true}}"; - - res = plain_wallet::sync_call("invoke", instance_id, invoke_body); LOG_PRINT_L0(res); } @@ -56,10 +83,10 @@ int main(int argc, char** argv) { epee::string_tools::set_module_name_and_folder(argv[0]); epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); - epee::log_space::log_singletone::add_logger(LOGGER_FILE, - epee::log_space::log_singletone::get_default_log_file().c_str(), - epee::log_space::log_singletone::get_default_log_folder().c_str()); + //epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); + //epee::log_space::log_singletone::add_logger(LOGGER_FILE, + // epee::log_space::log_singletone::get_default_log_file().c_str(), + // epee::log_space::log_singletone::get_default_log_folder().c_str()); test_plain_wallet(); //parse_weird_tx(); diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 9bda3809..a605d624 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -18,9 +18,10 @@ public: m_bob.generate(); uint64_t block_reward_without_fee = 0; - if (!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().account_address, m_bob.get_keys().account_address, m_tx, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4, blobdata(), CURRENCY_MINER_TX_MAX_OUTS)) - return false; + uint64_t block_reward = 0; + if(!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().account_address, m_bob.get_keys().account_address, m_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, blobdata(), CURRENCY_MINER_TX_MAX_OUTS)) + return false; m_tx_pub_key = get_tx_pub_key_from_extra(m_tx); return true; } diff --git a/tests/unit_tests/decoy_selection.cpp b/tests/unit_tests/decoy_selection.cpp index 47ff298d..19c2d178 100644 --- a/tests/unit_tests/decoy_selection.cpp +++ b/tests/unit_tests/decoy_selection.cpp @@ -10,32 +10,39 @@ TEST(decoy_selection_test, decoy_selection_test) { const uint64_t test_scale_size = 20000; decoy_selection_generator dsg; - dsg.init(test_scale_size - 1); + dsg.init(100); std::map hits; + + + + //std::vector decoys = dsg.generate_distribution(15); + + std::cout << ""; + //std::vector hits(test_scale_size, 0); -// while (true) -// { -// std::vector decoys = dsg.generate_distribution(15); -// for (auto d : decoys) -// { -// hits[d]++; -// } -// -// if (hits[10] > 500) -// break; -// -// } -// std::stringstream ss; -// for (auto it = hits.begin(); it != hits.end(); it++) -// { -// //if (hits[i] != 0) -// { -// ss << it->first << ", " << it->second << ENDL; -// } -// } -// epee::file_io_utils::save_string_to_file("distribution.csv", ss.str()); + while (true) + { + std::vector decoys = dsg.generate_distribution(15); + for (auto d : decoys) + { + hits[d]++; + } + + if (hits[10] > 500) + break; + + } + std::stringstream ss; + for (auto it = hits.begin(); it != hits.end(); it++) + { + if (it->second != 0) + { + ss << it->first << ", " << it->second << std::endl; + } + } + epee::file_io_utils::save_string_to_file("distribution.csv", ss.str()); diff --git a/utils/Directory.Build.props.in b/utils/Directory.Build.props.in new file mode 100644 index 00000000..5f263a21 --- /dev/null +++ b/utils/Directory.Build.props.in @@ -0,0 +1,8 @@ + + + + true + true + 11 + + diff --git a/utils/configure_win64_msvs2022_gui.cmd b/utils/configure_win64_msvs2022_gui.cmd new file mode 100644 index 00000000..b5dcbbf5 --- /dev/null +++ b/utils/configure_win64_msvs2022_gui.cmd @@ -0,0 +1,7 @@ +call configure_local_paths_msvs2022.cmd + +cd .. +@mkdir build_msvc2022_64 +cd build_msvc2022_64 + +cmake -D TESTNET=FALSE -D USE_PCH=TRUE -D BUILD_TESTS=TRUE -D OPENSSL_ROOT_DIR="%OPENSSL_ROOT_DIR%" -D CMAKE_PREFIX_PATH="%QT_PREFIX_PATH%"\msvc2019_64 -D BUILD_GUI=TRUE -D STATIC=FALSE -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_ROOT%\lib64-msvc-14.3" -G "Visual Studio 17 2022" -A x64 -T host=x64 ".." diff --git a/utils/configure_win64_msvs2022_gui_testnet.cmd b/utils/configure_win64_msvs2022_gui_testnet.cmd new file mode 100644 index 00000000..f41547c3 --- /dev/null +++ b/utils/configure_win64_msvs2022_gui_testnet.cmd @@ -0,0 +1,7 @@ +call configure_local_paths_msvs2022.cmd + +cd .. +@mkdir build_msvc2022_64_tn +cd build_msvc2022_64_tn + +cmake -D TESTNET=TRUE -D USE_PCH=TRUE -D BUILD_TESTS=TRUE -D OPENSSL_ROOT_DIR="%OPENSSL_ROOT_DIR%" -D CMAKE_PREFIX_PATH="%QT_PREFIX_PATH%"\msvc2019_64 -D BUILD_GUI=TRUE -D STATIC=FALSE -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_ROOT%\lib64-msvc-14.3" -G "Visual Studio 17 2022" -A x64 -T host=x64 ".." diff --git a/utils/macosx_build_uploader.sh b/utils/macosx_build_uploader.sh index acd23652..45813204 100644 --- a/utils/macosx_build_uploader.sh +++ b/utils/macosx_build_uploader.sh @@ -27,7 +27,7 @@ function upload_build() # $1 - path to the file to be uploaded counter=0 while [ ! -f DONE ] do - if [ "$counter" -ge 150 ] + if [ "$counter" -ge 500 ] then echo "ERROR: uploading is taking longer than expected" touch STOP diff --git a/utils/test_api_files/get_alias_by_address.json b/utils/test_api_files/get_alias_by_address.json new file mode 100644 index 00000000..c89f7e97 --- /dev/null +++ b/utils/test_api_files/get_alias_by_address.json @@ -0,0 +1 @@ +{"method": "get_alias_details","params": {"alias": "zoidb"}} \ No newline at end of file diff --git a/utils/test_api_files/get_alias_details.json b/utils/test_api_files/get_alias_details.json index c89f7e97..27871aa7 100644 --- a/utils/test_api_files/get_alias_details.json +++ b/utils/test_api_files/get_alias_details.json @@ -1 +1 @@ -{"method": "get_alias_details","params": {"alias": "zoidb"}} \ No newline at end of file +{"method": "get_alias_details","params": "ZxCjF84feY7GQZ1fdy9r3ZJABwaFjmb2Dd25H5qANWZ2VufpmyNu7ZnShMBDpiw8VW2k1EjPZswgFZnx3v1EYgJ32Rjn64mq9"} \ No newline at end of file