diff options
55 files changed, 904 insertions, 500 deletions
@@ -30,10 +30,10 @@ Our researchers are available on IRC in [#monero-research-lab on Freenode](https - You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed. ## Translations -The CLI wallet is available in different languages. If you want to help translate it, join Pootle, our self-hosted localization platform: [translate.getmonero.org](https://translate.getmonero.org). Every translation *must* be uploaded on Pootle, pull requests directly editing the code on this repository will be closed. +The CLI wallet is available in different languages. If you want to help translate it, see our self-hosted localization platform, Pootle, on [translate.getmonero.org](https://translate.getmonero.org/projects/CLI/). Every translation *must* be uploaded on the platform, pull requests directly editing the code in this repository will be closed. If you need help with Pootle, you can find a guide with screenshots [here](https://github.com/monero-ecosystem/monero-translations/blob/master/pootle.md). -If you need help/support/info, contact the localization workgroup. You can do so in various ways: by email (translate[at]getmonero[dot]org), on the official chat (#monero-translations). A complete list of contacts can be found on the repository of the workgroup: [monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts). +If you need help/support/info about translations, contact the localization workgroup. You can find the complete list of contacts on the repository of the workgroup: [monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts). ## Build @@ -180,8 +180,8 @@ Install all dependencies at once on Debian/Ubuntu: ``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` -Install all dependencies at once on macOS: -``` brew update && brew install cmake pkg-config openssl boost hidapi zmq libpgm unbound libsodium miniupnpc readline ldns expat doxygen graphviz protobuf ``` +Install all dependencies at once on macOS with the provided Brewfile: +``` brew update && brew bundle --file=contrib/brew/Brewfile ``` FreeBSD one liner for required to build dependencies ```pkg install git gmake cmake pkgconf boost-libs cppzmq libsodium``` diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake index 7e3bf156e..c7e09d4c0 100644 --- a/cmake/FindLibUSB.cmake +++ b/cmake/FindLibUSB.cmake @@ -99,9 +99,18 @@ if ( LibUSB_FOUND ) check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 ) check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 ) + if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux")) + find_library(LIBUDEV_LIBRARY udev) + if(LIBUDEV_LIBRARY) + set(LibUSB_LIBRARIES "${LibUSB_LIBRARIES};${LIBUDEV_LIBRARY}") + else() + message(WARNING "libudev library not found, binaries may fail to link.") + endif() + endif() + # Library 1.0.16+ compilation test. # The check_library_exists does not work well on Apple with shared libs. - if (APPLE OR LibUSB_VERSION_1.0.16) + if (APPLE OR LibUSB_VERSION_1.0.16 OR STATIC) if (APPLE) if(DEPENDS) list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit") diff --git a/contrib/brew/Brewfile b/contrib/brew/Brewfile new file mode 100644 index 000000000..1fdf45cfe --- /dev/null +++ b/contrib/brew/Brewfile @@ -0,0 +1,34 @@ +# Brewfile for Monero +# A homebrew Brewfile installs all required dependencies in one shot +# see https://coderwall.com/p/afmnbq/homebrew-s-new-feature-brewfiles +# https://github.com/Homebrew/homebrew-bundle +# execute brew bundle in the directory containing the Brewfile + +tap "homebrew/bundle" +tap "homebrew/cask" +tap "homebrew/cask-versions" +tap "homebrew/core" + +brew "autoconf" +brew "autogen" +brew "automake" +brew "binutils" +brew "coreutils" +brew "cmake" +brew "pkg-config" +brew "boost" +brew "openssl" +brew "hidapi" +brew "zmq" +brew "libpgm" +brew "unbound" +brew "libsodium" +brew "miniupnpc" +brew "readline" +brew "ldns" +brew "expat" +brew "doxygen" +brew "graphviz" +brew "libunwind-headers" +brew "xz" +brew "protobuf" diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index d0eabbba5..374a28a2e 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -93,11 +93,12 @@ namespace net_utils struct shared_state : connection_basic_shared_state { shared_state() - : connection_basic_shared_state(), pfilter(nullptr), config() + : connection_basic_shared_state(), pfilter(nullptr), config(), stop_signal_sent(false) {} i_connection_filter* pfilter; typename t_protocol_handler::config_type config; + bool stop_signal_sent; }; /// Construct a connection with the given io_service. diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 14fbec5d9..821594355 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -762,7 +762,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_timer.cancel(); boost::system::error_code ignored_ec; if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) - socket_.shutdown(ignored_ec); + { + const shared_state &state = static_cast<const shared_state&>(get_state()); + if (!state.stop_signal_sent) + socket_.shutdown(ignored_ec); + } socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); if (!m_host.empty()) { @@ -1130,6 +1134,8 @@ POP_WARNINGS void boosted_tcp_server<t_protocol_handler>::send_stop_signal() { m_stop_signal_sent = true; + typename connection<t_protocol_handler>::shared_state *state = static_cast<typename connection<t_protocol_handler>::shared_state*>(m_state.get()); + state->stop_signal_sent = true; TRY_ENTRY(); connections_mutex.lock(); for (auto &c: connections_) diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index bb10c8efc..588d5f0e3 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -274,6 +274,7 @@ namespace net_utils reciev_machine_state m_state; chunked_state m_chunked_state; std::string m_chunked_cache; + bool m_auto_connect; critical_section m_lock; public: @@ -291,6 +292,7 @@ namespace net_utils , m_state() , m_chunked_state() , m_chunked_cache() + , m_auto_connect(true) , m_lock() {} @@ -316,6 +318,11 @@ namespace net_utils m_net_client.set_ssl(std::move(ssl_options)); } + void set_auto_connect(bool auto_connect) + { + m_auto_connect = auto_connect; + } + template<typename F> void set_connector(F connector) { @@ -367,6 +374,11 @@ namespace net_utils CRITICAL_REGION_LOCAL(m_lock); if(!is_connected()) { + if (!m_auto_connect) + { + MWARNING("Auto connect attempt to " << m_host_buff << ":" << m_port << " disabled"); + return false; + } MDEBUG("Reconnecting..."); if(!connect(timeout)) { diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index e8fb40a0a..89cef8134 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -428,9 +428,10 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); - char local_buff[10000] = {0}; + static const size_t max_size = 16384; + buff.resize(max_size); - async_read(local_buff, sizeof(local_buff), boost::asio::transfer_at_least(1), hndlr); + async_read(&buff[0], max_size, 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 return false;*/ m_bytes_received += bytes_transfered; - buff.assign(local_buff, bytes_transfered); + buff.resize(bytes_transfered); return true; } diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index f5f7481f8..5a806dc69 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1,17 +1,16 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.96.5 +// Easylogging++ v9.96.7 // Cross-platform logging library for C++ applications // -// Copyright (c) 2012-2018 Muflihun Labs +// Copyright (c) 2012-2018 Zuhd Web Services // Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// https://github.com/muflihun/easyloggingpp/blob/master/LICENSE +// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE // -// https://github.com/muflihun/easyloggingpp -// https://muflihun.github.io/easyloggingpp +// https://zuhd.org // http://muflihun.com // @@ -962,7 +961,7 @@ void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::st std::size_t foundAt = base::type::string_t::npos; while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { - str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + str.erase(foundAt - 1, 1); ++foundAt; } else { str.replace(foundAt, replaceWhat.length(), replaceWith); @@ -1531,7 +1530,7 @@ void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { if (hasFlag(flag)) { // If we already have flag we remove the escape chars so that '%%' is turned to '%' // even after specifier resolution - this is because we only replaceFirst specifier - formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + formatCopy.erase(foundAt - 1, 1); ++foundAt; } } else { @@ -2206,20 +2205,26 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), m_flags(ELPP_DEFAULT_LOGGING_FLAGS), m_vRegistry(new base::VRegistry(0, &m_flags)), + #if ELPP_ASYNC_LOGGING m_asyncLogQueue(new base::AsyncLogQueue()), m_asyncDispatchWorker(asyncDispatchWorker), #endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { // Register default logger m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); // We register default logger anyway (worse case it's not going to register) just in case m_registeredLoggers->get("default"); + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) // Register performance logger and reconfigure format Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); m_registeredLoggers->get("performance"); performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); performanceLogger->reconfigure(); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + #if defined(ELPP_SYSLOG) // Register syslog logger and reconfigure format Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); @@ -2337,6 +2342,19 @@ base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* d namespace base { // DefaultLogDispatchCallback +const char* convertToChar(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "G"; + if (level == Level::Debug) return "D"; + if (level == Level::Info) return "I"; + if (level == Level::Warning) return "W"; + if (level == Level::Error) return "E"; + if (level == Level::Fatal) return "F"; + if (level == Level::Verbose) return "V"; + if (level == Level::Trace) return "T"; + return "?"; +} + void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { #if defined(ELPP_THREAD_SAFE) #if 0 @@ -2345,11 +2363,15 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { #endif #endif m_data = data; - dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + base::TypedConfigurations* tc = m_data->logMessage()->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(m_data->logMessage()->level()); + dispatch(base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), &tc->subsecondPrecision(m_data->logMessage()->level())) + + "\t" + convertToChar(m_data->logMessage()->level()) + " " + m_data->logMessage()->message() + "\n", + m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog)); } -void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLine, base::type::string_t&& logLine) { if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) { if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( @@ -2376,8 +2398,8 @@ void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { if (m_data->dispatchAction() != base::DispatchAction::FileOnlyLog) { if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&rawLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(rawLine); } } } @@ -3279,11 +3301,11 @@ const std::string &Loggers::getFilenameCommonPrefix() { // VersionInfo const std::string VersionInfo::version(void) { - return std::string("9.96.5"); + return std::string("9.96.7"); } /// @brief Release date of current version const std::string VersionInfo::releaseDate(void) { - return std::string("07-09-2018 0950hrs"); + return std::string("24-11-2018 0728hrs"); } } // namespace el diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index d9a2dc3d1..f0d8d5df7 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -1,17 +1,16 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.96.5 +// Easylogging++ v9.96.7 // Single-header only, cross-platform logging library for C++ applications // -// Copyright (c) 2012-2018 Muflihun Labs +// Copyright (c) 2012-2018 Zuhd Web Services // Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// https://github.com/muflihun/easyloggingpp/blob/master/LICENSE +// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE // -// https://github.com/muflihun/easyloggingpp -// https://muflihun.github.io/easyloggingpp +// https://zuhd.org // http://muflihun.com // @@ -758,10 +757,12 @@ static const char* kDefaultLoggerId = ELPP_DEFAULT_L static const char* kDefaultLoggerId = "default"; #endif +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) #ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; #else static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER #endif #if defined(ELPP_SYSLOG) @@ -2776,7 +2777,7 @@ class DefaultLogDispatchCallback : public LogDispatchCallback { void handle(const LogDispatchData* data); private: const LogDispatchData* m_data; - void dispatch(base::type::string_t&& logLine); + void dispatch(base::type::string_t&& rawLine, base::type::string_t&& logLine); }; #if ELPP_ASYNC_LOGGING class AsyncLogDispatchCallback : public LogDispatchCallback { @@ -3836,7 +3837,7 @@ class Helpers : base::StaticClass { return ELPP->hasCustomFormatSpecifier(formatSpecifier); } static inline void validateFileRolling(Logger* logger, Level level) { - if (logger == nullptr) return; + if (ELPP == nullptr || logger == nullptr) return; logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); } }; diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index d772bf4bb..2b039f557 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -211,8 +211,6 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck if (blk.tx_hashes.size() != txs.size()) throw std::runtime_error("Inconsistent tx/hashes sizes"); - block_txn_start(false); - TIME_MEASURE_START(time1); crypto::hash blk_hash = get_block_hash(blk); TIME_MEASURE_FINISH(time1); @@ -252,8 +250,6 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck m_hardfork->add(blk, prev_height); - block_txn_stop(); - ++num_calls; return prev_height; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 2c40b5a78..567be6a65 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -770,9 +770,12 @@ public: */ virtual void set_batch_transactions(bool) = 0; - virtual void block_txn_start(bool readonly=false) = 0; - virtual void block_txn_stop() = 0; - virtual void block_txn_abort() = 0; + virtual void block_wtxn_start() = 0; + virtual void block_wtxn_stop() = 0; + virtual void block_wtxn_abort() = 0; + virtual bool block_rtxn_start() const = 0; + virtual void block_rtxn_stop() const = 0; + virtual void block_rtxn_abort() const = 0; virtual void set_hard_fork(HardFork* hf); @@ -1699,6 +1702,52 @@ public: }; // class BlockchainDB +class db_txn_guard +{ +public: + db_txn_guard(BlockchainDB *db, bool readonly): db(db), readonly(readonly), active(false) + { + if (readonly) + { + active = db->block_rtxn_start(); + } + else + { + db->block_wtxn_start(); + active = true; + } + } + virtual ~db_txn_guard() + { + if (active) + stop(); + } + void stop() + { + if (readonly) + db->block_rtxn_stop(); + else + db->block_wtxn_stop(); + active = false; + } + void abort() + { + if (readonly) + db->block_rtxn_abort(); + else + db->block_wtxn_abort(); + active = false; + } + +private: + BlockchainDB *db; + bool readonly; + bool active; +}; + +class db_rtxn_guard: public db_txn_guard { public: db_rtxn_guard(BlockchainDB *db): db_txn_guard(db, true) {} }; +class db_wtxn_guard: public db_txn_guard { public: db_wtxn_guard(BlockchainDB *db): db_txn_guard(db, false) {} }; + BlockchainDB *new_db(const std::string& db_type); } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index a07e9ac55..340434888 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3611,16 +3611,15 @@ void BlockchainLMDB::block_rtxn_stop() const memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); } -void BlockchainLMDB::block_txn_start(bool readonly) +bool BlockchainLMDB::block_rtxn_start() const { - if (readonly) - { - MDB_txn *mtxn; - mdb_txn_cursors *mcur; - block_rtxn_start(&mtxn, &mcur); - return; - } + MDB_txn *mtxn; + mdb_txn_cursors *mcur; + return block_rtxn_start(&mtxn, &mcur); +} +void BlockchainLMDB::block_wtxn_start() +{ LOG_PRINT_L3("BlockchainLMDB::" << __func__); // Distinguish the exceptions here from exceptions that would be thrown while // using the txn and committing it. @@ -3652,10 +3651,13 @@ void BlockchainLMDB::block_txn_start(bool readonly) throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str())); } -void BlockchainLMDB::block_txn_stop() +void BlockchainLMDB::block_wtxn_stop() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (m_write_txn && m_writer == boost::this_thread::get_id()) + if (!m_write_txn) + throw0(DB_ERROR_TXN_START((std::string("Attempted to stop write txn when no such txn exists in ")+__FUNCTION__).c_str())); + if (m_writer != boost::this_thread::get_id()) + throw0(DB_ERROR_TXN_START((std::string("Attempted to stop write txn from the wrong thread in ")+__FUNCTION__).c_str())); { if (! m_batch_active) { @@ -3669,40 +3671,31 @@ void BlockchainLMDB::block_txn_stop() memset(&m_wcursors, 0, sizeof(m_wcursors)); } } - else if (m_tinfo->m_ti_rtxn) - { - mdb_txn_reset(m_tinfo->m_ti_rtxn); - memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - } } -void BlockchainLMDB::block_txn_abort() +void BlockchainLMDB::block_wtxn_abort() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (m_write_txn && m_writer == boost::this_thread::get_id()) - { - if (! m_batch_active) - { - delete m_write_txn; - m_write_txn = nullptr; - memset(&m_wcursors, 0, sizeof(m_wcursors)); - } - } - else if (m_tinfo->m_ti_rtxn) - { - mdb_txn_reset(m_tinfo->m_ti_rtxn); - memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - } - else + if (!m_write_txn) + throw0(DB_ERROR_TXN_START((std::string("Attempted to abort write txn when no such txn exists in ")+__FUNCTION__).c_str())); + if (m_writer != boost::this_thread::get_id()) + throw0(DB_ERROR_TXN_START((std::string("Attempted to abort write txn from the wrong thread in ")+__FUNCTION__).c_str())); + + if (! m_batch_active) { - // This would probably mean an earlier exception was caught, but then we - // proceeded further than we should have. - throw0(DB_ERROR((std::string("BlockchainLMDB::") + __func__ + - std::string(": block-level DB transaction abort called when write txn doesn't exist") - ).c_str())); + delete m_write_txn; + m_write_txn = nullptr; + memset(&m_wcursors, 0, sizeof(m_wcursors)); } } +void BlockchainLMDB::block_rtxn_abort() const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + mdb_txn_reset(m_tinfo->m_ti_rtxn); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); +} + uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<std::pair<transaction, blobdata>>& txs) { @@ -3728,11 +3721,6 @@ uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t { throw; } - catch (...) - { - block_txn_abort(); - throw; - } return ++m_height; } @@ -3742,16 +3730,16 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); - block_txn_start(false); + block_wtxn_start(); try { BlockchainDB::pop_block(blk, txs); - block_txn_stop(); + block_wtxn_stop(); } catch (...) { - block_txn_abort(); + block_wtxn_abort(); throw; } } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index f6b00817d..4b46f081e 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -310,11 +310,14 @@ public: virtual void batch_stop(); virtual void batch_abort(); - virtual void block_txn_start(bool readonly); - virtual void block_txn_stop(); - virtual void block_txn_abort(); - virtual bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const; + virtual void block_wtxn_start(); + virtual void block_wtxn_stop(); + virtual void block_wtxn_abort(); + virtual bool block_rtxn_start() const; virtual void block_rtxn_stop() const; + virtual void block_rtxn_abort() const; + + bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const; virtual void pop_block(block& blk, std::vector<transaction>& txs); diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 04fad26a4..1492fb2ae 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -55,9 +55,13 @@ public: virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } virtual void batch_stop() {} virtual void set_batch_transactions(bool) {} - virtual void block_txn_start(bool readonly=false) {} - virtual void block_txn_stop() {} - virtual void block_txn_abort() {} + virtual void block_wtxn_start() {} + virtual void block_wtxn_stop() {} + virtual void block_wtxn_abort() {} + virtual bool block_rtxn_start() const { return true; } + virtual void block_rtxn_stop() const {} + virtual void block_rtxn_abort() const {} + virtual void drop_hard_fork_info() {} virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 8454595ac..cb9154f29 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -194,7 +194,11 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block core.prevalidate_block_hashes(core.get_blockchain_storage().get_db().height(), hashes); std::vector<block> pblocks; - core.prepare_handle_incoming_blocks(blocks, pblocks); + if (!core.prepare_handle_incoming_blocks(blocks, pblocks)) + { + MERROR("Failed to prepare to add blocks"); + return 1; + } if (!pblocks.empty() && pblocks.size() != blocks.size()) { MERROR("Unexpected parsed blocks size"); diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 89bca2f09..d5710f727 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -185,26 +185,8 @@ void HardFork::init() else height = 1; - bool populate = false; - try - { - db.get_hard_fork_version(0); - } - catch (...) { populate = true; } - if (populate) { - MINFO("The DB has no hard fork info, reparsing from start"); - height = 1; - } - MDEBUG("reorganizing from " << height); - if (populate) { - reorganize_from_chain_height(height); - // reorg will not touch the genesis block, use this as a flag for populating done - db.set_hard_fork_version(0, original_version); - } - else { - rescan_from_chain_height(height); - } - MDEBUG("reorganization done"); + rescan_from_chain_height(height); + MDEBUG("init done"); } uint8_t HardFork::get_block_version(uint64_t height) const @@ -266,11 +248,9 @@ bool HardFork::reorganize_from_chain_height(uint64_t height) bool HardFork::rescan_from_block_height(uint64_t height) { CRITICAL_REGION_LOCAL(lock); - db.block_txn_start(true); - if (height >= db.height()) { - db.block_txn_stop(); + db_rtxn_guard rtxn_guard(&db); + if (height >= db.height()) return false; - } versions.clear(); @@ -293,8 +273,6 @@ bool HardFork::rescan_from_block_height(uint64_t height) current_fork_index = voted; } - db.block_txn_stop(); - return true; } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index e6c6bddb6..173679e21 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -528,6 +528,7 @@ namespace cryptonote uint32_t local_template_ver = 0; block b; slow_hash_allocate_state(); + ++m_threads_active; while(!m_stop) { if(m_pausers_count)//anti split workaround diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index fb96de226..2cbe89b01 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -30,6 +30,7 @@ set(cryptonote_core_sources blockchain.cpp cryptonote_core.cpp tx_pool.cpp + tx_sanity_check.cpp cryptonote_tx_utils.cpp) set(cryptonote_core_headers) @@ -39,6 +40,7 @@ set(cryptonote_core_private_headers blockchain.h cryptonote_core.h tx_pool.h + tx_sanity_check.h cryptonote_tx_utils.h) monero_private_headers(cryptonote_core diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7ef8f8c45..f733efb2f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -428,6 +428,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline block bl; block_verification_context bvc = boost::value_initialized<block_verification_context>(); generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); + db_wtxn_guard wtxn_guard(m_db); add_new_block(bl, bvc); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); } @@ -443,7 +444,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_db->fixup(); } - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); + // check how far behind we are uint64_t top_block_timestamp = m_db->get_top_block_timestamp(); uint64_t timestamp_diff = time(NULL) - top_block_timestamp; @@ -464,7 +466,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline #endif MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block()); - m_db->block_txn_stop(); + + rtxn_guard.stop(); uint64_t num_popped_blocks = 0; while (!m_db->is_read_only()) @@ -518,8 +521,11 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if (test_options && test_options->long_term_block_weight_window) m_long_term_block_weights_window = test_options->long_term_block_weight_window; - if (!update_next_cumulative_weight_limit()) - return false; + { + db_txn_guard txn_guard(m_db, m_db->is_read_only()); + if (!update_next_cumulative_weight_limit()) + return false; + } return true; } //------------------------------------------------------------------ @@ -725,6 +731,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) m_db->reset(); m_hardfork->init(); + db_wtxn_guard wtxn_guard(m_db); block_verification_context bvc = boost::value_initialized<block_verification_context>(); add_new_block(b, bvc); if (!update_next_cumulative_weight_limit()) @@ -772,7 +779,7 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const if(!sz) return true; - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); bool genesis_included = false; uint64_t current_back_offset = 1; while(current_back_offset < sz) @@ -799,7 +806,6 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const { ids.push_back(m_db->get_block_hash_from_height(0)); } - m_db->block_txn_stop(); return true; } @@ -1866,7 +1872,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard (m_db); rsp.current_blockchain_height = get_current_blockchain_height(); std::vector<std::pair<cryptonote::blobdata,block>> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); @@ -1893,7 +1899,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO // as done below if any standalone transactions were requested // and missed. rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end()); - m_db->block_txn_stop(); return false; } @@ -1903,7 +1908,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO //get and pack other transactions, if needed get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids); - m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -2075,14 +2079,13 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return false; } - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); // make sure that the last block in the request's block list matches // the genesis block auto gen_hash = m_db->get_block_hash_from_height(0); if(qblock_ids.back() != gen_hash) { MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block mismatch: " << std::endl << "id: " << qblock_ids.back() << ", " << std::endl << "expected: " << gen_hash << "," << std::endl << " dropping connection"); - m_db->block_txn_abort(); return false; } @@ -2100,11 +2103,9 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc catch (const std::exception& e) { MWARNING("Non-critical error trying to find block by hash in BlockchainDB, hash: " << *bl_it); - m_db->block_txn_abort(); return false; } } - m_db->block_txn_stop(); // this should be impossible, as we checked that we share the genesis block, // but just in case... @@ -2284,7 +2285,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. -bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const +bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2295,11 +2296,15 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return false; } - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); current_height = get_current_blockchain_height(); - const uint32_t pruning_seed = get_blockchain_pruning_seed(); - start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed); - uint64_t stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed); + uint64_t stop_height = current_height; + if (clip_pruned) + { + const uint32_t pruning_seed = get_blockchain_pruning_seed(); + start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed); + stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed); + } size_t count = 0; hashes.reserve(std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); for(size_t i = start_height; i < stop_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) @@ -2307,7 +2312,6 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc hashes.push_back(m_db->get_block_hash_from_height(i)); } - m_db->block_txn_stop(); return true; } @@ -2316,7 +2320,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); + bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height, true); if (result) { cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); @@ -2354,7 +2358,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } } - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); total_height = get_current_blockchain_height(); size_t count = 0, size = 0; blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); @@ -2380,7 +2384,6 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i]))); } } - m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -3535,7 +3538,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& static bool seen_future_version = false; - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); uint64_t blockchain_height; const crypto::hash top_hash = get_tail_id(blockchain_height); ++blockchain_height; // block height to chain height @@ -3544,7 +3547,6 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << top_hash); bvc.m_verifivation_failed = true; leave: - m_db->block_txn_stop(); return false; } @@ -3827,7 +3829,7 @@ leave: if(precomputed) block_processing_time += m_fake_pow_calc_time; - m_db->block_txn_stop(); + rtxn_guard.stop(); TIME_MEASURE_START(addblock); uint64_t new_height = 0; if (!bvc.m_verifivation_failed) @@ -3896,6 +3898,10 @@ leave: //------------------------------------------------------------------ bool Blockchain::prune_blockchain(uint32_t pruning_seed) { + m_tx_pool.lock(); + epee::misc_utils::auto_scope_leave_caller unlocker = epee::misc_utils::create_scope_leave_handler([&](){m_tx_pool.unlock();}); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_db->prune_blockchain(pruning_seed); } //------------------------------------------------------------------ @@ -3945,8 +3951,6 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti LOG_PRINT_L3("Blockchain::" << __func__); - m_db->block_txn_start(false); - // when we reach this, the last hf version is not yet written to the db const uint64_t db_height = m_db->height(); const uint8_t hf_version = get_current_hard_fork_version(); @@ -3990,7 +3994,6 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti new_weights[0] = long_term_block_weight; long_term_median = epee::misc_utils::median(new_weights); m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); - short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5; weights.clear(); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); @@ -4009,9 +4012,8 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti if (long_term_effective_median_block_weight) *long_term_effective_median_block_weight = m_long_term_effective_median_block_weight; - m_db->add_max_block_size(m_current_block_cumul_weight_limit); - - m_db->block_txn_stop(); + if (!m_db->is_read_only()) + m_db->add_max_block_size(m_current_block_cumul_weight_limit); return true; } @@ -4022,12 +4024,11 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc) crypto::hash id = get_block_hash(bl); CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process CRITICAL_REGION_LOCAL1(m_blockchain_lock); - m_db->block_txn_start(true); + db_rtxn_guard rtxn_guard(m_db); if(have_block(id)) { LOG_PRINT_L3("block with id = " << id << " already exists"); bvc.m_already_exists = true; - m_db->block_txn_stop(); m_blocks_txs_check.clear(); return false; } @@ -4037,14 +4038,14 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc) { //chain switching or wrong block bvc.m_added_to_main_chain = false; - m_db->block_txn_stop(); + rtxn_guard.stop(); bool r = handle_alternative_block(bl, id, bvc); m_blocks_txs_check.clear(); return r; //never relay alternative blocks } - m_db->block_txn_stop(); + rtxn_guard.stop(); return handle_block_to_main_chain(bl, id, bvc); } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3588bbd1b..244e2a89a 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -394,10 +394,11 @@ namespace cryptonote * @param hashes the hashes to be returned, return-by-reference * @param start_height the start height, return-by-reference * @param current_height the current blockchain height, return-by-reference + * @param clip_pruned whether to constrain results to unpruned data * * @return true if a block found in common, else false */ - bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const; + bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const; /** * @brief get recent block hashes for a foreign chain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 91dea4982..be1ea5a17 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1609,6 +1609,9 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_fork_time() { + if (m_nettype == FAKECHAIN) + return true; + HardFork::State state = m_blockchain_storage.get_hard_fork_state(); const el::Level level = el::Level::Warning; switch (state) { @@ -1824,7 +1827,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_block_rate() { - if (m_offline || m_target_blockchain_height > get_current_blockchain_height()) + if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height()) { MDEBUG("Not checking block rate, offline or syncing"); return true; diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp new file mode 100644 index 000000000..d3b225f1c --- /dev/null +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdint.h> +#include <vector> +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "blockchain.h" +#include "tx_sanity_check.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "txsanity" + +namespace cryptonote +{ + +bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob) +{ + cryptonote::transaction tx; + + if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx)) + { + MERROR("Failed to parse transaction"); + return false; + } + + if (cryptonote::is_coinbase(tx)) + { + MERROR("Transaction is coinbase"); + return false; + } + std::set<uint64_t> rct_indices; + size_t n_indices = 0; + + for (const auto &txin : tx.vin) + { + if (txin.type() != typeid(cryptonote::txin_to_key)) + continue; + const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(txin); + if (in_to_key.amount != 0) + continue; + const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets); + for (uint64_t offset: absolute) + rct_indices.insert(offset); + n_indices += in_to_key.key_offsets.size(); + } + + if (n_indices <= 10) + { + MERROR("n_indices is only " << n_indices); + return true; + } + + uint64_t n_available = blockchain.get_num_mature_outputs(0); + if (n_available < 10000) + return true; + + if (rct_indices.size() < n_indices * 9 / 10) + { + MERROR("unique indices is only " << rct_indices.size() << "/" << n_indices); + return false; + } + + std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end()); + uint64_t median = epee::misc_utils::median(offsets); + if (median < n_available * 9 / 10) + { + MERROR("median is " << median << "/" << n_available); + return false; + } + + return true; +} + +} diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h new file mode 100644 index 000000000..c12d1b0b1 --- /dev/null +++ b/src/cryptonote_core/tx_sanity_check.h @@ -0,0 +1,36 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "cryptonote_basic/blobdatatype.h" + +namespace cryptonote +{ + class Blockchain; + + bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob); +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b7a50783a..7adca3158 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2366,6 +2366,8 @@ skip: { MINFO("Target height decreasing from " << previous_target << " to " << target); m_core.set_target_blockchain_height(target); + if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping) + MCWARNING("global", "monerod is now disconnected from the network"); } m_block_queue.flush_spans(context.m_connection_id, false); diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index c3ac24b70..9e1c86b91 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -257,7 +257,12 @@ int main(int argc, char const * argv[]) bf::path log_file_path {data_dir / std::string(CRYPTONOTE_NAME ".log")}; if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_file)) log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file); - log_file_path = bf::absolute(log_file_path, relative_path_base); +#ifdef __WIN32 + if (!strchr(log_file_path.c_str(), '/') && !strchr(log_file_path.c_str(), '\\')) +#else + if (!strchr(log_file_path.c_str(), '/')) +#endif + log_file_path = bf::absolute(log_file_path, relative_path_base); mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size), command_line::get_arg(vm, daemon_args::arg_max_log_files)); // Set log level diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5901be662..186296dc9 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -553,7 +553,7 @@ bool t_rpc_command_executor::mining_status() { if (!mining_busy && mres.active && mres.speed > 0 && mres.block_target > 0 && mres.difficulty > 0) { - double ratio = mres.speed * mres.block_target / mres.difficulty; + double ratio = mres.speed * mres.block_target / (double)mres.difficulty; uint64_t daily = 86400ull / mres.block_target * mres.block_reward * ratio; uint64_t monthly = 86400ull / mres.block_target * 30.5 * mres.block_reward * ratio; uint64_t yearly = 86400ull / mres.block_target * 356 * mres.block_reward * ratio; @@ -2205,7 +2205,7 @@ bool t_rpc_command_executor::prune_blockchain() } } - tools::success_msg_writer() << "Blockchain pruned: seed " << epee::string_tools::to_string_hex(res.pruning_seed); + tools::success_msg_writer() << "Blockchain pruned"; return true; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 7d13b3216..be97edbe5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -176,8 +176,15 @@ namespace nodetool if(!addr.is_blockable()) return false; + const time_t now = time(nullptr); + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - m_blocked_hosts[addr.host_str()] = time(nullptr) + seconds; + time_t limit; + if (now > std::numeric_limits<time_t>::max() - seconds) + limit = std::numeric_limits<time_t>::max(); + else + limit = now + seconds; + m_blocked_hosts[addr.host_str()] = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index e394ef088..6270d4d14 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -48,12 +48,12 @@ extern "C" //#define DEBUG_BP -#if 1 +#if 0 #define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) #define PERF_TIMER_STOP_BP(x) PERF_TIMER_STOP(x) #else -#define PERF_TIMER_START_BP(x) ((void*)0) -#define PERF_TIMER_STOP_BP(x) ((void*)0) +#define PERF_TIMER_START_BP(x) ((void)0) +#define PERF_TIMER_STOP_BP(x) ((void)0) #endif #define STRAUS_SIZE_LIMIT 232 @@ -439,35 +439,35 @@ static epee::span<const rct::key> slice(const rct::keyV &a, size_t start, size_t static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) { - rct::keyV data; - data.reserve(3); - data.push_back(hash_cache); - data.push_back(mash0); - data.push_back(mash1); - return hash_cache = rct::hash_to_scalar(data); + rct::key data[3]; + data[0] = hash_cache; + data[1] = mash0; + data[2] = mash1; + rct::hash_to_scalar(hash_cache, data, sizeof(data)); + return hash_cache; } static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2) { - rct::keyV data; - data.reserve(4); - data.push_back(hash_cache); - data.push_back(mash0); - data.push_back(mash1); - data.push_back(mash2); - return hash_cache = rct::hash_to_scalar(data); + rct::key data[4]; + data[0] = hash_cache; + data[1] = mash0; + data[2] = mash1; + data[3] = mash2; + rct::hash_to_scalar(hash_cache, data, sizeof(data)); + return hash_cache; } static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2, const rct::key &mash3) { - rct::keyV data; - data.reserve(5); - data.push_back(hash_cache); - data.push_back(mash0); - data.push_back(mash1); - data.push_back(mash2); - data.push_back(mash3); - return hash_cache = rct::hash_to_scalar(data); + rct::key data[5]; + data[0] = hash_cache; + data[1] = mash0; + data[2] = mash1; + data[3] = mash2; + data[4] = mash3; + rct::hash_to_scalar(hash_cache, data, sizeof(data)); + return hash_cache; } /* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ @@ -825,6 +825,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) size_t inv_offset = 0; std::vector<rct::key> to_invert; to_invert.reserve(11 * sizeof(proofs)); + size_t max_logM = 0; for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -861,6 +862,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) size_t M; for (pd.logM = 0; (M = 1<<pd.logM) <= maxM && M < proof.V.size(); ++pd.logM); CHECK_AND_ASSERT_MES(proof.L.size() == 6+pd.logM, false, "Proof is not the expected size"); + max_logM = std::max(pd.logM, max_logM); const size_t rounds = pd.logM+logN; CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); @@ -888,7 +890,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) rct::key tmp; std::vector<MultiexpData> multiexp_data; - multiexp_data.reserve(nV + (2 * (10/*logM*/ + logN) + 4) * proofs.size() + 2 * maxMN); + multiexp_data.reserve(nV + (2 * (max_logM + logN) + 4) * proofs.size() + 2 * maxMN); multiexp_data.resize(2 * maxMN); PERF_TIMER_START_BP(VERIFY_line_24_25_invert); @@ -901,6 +903,8 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) rct::keyV m_z4(maxMN, rct::zero()), m_z5(maxMN, rct::zero()); rct::key m_y0 = rct::zero(), y1 = rct::zero(); int proof_data_index = 0; + rct::keyV w_cache; + rct::keyV proof8_V, proof8_L, proof8_R; for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -913,9 +917,9 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) const rct::key weight_z = rct::skGen(); // pre-multiply some points by 8 - rct::keyV proof8_V = proof.V; for (rct::key &k: proof8_V) k = rct::scalarmult8(k); - rct::keyV proof8_L = proof.L; for (rct::key &k: proof8_L) k = rct::scalarmult8(k); - rct::keyV proof8_R = proof.R; for (rct::key &k: proof8_R) k = rct::scalarmult8(k); + proof8_V.resize(proof.V.size()); for (size_t i = 0; i < proof.V.size(); ++i) proof8_V[i] = rct::scalarmult8(proof.V[i]); + proof8_L.resize(proof.L.size()); for (size_t i = 0; i < proof.L.size(); ++i) proof8_L[i] = rct::scalarmult8(proof.L[i]); + proof8_R.resize(proof.R.size()); for (size_t i = 0; i < proof.R.size(); ++i) proof8_R[i] = rct::scalarmult8(proof.R[i]); rct::key proof8_T1 = rct::scalarmult8(proof.T1); rct::key proof8_T2 = rct::scalarmult8(proof.T2); rct::key proof8_S = rct::scalarmult8(proof.S); @@ -976,7 +980,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) // precalc PERF_TIMER_START_BP(VERIFY_line_24_25_precalc); - rct::keyV w_cache(1<<rounds); + w_cache.resize(1<<rounds); w_cache[0] = winv[0]; w_cache[1] = pd.w[0]; for (size_t j = 1; j < rounds; ++j) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index e877c13ce..ff2a81d43 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -695,6 +695,7 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size"); } CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present"); + CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings"); rctSig rv; rv.type = RCTTypeFull; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 50d0f4d91..e5413f1dc 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -191,6 +191,8 @@ namespace rct { Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} + bool operator==(const Bulletproof &other) const { return V == other.V && A == other.A && S == other.S && T1 == other.T1 && T2 == other.T2 && taux == other.taux && mu == other.mu && L == other.L && R == other.R && a == other.a && b == other.b && t == other.t; } + BEGIN_SERIALIZE_OBJECT() // Commitments aren't saved, they're restored via outPk // FIELD(V) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index d3142345c..c41fb37d8 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -41,6 +41,7 @@ using namespace epee; #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_core/tx_sanity_check.h" #include "misc_language.h" #include "net/parse.h" #include "storages/http_abstract_invoke.h" @@ -432,17 +433,12 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r)) return r; - NOTIFY_RESPONSE_CHAIN_ENTRY::request resp; - - resp.start_height = req.start_height; - if(!m_core.find_blockchain_supplement(req.block_ids, resp)) + res.start_height = req.start_height; + if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false)) { res.status = "Failed"; return false; } - res.current_height = resp.total_height; - res.start_height = resp.start_height; - res.m_block_ids = std::move(resp.m_block_ids); res.status = CORE_RPC_STATUS_OK; return true; @@ -574,7 +570,7 @@ namespace cryptonote // try the pool for any missing txes size_t found_in_pool = 0; std::unordered_set<crypto::hash> pool_tx_hashes; - std::unordered_map<crypto::hash, bool> double_spend_seen; + std::unordered_map<crypto::hash, tx_info> per_tx_pool_tx_info; if (!missed_txs.empty()) { std::vector<tx_info> pool_tx_info; @@ -629,7 +625,7 @@ namespace cryptonote { if (ti.id_hash == hash_string) { - double_spend_seen.insert(std::make_pair(h, ti.double_spend_seen)); + per_tx_pool_tx_info.insert(std::make_pair(h, ti)); break; } } @@ -715,14 +711,17 @@ namespace cryptonote if (e.in_pool) { e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max(); - if (double_spend_seen.find(tx_hash) != double_spend_seen.end()) + auto it = per_tx_pool_tx_info.find(tx_hash); + if (it != per_tx_pool_tx_info.end()) { - e.double_spend_seen = double_spend_seen[tx_hash]; + e.double_spend_seen = it->second.double_spend_seen; + e.relayed = it->second.relayed; } else { - MERROR("Failed to determine double spend status for " << tx_hash); + MERROR("Failed to determine pool info for " << tx_hash); e.double_spend_seen = false; + e.relayed = false; } } else @@ -730,6 +729,7 @@ namespace cryptonote e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); e.double_spend_seen = false; + e.relayed = false; } // fill up old style responses too, in case an old wallet asks @@ -845,6 +845,14 @@ namespace cryptonote return true; } + if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob)) + { + res.status = "Failed"; + res.reason = "Sanity check failed"; + res.sanity_check_failed = true; + return true; + } + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed) @@ -1296,7 +1304,10 @@ namespace cryptonote LOG_ERROR("Failed to find tx pub key in blockblob"); return false; } - res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (req.reserve_size) + res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + else + res.reserved_offset = 0; if(res.reserved_offset + req.reserve_size > block_blob.size()) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1394,11 +1405,9 @@ namespace cryptonote submit_req.push_back(boost::value_initialized<std::string>()); res.height = m_core.get_blockchain_storage().get_current_blockchain_height(); - bool r = CORE_RPC_STATUS_OK; - for(size_t i = 0; i < req.amount_of_blocks; i++) { - r = on_getblocktemplate(template_req, template_res, error_resp, ctx); + bool r = on_getblocktemplate(template_req, template_res, error_resp, ctx); res.status = template_res.status; template_req.prev_block.clear(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index a42ca2494..e4683bbe2 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -40,6 +40,9 @@ #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" + // yes, epee doesn't properly use its full namespace when calling its // functions from macros. *sigh* using namespace epee; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 70a5a475a..cfe4bbf23 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -364,6 +364,7 @@ namespace cryptonote uint64_t block_height; uint64_t block_timestamp; std::vector<uint64_t> output_indices; + bool relayed; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -374,9 +375,16 @@ namespace cryptonote KV_SERIALIZE(as_json) KV_SERIALIZE(in_pool) KV_SERIALIZE(double_spend_seen) - KV_SERIALIZE(block_height) - KV_SERIALIZE(block_timestamp) - KV_SERIALIZE(output_indices) + if (!this_ref.in_pool) + { + KV_SERIALIZE(block_height) + KV_SERIALIZE(block_timestamp) + KV_SERIALIZE(output_indices) + } + else + { + KV_SERIALIZE(relayed) + } END_KV_SERIALIZE_MAP() }; @@ -577,10 +585,12 @@ namespace cryptonote { std::string tx_as_hex; bool do_not_relay; + bool do_sanity_checks; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_as_hex) KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(do_sanity_checks, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -599,6 +609,7 @@ namespace cryptonote bool overspend; bool fee_too_low; bool not_rct; + bool sanity_check_failed; bool untrusted; BEGIN_KV_SERIALIZE_MAP() @@ -613,6 +624,7 @@ namespace cryptonote KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) KV_SERIALIZE(not_rct) + KV_SERIALIZE(sanity_check_failed) KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 540afe6b9..7c8953930 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -141,7 +141,7 @@ namespace rpc auto& chain = m_core.get_blockchain_storage(); - if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height)) + if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height, false)) { res.status = Message::STATUS_FAILED; res.error_details = "Blockchain::find_blockchain_supplement() returned false"; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 2e134931f..560c5be24 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -118,7 +118,7 @@ typedef cryptonote::simple_wallet sw; if (!m_long_payment_id_support) { \ fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \ fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \ - fail_msg_writer() << tr("Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ + fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ return true; \ } \ } while(0) @@ -4092,6 +4092,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (welcome) message_writer(console_color_yellow, true) << tr("If you are new to Monero, type \"welcome\" for a brief overview."); + if (m_long_payment_id_support) + { + message_writer(console_color_red, false) << + tr("WARNING: obsolete long payment IDs are enabled. Sending transactions with those payment IDs are bad for your privacy."); + message_writer(console_color_red, false) << + tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy."); + } + return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index f5f3c0e1b..1d5078a11 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -37,9 +37,10 @@ namespace tools static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); -NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) +NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) + , m_offline(false) { invalidate(); } @@ -61,6 +62,8 @@ void NodeRPCProxy::invalidate() boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const { + if (m_offline) + return boost::optional<std::string>("offline"); if (m_rpc_version == 0) { cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); @@ -84,6 +87,8 @@ void NodeRPCProxy::set_height(uint64_t h) boost::optional<std::string> NodeRPCProxy::get_info() const { + if (m_offline) + return boost::optional<std::string>("offline"); const time_t now = time(NULL); if (now >= m_get_info_time + 30) // re-cache every 30 seconds { @@ -134,6 +139,8 @@ boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &bloc boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const { + if (m_offline) + return boost::optional<std::string>("offline"); if (m_earliest_height[version] == 0) { cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t); @@ -161,6 +168,8 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_ if (result) return result; + if (m_offline) + return boost::optional<std::string>("offline"); if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks) { cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); @@ -191,6 +200,8 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f if (result) return result; + if (m_offline) + return boost::optional<std::string>("offline"); if (m_dynamic_base_fee_estimate_cached_height != height) { cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 3630aec08..3b75c8b94 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -39,9 +39,10 @@ namespace tools class NodeRPCProxy { public: - NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex); + NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex); void invalidate(); + void set_offline(bool offline) { m_offline = offline; } boost::optional<std::string> get_rpc_version(uint32_t &version) const; boost::optional<std::string> get_height(uint64_t &height) const; @@ -56,7 +57,8 @@ private: boost::optional<std::string> get_info() const; epee::net_utils::http::http_simple_client &m_http_client; - boost::mutex &m_daemon_rpc_mutex; + boost::recursive_mutex &m_daemon_rpc_mutex; + bool m_offline; mutable uint64_t m_height; mutable uint64_t m_earliest_height[256]; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b288994a5..a7da9395c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -266,6 +266,7 @@ struct options { const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""}; const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" }; const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false}; + const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false}; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file) @@ -456,6 +457,9 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl if (command_line::get_arg(vm, opts.no_dns)) wallet->enable_dns(false); + if (command_line::get_arg(vm, opts.offline)) + wallet->set_offline(); + try { if (!command_line::is_arg_defaulted(vm, opts.tx_notify)) @@ -792,7 +796,7 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra // pseudoOuts size += 32 * n_inputs; // ecdhInfo - size += 2 * 32 * n_outputs; + size += 8 * n_outputs; // outPk - only commitment is saved size += 32 * n_outputs; // txnFee @@ -1083,7 +1087,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_unattended(unattended), m_devices_registered(false), m_device_last_key_image_sync(0), - m_use_dns(true) + m_use_dns(true), + m_offline(false) { } @@ -1139,6 +1144,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.hw_device_derivation_path); command_line::add_arg(desc_params, opts.tx_notify); command_line::add_arg(desc_params, opts.no_dns); + command_line::add_arg(desc_params, opts.offline); } std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) @@ -1184,6 +1190,8 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia //---------------------------------------------------------------------------------------------------- bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) { + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); + if(m_http_client.is_connected()) m_http_client.disconnect(); m_daemon_address = std::move(daemon_address); @@ -2392,7 +2400,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); @@ -2414,7 +2422,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, req.start_height = start_height; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin"); @@ -2691,7 +2699,7 @@ void wallet2::update_pool_state(bool refreshed) cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req; cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin"); @@ -2838,7 +2846,7 @@ void wallet2::update_pool_state(bool refreshed) req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); MDEBUG("Got " << r << " and " << res.status); if (r && res.status == CORE_RPC_STATUS_OK) @@ -2999,6 +3007,13 @@ std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create //---------------------------------------------------------------------------------------------------- void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool) { + if (m_offline) + { + blocks_fetched = 0; + received_money = 0; + return; + } + if(m_light_wallet) { // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. @@ -3258,7 +3273,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> req.binary = true; req.compress = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (!r) { @@ -5088,7 +5103,14 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) { THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized); - boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex); + if (m_offline) + { + if (version) + *version = 0; + if (ssl) + *ssl = false; + return false; + } // TODO: Add light wallet version check. if(m_light_wallet) { @@ -5099,20 +5121,23 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) return m_light_wallet_connected; } - if(!m_http_client.is_connected(ssl)) { - m_node_rpc_proxy.invalidate(); - if (!m_http_client.connect(std::chrono::milliseconds(timeout))) - return false; + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); if(!m_http_client.is_connected(ssl)) - return false; + { + m_node_rpc_proxy.invalidate(); + if (!m_http_client.connect(std::chrono::milliseconds(timeout))) + return false; + if(!m_http_client.is_connected(ssl)) + return false; + } } if (version) { cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client); + bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t); if(!r) { *version = 0; return false; @@ -5126,6 +5151,18 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) return true; } //---------------------------------------------------------------------------------------------------- +void wallet2::set_offline(bool offline) +{ + m_offline = offline; + m_http_client.set_auto_connect(!offline); + if (offline) + { + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); + if(m_http_client.is_connected()) + m_http_client.disconnect(); + } +} +//---------------------------------------------------------------------------------------------------- bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const { hw::device &hwdev = m_account.get_device(); @@ -5304,7 +5341,7 @@ void wallet2::trim_hashchain() cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); req.height = m_blockchain.size() - 1; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (r && res.status == CORE_RPC_STATUS_OK) { @@ -5660,7 +5697,7 @@ void wallet2::rescan_spent() for (size_t n = start_offset; n < start_offset + n_outputs; ++n) req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image)); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); + bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); @@ -5988,7 +6025,7 @@ void wallet2::commit_tx(pending_tx& ptx) oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); // MyMonero and OpenMonero use different status strings @@ -6000,9 +6037,10 @@ void wallet2::commit_tx(pending_tx& ptx) COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); req.do_not_relay = false; + req.do_sanity_checks = true; COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, m_http_client, rpc_timeout); + bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); @@ -6955,7 +6993,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority) m_daemon_rpc_mutex.lock(); getbh_req.start_height = m_blockchain.size() - N; getbh_req.end_height = m_blockchain.size() - 1; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange"); THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); @@ -7126,7 +7164,7 @@ bool wallet2::unset_ring(const crypto::hash &txid) req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon"); if (res.txs.empty()) @@ -7180,8 +7218,8 @@ bool wallet2::find_and_save_rings(bool force) req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s])); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -7322,7 +7360,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ oreq.count = light_wallet_requested_outputs_count; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); @@ -7462,7 +7500,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> req_t.unlocked = true; req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -7485,7 +7523,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> req_t.cumulative = true; req_t.binary = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution"); @@ -7884,7 +7922,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // get the keys for those req.get_txid = false; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -8596,7 +8634,7 @@ bool wallet2::light_wallet_login(bool &new_address) // Always create account if it doesn't exist. request.create_account = true; m_daemon_rpc_mutex.lock(); - bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, rpc_timeout, "POST"); + bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); // MyMonero doesn't send any status message. OpenMonero does. m_light_wallet_connected = connected && (response.status.empty() || response.status == "success"); @@ -8621,7 +8659,7 @@ bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLE oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request"); @@ -8647,7 +8685,7 @@ void wallet2::light_wallet_get_unspent_outs() m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_unspent_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs"); THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason); @@ -8792,7 +8830,7 @@ bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO: request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info"); // TODO: Validate result @@ -8809,7 +8847,7 @@ void wallet2::light_wallet_get_address_txs() ireq.address = get_account().get_public_address_str(m_nettype); ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs"); //OpenMonero sends status=success, Mymonero doesn't. @@ -10113,7 +10151,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co req_t.max_count = 0; req_t.unlocked = unlocked; req_t.recent_cutoff = 0; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -10151,7 +10189,7 @@ uint64_t wallet2::get_num_rct_outputs() req_t.max_count = 0; req_t.unlocked = true; req_t.recent_cutoff = 0; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -10276,7 +10314,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10319,8 +10357,8 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10369,8 +10407,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10431,8 +10469,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -10487,8 +10525,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10560,8 +10598,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -10659,7 +10697,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10708,7 +10746,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10863,7 +10901,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -11155,7 +11193,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr gettx_req.decode_as_json = false; gettx_req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client); + bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -11166,7 +11204,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr for (size_t i = 0; i < proofs.size(); ++i) kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image)); m_daemon_rpc_mutex.lock(); - ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout); + ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(), error::wallet_internal_error, "Failed to get key image spent status from daemon"); @@ -11718,7 +11756,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag { PERF_TIMER(import_key_images_RPC); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); + bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); @@ -11804,7 +11842,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag PERF_TIMER_START(import_key_images_E); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout); + bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -12032,14 +12070,15 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); } //---------------------------------------------------------------------------------------------------- -std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs() const +std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const { PERF_TIMER(export_outputs); std::vector<tools::wallet2::transfer_details> outs; size_t offset = 0; - while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request)) - ++offset; + if (!all) + while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request)) + ++offset; outs.reserve(m_transfers.size() - offset); for (size_t n = offset; n < m_transfers.size(); ++n) @@ -12052,13 +12091,13 @@ std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export return std::make_pair(offset, outs); } //---------------------------------------------------------------------------------------------------- -std::string wallet2::export_outputs_to_str() const +std::string wallet2::export_outputs_to_str(bool all) const { PERF_TIMER(export_outputs_to_str); std::stringstream oss; boost::archive::portable_binary_oarchive ar(oss); - const auto& outputs = export_outputs(); + const auto& outputs = export_outputs(all); ar << outputs; std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); @@ -12739,7 +12778,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui height_mid, height_max }; - bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout); if (!r || res.status != CORE_RPC_STATUS_OK) { std::ostringstream oss; @@ -12809,7 +12848,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std:: cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); @@ -12879,7 +12918,7 @@ uint64_t wallet2::get_segregation_fork_height() const if (m_segregation_height > 0) return m_segregation_height; - if (m_use_dns) + if (m_use_dns && !m_offline) { // All four MoneroPulse domains have DNSSEC on and valid static const std::vector<std::string> dns_urls = { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 39380c9df..8561c42ba 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1139,8 +1139,8 @@ namespace tools bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; // Import/Export wallet data - std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs() const; - std::string export_outputs_to_str() const; + std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs(bool all = false) const; + std::string export_outputs_to_str(bool all = false) const; size_t import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs); size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const; @@ -1232,19 +1232,22 @@ namespace tools template<class t_request, class t_response> inline bool invoke_http_json(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET") { - boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_json(uri, req, res, m_http_client, timeout, http_method); } template<class t_request, class t_response> inline bool invoke_http_bin(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET") { - boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_bin(uri, req, res, m_http_client, timeout, http_method); } template<class t_request, class t_response> inline bool invoke_http_json_rpc(const boost::string_ref uri, const std::string& method_name, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") { - boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_json_rpc(uri, method_name, req, res, m_http_client, timeout, http_method, req_id); } @@ -1291,6 +1294,7 @@ namespace tools uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const; void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash); void enable_dns(bool enable) { m_use_dns = enable; } + void set_offline(bool offline = true); private: /*! @@ -1422,7 +1426,7 @@ namespace tools std::atomic<bool> m_run; - boost::mutex m_daemon_rpc_mutex; + boost::recursive_mutex m_daemon_rpc_mutex; bool m_trusted_daemon; i_wallet2_callback* m_callback; @@ -1474,6 +1478,7 @@ namespace tools std::string m_device_derivation_path; uint64_t m_device_last_key_image_sync; bool m_use_dns; + bool m_offline; // Aux transaction data from device std::unordered_map<crypto::hash, std::string> m_tx_device; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 71c64d3c1..2039c6742 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1842,11 +1842,7 @@ namespace tools { if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0)) continue; - if (!transfers_found) - { - transfers_found = true; - } - auto txBlob = t_serializable_object_to_blob(td.m_tx); + transfers_found = true; wallet_rpc::transfer_details rpc_transfers; rpc_transfers.amount = td.amount(); rpc_transfers.spent = td.m_spent; @@ -2581,7 +2577,7 @@ namespace tools try { - res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str()); + res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all)); } catch (const std::exception &e) { @@ -3111,6 +3107,18 @@ namespace tools er.message = "Invalid filename"; return false; } + if (m_wallet && req.autosave_current) + { + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + } std::string wallet_file = m_wallet_dir + "/" + req.filename; { po::options_description desc("dummy"); @@ -3141,18 +3149,7 @@ namespace tools } if (m_wallet) - { - try - { - m_wallet->store(); - } - catch (const std::exception& e) - { - handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); - return false; - } delete m_wallet; - } m_wallet = wal.release(); return true; } @@ -3161,14 +3158,17 @@ namespace tools { if (!m_wallet) return not_open(er); - try + if (req.autosave_current) { - m_wallet->store(); - } - catch (const std::exception& e) - { - handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); - return false; + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } } delete m_wallet; m_wallet = NULL; @@ -3385,6 +3385,20 @@ namespace tools return false; } + if (m_wallet && req.autosave_current) + { + try + { + if (!wallet_file.empty()) + m_wallet->store(); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + } + try { if (!req.spendkey.empty()) @@ -3433,19 +3447,7 @@ namespace tools } if (m_wallet) - { - try - { - if (!wallet_file.empty()) - m_wallet->store(); - } - catch (const std::exception &e) - { - handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); - return false; - } delete m_wallet; - } m_wallet = wal.release(); res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); return true; @@ -3511,6 +3513,18 @@ namespace tools return false; } } + if (m_wallet && req.autosave_current) + { + try + { + m_wallet->store(); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + } // process seed_offset if given { @@ -3621,18 +3635,7 @@ namespace tools } if (m_wallet) - { - try - { - m_wallet->store(); - } - catch (const std::exception &e) - { - handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); - return false; - } delete m_wallet; - } m_wallet = wal.release(); res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); res.info = "Wallet has been restored successfully."; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index bb360ae01..8757acef2 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 9 +#define WALLET_RPC_VERSION_MINOR 10 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1626,7 +1626,10 @@ namespace wallet_rpc { struct request_t { + bool all; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(all) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2043,10 +2046,12 @@ namespace wallet_rpc { std::string filename; std::string password; + bool autosave_current; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(filename) KV_SERIALIZE(password) + KV_SERIALIZE_OPT(autosave_current, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2063,7 +2068,10 @@ namespace wallet_rpc { struct request_t { + bool autosave_current; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(autosave_current, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2108,6 +2116,7 @@ namespace wallet_rpc std::string spendkey; std::string viewkey; std::string password; + bool autosave_current; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_OPT(restore_height, (uint64_t)0) @@ -2116,6 +2125,7 @@ namespace wallet_rpc KV_SERIALIZE(spendkey) KV_SERIALIZE(viewkey) KV_SERIALIZE(password) + KV_SERIALIZE_OPT(autosave_current, true) END_KV_SERIALIZE_MAP() }; @@ -2141,6 +2151,7 @@ namespace wallet_rpc std::string seed_offset; std::string password; std::string language; + bool autosave_current; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_OPT(restore_height, (uint64_t)0) @@ -2149,6 +2160,7 @@ namespace wallet_rpc KV_SERIALIZE(seed_offset) KV_SERIALIZE(password) KV_SERIALIZE(language) + KV_SERIALIZE_OPT(autosave_current, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py index ba533c53c..b23da3d77 100755 --- a/tests/block_weight/block_weight.py +++ b/tests/block_weight/block_weight.py @@ -9,7 +9,6 @@ import math MEDIAN_WINDOW_SMALL = 100 # number of recent blocks for median computation MEDIAN_WINDOW_BIG = 5000 -MULTIPLIER_SMALL = 1.4 # multipliers for determining weights MULTIPLIER_BIG = 50.0 MEDIAN_THRESHOLD = 300*1000 # initial value for median (scaled kB -> B) lcg_seed = 0 @@ -24,9 +23,9 @@ def get_median(vec): #temp = vec temp = sorted(vec) if len(temp) % 2 == 1: - return temp[len(temp)/2] + return temp[len(temp)//2] else: - return int((temp[len(temp)/2]+temp[len(temp)/2-1])/2) + return int((temp[len(temp)//2]+temp[len(temp)//2-1])//2) def LCG(): global lcg_seed diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index efc751df7..f9768a316 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -97,7 +97,7 @@ template<> struct get_test_options<gen_bp_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(10, 73), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index f2bcb7787..797d1207b 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -643,7 +643,15 @@ public: log_event("cryptonote::block"); cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_c.handle_incoming_block(t_serializable_object_to_blob(b), &b, bvc); + cryptonote::blobdata bd = t_serializable_object_to_blob(b); + std::vector<cryptonote::block> pblocks; + if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, {bd, {}}), pblocks)) + { + m_c.handle_incoming_block(bd, &b, bvc); + m_c.cleanup_handle_incoming_blocks(); + } + else + bvc.m_verifivation_failed = true; bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator); CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed"); return r; @@ -666,7 +674,14 @@ public: log_event("serialized_block"); cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_c.handle_incoming_block(sr_block.data, NULL, bvc); + std::vector<cryptonote::block> pblocks; + if (m_c.prepare_handle_incoming_blocks(std::vector<cryptonote::block_complete_entry>(1, {sr_block.data, {}}), pblocks)) + { + m_c.handle_incoming_block(sr_block.data, NULL, bvc); + m_c.cleanup_handle_incoming_blocks(); + } + else + bvc.m_verifivation_failed = true; cryptonote::block blk; std::stringstream ss; @@ -748,7 +763,7 @@ template<typename t_test_class> struct get_test_options { const std::pair<uint8_t, uint64_t> hard_forks[2]; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} }; @@ -776,7 +791,7 @@ inline bool do_replay_events_get_core(std::vector<test_event_entry>& events, cry // Hardforks can be specified in events. v_hardforks_t hardforks; - cryptonote::test_options test_options_tmp{}; + cryptonote::test_options test_options_tmp{nullptr, 0}; const cryptonote::test_options * test_options_ = >o.test_options; if (extract_hard_forks(events, hardforks)){ hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index 1e8226d26..10fe6ffe8 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -84,7 +84,7 @@ template<> struct get_test_options<gen_multisig_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(4, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 72460d98e..00a2bd88c 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -83,7 +83,7 @@ template<> struct get_test_options<gen_rct_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 050277c51..bc2f5472b 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -29,6 +29,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import time +import json """Test simple transfers """ @@ -43,6 +44,7 @@ class TransferTest(): self.transfer() self.check_get_bulk_payments() self.check_double_spend_detection() + self.sweep_single() def create(self): print 'Creating wallets' @@ -569,6 +571,54 @@ class TransferTest(): assert tx.in_pool assert tx.double_spend_seen + def sweep_single(self): + daemon = Daemon() + + print("Sending single output") + + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) + self.wallet[0].refresh() + res = self.wallet[0].incoming_transfers(transfer_type = 'available') + for t in res.transfers: + assert not t.spent + assert len(res.transfers) > 8 # we mined a lot + index = 8 + assert not res.transfers[index].spent + assert res.transfers[index].amount > 0 + ki = res.transfers[index].key_image + amount = res.transfers[index].amount + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 10) # ensure unlocked + self.wallet[0].refresh() + res = self.wallet[0].get_balance() + balance = res.balance + res = self.wallet[0].incoming_transfers(transfer_type = 'all') + res = self.wallet[0].sweep_single('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', key_image = ki) + assert len(res.tx_hash) == 64 + tx_hash = res.tx_hash + daemon.generateblocks('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 1) + self.wallet[0].refresh() + res = self.wallet[0].get_balance() + new_balance = res.balance + res = daemon.get_transactions([tx_hash], decode_as_json = True) + assert len(res.txs) == 1 + tx = res.txs[0] + assert tx.tx_hash == tx_hash + assert not tx.in_pool + assert len(tx.as_json) > 0 + try: + j = json.loads(tx.as_json) + except: + j = None + assert j + assert new_balance == balance - amount + assert len(j['vin']) == 1 + assert j['vin'][0]['key']['k_image'] == ki + self.wallet[0].refresh() + res = self.wallet[0].incoming_transfers(transfer_type = 'available') + assert len([t for t in res.transfers if t.key_image == ki]) == 0 + res = self.wallet[0].incoming_transfers(transfer_type = 'unavailable') + assert len([t for t in res.transfers if t.key_image == ki]) == 1 + if __name__ == '__main__': TransferTest().run_test() diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index 31c2471ec..a867a4047 100644 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -304,7 +304,7 @@ static bool init_core_replay_events(std::vector<test_event_entry>& events, crypt // Hardforks can be specified in events. v_hardforks_t hardforks; - cryptonote::test_options test_options_tmp{}; + cryptonote::test_options test_options_tmp{nullptr, 0}; const cryptonote::test_options * test_options_ = >o.test_options; if (extract_hard_forks(events, hardforks)){ hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index eb1ee8932..c8ce19ba4 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -93,18 +93,7 @@ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_cor static bool is_blocked(Server &server, const epee::net_utils::network_address &address, time_t *t = NULL) { - const std::string host = address.host_str(); - std::map<std::string, time_t> hosts = server.get_blocked_hosts(); - for (auto rec: hosts) - { - if (rec.first == host) - { - if (t) - *t = rec.second; - return true; - } - } - return false; + return server.is_host_blocked(address.host_str(), t); } TEST(ban, add) @@ -192,5 +181,21 @@ TEST(ban, add) ASSERT_TRUE(t >= 4); } +TEST(ban, limit) +{ + test_core pr_core; + cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL); + Server server(cprotocol); + cprotocol.set_p2p_endpoint(&server); + + // starts empty + ASSERT_TRUE(server.get_blocked_hosts().empty()); + ASSERT_FALSE(is_blocked(server,MAKE_IPV4_ADDRESS(1,2,3,4))); + ASSERT_TRUE(server.block_host(MAKE_IPV4_ADDRESS(1,2,3,4), std::numeric_limits<time_t>::max() - 1)); + ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS(1,2,3,4))); + ASSERT_TRUE(server.block_host(MAKE_IPV4_ADDRESS(1,2,3,4), 1)); + ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS(1,2,3,4))); +} + namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; } namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; } diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 4fbc21ddc..f302d7946 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -271,6 +271,8 @@ TYPED_TEST(BlockchainDBTest, AddBlock) this->get_filenames(); this->init_hard_fork(); + db_wtxn_guard guard(this->m_db); + // adding a block with no parent in the blockchain should throw. // note: this shouldn't be possible, but is a good (and cheap) failsafe. // @@ -317,6 +319,8 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) this->get_filenames(); this->init_hard_fork(); + db_wtxn_guard guard(this->m_db); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp index bf1368618..b7713c63a 100644 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -198,9 +198,10 @@ TEST(long_term_block_weight, multi_pop) const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); - for (uint64_t h = 0; h < 4; ++h) + const uint64_t num_pop = 4; + for (uint64_t h = 0; h < num_pop; ++h) { - size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + size_t w = bc->get_current_cumulative_block_weight_limit(); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); @@ -208,10 +209,8 @@ TEST(long_term_block_weight, multi_pop) cryptonote::block b; std::vector<cryptonote::transaction> txs; - bc->get_db().pop_block(b, txs); - bc->get_db().pop_block(b, txs); - bc->get_db().pop_block(b, txs); - bc->get_db().pop_block(b, txs); + for (uint64_t h = 0; h < num_pop; ++h) + bc->get_db().pop_block(b, txs); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); @@ -294,9 +293,11 @@ TEST(long_term_block_weight, pop_invariant_random) { PREFIX(10); - for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + for (uint64_t h = 1; h < 2 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) { - size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + lcg_seed = bc->get_db().height(); + uint32_t r = lcg(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); uint64_t ltw = bc->get_next_long_term_block_weight(w); bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {}); ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index e239154cf..4d51ec434 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -143,13 +143,16 @@ TEST(ringct, range_proofs) //ct range proofs ctkeyV sc, pc; ctkey sctmp, pctmp; - //add fake input 5000 - tie(sctmp, pctmp) = ctskpkGen(6000); + std::vector<uint64_t> inamounts; + //add fake input 6000 + inamounts.push_back(6000); + tie(sctmp, pctmp) = ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); - tie(sctmp, pctmp) = ctskpkGen(7000); + inamounts.push_back(7000); + tie(sctmp, pctmp) = ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); vector<xmr_amount >amounts; @@ -173,14 +176,20 @@ TEST(ringct, range_proofs) const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; - //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); + //compute rct data with mixin 3 - should fail since full type with > 1 input + bool ok = false; + try { genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); } + catch(...) { ok = true; } + ASSERT_TRUE(ok); + + //compute rct data with mixin 3 + rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config, hw::get_device("default")); //verify rct data - ASSERT_TRUE(verRct(s)); + ASSERT_TRUE(verRctSimple(s)); //decode received amount - decodeRct(s, amount_keys[1], 1, mask, hw::get_device("default")); + decodeRctSimple(s, amount_keys[1], 1, mask, hw::get_device("default")); // Ring CT with failing MG sig part should not verify! // Since sum of inputs != outputs @@ -190,14 +199,14 @@ TEST(ringct, range_proofs) destinations[1] = Pk; - //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); + //compute rct data with mixin 3 + s = genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config, hw::get_device("default")); //verify rct data - ASSERT_FALSE(verRct(s)); + ASSERT_FALSE(verRctSimple(s)); //decode received amount - decodeRct(s, amount_keys[1], 1, mask, hw::get_device("default")); + decodeRctSimple(s, amount_keys[1], 1, mask, hw::get_device("default")); } TEST(ringct, range_proofs_with_fee) @@ -206,13 +215,16 @@ TEST(ringct, range_proofs_with_fee) //ct range proofs ctkeyV sc, pc; ctkey sctmp, pctmp; - //add fake input 5000 - tie(sctmp, pctmp) = ctskpkGen(6001); + std::vector<uint64_t> inamounts; + //add fake input 6001 + inamounts.push_back(6001); + tie(sctmp, pctmp) = ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); - tie(sctmp, pctmp) = ctskpkGen(7000); + inamounts.push_back(7000); + tie(sctmp, pctmp) = ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); vector<xmr_amount >amounts; @@ -227,10 +239,6 @@ TEST(ringct, range_proofs_with_fee) skpkGen(Sk, Pk); destinations.push_back(Pk); - //add txn fee for 1 - //has no corresponding destination.. - amounts.push_back(1); - //add output for 12500 amounts.push_back(12500); amount_keys.push_back(hash_to_scalar(zero())); @@ -239,14 +247,14 @@ TEST(ringct, range_proofs_with_fee) const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; - //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); + //compute rct data with mixin 3 + rctSig s = genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 1, 3, rct_config, hw::get_device("default")); //verify rct data - ASSERT_TRUE(verRct(s)); + ASSERT_TRUE(verRctSimple(s)); //decode received amount - decodeRct(s, amount_keys[1], 1, mask, hw::get_device("default")); + decodeRctSimple(s, amount_keys[1], 1, mask, hw::get_device("default")); // Ring CT with failing MG sig part should not verify! // Since sum of inputs != outputs @@ -256,14 +264,14 @@ TEST(ringct, range_proofs_with_fee) destinations[1] = Pk; - //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); + //compute rct data with mixin 3 + s = genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 500, 3, rct_config, hw::get_device("default")); //verify rct data - ASSERT_FALSE(verRct(s)); + ASSERT_FALSE(verRctSimple(s)); //decode received amount - decodeRct(s, amount_keys[1], 1, mask, hw::get_device("default")); + decodeRctSimple(s, amount_keys[1], 1, mask, hw::get_device("default")); } TEST(ringct, simple) @@ -538,10 +546,10 @@ TEST(ringct, range_proofs_accept_zero_out_middle_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true)); } -TEST(ringct, range_proofs_accept_zero_in_first) +TEST(ringct, range_proofs_accept_zero) { - const uint64_t inputs[] = {0, 5000}; - const uint64_t outputs[] = {5000}; + const uint64_t inputs[] = {0}; + const uint64_t outputs[] = {0}; EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); } @@ -552,13 +560,6 @@ TEST(ringct, range_proofs_accept_zero_in_first_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true)); } -TEST(ringct, range_proofs_accept_zero_in_last) -{ - const uint64_t inputs[] = {5000, 0}; - const uint64_t outputs[] = {5000}; - EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); -} - TEST(ringct, range_proofs_accept_zero_in_last_simple) { const uint64_t inputs[] = {5000, 0}; @@ -566,13 +567,6 @@ TEST(ringct, range_proofs_accept_zero_in_last_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true)); } -TEST(ringct, range_proofs_accept_zero_in_middle) -{ - const uint64_t inputs[] = {2500, 0, 2500}; - const uint64_t outputs[] = {5000}; - EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); -} - TEST(ringct, range_proofs_accept_zero_in_middle_simple) { const uint64_t inputs[] = {2500, 0, 2500}; @@ -762,13 +756,6 @@ TEST(ringct, range_proofs_accept_1_to_N_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false,true)); } -TEST(ringct, range_proofs_accept_N_to_1) -{ - const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000}; - const uint64_t outputs[] = {5000}; - EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); -} - TEST(ringct, range_proofs_accept_N_to_1_simple) { const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000}; @@ -776,13 +763,6 @@ TEST(ringct, range_proofs_accept_N_to_1_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true)); } -TEST(ringct, range_proofs_accept_N_to_N) -{ - const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000}; - const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000}; - EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); -} - TEST(ringct, range_proofs_accept_N_to_N_simple) { const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000}; @@ -790,20 +770,6 @@ TEST(ringct, range_proofs_accept_N_to_N_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true)); } -TEST(ringct, range_proofs_accept_very_long) -{ - const size_t N=12; - uint64_t inputs[N]; - uint64_t outputs[N]; - for (size_t n = 0; n < N; ++n) { - inputs[n] = n; - outputs[n] = n; - } - std::random_shuffle(inputs, inputs + N); - std::random_shuffle(outputs, outputs + N); - EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false)); -} - TEST(ringct, range_proofs_accept_very_long_simple) { const size_t N=12; @@ -861,7 +827,7 @@ TEST(ringct, prooveRange_is_non_deterministic) TEST(ringct, fee_0_valid) { - const uint64_t inputs[] = {1000, 1000}; + const uint64_t inputs[] = {2000}; const uint64_t outputs[] = {2000, 0}; EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false)); } @@ -875,7 +841,7 @@ TEST(ringct, fee_0_valid_simple) TEST(ringct, fee_non_0_valid) { - const uint64_t inputs[] = {1000, 1000}; + const uint64_t inputs[] = {2000}; const uint64_t outputs[] = {1900, 100}; EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false)); } @@ -917,7 +883,7 @@ TEST(ringct, fee_non_0_invalid_lower_simple) TEST(ringct, fee_burn_valid_one_out) { - const uint64_t inputs[] = {1000, 1000}; + const uint64_t inputs[] = {2000}; const uint64_t outputs[] = {0, 2000}; EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false)); } @@ -931,7 +897,7 @@ TEST(ringct, fee_burn_valid_one_out_simple) TEST(ringct, fee_burn_valid_zero_out) { - const uint64_t inputs[] = {1000, 1000}; + const uint64_t inputs[] = {2000}; const uint64_t outputs[] = {2000}; EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false)); } @@ -945,7 +911,7 @@ TEST(ringct, fee_burn_valid_zero_out_simple) static rctSig make_sig() { - static const uint64_t inputs[] = {1000, 1000}; + static const uint64_t inputs[] = {2000}; static const uint64_t outputs[] = {1000, 1000}; static rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); return sig; @@ -1044,7 +1010,7 @@ TEST(ringct, reject_gen_simple_ver_non_simple) TEST(ringct, reject_gen_non_simple_ver_simple) { - const uint64_t inputs[] = {1000, 1000}; + const uint64_t inputs[] = {2000}; const uint64_t outputs[] = {1000, 1000}; rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); ASSERT_FALSE(rct::verRctSimple(sig)); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 27b14ffff..23f028464 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -477,7 +477,7 @@ TEST(Serialization, serializes_ringct_types) rct::ecdhTuple ecdh0, ecdh1; rct::boroSig boro0, boro1; rct::mgSig mg0, mg1; - rct::rangeSig rg0, rg1; + rct::Bulletproof bp0, bp1; rct::rctSig s0, s1; cryptonote::transaction tx0, tx1; @@ -566,12 +566,15 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(!memcmp(&boro0, &boro1, sizeof(boro0))); // create a full rct signature to use its innards + vector<uint64_t> inamounts; rct::ctkeyV sc, pc; rct::ctkey sctmp, pctmp; - tie(sctmp, pctmp) = rct::ctskpkGen(6000); + inamounts.push_back(6000); + tie(sctmp, pctmp) = rct::ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); - tie(sctmp, pctmp) = rct::ctskpkGen(7000); + inamounts.push_back(7000); + tie(sctmp, pctmp) = rct::ctskpkGen(inamounts.back()); sc.push_back(sctmp); pc.push_back(pctmp); vector<uint64_t> amounts; @@ -588,9 +591,9 @@ TEST(Serialization, serializes_ringct_types) amount_keys.push_back(rct::hash_to_scalar(rct::zero())); rct::skpkGen(Sk, Pk); destinations.push_back(Pk); - //compute rct data with mixin 500 + //compute rct data with mixin 3 const rct::RCTConfig rct_config{ rct::RangeProofPaddedBulletproof, 0 }; - s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, NULL, NULL, 3, rct_config, hw::get_device("default")); + s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, inamounts, amounts, amount_keys, NULL, NULL, 0, 3, rct_config, hw::get_device("default")); mg0 = s0.p.MGs[0]; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); @@ -605,66 +608,12 @@ TEST(Serialization, serializes_ringct_types) // mixRing and II are not serialized, they are meant to be reconstructed ASSERT_TRUE(mg1.II.empty()); - rg0 = s0.p.rangeSigs.front(); - ASSERT_TRUE(serialization::dump_binary(rg0, blob)); - ASSERT_TRUE(serialization::parse_binary(blob, rg1)); - ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0))); - -#if 0 - ASSERT_TRUE(serialization::dump_binary(s0, blob)); - ASSERT_TRUE(serialization::parse_binary(blob, s1)); - ASSERT_TRUE(s0.type == s1.type); - ASSERT_TRUE(s0.p.rangeSigs.size() == s1.p.rangeSigs.size()); - for (size_t n = 0; n < s0.p.rangeSigs.size(); ++n) - { - ASSERT_TRUE(!memcmp(&s0.p.rangeSigs[n], &s1.p.rangeSigs[n], sizeof(s0.p.rangeSigs[n]))); - } - ASSERT_TRUE(s0.p.MGs.size() == s1.p.MGs.size()); - ASSERT_TRUE(s0.p.MGs[0].ss.size() == s1.p.MGs[0].ss.size()); - for (size_t n = 0; n < s0.p.MGs[0].ss.size(); ++n) - { - ASSERT_TRUE(s0.p.MGs[0].ss[n] == s1.p.MGs[0].ss[n]); - } - ASSERT_TRUE(s0.p.MGs[0].cc == s1.p.MGs[0].cc); - // mixRing and II are not serialized, they are meant to be reconstructed - ASSERT_TRUE(s1.p.MGs[0].II.empty()); - - // mixRing and II are not serialized, they are meant to be reconstructed - ASSERT_TRUE(s1.mixRing.size() == 0); - - ASSERT_TRUE(s0.ecdhInfo.size() == s1.ecdhInfo.size()); - for (size_t n = 0; n < s0.ecdhInfo.size(); ++n) - { - ASSERT_TRUE(!memcmp(&s0.ecdhInfo[n], &s1.ecdhInfo[n], sizeof(s0.ecdhInfo[n]))); - } - ASSERT_TRUE(s0.outPk.size() == s1.outPk.size()); - for (size_t n = 0; n < s0.outPk.size(); ++n) - { - // serialization only does the mask - ASSERT_TRUE(!memcmp(&s0.outPk[n].mask, &s1.outPk[n].mask, sizeof(s0.outPk[n].mask))); - } -#endif - - tx0.set_null(); - tx0.version = 2; - cryptonote::txin_to_key txin_to_key1{}; - txin_to_key1.amount = 100; - txin_to_key1.key_offsets.resize(4); - cryptonote::txin_to_key txin_to_key2{}; - txin_to_key2.amount = 200; - txin_to_key2.key_offsets.resize(4); - tx0.vin.push_back(txin_to_key1); - tx0.vin.push_back(txin_to_key2); - tx0.vout.push_back(cryptonote::tx_out()); - tx0.vout.push_back(cryptonote::tx_out()); - tx0.rct_signatures = s0; - ASSERT_EQ(tx0.rct_signatures.p.rangeSigs.size(), 2); - ASSERT_TRUE(serialization::dump_binary(tx0, blob)); - ASSERT_TRUE(serialization::parse_binary(blob, tx1)); - ASSERT_EQ(tx1.rct_signatures.p.rangeSigs.size(), 2); - std::string blob2; - ASSERT_TRUE(serialization::dump_binary(tx1, blob2)); - ASSERT_TRUE(blob == blob2); + ASSERT_FALSE(s0.p.bulletproofs.empty()); + bp0 = s0.p.bulletproofs.front(); + ASSERT_TRUE(serialization::dump_binary(bp0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, bp1)); + bp1.V = bp0.V; // this is not saved, as it is reconstructed from other tx data + ASSERT_EQ(bp0, bp1); } TEST(Serialization, portability_wallet) diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py index f4d5e90f0..04fc5b5cf 100644 --- a/utils/python-rpc/framework/daemon.py +++ b/utils/python-rpc/framework/daemon.py @@ -50,10 +50,11 @@ class Daemon(object): } return self.rpc.send_json_rpc_request(getblocktemplate) - def send_raw_transaction(self, tx_as_hex, do_not_relay = False): + def send_raw_transaction(self, tx_as_hex, do_not_relay = False, do_sanity_checks = True): send_raw_transaction = { 'tx_as_hex': tx_as_hex, 'do_not_relay': do_not_relay, + 'do_sanity_checks': do_sanity_checks, } return self.rpc.send_request("/send_raw_transaction", send_raw_transaction) diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index a80aaefec..695325a86 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -184,6 +184,27 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(sweep_all) + def sweep_single(self, address = '', priority = 0, ring_size = 0, outputs = 1, unlock_time = 0, payment_id = '', get_tx_keys = False, key_image = "", do_not_relay = False, get_tx_hex = False, get_tx_metadata = False): + sweep_single = { + 'method': 'sweep_single', + 'params' : { + 'address' : address, + 'priority' : priority, + 'ring_size' : ring_size, + 'outputs' : outputs, + 'unlock_time' : unlock_time, + 'payment_id' : payment_id, + 'get_tx_keys' : get_tx_keys, + 'key_image' : key_image, + 'do_not_relay' : do_not_relay, + 'get_tx_hex' : get_tx_hex, + 'get_tx_metadata' : get_tx_metadata, + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_json_rpc_request(sweep_single) + def get_address(self, account_index = 0, subaddresses = []): get_address = { 'method': 'get_address', @@ -265,7 +286,7 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(query_key) - def restore_deterministic_wallet(self, seed = '', seed_offset = '', filename = '', restore_height = 0, password = '', language = ''): + def restore_deterministic_wallet(self, seed = '', seed_offset = '', filename = '', restore_height = 0, password = '', language = '', autosave_current = True): restore_deterministic_wallet = { 'method': 'restore_deterministic_wallet', 'params' : { @@ -274,14 +295,15 @@ class Wallet(object): 'seed': seed, 'seed_offset': seed_offset, 'password': password, - 'language': language + 'language': language, + 'autosave_current': autosave_current, }, 'jsonrpc': '2.0', 'id': '0' } return self.rpc.send_json_rpc_request(restore_deterministic_wallet) - def generate_from_keys(self, restore_height = 0, filename = "", password = "", address = "", spendkey = "", viewkey = ""): + def generate_from_keys(self, restore_height = 0, filename = "", password = "", address = "", spendkey = "", viewkey = "", autosave_current = True): generate_from_keys = { 'method': 'generate_from_keys', 'params' : { @@ -291,16 +313,31 @@ class Wallet(object): 'spendkey': spendkey, 'viewkey': viewkey, 'password': password, + 'autosave_current': autosave_current, }, 'jsonrpc': '2.0', 'id': '0' } return self.rpc.send_json_rpc_request(generate_from_keys) - def close_wallet(self): + def open_wallet(self, filename, password='', autosave_current = True): + open_wallet = { + 'method': 'open_wallet', + 'params' : { + 'filename': filename, + 'password': password, + 'autosave_current': autosave_current, + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_json_rpc_request(open_wallet) + + def close_wallet(self, autosave_current = True): close_wallet = { 'method': 'close_wallet', 'params' : { + 'autosave_current': autosave_current }, 'jsonrpc': '2.0', 'id': '0' |