diff options
46 files changed, 656 insertions, 259 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eddfca0e..2964e299c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -658,6 +658,11 @@ if(NOT Boost_FOUND) die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (1.58) or the equivalent") elseif(Boost_FOUND) message(STATUS "Found Boost Version: ${Boost_VERSION}") + if (Boost_VERSION VERSION_LESS 1.62 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) + message(FATAL_ERROR "Boost older than 1.62 is too old to link with OpenSSL 1.1 or newer. " + "Update Boost or install OpenSSL 1.0 and set path to it when running cmake: " + "cmake -DOPENSSL_ROOT_DIR='/usr/include/openssl-1.0;/usr/lib/openssl-1.0'") + endif() endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) @@ -702,6 +707,8 @@ include(version.cmake) find_path(ZMQ_INCLUDE_PATH zmq.hpp) find_library(ZMQ_LIB zmq) +find_library(PGM_LIBRARY pgm) +find_library(NORM_LIBRARY norm) find_library(SODIUM_LIBRARY sodium) if(NOT ZMQ_INCLUDE_PATH) @@ -710,6 +717,12 @@ endif() if(NOT ZMQ_LIB) message(FATAL_ERROR "Could not find required libzmq") endif() +if(PGM_LIBRARY) + set(ZMQ_LIB "${ZMQ_LIB};${PGM_LIBRARY}") +endif() +if(NORM_LIBRARY) + set(ZMQ_LIB "${ZMQ_LIB};${NORM_LIBRARY}") +endif() if(SODIUM_LIBRARY) set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}") endif() @@ -1,7 +1,7 @@ # Monero -Copyright (c) 2014-2017, The Monero Project -Portions Copyright (c) 2012-2013, The Cryptonote developers +Copyright (c) 2014-2017 The Monero Project. +Portions Copyright (c) 2012-2013 The Cryptonote developers. ## Development Resources diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index 144acad9d..a66fb7c23 100644..100755 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -46,6 +46,7 @@ namespace net_utils { enum http_method{ + http_method_options, http_method_get, http_method_post, http_method_put, @@ -115,6 +116,7 @@ namespace net_utils std::string m_host; //"Host:" std::string m_cookie; //"Cookie:" std::string m_user_agent; //"User-Agent:" + std::string m_origin; //"Origin:" fields_list m_etc_fields; void clear() @@ -128,6 +130,7 @@ namespace net_utils m_host.clear(); m_cookie.clear(); m_user_agent.clear(); + m_origin.clear(); m_etc_fields.clear(); } }; diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index ed89ca0c7..80a4504e3 100644..100755 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -749,10 +749,10 @@ using namespace std; MTRACE("http_stream_filter::parse_cached_header(*)"); STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" + // 12 3 4 5 6 7 8 9 10 11 "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //11 1213 14 + //12 13 14 15 boost::regex::icase | boost::regex::normal); boost::smatch result; @@ -764,7 +764,7 @@ using namespace std; //lookup all fields and fill well-known fields while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) { - const size_t field_val = 13; + const size_t field_val = 14; //const size_t field_etc_name = 11; int i = 2; //start position = 2 @@ -788,8 +788,10 @@ using namespace std; body_info.m_cookie = result[field_val]; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; + else if(result[i++].matched)//"Origin" + body_info.m_origin = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - body_info.m_etc_fields.emplace_back(result[11], result[field_val]); + body_info.m_etc_fields.emplace_back(result[12], result[field_val]); else {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);} diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index babe49ad7..652d8ff6f 100644..100755 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -54,6 +54,7 @@ namespace net_utils struct http_server_config { std::string m_folder; + std::vector<std::string> m_access_control_origins; boost::optional<login> m_user; critical_section m_lock; }; @@ -193,6 +194,7 @@ namespace net_utils response.m_response_code = 200; response.m_response_comment = "OK"; response.m_body.clear(); + return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); } diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index c92a13bcc..c3350bf73 100644..100755 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -316,7 +316,10 @@ namespace net_utils CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); http_ver_major = boost::lexical_cast<int>(result[11]); http_ver_minor = boost::lexical_cast<int>(result[12]); - if(result[4].matched) + + if(result[3].matched) + method = http::http_method_options; + else if(result[4].matched) method = http::http_method_get; else if(result[5].matched) method = http::http_method_head; @@ -472,8 +475,8 @@ namespace net_utils bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) { STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" + // 12 3 4 5 6 7 8 9 10 11 "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", //11 1213 14 boost::regex::icase | boost::regex::normal); @@ -487,8 +490,8 @@ namespace net_utils //lookup all fields and fill well-known fields while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) { - const size_t field_val = 13; - const size_t field_etc_name = 11; + const size_t field_val = 14; + const size_t field_etc_name = 12; int i = 2; //start position = 2 if(result[i++].matched)//"Connection" @@ -509,6 +512,8 @@ namespace net_utils body_info.m_cookie = result[field_val]; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; + else if(result[i++].matched)//"Origin" + body_info.m_origin = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val])); else @@ -537,17 +542,27 @@ namespace net_utils template<class t_connection_context> bool simple_http_connection_handler<t_connection_context>::handle_request_and_send_response(const http::http_request_info& query_info) { - http_response_info response; - bool res = handle_request(query_info, response); + http_response_info response{}; //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); + bool res = true; + + if (query_info.m_http_method != http::http_method_options) + { + res = handle_request(query_info, response); + } + else + { + response.m_response_code = 200; + response.m_response_comment = "OK"; + } std::string response_data = get_response_header(response); - //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); + LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - if(response.m_body.size() && (query_info.m_http_method != http::http_method_head)) + if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options)) m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); return res; } @@ -579,7 +594,6 @@ namespace net_utils response.m_response_comment = "OK"; response.m_mime_tipe = get_file_mime_tipe(uri_to_path); - return true; } //----------------------------------------------------------------------------------- @@ -591,8 +605,12 @@ namespace net_utils "Server: Epee-based\r\n" "Content-Length: "; buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n"; - buf += "Content-Type: "; - buf += response.m_mime_tipe + "\r\n"; + + if(!response.m_mime_tipe.empty()) + { + buf += "Content-Type: "; + buf += response.m_mime_tipe + "\r\n"; + } buf += "Last-Modified: "; time_t tm; @@ -612,6 +630,19 @@ namespace net_utils m_want_close = true; } } + + // Cross-origin resource sharing + if(m_query_info.m_header_info.m_origin.size()) + { + if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin)) + { + buf += "Access-Control-Allow-Origin: "; + buf += m_query_info.m_header_info.m_origin; + buf += "\r\n"; + buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n"; + } + } + //add additional fields, if it is for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) buf += it->first + ":" + it->second + "\r\n"; diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 429e3e1af..429e3e1af 100644..100755 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index acecbb2d4..0788c6a4b 100644..100755 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -56,6 +56,7 @@ namespace epee {} bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", + std::vector<std::string> access_control_origins = std::vector<std::string>(), boost::optional<net_utils::http::login> user = boost::none) { @@ -65,6 +66,10 @@ namespace epee //here set folder for hosting reqests m_net_server.get_config_object().m_folder = ""; + //set access control allow origins if configured + std::sort(access_control_origins.begin(), access_control_origins.end()); + m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins); + m_net_server.get_config_object().m_user = std::move(user); MGINFO("Binding on " << bind_ip << ":" << bind_port); diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index c8e4c7818..ee0e13fc1 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -36,6 +36,7 @@ #include <istream> #include <ostream> #include <string> +#include <boost/version.hpp> #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <boost/asio/steady_timer.hpp> @@ -382,7 +383,7 @@ namespace net_utils char local_buff[10000] = {0}; - async_read(local_buff, boost::asio::transfer_at_least(1), hndlr); + async_read(local_buff, sizeof(local_buff), boost::asio::transfer_at_least(1), hndlr); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) @@ -463,7 +464,7 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); - async_read((char*)buff.data(), boost::asio::transfer_at_least(buff.size()), hndlr); + async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) @@ -576,7 +577,14 @@ namespace net_utils m_io_service.run_one(); } // Ignore "short read" error - if (ec.category() == boost::asio::error::get_ssl_category() && ec.value() != ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) + if (ec.category() == boost::asio::error::get_ssl_category() && + ec.value() != +#if BOOST_VERSION >= 106200 + boost::asio::ssl::error::stream_truncated +#else // older Boost supports only OpenSSL 1.0, so 1.0-only macros are appropriate + ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ) +#endif + ) MDEBUG("Problems at ssl shutdown: " << ec.message()); } @@ -599,12 +607,12 @@ namespace net_utils boost::asio::async_write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); } - void async_read(char* buff, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) + void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) { if(!m_ssl) - boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sizeof(buff)), transfer_at_least, hndlr); + boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); else - boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sizeof(buff)), transfer_at_least, hndlr); + boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); } diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index af3c6ca5f..3b41f415e 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -49,6 +49,5 @@ target_link_libraries(epee easylogging ${Boost_FILESYSTEM_LIBRARY} PRIVATE - ssl - crypto + ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 3979a5edf..5bd02bcf7 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1123,6 +1123,12 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) m_folder = filename; +#ifdef __OpenBSD__ + if ((mdb_flags & MDB_WRITEMAP) == 0) { + MCLOG_RED(el::Level::Info, "global", "Running on OpenBSD: forcing WRITEMAP"); + mdb_flags |= MDB_WRITEMAP; + } +#endif // set up lmdb environment if ((result = mdb_env_create(&m_env))) throw0(DB_ERROR(lmdb_error("Failed to create lmdb environment: ", result).c_str())); diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 0eaf71084..bd32e0c55 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -93,6 +93,7 @@ endif() set_property(TARGET blockchain_import PROPERTY OUTPUT_NAME "monero-blockchain-import") +install(TARGETS blockchain_import DESTINATION bin) monero_add_executable(blockchain_export ${blockchain_export_sources} @@ -114,4 +115,5 @@ target_link_libraries(blockchain_export set_property(TARGET blockchain_export PROPERTY OUTPUT_NAME "monero-blockchain-export") +install(TARGETS blockchain_export DESTINATION bin) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 5a61fc78c..e38e15cb2 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -150,7 +150,7 @@ namespace config uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; - uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 26; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; uint16_t const P2P_DEFAULT_PORT = 18080; uint16_t const RPC_DEFAULT_PORT = 18081; uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 933914363..9d89c6280 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -139,14 +139,20 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : bool Blockchain::have_tx(const crypto::hash &id) const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. return m_db->tx_exists(id); } //------------------------------------------------------------------ bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. return m_db->has_key_image(key_im); } //------------------------------------------------------------------ @@ -287,7 +293,10 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke uint64_t Blockchain::get_current_blockchain_height() const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. return m_db->height(); } //------------------------------------------------------------------ @@ -571,7 +580,10 @@ crypto::hash Blockchain::get_tail_id(uint64_t& height) const crypto::hash Blockchain::get_tail_id() const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. return m_db->top_block_hash(); } //------------------------------------------------------------------ @@ -633,7 +645,10 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const crypto::hash Blockchain::get_block_id_by_height(uint64_t height) const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. try { return m_db->get_block_hash_from_height(height); @@ -1928,7 +1943,10 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc uint64_t Blockchain::block_difficulty(uint64_t i) const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. try { return m_db->get_block_difficulty(i); @@ -2209,7 +2227,10 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_ size_t Blockchain::get_total_transactions() const { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. return m_db->get_tx_count(); } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 586df9079..96f6ee872 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -158,7 +158,7 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const cryptonote::account_public_address& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct) { if (destinations.empty()) { @@ -301,7 +301,7 @@ namespace cryptonote account_public_address single_dest_subaddress; for(const tx_destination_entry& dst_entr: destinations) { - if (dst_entr.addr == change_addr) + if (change_addr && dst_entr.addr == *change_addr) continue; if (unique_dst_addresses.count(dst_entr.addr) == 0) { @@ -354,7 +354,7 @@ namespace cryptonote } bool r; - if (dst_entr.addr == change_addr) + if (change_addr && dst_entr.addr == *change_addr) { // sending change to yourself; derivation = a*R r = crypto::generate_key_derivation(txkey.pub, sender_account_keys.m_view_secret_key, derivation); @@ -565,13 +565,13 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) { std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, destinations.back().addr, extra, tx, unlock_time, tx_key, additional_tx_keys); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index b5de44b88..9a3b2484d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -73,8 +73,8 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const cryptonote::account_public_address& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false); + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false); bool generate_genesis_block( block& bl diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index c85e5edb5..5307b2472 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -187,9 +187,24 @@ bool t_command_parser_executor::print_block(const std::vector<std::string>& args bool t_command_parser_executor::print_transaction(const std::vector<std::string>& args) { + bool include_hex = false; + bool include_json = false; + + // Assumes that optional flags come after mandatory argument <transaction_hash> + for (unsigned int i = 1; i < args.size(); ++i) { + if (args[i] == "+hex") + include_hex = true; + else if (args[i] == "+json") + include_json = true; + else + { + std::cout << "unexpected argument: " << args[i] << std::endl; + return true; + } + } if (args.empty()) { - std::cout << "expected: print_tx <transaction hash>" << std::endl; + std::cout << "expected: print_tx <transaction_hash> [+hex] [+json]" << std::endl; return true; } @@ -197,7 +212,7 @@ bool t_command_parser_executor::print_transaction(const std::vector<std::string> crypto::hash tx_hash; if (parse_hash256(str_hash, tx_hash)) { - m_executor.print_transaction(tx_hash); + m_executor.print_transaction(tx_hash, include_hex, include_json); } return true; diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 3f1543857..b134aee61 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -91,7 +91,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "print_tx" , std::bind(&t_command_parser_executor::print_transaction, &m_parser, p::_1) - , "Print transaction, print_tx <transaction_hash>" + , "Print transaction, print_tx <transaction_hash> [+hex] [+json]" ); m_command_lookup.set_handler( "is_key_image_spent" diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 867c523c9..2c11dcb31 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -693,7 +693,9 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) { return true; } -bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { +bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash, + bool include_hex, + bool include_json) { cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; @@ -728,30 +730,34 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { tools::success_msg_writer() << "Found in blockchain at height " << res.txs.front().block_height; } - // first as hex const std::string &as_hex = (1 == res.txs.size()) ? res.txs.front().as_hex : res.txs_as_hex.front(); - tools::success_msg_writer() << as_hex; + // Print raw hex if requested + if (include_hex) + tools::success_msg_writer() << as_hex << std::endl; - // then as json - crypto::hash tx_hash, tx_prefix_hash; - cryptonote::transaction tx; - cryptonote::blobdata blob; - if (!string_tools::parse_hexstr_to_binbuff(as_hex, blob)) - { - tools::fail_msg_writer() << "Failed to parse tx"; - } - else if (!cryptonote::parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash)) + // Print json if requested + if (include_json) { - tools::fail_msg_writer() << "Failed to parse tx blob"; - } - else - { - tools::success_msg_writer() << cryptonote::obj_to_json_str(tx) << std::endl; + crypto::hash tx_hash, tx_prefix_hash; + cryptonote::transaction tx; + cryptonote::blobdata blob; + if (!string_tools::parse_hexstr_to_binbuff(as_hex, blob)) + { + tools::fail_msg_writer() << "Failed to parse tx to get json format"; + } + else if (!cryptonote::parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash)) + { + tools::fail_msg_writer() << "Failed to parse tx blob to get json format"; + } + else + { + tools::success_msg_writer() << cryptonote::obj_to_json_str(tx) << std::endl; + } } } else { - tools::fail_msg_writer() << "transaction wasn't found: " << transaction_hash << std::endl; + tools::fail_msg_writer() << "Transaction wasn't found: " << transaction_hash << std::endl; } return true; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index c9d0ac671..efe1ae56a 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -95,7 +95,7 @@ public: bool print_block_by_height(uint64_t height); - bool print_transaction(crypto::hash transaction_hash); + bool print_transaction(crypto::hash transaction_hash, bool include_hex, bool include_json); bool is_key_image_spent(const crypto::key_image &ki); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9095aacae..b3ce30d0c 100644..100755 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -101,7 +101,7 @@ namespace cryptonote http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); return epee::http_server_impl_base<core_rpc_server, connection_context>::init( - std::move(port), std::move(rpc_config->bind_ip), std::move(http_login) + std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 086820180..d6da124d1 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -45,6 +45,15 @@ const char* Message::STATUS_FAILED = "Failed"; const char* Message::STATUS_BAD_REQUEST = "Invalid request type"; const char* Message::STATUS_BAD_JSON = "Malformed json"; +namespace +{ +constexpr const char error_field[] = "error"; +constexpr const char id_field[] = "id"; +constexpr const char method_field[] = "method"; +constexpr const char params_field[] = "params"; +constexpr const char result_field[] = "result"; +} + rapidjson::Value Message::toJson(rapidjson::Document& doc) const { rapidjson::Value val(rapidjson::kObjectType); @@ -70,8 +79,8 @@ FullMessage::FullMessage(const std::string& request, Message* message) { doc.SetObject(); - doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator()); - doc.AddMember("params", message->toJson(doc), doc.GetAllocator()); + doc.AddMember(method_field, rapidjson::StringRef(request.c_str()), doc.GetAllocator()); + doc.AddMember(params_field, message->toJson(doc), doc.GetAllocator()); // required by JSON-RPC 2.0 spec doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator()); @@ -86,7 +95,7 @@ FullMessage::FullMessage(Message* message) if (message->status == Message::STATUS_OK) { - doc.AddMember("response", message->toJson(doc), doc.GetAllocator()); + doc.AddMember(result_field, message->toJson(doc), doc.GetAllocator()); } else { @@ -111,14 +120,14 @@ FullMessage::FullMessage(const std::string& json_string, bool request) if (request) { - OBJECT_HAS_MEMBER_OR_THROW(doc, "method") - OBJECT_HAS_MEMBER_OR_THROW(doc, "params") + OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) + OBJECT_HAS_MEMBER_OR_THROW(doc, params_field) } else { - if (!doc.HasMember("response") && !doc.HasMember("error")) + if (!doc.HasMember(result_field) && !doc.HasMember(error_field)) { - throw cryptonote::json::MISSING_KEY("error/response"); + throw cryptonote::json::MISSING_KEY("error/result"); } } } @@ -126,9 +135,9 @@ FullMessage::FullMessage(const std::string& json_string, bool request) std::string FullMessage::getJson() { - if (!doc.HasMember("id")) + if (!doc.HasMember(id_field)) { - doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator()); + doc.AddMember(id_field, rapidjson::Value("unused"), doc.GetAllocator()); } rapidjson::StringBuffer buf; @@ -142,24 +151,24 @@ std::string FullMessage::getJson() std::string FullMessage::getRequestType() const { - OBJECT_HAS_MEMBER_OR_THROW(doc, "method") - return doc["method"].GetString(); + OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) + return doc[method_field].GetString(); } rapidjson::Value& FullMessage::getMessage() { - if (doc.HasMember("params")) + if (doc.HasMember(params_field)) { - return doc["params"]; + return doc[params_field]; } - else if (doc.HasMember("response")) + else if (doc.HasMember(result_field)) { - return doc["response"]; + return doc[result_field]; } //else - OBJECT_HAS_MEMBER_OR_THROW(doc, "error") - return doc["error"]; + OBJECT_HAS_MEMBER_OR_THROW(doc, error_field) + return doc[error_field]; } @@ -172,20 +181,20 @@ rapidjson::Value FullMessage::getMessageCopy() rapidjson::Value& FullMessage::getID() { - OBJECT_HAS_MEMBER_OR_THROW(doc, "id") - return doc["id"]; + OBJECT_HAS_MEMBER_OR_THROW(doc, id_field) + return doc[id_field]; } void FullMessage::setID(rapidjson::Value& id) { - auto itr = doc.FindMember("id"); + auto itr = doc.FindMember(id_field); if (itr != doc.MemberEnd()) { itr->value = id; } else { - doc.AddMember("id", id, doc.GetAllocator()); + doc.AddMember(id_field, id, doc.GetAllocator()); } } @@ -193,7 +202,7 @@ cryptonote::rpc::error FullMessage::getError() { cryptonote::rpc::error err; err.use = false; - if (doc.HasMember("error")) + if (doc.HasMember(error_field)) { GET_FROM_JSON_OBJECT(doc, err, error); err.use = true; diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 4435f74d1..93309bf3c 100644..100755 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -28,6 +28,7 @@ // #include "rpc_args.h" +#include <boost/algorithm/string.hpp> #include <boost/asio/ip/address.hpp> #include "common/command_line.h" #include "common/i18n.h" @@ -38,6 +39,7 @@ namespace cryptonote : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"}) , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")}) + , rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""}) {} const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } @@ -48,6 +50,7 @@ namespace cryptonote command_line::add_arg(desc, arg.rpc_bind_ip); command_line::add_arg(desc, arg.rpc_login); command_line::add_arg(desc, arg.confirm_external_bind); + command_line::add_arg(desc, arg.rpc_access_control_origins); } boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm) @@ -91,6 +94,21 @@ namespace cryptonote } } + auto access_control_origins_input = command_line::get_arg(vm, arg.rpc_access_control_origins); + if (!access_control_origins_input.empty()) + { + if (!config.login) + { + LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RFC server password --") << arg.rpc_login.name << tr(" cannot be empty")); + return boost::none; + } + + std::vector<std::string> access_control_origins; + boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(",")); + std::for_each(access_control_origins.begin(), access_control_origins.end(), boost::bind(&boost::trim<std::string>, _1, std::locale::classic())); + config.access_control_origins = std::move(access_control_origins); + } + return {std::move(config)}; } } diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index d6e7bab07..72b5aa706 100644..100755 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -53,6 +53,7 @@ namespace cryptonote const command_line::arg_descriptor<std::string> rpc_bind_ip; const command_line::arg_descriptor<std::string> rpc_login; const command_line::arg_descriptor<bool> confirm_external_bind; + const command_line::arg_descriptor<std::string> rpc_access_control_origins; }; static const char* tr(const char* str); @@ -62,6 +63,7 @@ namespace cryptonote static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm); std::string bind_ip; + std::vector<std::string> access_control_origins; boost::optional<tools::login> login; // currently `boost::none` if unspecified by user }; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a0d9a32c6..c936f481e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -107,7 +107,7 @@ enum TransferType { namespace { - const auto allowed_priority_strings = {"default", "unimportant", "normal", "elevated", "priority"}; + const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}}; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; @@ -278,6 +278,18 @@ namespace } } +bool parse_priority(const std::string& arg, uint32_t& priority) +{ + auto priority_pos = std::find( + allowed_priority_strings.begin(), + allowed_priority_strings.end(), + arg); + if(priority_pos != allowed_priority_strings.end()) { + priority = std::distance(allowed_priority_strings.begin(), priority_pos); + return true; + } + return false; +} std::string simple_wallet::get_commands_str() { @@ -820,8 +832,8 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>] - Same as transfer, but with number of blocks to lock the transaction for, max 1000000")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); - m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [index=<N1>[,<N2>,...]] [<ring_size>] <address> [<payment_id>] - Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); - m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<ring_size>] <address> [<payment_id>] - Send all unlocked outputs below the threshold to an address")); + m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); + m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked outputs below the threshold to an address")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - Donate <amount> to the development team (donate.getmonero.org)")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file")); @@ -2485,17 +2497,9 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri local_args.erase(local_args.begin()); } - int priority = 0; - if(local_args.size() > 0) { - auto priority_pos = std::find( - allowed_priority_strings.begin(), - allowed_priority_strings.end(), - local_args[0]); - if(priority_pos != allowed_priority_strings.end()) { - local_args.erase(local_args.begin()); - priority = std::distance(allowed_priority_strings.begin(), priority_pos); - } - } + uint32_t priority = 0; + if (local_args.size() > 0 && parse_priority(local_args[0], priority)) + local_args.erase(local_args.begin()); size_t fake_outs_count = 0; if(local_args.size() > 0) { @@ -2823,13 +2827,20 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } - catch (const tools::error::not_enough_money& e) + catch (const tools::error::not_enough_unlocked_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); } + catch (const tools::error::not_enough_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in overall balance"); + } catch (const tools::error::tx_not_possible& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % @@ -2995,13 +3006,20 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } - catch (const tools::error::not_enough_money& e) + catch (const tools::error::not_enough_unlocked_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); } + catch (const tools::error::not_enough_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in overall balance"); + } catch (const tools::error::tx_not_possible& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % @@ -3084,6 +3102,10 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a local_args.erase(local_args.begin()); } + uint32_t priority = 0; + if (local_args.size() > 0 && parse_priority(local_args[0], priority)) + local_args.erase(local_args.begin()); + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; @@ -3188,7 +3210,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_current_subaddress_account, subaddr_indices, m_trusted_daemon); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, m_trusted_daemon); if (ptx_vector.empty()) { @@ -3275,13 +3297,20 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } - catch (const tools::error::not_enough_money& e) + catch (const tools::error::not_enough_unlocked_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); } + catch (const tools::error::not_enough_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in overall balance"); + } catch (const tools::error::tx_not_possible& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % @@ -3625,13 +3654,20 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } - catch (const tools::error::not_enough_money& e) + catch (const tools::error::not_enough_unlocked_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); } + catch (const tools::error::not_enough_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in overall balance"); + } catch (const tools::error::tx_not_possible& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index db7e60cd7..8e747d16b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1044,8 +1044,7 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex // - confirmed_transfer_details) PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority priority) + PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) { clearStatus(); @@ -1147,7 +1146,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const m_errorString = (boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str(); m_status = Status_Error; - } catch (const tools::error::not_enough_money& e) { + } catch (const tools::error::not_enough_unlocked_money& e) { m_status = Status_Error; std::ostringstream writer; @@ -1156,6 +1155,15 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const print_money(e.tx_amount()); m_errorString = writer.str(); + } catch (const tools::error::not_enough_money& e) { + m_status = Status_Error; + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + m_errorString = writer.str(); + } catch (const tools::error::tx_not_possible& e) { m_status = Status_Error; std::ostringstream writer; @@ -1241,7 +1249,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() m_errorString = tr("failed to get random outputs to mix"); m_status = Status_Error; - } catch (const tools::error::not_enough_money& e) { + } catch (const tools::error::not_enough_unlocked_money& e) { m_status = Status_Error; std::ostringstream writer; @@ -1250,6 +1258,15 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() print_money(e.tx_amount()); m_errorString = writer.str(); + } catch (const tools::error::not_enough_money& e) { + m_status = Status_Error; + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + m_errorString = writer.str(); + } catch (const tools::error::tx_not_possible& e) { m_status = Status_Error; std::ostringstream writer; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index ecb218ea0..051eda3ba 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -73,7 +73,7 @@ public: int status() const; std::string errorString() const; bool setPassword(const std::string &password); - std::string address(uint32_t accountIndex, uint32_t addressIndex) const; + std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const; std::string integratedAddress(const std::string &payment_id) const; std::string secretViewKey() const; std::string publicViewKey() const; @@ -88,8 +88,8 @@ public: ConnectionStatus connected() const; void setTrustedDaemon(bool arg); bool trustedDaemon() const; - uint64_t balance(uint32_t accountIndex) const; - uint64_t unlockedBalance(uint32_t accountIndex) const; + uint64_t balance(uint32_t accountIndex = 0) const; + uint64_t unlockedBalance(uint32_t accountIndex = 0) const; uint64_t blockChainHeight() const; uint64_t approximateBlockChainHeight() const; uint64_t daemonBlockChainHeight() const; @@ -117,9 +117,9 @@ public: PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, - std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low); + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set<uint32_t> subaddr_indices = {}); virtual PendingTransaction * createSweepUnmixableTransaction(); bool submitTransaction(const std::string &fileName); virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 2e05b1484..f0eaf2331 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -692,20 +692,20 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) //---------------------------------------------------------------------------------------------------- std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& index) const { - if (index.major >= m_subaddress_labels.size()) - throw std::runtime_error("index.major is out of bound"); - if (index.minor >= m_subaddress_labels[index.major].size()) - throw std::runtime_error("index.minor is out of bound"); + if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size()) + { + MERROR("Subaddress label doesn't exist"); + return ""; + } return m_subaddress_labels[index.major][index.minor]; } //---------------------------------------------------------------------------------------------------- void wallet2::set_subaddress_label(const cryptonote::subaddress_index& index, const std::string &label) { - if (index.major >= m_subaddress_labels.size()) - throw std::runtime_error("index.major is out of bound"); - if (index.minor >= m_subaddress_labels[index.major].size()) - throw std::runtime_error("index.minor is out of bound"); - m_subaddress_labels[index.major][index.minor] = label; + if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size()) + MERROR("Subaddress index is out of bounds. Failed to set subaddress label."); + else + m_subaddress_labels[index.major][index.minor] = label; } //---------------------------------------------------------------------------------------------------- /*! @@ -788,7 +788,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask); } - tx_money_got_in_outs[tx_scan_info.received->index] = tx_scan_info.money_transfered; + tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; tx_scan_info.amount = tx_scan_info.money_transfered; ++num_vouts_received; } @@ -1520,7 +1520,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes) { // remove pool txes to us that aren't in the pool anymore - std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); + std::unordered_multimap<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { const crypto::hash &txid = uit->second.m_tx_hash; @@ -4441,7 +4441,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) @@ -4598,7 +4598,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) @@ -4751,7 +4751,7 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) size += (2*64*32+32+64*32) * n_outputs; // MGs - size += n_inputs * (32 * (mixin+1) + 32); + size += n_inputs * (64 * (mixin+1) + 32); // mixRing - not serialized, can be reconstructed /* size += 2 * 32 * (mixin+1) * n_inputs; */ @@ -5475,6 +5475,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } } + // early out if we know we can't make it anyway + // we could also check for being within FEE_PER_KB, but if the fee calculation + // ever changes, this might be missed, so let this go through + // first check overall balance is enough, then unlocked one, so we throw distinct exceptions + THROW_WALLET_EXCEPTION_IF(needed_money > balance(), error::not_enough_money, + unlocked_balance(), needed_money, 0); + THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance(), error::not_enough_unlocked_money, + unlocked_balance(), needed_money, 0); + // shuffle & sort output indices { std::random_device rd; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2ef1e56d2..f1e12a700 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -467,8 +467,8 @@ namespace tools size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; } void add_subaddress(uint32_t index_major, const std::string& label); // throws when index is out of bound void expand_subaddresses(const cryptonote::subaddress_index& index); - std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; // throws when index is out of bound - void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); // throws when index is out of bound + std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; + void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); /*! * \brief Tells if the wallet file is deprecated. */ @@ -1068,7 +1068,10 @@ namespace boost x.m_amount_out += x.m_change; } if (ver < 7) + { + x.m_subaddr_account = 0; return; + } a & x.m_subaddr_account; a & x.m_subaddr_indices; } @@ -1109,7 +1112,10 @@ namespace boost } a & x.m_unlock_time; if (ver < 5) + { + x.m_subaddr_account = 0; return; + } a & x.m_subaddr_account; a & x.m_subaddr_indices; } @@ -1125,7 +1131,10 @@ namespace boost return; a & x.m_timestamp; if (ver < 2) + { + x.m_subaddr_index = {}; return; + } a & x.m_subaddr_index; } @@ -1136,7 +1145,10 @@ namespace boost a & x.m_payment_id; a & x.m_description; if (ver < 17) + { + x.m_is_subaddress = false; return; + } a & x.m_is_subaddress; } @@ -1166,7 +1178,10 @@ namespace boost a & x.use_rct; a & x.dests; if (ver < 1) + { + x.subaddr_account = 0; return; + } a & x.subaddr_account; a & x.subaddr_indices; } @@ -1275,10 +1290,10 @@ namespace tools } // randomly select inputs for transaction - // throw if requested send amount is greater than amount available to send + // throw if requested send amount is greater than (unlocked) amount available to send std::list<size_t> selected_transfers; uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 4d734ab94..a8c150ca7 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -360,7 +360,7 @@ struct Wallet //! in case error status, returns error string virtual std::string errorString() const = 0; virtual bool setPassword(const std::string &password) = 0; - virtual std::string address(uint32_t accountIndex, uint32_t addressIndex) const = 0; + virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; std::string mainAddress() const { return address(0, 0); } virtual std::string path() const = 0; virtual bool testnet() const = 0; @@ -476,14 +476,14 @@ struct Wallet virtual ConnectionStatus connected() const = 0; virtual void setTrustedDaemon(bool arg) = 0; virtual bool trustedDaemon() const = 0; - virtual uint64_t balance(uint32_t accountIndex) const = 0; + virtual uint64_t balance(uint32_t accountIndex = 0) const = 0; uint64_t balanceAll() const { uint64_t result = 0; for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) result += balance(i); return result; } - virtual uint64_t unlockedBalance(uint32_t accountIndex) const = 0; + virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0; uint64_t unlockedBalanceAll() const { uint64_t result = 0; for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) @@ -623,9 +623,9 @@ struct Wallet virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, - std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set<uint32_t> subaddr_indices = {}) = 0; /*! * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index d1f4a796d..9d66f125e 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -68,6 +68,7 @@ namespace tools // get_tx_pool_error // transfer_error * // get_random_outs_general_error + // not_enough_unlocked_money // not_enough_money // tx_not_possible // not_enough_outs_to_mix @@ -356,11 +357,37 @@ namespace tools //---------------------------------------------------------------------------------------------------- typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error; //---------------------------------------------------------------------------------------------------- + struct not_enough_unlocked_money : public transfer_error + { + explicit not_enough_unlocked_money(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) + : transfer_error(std::move(loc), "not enough unlocked money") + , m_available(available) + , m_tx_amount(tx_amount) + { + } + + uint64_t available() const { return m_available; } + uint64_t tx_amount() const { return m_tx_amount; } + + std::string to_string() const + { + std::ostringstream ss; + ss << transfer_error::to_string() << + ", available = " << cryptonote::print_money(m_available) << + ", tx_amount = " << cryptonote::print_money(m_tx_amount); + return ss.str(); + } + + private: + uint64_t m_available; + uint64_t m_tx_amount; + }; + //---------------------------------------------------------------------------------------------------- struct not_enough_money : public transfer_error { - explicit not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit not_enough_money(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "not enough money") - , m_available(availbable) + , m_available(available) , m_tx_amount(tx_amount) { } @@ -384,9 +411,9 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_not_possible : public transfer_error { - explicit tx_not_possible(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit tx_not_possible(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "tx not possible") - , m_available(availbable) + , m_available(available) , m_tx_amount(tx_amount) , m_fee(fee) { diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b360a3efd..9e6a97bdc 100644..100755 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -216,7 +216,7 @@ namespace tools m_net_server.set_threads_prefix("RPC"); return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( - std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login) + std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -341,8 +341,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -369,8 +368,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -406,8 +404,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -436,8 +433,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -454,8 +450,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -476,8 +471,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -492,8 +486,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -618,7 +611,7 @@ namespace tools // reject proposed transactions if there are more than one. see on_transfer_split below. if (ptx_vector.size() != 1) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.code = WALLET_RPC_ERROR_CODE_TX_TOO_LARGE; er.message = "Transaction would be too large. try /transfer_split."; return false; } @@ -642,22 +635,9 @@ namespace tools } return true; } - catch (const tools::error::daemon_busy& e) - { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; - } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } - catch (...) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -725,22 +705,9 @@ namespace tools return true; } - catch (const tools::error::daemon_busy& e) - { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; - } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } - catch (...) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -782,22 +749,9 @@ namespace tools return true; } - catch (const tools::error::daemon_busy& e) - { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; - } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } - catch (...) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -852,22 +806,9 @@ namespace tools return true; } - catch (const tools::error::daemon_busy& e) - { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; - } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } - catch (...) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -897,10 +838,9 @@ namespace tools res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -929,10 +869,9 @@ namespace tools res.payment_id = epee::string_tools::pod_to_hex(info.payment_id); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -954,8 +893,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1185,8 +1123,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1258,8 +1195,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1523,10 +1459,9 @@ namespace tools } } - catch (...) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Failed"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } @@ -1579,10 +1514,9 @@ namespace tools res.height = height; } - catch (...) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Failed"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } @@ -1759,10 +1693,9 @@ namespace tools m_wallet->rescan_spent(); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1827,7 +1760,7 @@ namespace tools { if (m_wallet_dir.empty()) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; er.message = "No wallet dir configured"; return false; } @@ -1895,6 +1828,11 @@ namespace tools } catch (const std::exception& e) { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + if (!wal) + { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to generate wallet"; return false; @@ -1909,7 +1847,7 @@ namespace tools { if (m_wallet_dir.empty()) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; er.message = "No wallet dir configured"; return false; } @@ -1943,13 +1881,13 @@ namespace tools command_line::add_arg(desc, arg_password); po::store(po::parse_command_line(argc, argv, desc), vm2); } - std::unique_ptr<tools::wallet2> wal; + std::unique_ptr<tools::wallet2> wal = nullptr; try { wal = tools::wallet2::make_from_file(vm2, wallet_file).first; } catch (const std::exception& e) { - wal = nullptr; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); } if (!wal) { @@ -1963,6 +1901,63 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) { + try + { + std::rethrow_exception(e); + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + } + catch (const tools::error::zero_destination& e) + { + er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION; + er.message = e.what(); + } + catch (const tools::error::not_enough_money& e) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY; + er.message = e.what(); + } + catch (const tools::error::tx_not_possible& e) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = (boost::format(tr("Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) % + cryptonote::print_money(e.available()) % + cryptonote::print_money(e.tx_amount() + e.fee()) % + cryptonote::print_money(e.tx_amount()) % + cryptonote::print_money(e.fee())).str(); + er.message = e.what(); + } + catch (const tools::error::not_enough_outs_to_mix& e) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX; + er.message = e.what(); + } + catch (const error::file_exists& e) + { + er.code = WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS; + er.message = "Cannot create wallet. Already exists."; + } + catch (const error::invalid_password& e) + { + er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD; + er.message = "Invalid password."; + } + catch (const std::exception& e) + { + er.code = default_error_code; + er.message = e.what(); + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + } + } + //------------------------------------------------------------------------------------------------------------------------------ } int main(int argc, char** argv) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 02c1d4082..b38726cb7 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -166,6 +166,7 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); bool not_open(epee::json_rpc::error& er); uint64_t adjust_mixin(uint64_t mixin); + void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code); wallet2 *m_wallet; std::string m_wallet_dir; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index cc9fd3856..e74e9110b 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -46,3 +46,11 @@ #define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 #define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND -14 #define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND -15 +#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE -16 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -17 +#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE -18 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX -19 +#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION -20 +#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS -21 +#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD -22 +#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23 diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..48a6c41a7 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,117 @@ +# Running all tests + +To run all tests, run: + +``` +cd /path/to/monero +make [-jn] debug-test # where n is number of compiler processes +``` + +To test a release build, replace `debug-test` with `release-test` in the previous command. + +# Core tests + +Core tests take longer than any other Monero tests, due to the high amount of computational work involved in validating core components. + +Tests are located in `tests/core_tests/`, and follow a straightforward naming convention. Most cases cover core functionality (`block_reward.cpp`, `chaingen.cpp`, `rct.cpp`, etc.), while some cover basic security tests (`double_spend.cpp` & `integer_overflow.cpp`). + +To run only Monero's core tests (after building): + +``` +cd build/debug/tests/core +ctest +``` + +To run the same tests on a release build, replace `debug` with `release`. + + +# Crypto Tests + +Crypto tests are located under the `tests/crypto` directory. + +- `crypto-tests.h` contains test harness headers +- `main.cpp` implements the driver for the crypto tests + +Tests correspond to components under `src/crypto/`. A quick comparison reveals the pattern, and new tests should continue the naming convention. + +To run only Monero's crypto tests (after building): + +``` +cd build/debug/tests/crypto +ctest +``` + +To run the same tests on a release build, replace `debug` with `release`. + +# Daemon tests + +[TODO] + +# Functional tests + +[TODO] + +# Fuzz tests + +Fuzz tests are written using American Fuzzy Lop (AFL), and located under the `tests/fuzz` directory. + +An additional helper utility is provided `contrib/fuzz_testing/fuzz.sh`. AFL must be installed, and some additional setup may be necessary for the script to run properly. + +# Hash tests + +Hash tests exist under `tests/hash`, and include a set of target hashes in text files. + +To run only Monero's hash tests (after building): + +``` +cd build/debug/tests/hash +ctest +``` + +To run the same tests on a release build, replace `debug` with `release`. + +# Libwallet API tests + +[TODO] + +# Net Load tests + +[TODO] + +# Performance tests + +Performance tests are located in `tests/performance_tests`, and test features for performance metrics on the host machine. + +To run only Monero's performance tests (after building): + +``` +cd build/debug/tests/performance_tests +./performance_tests +``` + +If the `performance_tests` binary does not exist, try running `make` in the `build/debug/tests/performance_tests` directory. + +To run the same tests on a release build, replace `debug` with `release`. + +# Unit tests + +Unit tests are defined under the `tests/unit_tests` directory. Independent components are tested individually to ensure they work properly on their own. + +To run only Monero's unit tests (after building): + +``` +cd build/debug/tests/unit_tests +ctest +``` + +To run the same tests on a release build, replace `debug` with `release`. + +# Writing new tests + +## Test hygiene + +When writing new tests, please implement all functions in `.cpp` or `.c` files, and only put function headers in `.h` files. This will help keep the fairly complex test suites somewhat sane going forward. + +## Writing fuzz tests + +[TODO] diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 527059141..db44cd279 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -350,7 +350,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events destinations.push_back(de); transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); @@ -393,7 +393,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry> destinations.push_back(de); transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_1); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index f23aa8ecb..2b713cab9 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -563,7 +563,7 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote vector<tx_destination_entry> destinations; fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations); - return construct_tx(from.get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0); + return construct_tx(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0); } transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head, diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 438e39e47..bf63503ae 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -144,7 +144,7 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even destinations.push_back(de); cryptonote::transaction tx_1; - if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_1, 0)) + if (!construct_tx(bob_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_1, 0)) return false; SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 3ac55c073..5a9604fc1 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -65,6 +65,7 @@ namespace se.real_output = 0; se.rct = false; se.real_out_tx_key = get_tx_pub_key_from_extra(tx); + se.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(tx); se.real_output_in_tx_index = out_idx; sources.push_back(se); @@ -175,7 +176,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr, false)); cryptonote::transaction tx_1; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_1, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_1, 0)) return false; events.push_back(tx_1); @@ -201,7 +202,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const destinations.push_back(de); cryptonote::transaction tx_2; - if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_2, 0)) + if (!construct_tx(bob_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_2, 0)) return false; events.push_back(tx_2); diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index dcba36e80..00c602103 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -108,7 +108,7 @@ bool test_transaction_generation_and_ring_signature() destinations.push_back(td); transaction tx_rc1; - bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_rc1, 0); + bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_rc1, 0); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); crypto::hash pref_hash = get_transaction_prefix_hash(tx_rc1); diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index f1256cb64..6c94ac76c 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -108,7 +108,7 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve destinations.push_back(td); transaction tx; - bool r = construct_tx(miner_accounts[0].get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0); + bool r = construct_tx(miner_accounts[0].get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); if (!valid) DO_CALLBACK(events, "mark_invalid_tx"); diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index a3cf227de..853ad7c8d 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -580,9 +580,9 @@ TEST_F(WalletTest1, WalletTransaction) PAYMENT_ID_EMPTY, AMOUNT_10XMR, MIXIN_COUNT, + Monero::PendingTransaction::Priority_Medium, 0, - std::set<uint32_t>{}, - Monero::PendingTransaction::Priority_Medium); + std::set<uint32_t>{}); ASSERT_TRUE(transaction->status() == Monero::PendingTransaction::Status_Ok); wallet1->refresh(); @@ -621,7 +621,7 @@ TEST_F(WalletTest1, WalletTransactionWithMixin) std::cerr << "Transaction mixin count: " << mixin << std::endl; Monero::PendingTransaction * transaction = wallet1->createTransaction( - recepient_address, payment_id, AMOUNT_5XMR, mixin, 0, std::set<uint32_t>{}); + recepient_address, payment_id, AMOUNT_5XMR, mixin, Monero::PendingTransaction::Priority_Medium, 0, std::set<uint32_t>{}); std::cerr << "Transaction status: " << transaction->status() << std::endl; std::cerr << "Transaction fee: " << Monero::Wallet::displayAmount(transaction->fee()) << std::endl; @@ -663,7 +663,7 @@ TEST_F(WalletTest1, WalletTransactionWithPriority) std::cerr << "Transaction priority: " << *it << std::endl; Monero::PendingTransaction * transaction = wallet1->createTransaction( - recepient_address, payment_id, AMOUNT_5XMR, mixin, 0, std::set<uint32_t>{}, *it); + recepient_address, payment_id, AMOUNT_5XMR, mixin, *it, 0, std::set<uint32_t>{}); std::cerr << "Transaction status: " << transaction->status() << std::endl; std::cerr << "Transaction fee: " << Monero::Wallet::displayAmount(transaction->fee()) << std::endl; std::cerr << "Transaction error: " << transaction->errorString() << std::endl; @@ -719,7 +719,7 @@ TEST_F(WalletTest1, WalletTransactionAndHistory) Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr, PAYMENT_ID_EMPTY, - AMOUNT_10XMR * 5, 1, 0, std::set<uint32_t>{}); + AMOUNT_10XMR * 5, 1, Monero::PendingTransaction::Priority_Medium, 0, std::set<uint32_t>{}); ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->commit()); @@ -761,7 +761,7 @@ TEST_F(WalletTest1, WalletTransactionWithPaymentId) Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr, payment_id, - AMOUNT_1XMR, 1, 0, std::set<uint32_t>{}); + AMOUNT_1XMR, 1, Monero::PendingTransaction::Priority_Medium, 0, std::set<uint32_t>{}); ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->commit()); @@ -934,7 +934,7 @@ TEST_F(WalletTest2, WalletCallbackSent) Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->mainAddress(), PAYMENT_ID_EMPTY, - amount, 1, 0, std::set<uint32_t>{}); + amount, 1, Monero::PendingTransaction::Priority_Medium, 0, std::set<uint32_t>{}); std::cout << "** Committing transaction: " << Monero::Wallet::displayAmount(tx->amount()) << " with fee: " << Monero::Wallet::displayAmount(tx->fee()); @@ -975,7 +975,7 @@ TEST_F(WalletTest2, WalletCallbackReceived) std::cout << "** Sending " << Monero::Wallet::displayAmount(amount) << " to " << wallet_dst->mainAddress(); Monero::PendingTransaction * tx = wallet_src->createTransaction(wallet_dst->mainAddress(), PAYMENT_ID_EMPTY, - amount, 1, 0, std::set<uint32_t>{}); + amount, 1, Monero::PendingTransaction::Priority_Medium, 0, std::set<uint32_t>{}); std::cout << "** Committing transaction: " << Monero::Wallet::displayAmount(tx->amount()) << " with fee: " << Monero::Wallet::displayAmount(tx->fee()); diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index a5e64aeb2..ea72fcd26 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -54,7 +54,7 @@ public: std::vector<tx_destination_entry> destinations; destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); - return construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector<uint8_t>(), m_tx, 0); + return construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_tx, 0); } bool test() diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 7da061c1e..c31897628 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -64,6 +64,7 @@ public: { const cryptonote::txout_to_key& tx_out = boost::get<cryptonote::txout_to_key>(m_tx.vout[0].target); std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; + subaddresses[m_bob.get_keys().m_account_address.m_spend_public_key] = {0,0}; std::vector<crypto::key_derivation> additional_derivations; boost::optional<cryptonote::subaddress_receive_info> info = cryptonote::is_out_to_acc_precomp(subaddresses, tx_out.key, m_derivation, additional_derivations, 0); return (bool)info; diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 3a8e787ec..51e26c2bb 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -47,6 +47,14 @@ namespace "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + static std::uint8_t md[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + template<typename T> bool is_formatted() { @@ -61,6 +69,26 @@ namespace out << "BEGIN" << value << "END"; return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END"; } + + bool keccak_harness() + { + size_t inlen = sizeof(source); + int mdlen = (int)sizeof(md); + int ret = keccak(source, inlen, md, mdlen); + + if (md[0] != 0x00) + { + return true; + } + else if (!ret) + { + return true; + } + else + { + return false; + } + } } TEST(Crypto, Ostream) diff --git a/tests/unit_tests/sha256.cpp b/tests/unit_tests/sha256.cpp index f4ad39466..0d657a1a9 100644 --- a/tests/unit_tests/sha256.cpp +++ b/tests/unit_tests/sha256.cpp @@ -28,8 +28,8 @@ #include "gtest/gtest.h" -#include "string_tools.h" #include "common/util.h" +#include "string_tools.h" static bool check(const std::string &data, const char *expected_hash_hex) { |