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 c9340683..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,11 +229,11 @@ 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") @@ -260,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 09555a9e..7dc812ad 100644 --- a/contrib/epee/include/serialization/keyvalue_helpers.h +++ b/contrib/epee/include/serialization/keyvalue_helpers.h @@ -97,6 +97,40 @@ namespace epee 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 50f917b4..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); @@ -91,20 +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_BLOB_AS_BASE64_STRING(variable) KV_SERIALIZE_BLOB_AS_BASE64_STRING_N(variable, #variable) +#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/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/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index c12e5ca9..a16302f1 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -67,6 +67,8 @@ using namespace currency; #define BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION 2 #define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION 3 //DON'T CHANGE THIS, if you need to resync db change BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION #define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION 4 //mismatch here means some reinitializations +#define BLOCKCHAIN_STORAGE_OPTIONS_ID_MAJOR_FAILURE 5 //if not blocks should ever be added with this condition + #define TARGETDATA_CACHE_SIZE DIFFICULTY_WINDOW + 10 @@ -96,6 +98,7 @@ blockchain_storage::blockchain_storage(tx_memory_pool& tx_pool) :m_db(nullptr, m m_db_last_worked_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION, m_db_solo_options), m_db_storage_major_compatibility_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION, m_db_solo_options), m_db_storage_minor_compatibility_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION, m_db_solo_options), + m_db_major_failure(BLOCKCHAIN_STORAGE_OPTIONS_ID_MAJOR_FAILURE, m_db_solo_options), m_db_per_block_gindex_incs(m_db), m_tx_pool(tx_pool), m_is_in_checkpoint_zone(false), @@ -297,10 +300,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 +385,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 +411,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 +505,13 @@ 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() << ENDL + << " major failure: " << (m_db_major_failure ? "true" : "false"), LOG_LEVEL_0); return true; @@ -1131,14 +1185,9 @@ bool blockchain_storage::switch_to_alternative_blockchain(alt_chain_type& alt_ch return true; } //------------------------------------------------------------------ -wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) const +void blockchain_storage::collect_timestamps_and_c_difficulties_main(std::vector& timestamps, std::vector& commulative_difficulties, bool pos) const { CRITICAL_REGION_LOCAL(m_read_lock); - std::vector timestamps; - std::vector commulative_difficulties; - if (!m_db_blocks.size()) - return DIFFICULTY_POW_STARTER; - //skip genesis timestamp TIME_MEASURE_START_PD(target_calculating_enum_blocks); CRITICAL_REGION_BEGIN(m_targetdata_cache_lock); std::list>& targetdata_cache = pos ? m_pos_targetdata_cache : m_pow_targetdata_cache; @@ -1153,11 +1202,14 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) con ++count; } CRITICAL_REGION_END(); - - wide_difficulty_type& dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty; TIME_MEASURE_FINISH_PD(target_calculating_enum_blocks); +} +//------------------------------------------------------------------ +wide_difficulty_type blockchain_storage::calc_diff_at_h_from_timestamps(std::vector& timestamps, std::vector& commulative_difficulties, uint64_t h, bool pos) const +{ + wide_difficulty_type dif; TIME_MEASURE_START_PD(target_calculating_calc); - if (m_core_runtime_config.is_hardfork_active_for_height(1, m_db_blocks.size())) + if (m_core_runtime_config.is_hardfork_active_for_height(1, h)) { dif = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); } @@ -1165,22 +1217,36 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) con { dif = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); } - - TIME_MEASURE_FINISH_PD(target_calculating_calc); return dif; } //------------------------------------------------------------------ -wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const +wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) const { - CRITICAL_REGION_LOCAL(m_read_lock); + { + //skip genesis timestamp + CRITICAL_REGION_LOCAL(m_read_lock); + if (!m_db_blocks.size()) + return DIFFICULTY_POW_STARTER; + } + std::vector timestamps; std::vector commulative_difficulties; - size_t count = 0; - if (!m_db_blocks.size()) - return DIFFICULTY_POW_STARTER; + collect_timestamps_and_c_difficulties_main(timestamps, commulative_difficulties, pos); - auto cb = [&](const block_extended_info& bei, bool is_main){ + + wide_difficulty_type& dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty; + dif = calc_diff_at_h_from_timestamps(timestamps, commulative_difficulties, m_db_blocks.size(), pos); + + return dif; +} +//------------------------------------------------------------------ +void blockchain_storage::collect_timestamps_and_c_difficulties_alt(std::vector& timestamps, std::vector& commulative_difficulties, bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + size_t count = 0; + + auto cb = [&](const block_extended_info& bei, bool is_main) { if (!bei.height) return false; bool is_pos_bl = is_pos_block(bei.bl); @@ -1192,15 +1258,22 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, co if (count >= DIFFICULTY_WINDOW) return false; return true; - }; + }; enum_blockchain(cb, alt_chain, split_height); +} +//------------------------------------------------------------------ +wide_difficulty_type blockchain_storage::get_next_diff_conditional_alt(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const +{ + { + CRITICAL_REGION_LOCAL(m_read_lock); + if (!m_db_blocks.size()) + return DIFFICULTY_POW_STARTER; + } + std::vector timestamps; + std::vector commulative_difficulties; + collect_timestamps_and_c_difficulties_alt(timestamps, commulative_difficulties, pos, alt_chain, split_height); - wide_difficulty_type diff = 0; - if(m_core_runtime_config.is_hardfork_active_for_height(1, abei.height)) - diff = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - else - diff = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - return diff; + return calc_diff_at_h_from_timestamps(timestamps, commulative_difficulties, abei.height, pos); } //------------------------------------------------------------------ wide_difficulty_type blockchain_storage::get_cached_next_difficulty(bool pos) const @@ -1264,8 +1337,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 +1346,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 +1360,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 +1375,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 +1385,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 +1398,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; } //------------------------------------------------------------------ @@ -1824,7 +1888,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: } // PoW / PoS validation (heavy checks) - wide_difficulty_type current_diff = get_next_diff_conditional2(pos_block, alt_chain, connection_height, abei); + wide_difficulty_type current_diff = get_next_diff_conditional_alt(pos_block, alt_chain, connection_height, abei); CHECK_AND_ASSERT_MES_CUSTOM(current_diff, false, bvc.m_verification_failed = true, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; @@ -1910,7 +1974,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 @@ -3728,7 +3796,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(); @@ -3806,6 +3873,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); @@ -4222,16 +4313,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; } //------------------------------------------------------------------ @@ -4480,7 +4572,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() @@ -4676,7 +4767,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(); } @@ -4839,12 +4931,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; @@ -4939,7 +5025,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) @@ -5126,9 +5212,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; @@ -5173,9 +5257,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(); @@ -5272,7 +5356,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 @@ -5448,7 +5532,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; } } @@ -5690,6 +5774,12 @@ void blockchain_storage::get_pos_mining_estimate(uint64_t amount_coins, //------------------------------------------------------------------ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id) const { + if (m_db_major_failure) + { + LOG_ERROR("MAJOR FAILURE: POS DIFFICULTY IS GOT TO HIGH! Contact the team immediately if you see this error in logs and watch them having panic attack."); + return false; + } + uint64_t block_height = m_db_blocks.size(); return validate_tx_for_hardfork_specific_terms(tx, tx_id, block_height); } @@ -5847,7 +5937,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) @@ -5874,7 +5964,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]); @@ -6051,8 +6141,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 @@ -6504,15 +6594,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"); @@ -6603,6 +6697,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 << ")"); @@ -6610,7 +6705,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; @@ -6665,7 +6760,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 @@ -6687,6 +6782,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); @@ -6695,8 +6794,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() << ")" @@ -6711,6 +6809,13 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt << range_proofs_agregated.size() << ")" << "))"); + if (is_pos_bl && current_diffic > m_core_runtime_config.max_pos_difficulty) + { + m_db_major_failure = true; //burn safety fuse + LOG_ERROR("MAJOR FAILURE: POS DIFFICULTY IS GOT TO HIGH! Contact the team immediately if you see this error in logs and watch them having panic attack." + << ENDL << "Block id:" << id); + } + { static epee::math_helper::average blocks_processing_time_avg_pos, blocks_processing_time_avg_pow; @@ -6823,6 +6928,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) { @@ -6882,6 +6992,15 @@ bool blockchain_storage::add_new_block(const block& bl, block_verification_conte { try { + + if (m_db_major_failure) + { + LOG_PRINT_RED_L0("Block processing is stoped due to MAJOR FAILURE fuse burned"); + bvc.m_added_to_main_chain = false; + bvc.m_verification_failed = true; + return false; + } + m_db.begin_transaction(); //block bl = bl_; @@ -6905,10 +7024,6 @@ bool blockchain_storage::add_new_block(const block& bl, block_verification_conte m_db.commit_transaction(); return false; } - - - //check that block refers to chain tail - if (!(bl.prev_id == get_top_block_id())) { @@ -7358,7 +7473,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 c24e1104..489f4635 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -266,8 +266,12 @@ namespace currency crypto::hash get_top_block_id(uint64_t& height) const; bool get_top_block(block& b) const; wide_difficulty_type get_next_diff_conditional(bool pos) const; - wide_difficulty_type get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const; + wide_difficulty_type get_next_diff_conditional_alt(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const; wide_difficulty_type get_cached_next_difficulty(bool pos) const; + wide_difficulty_type calc_diff_at_h_from_timestamps(std::vector& timestamps, std::vector& commulative_difficulties, uint64_t h, bool pos) const; + void collect_timestamps_and_c_difficulties_main(std::vector& timestamps, std::vector& commulative_difficulties, bool pos) const; + void collect_timestamps_and_c_difficulties_alt(std::vector& timestamps, std::vector& commulative_difficulties, bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const; + bool create_block_template(const account_public_address& miner_address, const blobdata& ex_nonce, block& b, wide_difficulty_type& di, uint64_t& height) const; @@ -302,6 +306,7 @@ namespace currency 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; @@ -339,7 +344,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; @@ -347,6 +353,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; @@ -363,7 +370,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, @@ -548,6 +555,8 @@ namespace currency tools::db::solo_db_value m_db_last_worked_version; tools::db::solo_db_value m_db_storage_major_compatibility_version; tools::db::solo_db_value m_db_storage_minor_compatibility_version; + tools::db::solo_db_value m_db_major_failure; //safety fuse + outputs_container m_db_outputs; multisig_outs_container m_db_multisig_outs; aliases_container m_db_aliases; 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/core_runtime_config.h b/src/currency_core/core_runtime_config.h index f970e144..c1ba86af 100644 --- a/src/currency_core/core_runtime_config.h +++ b/src/currency_core/core_runtime_config.h @@ -106,6 +106,7 @@ namespace currency crypto::public_key alias_validation_pubkey; core_time_func_t get_core_time; uint64_t hf4_minimum_mixins; + wide_difficulty_type max_pos_difficulty; hard_forks_descriptor hard_forks; @@ -129,6 +130,7 @@ namespace currency pc.tx_default_fee = TX_DEFAULT_FEE; pc.max_alt_blocks = CURRENCY_ALT_BLOCK_MAX_COUNT; pc.hf4_minimum_mixins = CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE; + pc.max_pos_difficulty = wide_difficulty_type(POS_MAX_DIFFICULTY_ALLOWED); // TODO: refactor the following pc.hard_forks.set_hardfork_height(1, ZANO_HARDFORK_01_AFTER_HEIGHT); 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 2afc400a..c7384510 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -157,6 +157,8 @@ #define POS_MODFIFIER_INTERVAL 10 #define POS_WALLET_MINING_SCAN_INTERVAL POS_SCAN_STEP //seconds #define POS_MINIMUM_COINSTAKE_AGE 10 // blocks count +#define POS_MAX_DIFFICULTY_ALLOWED "25000000000000000000000" // maximum expected PoS difficuty (need to change it probaly in 20 years) + #ifndef TESTNET # define BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION 57000 @@ -243,14 +245,14 @@ #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" -#define WALLET_FILE_SERIALIZATION_VERSION 165 +#define WALLET_FILE_SERIALIZATION_VERSION 167 #define WALLET_FILE_LAST_SUPPORTED_VERSION 165 #define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31) @@ -265,6 +267,7 @@ #define ZANO_HARDFORK_03_AFTER_HEIGHT 1082577 // 2021-06-01 23:28:10 #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 @@ -272,6 +275,7 @@ #define ZANO_HARDFORK_03_AFTER_HEIGHT 0 #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 diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 410d6a91..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); @@ -1117,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) { @@ -1257,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); @@ -1289,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); } @@ -1318,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) @@ -1912,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, @@ -2582,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"); @@ -2602,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) @@ -2780,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; @@ -3083,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) @@ -3125,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) @@ -4022,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; @@ -4041,7 +4076,7 @@ namespace currency gindices[amount] += 1; } VARIANT_CASE_CONST(tx_out_zarcanum, o) - //@#@ + gindices[0] += 1; VARIANT_SWITCH_END(); } } @@ -4075,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; @@ -4083,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 70751c8d..fd33a6b8 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -414,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); @@ -522,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); @@ -535,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/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_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/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 f8e9556f..9be6a53f 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit f8e9556fbaccd49841ce91afc3c90c8e3142ac95 +Subproject commit 9be6a53f757a17403ea5798d08b577d450eb620a diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index cd9c20d2..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); @@ -738,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)) diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index daf9452c..ad4786f2 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -88,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); @@ -150,7 +151,8 @@ namespace currency 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) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 42648bfa..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 @@ -162,11 +208,11 @@ namespace currency struct request { 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(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() }; @@ -178,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() }; }; @@ -192,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 @@ -209,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() }; }; @@ -219,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() }; @@ -234,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() @@ -255,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() }; @@ -278,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() }; @@ -302,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() }; }; @@ -311,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; @@ -330,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() }; }; @@ -340,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; @@ -357,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; @@ -373,10 +432,10 @@ 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() }; @@ -410,8 +469,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.") DOC_END END_KV_SERIALIZE_MAP() }; @@ -420,22 +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_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 global_offsets; //[i] = global_index to pick up BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(global_offsets) + 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() }; @@ -447,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() }; @@ -485,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() }; @@ -494,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() }; }; @@ -512,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() }; }; @@ -520,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; @@ -528,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() }; @@ -538,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 { @@ -551,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() }; @@ -561,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() }; @@ -584,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; @@ -603,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) @@ -771,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() }; @@ -843,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 { @@ -927,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() }; @@ -977,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 @@ -1000,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() }; @@ -1023,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; @@ -1043,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 { @@ -1068,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() }; @@ -1091,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() }; @@ -1115,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() }; @@ -1138,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() }; @@ -1160,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() @@ -1181,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() }; @@ -1206,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 { @@ -1245,20 +1371,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_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) + 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() }; @@ -1267,13 +1396,17 @@ 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_GET_POS_MINING_DETAILS - { + { + DOC_COMMAND("Retrieves basic information regarding PoS mining, including current PoS conditions and constraints."); + struct request { BEGIN_KV_SERIALIZE_MAP() @@ -1291,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; @@ -1310,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() }; @@ -1327,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() }; @@ -1342,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() }; @@ -1366,19 +1501,19 @@ namespace currency std::string object_in_json; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_BLOB_AS_BASE64_STRING(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_BLOB_AS_BASE64_STRING(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() }; @@ -1390,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() }; @@ -1424,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; @@ -1466,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() }; @@ -1478,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() }; @@ -1503,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() }; @@ -1527,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() }; @@ -1551,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() }; @@ -1575,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() @@ -1595,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 @@ -1629,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() }; @@ -1646,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 { @@ -1660,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() }; @@ -1675,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() }; @@ -1699,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() @@ -1720,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 @@ -1737,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 24a2053f..c953138f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -290,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"); @@ -315,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; } //---------------------------------------------------------------------------------------------------- @@ -2511,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__() @@ -2679,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); @@ -2736,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)) { @@ -2816,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 @@ -2828,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(); 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 a6b75ab0..6819f5d7 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 300 +#define PROJECT_VERSION_BUILD_NO 317 #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 0836cb75..4bd94188 100644 --- a/src/wallet/core_default_rpc_proxy.cpp +++ b/src/wallet/core_default_rpc_proxy.cpp @@ -51,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); diff --git a/src/wallet/core_default_rpc_proxy.h b/src/wallet/core_default_rpc_proxy.h index b27cbbcf..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; @@ -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 d96ee86f..bf8ac62e 100644 --- a/src/wallet/core_fast_rpc_proxy.h +++ b/src/wallet/core_fast_rpc_proxy.h @@ -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 1ab16633..5a4a25b9 100644 --- a/src/wallet/core_rpc_proxy.h +++ b/src/wallet/core_rpc_proxy.h @@ -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/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 fe7efa42..d5720fe3 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -352,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)) @@ -424,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: " << (int)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 }); @@ -448,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 ) { @@ -460,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:" @@ -511,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); + } } } @@ -523,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 @@ -540,11 +576,9 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t { if (pglobal_indexes->size()) { - //store global index that is under coinage, so we can used in decoy selection algo - if (ptc.height < m_last_known_daemon_height && m_last_known_daemon_height - ptc.height > WALLET_DEFAULT_TX_SPENDABLE_AGE) - { - m_last_zc_global_index = pglobal_indexes->back(); - } + //@#@ + WLT_LOG_L2("add_to_last_zc_global_indexs: h: " << height << ", b: " << currency::get_block_hash(b) << " , tx: " << currency::get_transaction_hash(tx) << ", last_zc_output_index: " << pglobal_indexes->back()); + add_to_last_zc_global_indexs(ptc.height, pglobal_indexes->back()); } } @@ -742,15 +776,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()); @@ -1120,7 +1155,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 */) @@ -1857,9 +1893,16 @@ void wallet2::process_new_blockchain_entry(const currency::block& b, const curre TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); + size_t count = 0; for(const auto& tx_entry: bche.txs_ptr) { + if (b.tx_hashes.size() < count || currency::get_transaction_hash(tx_entry->tx) != b.tx_hashes[count]) + { + LOG_ERROR("Found tx order fail in process_new_blockchain_entry: count=" << count + << ", b.tx_hashes.size() = " << b.tx_hashes.size() << ", tx real id: " << currency::get_transaction_hash(tx_entry->tx) << ", bl_id: " << bl_id); + } process_new_transaction(tx_entry->tx, height, b, &(tx_entry->m_global_output_indexes)); + count++; } TIME_MEASURE_FINISH(txs_handle_time); WLT_LOG_L3("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -2200,6 +2243,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); @@ -2861,6 +3088,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); @@ -3444,6 +3677,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) { @@ -3464,6 +3698,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; } } @@ -3634,7 +3871,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); @@ -3643,6 +3880,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) @@ -3656,9 +3908,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 @@ -3778,6 +4031,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); @@ -4249,6 +4555,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; @@ -4291,7 +4602,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 @@ -4576,15 +4887,16 @@ bool wallet2::try_mint_pos(const currency::account_public_address& miner_address return true; }, m_core_runtime_config); + bool res = true; if (ctx.status == API_RETURN_CODE_OK) { - build_minted_block(ctx, miner_address); + res = build_minted_block(ctx, miner_address); } TIME_MEASURE_FINISH_MS(mining_duration_ms); WLT_LOG_L0("PoS mining: " << ctx.iterations_processed << " iterations finished (" << std::fixed << std::setprecision(2) << (mining_duration_ms / 1000.0f) << "s), status: " << ctx.status << ", " << ctx.total_items_checked << " entries with total amount: " << print_money_brief(ctx.total_amount_checked)); - return true; + return res; } //------------------------------------------------------------------ void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transfer_index) @@ -5644,7 +5956,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; @@ -5789,10 +6101,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; + //} } @@ -5991,10 +6303,18 @@ 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) @@ -6024,7 +6344,12 @@ void wallet2::prefetch_global_indicies_if_needed(const std::vector& se }*/ } //---------------------------------------------------------------------------------------------------- -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; @@ -6037,13 +6362,6 @@ 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(m_last_zc_global_index); - } bool need_to_request = fake_outputs_count != 0; COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request req = AUTO_VAL_INIT(req); @@ -6069,7 +6387,7 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, std::vectorm_global_output_index); + build_distribution_for_input(rdisttib.global_offsets, it->m_global_output_index); need_to_request = true; } else @@ -6109,14 +6427,15 @@ 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(size_t i = 0; i != daemon_resp.outs.size(); i++) + + if (!use_all_decoys_if_found_less_than_required) { - 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]); - } + // 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); } } @@ -6287,12 +6606,16 @@ void wallet2::select_decoys(currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS amount_entry.outs = local_outs; } //---------------------------------------------------------------------------------------------------------------- -void wallet2::build_distribution_for_input(decoy_selection_generator& zarcanum_decoy_set_generator, std::vector& offsets, uint64_t own_index) +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) { - offsets = zarcanum_decoy_set_generator.generate_unique_reversed_distribution(m_last_zc_global_index - 1 > WALLET_FETCH_RANDOM_OUTS_SIZE ? WALLET_FETCH_RANDOM_OUTS_SIZE: m_last_zc_global_index - 1, own_index); + 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); } } //---------------------------------------------------------------------------------------------------------------- @@ -6709,8 +7032,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) @@ -6737,8 +7058,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_) @@ -6777,7 +7097,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)) { @@ -6786,7 +7106,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; @@ -6854,7 +7174,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); @@ -7092,7 +7412,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)) { @@ -7244,10 +7564,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 b0c55331..725c2c42 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -148,9 +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; - uint64_t m_last_zc_global_index = 0; - - + std::list > m_last_zc_global_indexs; // , biggest height comes in front //variables that not being serialized std::atomic m_last_bc_timestamp = 0; @@ -158,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) @@ -222,7 +222,19 @@ namespace tools a & m_rollback_events; a & m_whitelisted_assets; a & m_use_assets_whitelisting; - a & m_last_zc_global_index; + 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)); + return; + } + a& m_last_zc_global_indexs; + if (ver == 166 && m_last_zc_global_indexs.size()) + { + //workaround for m_last_zc_global_indexs holding invalid index for last item + m_last_zc_global_indexs.pop_front(); + } } }; @@ -232,6 +244,7 @@ namespace tools wallet2(const wallet2&) = delete; public: wallet2(); + virtual ~wallet2() {} static std::string transfer_flags_to_str(uint32_t flags); @@ -342,6 +355,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; + }; @@ -377,6 +397,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(); @@ -400,7 +426,6 @@ namespace tools 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_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; } @@ -540,6 +565,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; @@ -576,8 +602,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); @@ -660,6 +686,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; @@ -696,6 +726,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(); @@ -748,7 +780,8 @@ 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); @@ -762,6 +795,7 @@ private: 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); @@ -854,7 +888,7 @@ 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(decoy_selection_generator& zarcanum_decoy_set_generator, std::vector& offsets, uint64_t own_index); + 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); @@ -895,7 +929,6 @@ private: 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; @@ -1076,7 +1109,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]; @@ -1144,7 +1176,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 523aa2d4..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() @@ -164,27 +168,29 @@ namespace wallet_public 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 516329ff..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); @@ -37,7 +50,7 @@ using namespace epee; catch (const tools::error::wallet_error& e) \ { \ er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; \ - er.message = std::string("WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR") + e.error_code(); \ + er.message = e.error_code(); \ return false; \ } \ catch (const std::exception& e) \ @@ -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/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index d41ccca1..02287be5 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -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/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 73340589..21005216 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -828,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 @@ -2319,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_main.cpp b/tests/core_tests/chaingen_main.cpp index aa0974a4..aad760ea 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); @@ -1246,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); @@ -1270,6 +1274,9 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(zarcanum_block_with_txs); GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos); GENERATE_AND_PLAY_HF(assets_and_pos_mining, "4-*"); + + GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*"); + GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*"); diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index c6a952f6..abac7b00 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -45,3 +45,4 @@ #include "multiassets_test.h" #include "ionic_swap_tests.h" #include "attachment_isolation_encryption_test.h" +#include "pos_fuse_test.h" \ No newline at end of file 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 e5b4d6b4..396ac2ec 100644 --- a/tests/core_tests/hard_fork_4.cpp +++ b/tests/core_tests/hard_fork_4.cpp @@ -242,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 f8235675..5e9014b8 100644 --- a/tests/core_tests/hard_fork_4.h +++ b/tests/core_tests/hard_fork_4.h @@ -25,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/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_fuse_test.cpp b/tests/core_tests/pos_fuse_test.cpp new file mode 100644 index 00000000..109e0b02 --- /dev/null +++ b/tests/core_tests/pos_fuse_test.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2014-2022 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chaingen.h" +#include "pos_fuse_test.h" +#include "wallet_test_core_proxy.h" + +#include "random_helper.h" +#include "wallet/wallet_debug_events_definitions.h" +using namespace currency; + + +using namespace currency; +pos_fuse_test::pos_fuse_test() +{ + REGISTER_CALLBACK_METHOD(pos_fuse_test, c1); + REGISTER_CALLBACK_METHOD(pos_fuse_test, configure_core); +} + + bool pos_fuse_test::configure_core(currency::core& c, size_t ev_index, const std::vector& events) +{ + wallet_test::configure_core(c, ev_index, events); + currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config(); + pc.max_pos_difficulty = wide_difficulty_type(1); + //currency::core_runtime_config pc2; + //pc2 = pc; + c.get_blockchain_storage().set_core_runtime_config(pc); + + + currency::core_runtime_config pc2 = c.get_blockchain_storage().get_core_runtime_config(); + LOG_PRINT_L1("Difficulty: " << pc2.max_pos_difficulty); + + return true; +} + +bool pos_fuse_test::generate(std::vector& events) const +{ + 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"); // necessary to set m_hardforks + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 10); + + DO_CALLBACK(events, "c1"); + return true; +} + +bool pos_fuse_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); + miner_wlt->refresh(); + + + + while (true) + { + miner_wlt->refresh(); + wide_difficulty_type pos_diff = c.get_blockchain_storage().get_next_diff_conditional(true); + + 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_block_in_playtime failed"); + + bool r = miner_wlt->try_mint_pos(); + CHECK_AND_ASSERT_MES(r, false, "Failed ot mint pos block"); + + currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config(); + LOG_PRINT_MAGENTA("POS Difficulty: " << pos_diff << ", max allowed diff: " << pc.max_pos_difficulty, LOG_LEVEL_0); + if (pos_diff > pc.max_pos_difficulty) + { + break; + } + } + + //check that PoW blocks not going + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(!r, false, "PoW block unexpectedly generated"); + + //check that PoS blocks not going + r = miner_wlt->try_mint_pos(); + CHECK_AND_ASSERT_MES(!r, false, "PoS block unexpectedly mined"); + + try + { + miner_wlt->transfer(1000000, m_accounts[ALICE_ACC_IDX].get_public_address()); + CHECK_AND_ASSERT_MES(false, false, "Transaction unexpectedly sent"); + } + catch (...) + { + LOG_PRINT_L0("Expected exception catched"); + } + + return true; +} diff --git a/tests/core_tests/pos_fuse_test.h b/tests/core_tests/pos_fuse_test.h new file mode 100644 index 00000000..fb9d8c56 --- /dev/null +++ b/tests/core_tests/pos_fuse_test.h @@ -0,0 +1,14 @@ +// Copyright (c) 2014-2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include "chaingen.h" +#include "wallet_tests_basic.h" + +struct pos_fuse_test : public wallet_test +{ + pos_fuse_test(); + virtual 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); +}; \ No newline at end of file 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/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 e84f17d3..58fbd85f 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -27,16 +27,24 @@ #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("127.0.0.1", "12111", "C:\\Users\\roky\\home\\", 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_restored.zan", "111"); - //res = plain_wallet::restore("heart level clear fate sorrow childhood sent fate ceiling party third steel came ask mix neither message already almost vast date glide tumble color okay space", - // "test_restored.zan", "111", ""); + res = plain_wallet::open("test_restored_2.zan", "111"); + //res = plain_wallet::restore("", + // "test_restored_2.zan", "111", ""); while(true) @@ -49,24 +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); + 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 = "{\"method\":\"getbalance\",\"params\":{}}"; + 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); - - invoke_body = "{\r\n \"method\": \"transfer\",\r\n \"params\": {\r\n \"destinations\": [\r\n {\r\n \"amount\": \"1000000000000\",\r\n \"address\": \"ZxD9oVwGwW6ULix9Pqttnr7JDpaoLvDVA1KJ9eA9KRxPMRZT5X7WwtU94XH1Z6q6XTMxNbHmbV2xfZ429XxV6fST2DxEg4BQV\" }\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 res5 = plain_wallet::sync_call("invoke", instance_id, invoke_body); - LOG_PRINT_L0(res); } 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