diff options
106 files changed, 1702 insertions, 830 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 971c097ff..1f74f59e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,7 @@ endif() # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") # set(BSDI TRUE) -include_directories(external/easylogging++ src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") +include_directories(external/easylogging++ src contrib/epee/include external) if(APPLE) include_directories(SYSTEM /usr/include/malloc) @@ -234,6 +234,7 @@ if(STATIC) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZMQ_STATIC") endif() # Set default blockchain storage location: @@ -319,6 +320,12 @@ else() message(STATUS "Stack trace on exception disabled") endif() +if (UNIX AND NOT APPLE) + # Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) +endif() + # Handle OpenSSL, used for sha256sum on binary updates if (APPLE AND NOT IOS) if (NOT OpenSSL_DIR) @@ -332,16 +339,10 @@ endif() find_package(OpenSSL REQUIRED) if(STATIC AND NOT IOS) if(UNIX) - set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES};${CMAKE_DL_LIBS}") + set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES};${CMAKE_DL_LIBS};${CMAKE_THREAD_LIBS_INIT}") endif() endif() -if (UNIX AND NOT APPLE) - # Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads) -endif() - add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) add_subdirectory(external) @@ -407,7 +408,7 @@ else() set(WARNINGS "${WARNINGS} -Wno-error=inline-asm") endif() else() - set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized") + set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=cpp") endif() if(MINGW) set(WARNINGS "${WARNINGS} -Wno-error=unused-value -Wno-error=unused-but-set-variable") @@ -691,25 +692,17 @@ include(version.cmake) find_path(ZMQ_INCLUDE_PATH zmq.hpp) find_library(ZMQ_LIB zmq) +find_library(SODIUM_LIBRARY sodium) if(NOT ZMQ_INCLUDE_PATH) message(FATAL_ERROR "Could not find required header zmq.hpp") endif() if(NOT ZMQ_LIB) - message(FATAL_ERROR "Could not find require libzmq") + message(FATAL_ERROR "Could not find required libzmq") +endif() +if(SODIUM_LIBRARY) + set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}") endif() - -function (treat_warnings_as_errors dirs) - foreach(dir ${ARGV}) - set_property(DIRECTORY ${dir} - APPEND PROPERTY COMPILE_FLAGS "-Werror") - endforeach() -endfunction() - -add_subdirectory(contrib) -add_subdirectory(src) - -treat_warnings_as_errors(contrib src) option(BUILD_TESTS "Build tests." OFF) @@ -717,7 +710,13 @@ if(BUILD_TESTS) add_subdirectory(tests) endif() +# warnings are cleared only for GCC on Linux +if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY)) +add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow +endif() +add_subdirectory(contrib) +add_subdirectory(src) if(BUILD_DOCUMENTATION) set(DOC_GRAPHS "YES" CACHE STRING "Create dependency graphs (needs graphviz)") @@ -11,6 +11,11 @@ Portions Copyright (c) 2012-2013, The Cryptonote developers - GitHub: [https://github.com/monero-project/monero](https://github.com/monero-project/monero) - IRC: [#monero-dev on Freenode](http://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4) +## Vulnerability Response + +- Our [Vulnerability Response Process](https://github.com/monero-project/meta/blob/master/VULNERABILITY_RESPONSE_PROCESS.md) encourages responsible disclosure +- We are also available via [HackerOne](https://hackerone.com/monero) + ## Build | Operating System | Processor | Status | @@ -89,18 +94,22 @@ If you want to help out, see [CONTRIBUTING](CONTRIBUTING.md) for a set of guidel See [Vulnerability Response Process](VULNERABILITY_RESPONSE_PROCESS.md). -## Monero software updates and consensus protocol changes (hard fork schedule) +## Monero software updates and Network Consensus Protocol Upgrade (hard fork schedule) -Monero uses a fixed-schedule hard fork mechanism to implement new features. This means that users of Monero (end users and service providers) need to run current versions and update their software on a regular schedule. Here is the current schedule, versions, and compatibility. +Monero uses a fixed-schedule network consensus protocol upgrade (hard fork) mechanism to implement new features. This means that users of Monero (end users and service providers) need to run current versions and upgrade their software on a regular schedule. Network consensus protocol upgrades occur during the months of March and September. Required software for these consensus protocol upgrades is available prior to the date of the consensus protocol upgrade. Please check the git repository prior to this date for the proper Monero software version. Below is the historical schedule and the projected schedule for the next upgrade. Dates are provided in the format YYYY-MM-DD. -| Fork Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details | -| ----------------- | ----------------- | ---------------------- | -------------------------- | ------------------ | -| 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations | -| 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | -| 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | -| 2017-09-21 | v6 | Not determined as of 2017-03-27 | Not determined as of 2017-03-27 | Allow only RingCT transactions | +| Consensus Upgrade Block Height | Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details | +| ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- | +| 1009827 | 2016-03-22 | v2 | v0.9.4 | v0.9.4 | Allow only >= ringsize 3, blocktime = 120 seconds, fee-free blocksize 60 kb | +| 1141317 | 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations | +| 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | +| 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | +| 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | +| XXXXXXX | 2018-03-XX | XX | XXXXXXXXX | XXXXXXXXX | XXXXXX + +X's indicate that these details have not been determined as of commit date, 2017-09-20. ## Installing Monero from a Package @@ -153,10 +162,12 @@ library archives (`.a`). | pkg-config | any | NO | `pkg-config` | `base-devel` | NO | | | Boost | 1.58 | NO | `libboost-all-dev` | `boost` | NO | C++ libraries | | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | NO | sha256 sum | +| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | NO | ZeroMQ library | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | NO | DNS resolver | | libminiupnpc | 2.0 | YES | `libminiupnpc-dev` | `miniupnpc` | YES | NAT punching | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | YES | Stack traces | | liblzma | any | NO | `liblzma-dev` | `xz` | YES | For libunwind | +| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | YES | Input editing | | ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | YES | SSL toolkit | | expat | 1.1 | NO | `libexpat1-dev` | `expat` | YES | XML parsing | | GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | YES | Test suite | @@ -184,6 +195,9 @@ invokes cmake commands as needed. this to be worthwhile, the machine should have one core and about 2GB of RAM available per thread. + *Note*: If cmake can not find zmq.hpp file on OS X, installing `zmq.hpp` from + https://github.com/zeromq/cppzmq to `/usr/local/include` should fix that error. + * The resulting executables can be found in `build/release/bin` * Add `PATH="$PATH:$HOME/monero/build/release/bin"` to `.profile` @@ -280,11 +294,11 @@ application. To build for 64-bit Windows: - pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost + pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium To build for 32-bit Windows: - pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost + pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium * Open the MingW shell via `MinGW-w64-Win64 Shell` shortcut on 64-bit Windows or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are @@ -394,14 +408,6 @@ TAILS ships with a very restrictive set of firewall rules. Therefore, you need t `./monero-wallet-cli` -## Using readline - -While monerod and monero-wallet-cli do not use readline directly, most of the functionality can be obtained by running them via rlwrap. This allows command recall, edit capabilities, etc. It does not give autocompletion without an extra completion file, however. To use rlwrap, simply prepend `rlwrap` to the command line, eg: - -`rlwrap bin/monero-wallet-cli --wallet-file /path/to/wallet` - -Note: rlwrap will save things like your seed and private keys, if you supply them on prompt. You may want to not use rlwrap when you use simplewallet to restore from seed, etc. - # Debugging This section contains general instructions for debugging failed installs or problems encountered with Monero. First ensure you are running the latest version built from the github repo. diff --git a/VULNERABILITY_RESPONSE_PROCESS.md b/VULNERABILITY_RESPONSE_PROCESS.md deleted file mode 100644 index eea3a06e7..000000000 --- a/VULNERABILITY_RESPONSE_PROCESS.md +++ /dev/null @@ -1,143 +0,0 @@ -# Monero Vulnerability Response Process - -## Preamble - -Researchers/Hackers: while you research/hack, we ask that you please refrain from committing the following: -- Denial of Service / Active exploiting against the network -- Social Engineering of Monero staff or contractors -- Any physical or electronic attempts against Monero community property and/or data centers - -## I. Point of Contacts for Security Issues - -``` -ric@getmonero.org -BDA6 BD70 42B7 21C4 67A9 759D 7455 C5E3 C0CD CEB9 - -luigi1111@getmonero.org -8777 AB8F 778E E894 87A2 F8E7 F4AC A018 3641 E010 - -moneromooo.monero@gmail.com -48B0 8161 FBDA DFE3 93AD FC3E 686F 0745 4D6C EFC3 -``` - -## II. Security Response Team - -- fluffypony -- luigi1111 -- moneromooo - -## III. Incident Response - -1. Researcher submits report via one or both of two methods: - - a. Email - - b. [HackerOne](https://hackerone.com/monero) - -2. Response Team designates a Response Manager who is in charge of the particular report based on availability and/or knowledge-set - -3. In no more than 3 working days, Response Team should gratefully respond to researcher using only encrypted, secure channels - -4. Response Manager makes inquiries to satisfy any needed information to confirm if submission is indeed a vulnerability - - a. If submission proves to be vulnerable, proceed to next step - - b. If not vulnerable: - - i. Response Manager responds with reasons why submission is not a vulnerability - - ii. Response Manager moves discussion to a new or existing ticket on GitHub if necessary - -5. If over email, Response Manager opens a HackerOne issue for new submission - -6. Establish severity of vulnerability: - - a. HIGH: impacts network as a whole, has potential to break entire network, results in the loss of monero, or is on a scale of great catastrophe - - b. MEDIUM: impacts individual nodes, wallets, or must be carefully exploited - - c. LOW: is not easily exploitable - -7. Respond according to the severity of the vulnerability: - - a. HIGH severities must be notified on website and reddit /r/Monero within 3 working days of classification - - i. The notification should list appropriate steps for users to take, if any - - ii. The notification must not include any details that could suggest an exploitation path - - iii. The latter takes precedence over the former - - b. MEDIUM and HIGH severities will require a Point Release - - c. LOW severities will be addressed in the next Regular Release - -8. Response Team applies appropriate patch(es) - - a. Response Manager designates a PRIVATE git "hotfix branch" to work in - - b. Patches are reviewed with the researcher - - c. Any messages associated with PUBLIC commits during the time of review should not make reference to the security nature of the PRIVATE branch or its commits - - d. Vulnerability announcement is drafted - - i. Include the severity of the vulnerability - - ii. Include all vulnerable systems/apps/code - - iii. Include solutions (if any) if patch cannot be applied - - e. Release date is discussed - -9. At release date, Response Team coordinates with developers to finalize update: - - a. Response Manager propagates the "hotfix branch" to trunk - - b. Response Manager includes vulnerability announcement draft in release notes - - c. Proceed with the Point or Regular Release - -## IV. Post-release Disclosure Process - -1. Response Team has 90 days to fulfill all points within section III - -2. If the Incident Response process in section III is successfully completed: - - a. Response Manager contacts researcher and asks if researcher wishes for credit - - b. Finalize vulnerability announcement draft and include the following: - - i. Project name and URL - - ii. Versions known to be affected - - iii. Versions known to be not affected (for example, the vulnerable code was introduced in a recent version, and older versions are therefore unaffected) - - iv. Versions not checked - - v. Type of vulnerability and its impact - - vi. If already obtained or applicable, a CVE-ID - - vii. The planned, coordinated release date - - viii. Mitigating factors (for example, the vulnerability is only exposed in uncommon, non-default configurations) - - ix. Workarounds (configuration changes users can make to reduce their exposure to the vulnerability) - - x. If applicable, credits to the original reporter - - c. Release finalized vulnerability announcement on website and reddit /r/Monero - - d. For HIGH severities, release finalized vulnerability announcement on well-known mailing lists: - - i. oss-security@lists.openwall.com - - ii. bugtraq@securityfocus.com - - e. If applicable, developers request a CVE-ID - - i. The commit that applied the fix is made reference too in a future commit and includes a CVE-ID - -3. If the Incident Response process in section III is *not* successfully completed: - - a. Response Team and developers organize an IRC meeting to discuss why/what points in section III were not resolved and how the team can resolve them in the future - - b. Any developer meetings immediately following the incident should include points made in section V - - c. If disputes arise about whether or when to disclose information about a vulnerability, the Response Team will publicly discuss the issue via IRC and attempt to reach consensus - - d. If consensus on a timely disclosure is not met (no later than 90 days), the researcher (after 90 days) has every right to expose the vulnerability to the public - -## V. Incident Analysis - -1. Isolate codebase - - a. Response Team and developers should coordinate to work on the following: - - i. Problematic implementation of classes/libraries/functions, etc. - - ii. Focus on apps/distro packaging, etc. - - iii. Operator/config error, etc. - -2. Auditing - - a. Response Team and developers should coordinate to work on the following: - - i. Auditing of problem area(s) as discussed in point 1 - - ii. Generate internal reports and store for future reference - - iii. If results are not sensitive, share with the public via IRC or GitHub - -3. Response Team has 45 days following completion of section III to ensure completion of section V - -## VI. Resolutions - -Any further questions or resolutions regarding the incident(s) between the researcher and response + development team after public disclosure can be addressed via the following: - -- [GitHub](https://github.com/monero-project/monero/issues/) -- [HackerOne](https://hackerone.com/monero) -- [Reddit /r/Monero](https://reddit.com/r/Monero/) -- IRC -- Email - -## VII. Continuous Improvement - -1. Response Team and developers should hold annual meetings to review the previous year's incidents - -2. Response Team or designated person(s) should give a brief presentation, including: - - a. Areas of Monero affected by the incidents - - b. Any network downtime or monetary cost (if any) of the incidents - - c. Ways in which the incidents could have been avoided (if any) - - d. How effective this process was in dealing with the incidents - -3. After the presentation, Response Team and developers should discuss: - - a. Potential changes to development processes to reduce future incidents - - b. Potential changes to this process to improve future responses diff --git a/cmake/FindMiniupnpc.cmake b/cmake/FindMiniupnpc.cmake index 77675db48..ad2004afc 100644 --- a/cmake/FindMiniupnpc.cmake +++ b/cmake/FindMiniupnpc.cmake @@ -46,13 +46,13 @@ IF(MINIUPNPC_FOUND) file(STRINGS "${MINIUPNP_INCLUDE_DIR}/miniupnpc.h" MINIUPNPC_API_VERSION_STR REGEX "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+[0-9]+") if(MINIUPNPC_API_VERSION_STR MATCHES "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+([0-9]+)") set(MINIUPNPC_API_VERSION "${CMAKE_MATCH_1}") + if (${MINIUPNPC_API_VERSION} GREATER "10" OR ${MINIUPNPC_API_VERSION} EQUAL "10") + message(STATUS "Found miniupnpc API version " ${MINIUPNPC_API_VERSION}) + set(MINIUPNP_FOUND true) + set(MINIUPNPC_VERSION_1_7_OR_HIGHER true) + endif() endif() - if (${MINIUPNPC_API_VERSION} GREATER "10" OR ${MINIUPNPC_API_VERSION} EQUAL "10") - message(STATUS "Found miniupnpc API version " ${MINIUPNPC_API_VERSION}) - set(MINIUPNP_FOUND true) - set(MINIUPNPC_VERSION_1_7_OR_HIGHER true) - endif() ENDIF() mark_as_advanced(MINIUPNP_INCLUDE_DIR MINIUPNP_LIBRARY MINIUPNP_STATIC_LIBRARY) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index ec4bcbe2d..d351b024d 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -52,6 +52,7 @@ #include "easylogging++.h" #define MONERO_DEFAULT_LOG_CATEGORY "default" +#define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes #define MCFATAL(cat,x) CLOG(FATAL,cat) << x #define MCERROR(cat,x) CLOG(ERROR,cat) << x @@ -123,7 +124,7 @@ #endif std::string mlog_get_default_log_path(const char *default_filename); -void mlog_configure(const std::string &filename_base, bool console); +void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size = MAX_LOG_FILE_SIZE); void mlog_set_categories(const char *categories); void mlog_set_log_level(int level); void mlog_set_log(const char *log); diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index ca58d5467..03f143fe4 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -281,8 +281,6 @@ namespace net_utils bool is_thread_worker(); - bool cleanup_connections(); - /// The io_service used to perform asynchronous operations. std::unique_ptr<boost::asio::io_service> m_io_service_local_instance; boost::asio::io_service& io_service_; @@ -309,7 +307,7 @@ namespace net_utils connection_ptr new_connection_; boost::mutex connections_mutex; - std::deque<std::pair<boost::system_time, connection_ptr>> connections_; + std::set<connection_ptr> connections_; }; // class <>boosted_tcp_server diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 61276e761..76988a26e 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -54,8 +54,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" -#define CONNECTION_CLEANUP_TIME 30 // seconds - PRAGMA_WARNING_PUSH namespace epee { @@ -808,7 +806,6 @@ POP_WARNINGS m_threads_count = threads_count; m_main_thread_id = boost::this_thread::get_id(); MLOG_SET_THREAD_NAME("[SRV_MAIN]"); - add_idle_handler(boost::bind(&boosted_tcp_server::cleanup_connections, this), 5000); while(!m_stop_signal_sent) { @@ -898,7 +895,7 @@ POP_WARNINGS connections_mutex.lock(); for (auto &c: connections_) { - c.second->cancel(); + c->cancel(); } connections_.clear(); connections_mutex.unlock(); @@ -907,19 +904,6 @@ POP_WARNINGS } //--------------------------------------------------------------------------------- template<class t_protocol_handler> - bool boosted_tcp_server<t_protocol_handler>::cleanup_connections() - { - connections_mutex.lock(); - boost::system_time cutoff = boost::get_system_time() - boost::posix_time::seconds(CONNECTION_CLEANUP_TIME); - while (!connections_.empty() && connections_.front().first < cutoff) - { - connections_.pop_front(); - } - connections_mutex.unlock(); - return true; - } - //--------------------------------------------------------------------------------- - template<class t_protocol_handler> bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent() { return m_stop_signal_sent; @@ -958,9 +942,10 @@ POP_WARNINGS connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connections_mutex.lock(); - connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l)); + connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); connections_mutex.unlock(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); }); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); ////////////////////////////////////////////////////////////////////////// @@ -1038,6 +1023,10 @@ POP_WARNINGS _dbg3("Connected success to " << adr << ':' << port); + // start adds the connection to the config object's list, so we don't need to have it locally anymore + connections_mutex.lock(); + connections_.erase(new_connection_l); + connections_mutex.unlock(); bool r = new_connection_l->start(false, 1 < m_threads_count); if (r) { @@ -1062,9 +1051,10 @@ POP_WARNINGS TRY_ENTRY(); connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connections_mutex.lock(); - connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l)); + connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); connections_mutex.unlock(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); }); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); ////////////////////////////////////////////////////////////////////////// @@ -1113,6 +1103,11 @@ POP_WARNINGS { _dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port << " from " << lep.address().to_string() << ':' << lep.port()); + + // start adds the connection to the config object's list, so we don't need to have it locally anymore + connections_mutex.lock(); + connections_.erase(new_connection_l); + connections_mutex.unlock(); bool r = new_connection_l->start(false, 1 < m_threads_count); if (r) { diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index e5aa06cb4..144acad9d 100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -155,7 +155,8 @@ namespace net_utils http_request_info():m_http_method(http_method_unknown), m_http_ver_hi(0), m_http_ver_lo(0), - m_have_to_block(false) + m_have_to_block(false), + m_full_request_buf_size(0) {} http_method m_http_method; diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 1a58cab99..4423f2608 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -227,6 +227,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template<class t_type, class t_storage> + static bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return serialize_stl_container_t_val(d, stg, hparent_section, pname); @@ -268,6 +280,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template<class t_type, class t_storage> + static bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return serialize_stl_container_t_obj(d, stg, hparent_section, pname); @@ -353,6 +377,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template<class t_type, class t_storage> + bool kv_serialize(const std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_unserialize(std::deque<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types<t_storage>, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index a3f38e677..964ba41df 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -111,7 +111,7 @@ static const char *get_default_categories(int level) return categories; } -void mlog_configure(const std::string &filename_base, bool console) +void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size) { el::Configurations c; c.setGlobally(el::ConfigurationType::Filename, filename_base); @@ -121,7 +121,7 @@ void mlog_configure(const std::string &filename_base, bool console) log_format = MLOG_BASE_FORMAT; c.setGlobally(el::ConfigurationType::Format, log_format); c.setGlobally(el::ConfigurationType::ToStandardOutput, console ? "true" : "false"); - c.setGlobally(el::ConfigurationType::MaxLogFileSize, "104850000"); // 100 MB - 7600 bytes + c.setGlobally(el::ConfigurationType::MaxLogFileSize, std::to_string(max_log_file_size)); el::Loggers::setDefaultConfigurations(c, true); el::Loggers::addFlag(el::LoggingFlag::HierarchicalLogging); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3cb7cfd6..57ff28bfc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,9 +96,13 @@ function (monero_add_library name) PRIVATE $<TARGET_PROPERTY:${name},INTERFACE_COMPILE_DEFINITIONS>) endfunction () +set_source_files_properties(${CMAKE_BINARY_DIR}/version.cpp PROPERTIES GENERATED ON) +monero_add_library(version ${CMAKE_BINARY_DIR}/version.cpp) + add_subdirectory(common) add_subdirectory(crypto) add_subdirectory(ringct) +add_subdirectory(checkpoints) add_subdirectory(cryptonote_basic) add_subdirectory(cryptonote_core) if(NOT IOS) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 85a494ce7..838385e8a 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -711,7 +711,7 @@ public: * * @return true if we started the batch, false if already started */ - virtual bool batch_start(uint64_t batch_num_blocks=0) = 0; + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) = 0; /** * @brief ends a batch transaction diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b6978bdc4..985244f6b 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -548,7 +548,7 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const #endif } -void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks) +void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L1("[" << __func__ << "] " << "checking DB size"); @@ -557,7 +557,7 @@ void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks) uint64_t increase_size = 0; if (batch_num_blocks > 0) { - threshold_size = get_estimated_batch_size(batch_num_blocks); + threshold_size = get_estimated_batch_size(batch_num_blocks, batch_bytes); LOG_PRINT_L1("calculated batch size: " << threshold_size); // The increased DB size could be a multiple of threshold_size, a fixed @@ -580,7 +580,7 @@ void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks) } } -uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) const +uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); uint64_t threshold_size = 0; @@ -607,6 +607,11 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con uint64_t total_block_size = 0; LOG_PRINT_L1("[" << __func__ << "] " << "m_height: " << m_height << " block_start: " << block_start << " block_stop: " << block_stop); size_t avg_block_size = 0; + if (batch_bytes) + { + avg_block_size = batch_bytes / batch_num_blocks; + goto estim; + } if (m_height == 0) { LOG_PRINT_L1("No existing blocks to check for average block size"); @@ -635,6 +640,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con avg_block_size = total_block_size / num_blocks_used; LOG_PRINT_L1("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } +estim: if (avg_block_size < min_block_size) avg_block_size = min_block_size; LOG_PRINT_L1("estimated average block size for batch: " << avg_block_size); @@ -2420,8 +2426,8 @@ bool BlockchainLMDB::for_blocks_range(const uint64_t& h1, const uint64_t& h2, st MDB_cursor_op op; if (h1) { - MDB_val_set(k, h1); - op = MDB_SET; + k = MDB_val{sizeof(h1), (void*)&h1}; + op = MDB_SET; } else { op = MDB_FIRST; @@ -2540,7 +2546,7 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c } // batch_num_blocks: (optional) Used to check if resize needed before batch transaction starts. -bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks) +bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks, uint64_t batch_bytes) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); if (! m_batch_transactions) @@ -2554,7 +2560,7 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks) check_open(); m_writer = boost::this_thread::get_id(); - check_and_resize_for_batch(batch_num_blocks); + check_and_resize_for_batch(batch_num_blocks, batch_bytes); m_write_batch_txn = new mdb_txn_safe(); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 90274b904..98571a7f8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -264,7 +264,7 @@ public: ); virtual void set_batch_transactions(bool batch_transactions); - virtual bool batch_start(uint64_t batch_num_blocks=0); + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0); virtual void batch_commit(); virtual void batch_stop(); virtual void batch_abort(); @@ -294,8 +294,8 @@ private: void do_resize(uint64_t size_increase=0); bool need_resize(uint64_t threshold_size=0) const; - void check_and_resize_for_batch(uint64_t batch_num_blocks); - uint64_t get_estimated_batch_size(uint64_t batch_num_blocks) const; + void check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes); + uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const; virtual void add_block( const block& blk , const size_t& block_size diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index ffdaad4af..0eaf71084 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -77,6 +77,7 @@ target_link_libraries(blockchain_import cryptonote_core blockchain_db p2p + version epee ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} @@ -89,8 +90,6 @@ if(ARCH_WIDTH) PUBLIC -DARCH_WIDTH=${ARCH_WIDTH}) endif() -add_dependencies(blockchain_import - version) set_property(TARGET blockchain_import PROPERTY OUTPUT_NAME "monero-blockchain-import") @@ -104,6 +103,7 @@ target_link_libraries(blockchain_export cryptonote_core blockchain_db p2p + version epee ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} @@ -111,8 +111,6 @@ target_link_libraries(blockchain_export ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) -add_dependencies(blockchain_export - version) set_property(TARGET blockchain_export PROPERTY OUTPUT_NAME "monero-blockchain-export") diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 635a70b10..d6302ea1d 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -230,11 +230,22 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path return false; } + uint64_t start_height = 1, seek_height; + if (opt_resume) + start_height = core.get_blockchain_storage().get_current_blockchain_height(); + + seek_height = start_height; BootstrapFile bootstrap; + streampos pos; // BootstrapFile bootstrap(import_file_path); - uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path); + uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path, pos, seek_height); MINFO("bootstrap file last block number: " << total_source_blocks-1 << " (zero-based height) total blocks: " << total_source_blocks); + if (total_source_blocks-1 <= start_height) + { + return false; + } + std::cout << ENDL; std::cout << "Preparing to read blocks..." << ENDL; std::cout << ENDL; @@ -259,11 +270,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path block b; transaction tx; int quit = 0; - uint64_t bytes_read = 0; - - uint64_t start_height = 1; - if (opt_resume) - start_height = core.get_blockchain_storage().get_current_blockchain_height(); + uint64_t bytes_read; // Note that a new blockchain will start with block number 0 (total blocks: 1) // due to genesis block being added at initialization. @@ -280,18 +287,35 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path bool use_batch = opt_batch && !opt_verify; - if (use_batch) - core.get_blockchain_storage().get_db().batch_start(db_batch_size); - MINFO("Reading blockchain from bootstrap file..."); std::cout << ENDL; std::list<block_complete_entry> blocks; - // Within the loop, we skip to start_height before we start adding. - // TODO: Not a bottleneck, but we can use what's done in count_blocks() and - // only do the chunk size reads, skipping the chunk content reads until we're - // at start_height. + // Skip to start_height before we start adding. + { + bool q2 = false; + import_file.seekg(pos); + bytes_read = bootstrap.count_bytes(import_file, start_height-seek_height, h, q2); + if (q2) + { + quit = 2; + goto quitting; + } + h = start_height; + } + + if (use_batch) + { + uint64_t bytes, h2; + bool q2; + pos = import_file.tellg(); + bytes = bootstrap.count_bytes(import_file, db_batch_size, h2, q2); + if (import_file.eof()) + import_file.clear(); + import_file.seekg(pos); + core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes); + } while (! quit) { uint32_t chunk_size; @@ -344,11 +368,6 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path bytes_read += chunk_size; MDEBUG("Total bytes read: " << bytes_read); - if (h + NUM_BLOCKS_PER_CHUNK < start_height + 1) - { - h += NUM_BLOCKS_PER_CHUNK; - continue; - } if (h > block_stop) { std::cout << refresh_string << "block " << h-1 @@ -456,11 +475,16 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { if ((h-1) % db_batch_size == 0) { + uint64_t bytes, h2; + bool q2; std::cout << refresh_string; // zero-based height std::cout << ENDL << "[- batch commit at height " << h-1 << " -]" << ENDL; core.get_blockchain_storage().get_db().batch_stop(); - core.get_blockchain_storage().get_db().batch_start(db_batch_size); + pos = import_file.tellg(); + bytes = bootstrap.count_bytes(import_file, db_batch_size, h2, q2); + import_file.seekg(pos); + core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes); std::cout << ENDL; core.get_blockchain_storage().get_db().show_stats(); } @@ -477,6 +501,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path } } // while +quitting: import_file.close(); if (opt_verify) diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index 2b1a5d6c7..a004d3547 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -375,39 +375,15 @@ uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file) return full_header_size; } -uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) +uint64_t BootstrapFile::count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit) { - boost::filesystem::path raw_file_path(import_file_path); - boost::system::error_code ec; - if (!boost::filesystem::exists(raw_file_path, ec)) - { - MFATAL("bootstrap file not found: " << raw_file_path); - throw std::runtime_error("Aborting"); - } - std::ifstream import_file; - import_file.open(import_file_path, std::ios_base::binary | std::ifstream::in); - - uint64_t h = 0; - if (import_file.fail()) - { - MFATAL("import_file.open() fail"); - throw std::runtime_error("Aborting"); - } - - uint64_t full_header_size; // 4 byte magic + length of header structures - full_header_size = seek_to_first_chunk(import_file); - - MINFO("Scanning blockchain from bootstrap file..."); - block b; - bool quit = false; uint64_t bytes_read = 0; - int progress_interval = 10; - + uint32_t chunk_size; + char buf1[sizeof(chunk_size)]; std::string str1; - char buf1[2048]; - while (! quit) + h = 0; + while (1) { - uint32_t chunk_size; import_file.read(buf1, sizeof(chunk_size)); if (!import_file) { std::cout << refresh_string; @@ -415,15 +391,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) quit = true; break; } - h += NUM_BLOCKS_PER_CHUNK; - if ((h-1) % progress_interval == 0) - { - std::cout << "\r" << "block height: " << h-1 << - " " << - std::flush; - } bytes_read += sizeof(chunk_size); - str1.assign(buf1, sizeof(chunk_size)); if (! ::serialization::parse_binary(str1, chunk_size)) throw std::runtime_error("Error in deserialization of chunk_size"); @@ -456,6 +424,64 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) throw std::runtime_error("Aborting"); } bytes_read += chunk_size; + h += NUM_BLOCKS_PER_CHUNK; + if (h >= blocks) + break; + } + return bytes_read; +} + +uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) +{ + streampos dummy_pos; + uint64_t dummy_height = 0; + return count_blocks(import_file_path, dummy_pos, dummy_height); +} + +// If seek_height is non-zero on entry, return a stream position <= this height when finished. +// And return the actual height corresponding to this position. Allows the caller to locate its +// starting position without having to reread the entire file again. +uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, streampos &start_pos, uint64_t& seek_height) +{ + boost::filesystem::path raw_file_path(import_file_path); + boost::system::error_code ec; + if (!boost::filesystem::exists(raw_file_path, ec)) + { + MFATAL("bootstrap file not found: " << raw_file_path); + throw std::runtime_error("Aborting"); + } + std::ifstream import_file; + import_file.open(import_file_path, std::ios_base::binary | std::ifstream::in); + + uint64_t start_height = seek_height; + uint64_t h = 0; + if (import_file.fail()) + { + MFATAL("import_file.open() fail"); + throw std::runtime_error("Aborting"); + } + + uint64_t full_header_size; // 4 byte magic + length of header structures + full_header_size = seek_to_first_chunk(import_file); + + MINFO("Scanning blockchain from bootstrap file..."); + bool quit = false; + uint64_t bytes_read = 0, blocks; + int progress_interval = 10; + + while (! quit) + { + if (start_height && h + progress_interval >= start_height - 1) + { + start_height = 0; + start_pos = import_file.tellg(); + seek_height = h; + } + bytes_read += count_bytes(import_file, progress_interval, blocks, quit); + h += blocks; + std::cout << "\r" << "block height: " << h-1 << + " " << + std::flush; // std::cout << refresh_string; MDEBUG("Number bytes scanned: " << bytes_read); diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 1a646b54b..c3969a357 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -56,6 +56,8 @@ class BootstrapFile { public: + uint64_t count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit); + uint64_t count_blocks(const std::string& dir_path, streampos& start_pos, uint64_t& seek_height); uint64_t count_blocks(const std::string& dir_path); uint64_t seek_to_first_chunk(std::ifstream& import_file); diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt new file mode 100644 index 000000000..bc7a27e36 --- /dev/null +++ b/src/checkpoints/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (c) 2014-2017, 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. + +if(APPLE) + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) +endif() + +set(checkpoints_sources + checkpoints.cpp) + +set(checkpoints_headers) + +set(checkpoints_private_headers + checkpoints.h) + +monero_private_headers(checkpoints + ${checkpoints_private_headers}) +monero_add_library(checkpoints + ${checkpoints_sources} + ${checkpoints_headers} + ${checkpoints_private_headers}) +target_link_libraries(checkpoints + PUBLIC + common + cncrypto + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_basic/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 98e509561..bea392db0 100644 --- a/src/cryptonote_basic/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -36,6 +36,7 @@ using namespace epee; #include "common/dns_utils.h" #include "include_base_utils.h" +#include "storages/portable_storage_template_helper.h" // epee json include #include <sstream> #include <random> @@ -51,7 +52,7 @@ namespace cryptonote //--------------------------------------------------------------------------- bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) { - crypto::hash h = null_hash; + crypto::hash h = crypto::null_hash; bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!"); @@ -135,8 +136,14 @@ namespace cryptonote return true; } - bool checkpoints::init_default_checkpoints() + bool checkpoints::init_default_checkpoints(bool testnet) { + if (testnet) + { + // just use the genesis block on testnet + ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); + return true; + } ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); diff --git a/src/cryptonote_basic/checkpoints.h b/src/checkpoints/checkpoints.h index 3a74d8a69..a643c5790 100644 --- a/src/cryptonote_basic/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -31,9 +31,9 @@ #pragma once #include <map> #include <vector> -#include "cryptonote_basic_impl.h" #include "misc_log_ex.h" -#include "storages/portable_storage_template_helper.h" // epee json include +#include "crypto/hash.h" +#include "serialization/keyvalue_serialization.h" #define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false); #define JSON_HASH_FILE_NAME "checkpoints.json" @@ -148,10 +148,11 @@ namespace cryptonote /** * @brief loads the default main chain checkpoints + * @param testnet whether to load testnet checkpoints or mainnet * * @return true unless adding a checkpoint fails */ - bool init_default_checkpoints(); + bool init_default_checkpoints(bool testnet); /** * @brief load new checkpoints diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 19d90253b..50887e35c 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -47,6 +47,7 @@ endif() set(common_headers) set(common_private_headers + apply_permutation.h base58.h boost_serialization_helper.h command_line.h diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h new file mode 100644 index 000000000..4fd952686 --- /dev/null +++ b/src/common/apply_permutation.h @@ -0,0 +1,68 @@ +// Copyright (c) 2017, 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. +// +// Most of this file is originally copyright (c) 2017 Raymond Chen, Microsoft +// This algorithm is adapted from Raymond Chen's code: +// https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145 + +#include <vector> +#include <functional> +#include "misc_log_ex.h" + +namespace tools +{ + +template<typename F> +void apply_permutation(std::vector<size_t> permutation, const F &swap) +{ + //sanity check + for (size_t n = 0; n < permutation.size(); ++n) + CHECK_AND_ASSERT_THROW_MES(std::find(permutation.begin(), permutation.end(), n) != permutation.end(), "Bad permutation"); + + for (size_t i = 0; i < permutation.size(); ++i) + { + size_t current = i; + while (i != permutation[current]) + { + size_t next = permutation[current]; + swap(current, next); + permutation[current] = current; + current = next; + } + permutation[current] = current; + } +} + +template<typename T> +void apply_permutation(const std::vector<size_t> &permutation, std::vector<T> &v) +{ + CHECK_AND_ASSERT_THROW_MES(permutation.size() == v.size(), "Mismatched vector sizes"); + apply_permutation(permutation, [&v](size_t i0, size_t i1){ std::swap(v[i0], v[i1]); }); +} + +} diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 64cb7c0de..941373443 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -111,13 +111,13 @@ namespace tools uint64_t res = 0; switch (9 - size) { - case 1: res |= *data++; - case 2: res <<= 8; res |= *data++; - case 3: res <<= 8; res |= *data++; - case 4: res <<= 8; res |= *data++; - case 5: res <<= 8; res |= *data++; - case 6: res <<= 8; res |= *data++; - case 7: res <<= 8; res |= *data++; + case 1: res |= *data++; /* FALLTHRU */ + case 2: res <<= 8; res |= *data++; /* FALLTHRU */ + case 3: res <<= 8; res |= *data++; /* FALLTHRU */ + case 4: res <<= 8; res |= *data++; /* FALLTHRU */ + case 5: res <<= 8; res |= *data++; /* FALLTHRU */ + case 6: res <<= 8; res |= *data++; /* FALLTHRU */ + case 7: res <<= 8; res |= *data++; /* FALLTHRU */ case 8: res <<= 8; res |= *data; break; default: assert(false); } diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index e7ff11c5c..9c306505e 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -27,8 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common/dns_utils.h" -#include "common/i18n.h" -#include "cryptonote_basic/cryptonote_basic_impl.h" // check local first (in the event of static or in-source compilation of libunbound) #include "unbound.h" @@ -326,8 +324,6 @@ bool DNSResolver::check_address_syntax(const char *addr) const namespace dns_utils { -const char *tr(const char *str) { return i18n_translate(str, "tools::dns_utils"); } - //----------------------------------------------------------------------- // TODO: parse the string in a less stupid way, probably with regex std::string address_from_txt_record(const std::string& s) diff --git a/src/common/util.cpp b/src/common/util.cpp index 046961b06..74a6babf1 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -39,11 +39,13 @@ using namespace epee; #include "net/http_client.h" // epee::net_utils::... #ifdef WIN32 -#include <windows.h> -#include <shlobj.h> -#include <strsafe.h> + #include <windows.h> + #include <shlobj.h> + #include <strsafe.h> #else -#include <sys/utsname.h> + #include <sys/file.h> + #include <sys/utsname.h> + #include <sys/stat.h> #endif #include <boost/filesystem.hpp> #include <boost/asio.hpp> @@ -53,7 +55,12 @@ namespace tools { std::function<void(int)> signal_handler::m_handler; - std::unique_ptr<std::FILE, tools::close_file> create_private_file(const std::string& name) + private_file::private_file() noexcept : m_handle(), m_filename() {} + + private_file::private_file(std::FILE* handle, std::string&& filename) noexcept + : m_handle(handle), m_filename(std::move(filename)) {} + + private_file private_file::create(std::string name) { #ifdef WIN32 struct close_handle @@ -70,17 +77,17 @@ namespace tools const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0; process.reset(temp); if (fail) - return nullptr; + return {}; } DWORD sid_size = 0; GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size)); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - return nullptr; + return {}; std::unique_ptr<char[]> sid{new char[sid_size]}; if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size))) - return nullptr; + return {}; const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner; const DWORD daclSize = @@ -88,17 +95,17 @@ namespace tools const std::unique_ptr<char[]> dacl{new char[daclSize]}; if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION)) - return nullptr; + return {}; if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid)) - return nullptr; + return {}; SECURITY_DESCRIPTOR descriptor{}; if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION)) - return nullptr; + return {}; if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false)) - return nullptr; + return {}; SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false}; std::unique_ptr<void, close_handle> file{ @@ -106,7 +113,7 @@ namespace tools name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, std::addressof(attributes), - CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, + CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE), nullptr ) }; @@ -121,22 +128,49 @@ namespace tools { _close(fd); } - return {real_file, tools::close_file{}}; + return {real_file, std::move(name)}; } } #else - const int fd = open(name.c_str(), (O_RDWR | O_EXCL | O_CREAT), S_IRUSR); - if (0 <= fd) + const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR); + if (0 <= fdr) { - std::FILE* file = fdopen(fd, "w"); - if (!file) + struct stat rstats = {}; + if (fstat(fdr, std::addressof(rstats)) != 0) { - close(fd); + close(fdr); + return {}; + } + fchmod(fdr, (S_IRUSR | S_IWUSR)); + const int fdw = open(name.c_str(), O_RDWR); + fchmod(fdr, rstats.st_mode); + close(fdr); + + if (0 <= fdw) + { + struct stat wstats = {}; + if (fstat(fdw, std::addressof(wstats)) == 0 && + rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino && + flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0) + { + std::FILE* file = fdopen(fdw, "w"); + if (file) return {file, std::move(name)}; + } + close(fdw); } - return {file, tools::close_file{}}; } #endif - return nullptr; + return {}; + } + + private_file::~private_file() noexcept + { + try + { + boost::system::error_code ec{}; + boost::filesystem::remove(filename(), ec); + } + catch (...) {} } #ifdef WIN32 diff --git a/src/common/util.h b/src/common/util.h index 2452bc9d5..48bdbbc28 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -60,8 +60,30 @@ namespace tools } }; - //! \return File only readable by owner. nullptr if `filename` exists. - std::unique_ptr<std::FILE, close_file> create_private_file(const std::string& filename); + //! A file restricted to process owner AND process. Deletes file on destruction. + class private_file { + std::unique_ptr<std::FILE, close_file> m_handle; + std::string m_filename; + + private_file(std::FILE* handle, std::string&& filename) noexcept; + public: + + //! `handle() == nullptr && filename.empty()`. + private_file() noexcept; + + /*! \return File only readable by owner and only used by this process + OR `private_file{}` on error. */ + static private_file create(std::string filename); + + private_file(private_file&&) = default; + private_file& operator=(private_file&&) = default; + + //! Deletes `filename()` and closes `handle()`. + ~private_file() noexcept; + + std::FILE* handle() const noexcept { return m_handle.get(); } + const std::string& filename() const noexcept { return m_filename; } + }; /*! \brief Returns the default data directory. * diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index e99b6651f..94f924296 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -31,12 +31,16 @@ #pragma once #include <cstddef> +#include <iostream> #include <boost/thread/mutex.hpp> #include <boost/thread/lock_guard.hpp> +#include <boost/utility/value_init.hpp> #include <vector> #include "common/pod-class.h" #include "generic-ops.h" +#include "hex.h" +#include "span.h" #include "hash.h" namespace crypto { @@ -248,6 +252,24 @@ namespace crypto { const signature *sig) { return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); } + + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + + const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>(); } CRYPTO_MAKE_HASHABLE(public_key) diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 22991e513..610b4502f 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -31,9 +31,13 @@ #pragma once #include <stddef.h> +#include <iostream> +#include <boost/utility/value_init.hpp> #include "common/pod-class.h" #include "generic-ops.h" +#include "hex.h" +#include "span.h" namespace crypto { @@ -75,6 +79,15 @@ namespace crypto { tree_hash(reinterpret_cast<const char (*)[HASH_SIZE]>(hashes), count, reinterpret_cast<char *>(&root_hash)); } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + + const static crypto::hash null_hash = boost::value_initialized<crypto::hash>(); + const static crypto::hash8 null_hash8 = boost::value_initialized<crypto::hash8>(); } CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 1503b277e..750be69f1 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -34,7 +34,6 @@ endif() set(cryptonote_basic_sources account.cpp - checkpoints.cpp cryptonote_basic_impl.cpp cryptonote_format_utils.cpp difficulty.cpp @@ -46,7 +45,6 @@ set(cryptonote_basic_headers) set(cryptonote_basic_private_headers account.h account_boost_serialization.h - checkpoints.h connection_context.h cryptonote_basic.h cryptonote_basic_impl.h @@ -60,7 +58,7 @@ set(cryptonote_basic_private_headers verification_context.h) monero_private_headers(cryptonote_basic - ${crypto_private_headers}) + ${cryptonote_basic_private_headers}) monero_add_library(cryptonote_basic ${cryptonote_basic_sources} ${cryptonote_basic_headers} @@ -69,6 +67,7 @@ target_link_libraries(cryptonote_basic PUBLIC common cncrypto + checkpoints ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 3283543e2..da4b6512e 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -40,7 +40,7 @@ namespace cryptonote struct cryptonote_connection_context: public epee::net_utils::connection_context_base { cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), - m_last_known_hash(cryptonote::null_hash) {} + m_last_known_hash(crypto::null_hash) {} enum state { diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index c4adf1fcb..eb03d33b9 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -53,11 +53,6 @@ namespace cryptonote { - - const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); - const static crypto::hash8 null_hash8 = AUTO_VAL_INIT(null_hash8); - const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); - typedef std::vector<crypto::signature> ring_signature; diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 7a2259b32..5523846d6 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -33,8 +33,6 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "crypto/hash.h" -#include "hex.h" -#include "span.h" namespace cryptonote { @@ -136,26 +134,3 @@ namespace cryptonote { bool parse_hash256(const std::string str_hash, crypto::hash& hash); -namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } -} diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 745dfb72e..fc979f288 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -632,7 +632,7 @@ namespace cryptonote // prunable rct if (t.rct_signatures.type == rct::RCTTypeNull) { - hashes[2] = cryptonote::null_hash; + hashes[2] = crypto::null_hash; } else { @@ -869,4 +869,21 @@ namespace cryptonote block_hashes_calculated = block_hashes_calculated_count; block_hashes_cached = block_hashes_cached_count; } + //--------------------------------------------------------------- + crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase) + { + crypto::hash hash; + crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); + return key; + } + //--------------------------------------------------------------- + crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase) + { + crypto::hash hash; + crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); + return key; + } + } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index d8ccf8eec..00080fb98 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -212,6 +212,8 @@ namespace cryptonote bool is_valid_decomposed_amount(uint64_t amount); void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached); + crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase); + crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get<specific_type>(variant_var); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 3c5811d61..b620e3426 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -858,19 +858,6 @@ namespace cryptonote const boost::filesystem::path& power_supply_path = iter->path(); if (boost::filesystem::is_directory(power_supply_path)) { - std::ifstream power_supply_present_stream((power_supply_path / "present").string()); - if (power_supply_present_stream.fail()) - { - LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present"); - continue; - } - - if (power_supply_present_stream.get() != '1') - { - LOG_PRINT_L4("Power supply not present at " << power_supply_path); - continue; - } - boost::filesystem::path power_supply_type_path = power_supply_path / "type"; if (boost::filesystem::is_regular_file(power_supply_type_path)) { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index cdb46dda9..a143c307f 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -91,7 +91,6 @@ #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading -#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block #define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 7c43323d4..169a38f0a 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -48,13 +48,14 @@ else() endif() monero_private_headers(cryptonote_core - ${crypto_private_headers}) + ${cryptonote_core_private_headers}) monero_add_library(cryptonote_core ${cryptonote_core_sources} ${cryptonote_core_headers} ${cryptonote_core_private_headers}) target_link_libraries(cryptonote_core PUBLIC + version common cncrypto blockchain_db diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 619dbdc07..0b09d503c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -49,7 +49,6 @@ #include "common/boost_serialization_helper.h" #include "warnings.h" #include "crypto/hash.h" -#include "cryptonote_basic/checkpoints.h" #include "cryptonote_core.h" #include "ringct/rctSigs.h" #include "common/perf_timer.h" @@ -2384,6 +2383,26 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } + // from v7, sorted outs + if (m_hardfork->get_current_version() >= 7) { + const crypto::public_key *last_key = NULL; + for (size_t n = 0; n < tx.vout.size(); ++n) + { + const tx_out &o = tx.vout[n]; + if (o.target.type() == typeid(txout_to_key)) + { + const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target); + if (last_key && memcmp(&out_to_key.key, last_key, sizeof(*last_key)) >= 0) + { + MERROR_VER("transaction has unsorted outputs"); + tvc.m_invalid_output = true; + return false; + } + last_key = &out_to_key.key; + } + } + } + return true; } //------------------------------------------------------------------ @@ -2552,6 +2571,25 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } + // from v7, sorted ins + if (hf_version >= 7) { + const crypto::key_image *last_key_image = NULL; + for (size_t n = 0; n < tx.vin.size(); ++n) + { + const txin_v &txin = tx.vin[n]; + if (txin.type() == typeid(txin_to_key)) + { + const txin_to_key& in_to_key = boost::get<txin_to_key>(txin); + if (last_key_image && memcmp(&in_to_key.k_image, last_key_image, sizeof(*last_key_image)) >= 0) + { + MERROR_VER("transaction has unsorted inputs"); + tvc.m_verifivation_failed = true; + return false; + } + last_key_image = &in_to_key.k_image; + } + } + } auto it = m_check_txin_table.find(tx_prefix_hash); if(it == m_check_txin_table.end()) { @@ -3276,7 +3314,7 @@ leave: // XXX old code adds miner tx here - int tx_index = 0; + size_t tx_index = 0; // Iterate over the block's transaction hashes, grabbing each // from the tx_pool and validating them. Each is then added // to txs. Keys spent in each are added to <keys> by the double spend check. @@ -3358,7 +3396,7 @@ leave: { // ND: if fast_check is enabled for blocks, there is no need to check // the transaction inputs, but do some sanity checks anyway. - if (memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0) + if (tx_index >= m_blocks_txs_check.size() || memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0) { MERROR_VER("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs."); //TODO: why is this done? make sure that keeping invalid blocks makes sense. @@ -3693,6 +3731,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e MTRACE("Blockchain::" << __func__); TIME_MEASURE_START(prepare); bool stop_batch; + uint64_t bytes = 0; // Order of locking must be: // m_incoming_tx_lock (optional) @@ -3714,7 +3753,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e if(blocks_entry.size() == 0) return false; - while (!(stop_batch = m_db->batch_start(blocks_entry.size()))) { + for (const auto &entry : blocks_entry) + { + bytes += entry.block.size(); + for (const auto &tx_blob : entry.txs) + { + bytes += tx_blob.size(); + } + } + while (!(stop_batch = m_db->batch_start(blocks_entry.size(), bytes))) { m_blockchain_lock.unlock(); m_tx_pool.unlock(); epee::misc_utils::sleep_no_w(1000); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index e2da535cd..00b40d0ad 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -51,7 +51,7 @@ #include "cryptonote_tx_utils.h" #include "cryptonote_basic/verification_context.h" #include "crypto/hash.h" -#include "cryptonote_basic/checkpoints.h" +#include "checkpoints/checkpoints.h" #include "cryptonote_basic/hardfork.h" #include "blockchain_db/blockchain_db.h" diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 56485aedf..57347fdbc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -45,7 +45,7 @@ using namespace epee; #include "misc_language.h" #include <csignal> #include <p2p/net_node.h> -#include "cryptonote_basic/checkpoints.h" +#include "checkpoints/checkpoints.h" #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" @@ -183,7 +183,7 @@ namespace cryptonote if (!m_testnet && !m_fakechain) { cryptonote::checkpoints checkpoints; - if (!checkpoints.init_default_checkpoints()) + if (!checkpoints.init_default_checkpoints(m_testnet)) { throw std::runtime_error("Failed to initialize checkpoints"); } @@ -211,10 +211,9 @@ namespace cryptonote return m_blockchain_storage.get_current_blockchain_height(); } //----------------------------------------------------------------------------------------------- - bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const + void core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const { top_id = m_blockchain_storage.get_tail_id(height); - return true; } //----------------------------------------------------------------------------------------------- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const @@ -1041,7 +1040,6 @@ namespace cryptonote { cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>(); NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); std::list<crypto::hash> missed_txs; std::list<cryptonote::blobdata> txs; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4ea33dbe1..1aed86b25 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -299,10 +299,8 @@ namespace cryptonote * * @param height return-by-reference height of the block * @param top_id return-by-reference hash of the block - * - * @return true */ - bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; + void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; /** * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 94f069827..19ed57aba 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -31,6 +31,7 @@ #include "include_base_utils.h" using namespace epee; +#include "common/apply_permutation.h" #include "cryptonote_tx_utils.h" #include "cryptonote_config.h" #include "cryptonote_basic/miner.h" @@ -156,7 +157,7 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry> sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) { std::vector<rct::key> amount_keys; tx.set_null(); @@ -263,14 +264,25 @@ namespace cryptonote tx.vin.push_back(input_to_key); } - // "Shuffle" outs - std::vector<tx_destination_entry> shuffled_dsts(destinations); - std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](unsigned int i) { return crypto::rand<unsigned int>() % i; }); + // sort ins by their key image + std::vector<size_t> ins_order(sources.size()); + for (size_t n = 0; n < sources.size(); ++n) + ins_order[n] = n; + std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) { + const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]); + const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]); + return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) < 0; + }); + tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) { + std::swap(tx.vin[i0], tx.vin[i1]); + std::swap(in_contexts[i0], in_contexts[i1]); + std::swap(sources[i0], sources[i1]); + }); uint64_t summary_outs_money = 0; //fill outputs size_t output_index = 0; - for(const tx_destination_entry& dst_entr: shuffled_dsts) + for(const tx_destination_entry& dst_entr: destinations) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); crypto::key_derivation derivation; @@ -297,6 +309,23 @@ namespace cryptonote summary_outs_money += dst_entr.amount; } +#if 0 + // sort outs by their public key + std::vector<size_t> outs_order(tx.vout.size()); + for (size_t n = 0; n < tx.vout.size(); ++n) + outs_order[n] = n; + std::sort(outs_order.begin(), outs_order.end(), [&](size_t i0, size_t i1) { + const txout_to_key &tk0 = boost::get<txout_to_key>(tx.vout[i0].target); + const txout_to_key &tk1 = boost::get<txout_to_key>(tx.vout[i1].target); + return memcmp(&tk0.key, &tk1.key, sizeof(tk0.key)) < 0; + }); + tools::apply_permutation(outs_order, [&] (size_t i0, size_t i1) { + std::swap(tx.vout[i0], tx.vout[i1]); + if (!amount_keys.empty()) + std::swap(amount_keys[i0], amount_keys[i1]); + }); +#endif + //check money if(summary_outs_money > summary_inputs_money ) { @@ -484,8 +513,9 @@ namespace cryptonote std::string genesis_coinbase_tx_hex = config::GENESIS_TX; blobdata tx_bl; - string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); - bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + bool r = string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 7aa7c280d..69254fb5f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -71,7 +71,7 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry> sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); bool generate_genesis_block( block& bl diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 7de392036..9071c330c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -202,6 +202,9 @@ namespace cryptonote return false; } + // assume failure during verification steps until success is certain + tvc.m_verifivation_failed = true; + time_t receive_time = time(nullptr); crypto::hash max_used_block_id = null_hash; @@ -246,6 +249,7 @@ namespace cryptonote { LOG_PRINT_L1("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; + tvc.m_invalid_input = true; return false; } }else @@ -285,9 +289,6 @@ namespace cryptonote tvc.m_should_be_relayed = true; } - // assume failure during verification steps until success is certain - tvc.m_verifivation_failed = true; - tvc.m_verifivation_failed = false; MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); @@ -298,7 +299,8 @@ namespace cryptonote { crypto::hash h = null_hash; size_t blob_size = 0; - get_transaction_hash(tx, h, blob_size); + if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0) + return false; return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); } //--------------------------------------------------------------------------------- @@ -1090,12 +1092,13 @@ namespace cryptonote m_txs_by_fee_and_receive_time.clear(); m_spent_key_images.clear(); - return m_blockchain.for_all_txpool_txes([this](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) { + std::vector<crypto::hash> remove; + bool r = m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) { cryptonote::transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) { - MERROR("Failed to parse tx from txpool"); - return false; + MWARNING("Failed to parse tx from txpool, removing"); + remove.push_back(txid); } if (!insert_key_images(tx, meta.kept_by_block)) { @@ -1105,6 +1108,25 @@ namespace cryptonote m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid); return true; }, true); + if (!r) + return false; + if (!remove.empty()) + { + LockedTXN lock(m_blockchain); + for (const auto &txid: remove) + { + try + { + m_blockchain.remove_txpool_tx(txid); + } + catch (const std::exception &e) + { + MWARNING("Failed to remove corrupt transaction: " << txid); + // ignore error + } + } + } + return true; } //--------------------------------------------------------------------------------- diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index 4ce380a48..347e48eee 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -39,5 +39,3 @@ target_link_libraries(cryptonote_protocol p2p PRIVATE ${EXTRA_LIBRARIES}) -add_dependencies(cryptonote_protocol - version) diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 02a8e3ec2..64dd1fb50 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -340,7 +340,7 @@ size_t block_queue::get_num_filled_spans() const crypto::hash block_queue::get_last_known_hash(const boost::uuids::uuid &connection_id) const { boost::unique_lock<boost::recursive_mutex> lock(mutex); - crypto::hash hash = cryptonote::null_hash; + crypto::hash hash = crypto::null_hash; uint64_t highest_height = 0; for (const auto &span: blocks) { diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 71e205c23..1804cc101 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -128,12 +128,10 @@ namespace cryptonote { block_complete_entry b; uint64_t current_blockchain_height; - uint32_t hop; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(b) KV_SERIALIZE(current_blockchain_height) - KV_SERIALIZE(hop) END_KV_SERIALIZE_MAP() }; }; @@ -254,12 +252,10 @@ namespace cryptonote { block_complete_entry b; uint64_t current_blockchain_height; - uint32_t hop; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(b) KV_SERIALIZE(current_blockchain_height) - KV_SERIALIZE(hop) END_KV_SERIALIZE_MAP() }; }; @@ -276,13 +272,11 @@ namespace cryptonote crypto::hash block_hash; uint64_t current_blockchain_height; std::vector<size_t> missing_tx_indices; - uint32_t hop; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash) KV_SERIALIZE(current_blockchain_height) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) - KV_SERIALIZE(hop) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 803d948cc..58e5fc380 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -336,7 +336,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (" << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks @@ -379,7 +379,6 @@ namespace cryptonote } if(bvc.m_added_to_main_chain) { - ++arg.hop; //TODO: Add here announce protocol usage relay_block(arg, context); }else if(bvc.m_marked_as_orphaned) @@ -397,7 +396,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks @@ -607,7 +606,6 @@ namespace cryptonote MDEBUG(" tx " << new_block.tx_hashes[txidx]); NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req; missing_tx_req.block_hash = get_block_hash(new_block); - missing_tx_req.hop = arg.hop; missing_tx_req.current_blockchain_height = arg.current_blockchain_height; missing_tx_req.missing_tx_indices = std::move(need_tx_indices); @@ -644,10 +642,8 @@ namespace cryptonote } if( bvc.m_added_to_main_chain ) { - ++arg.hop; //TODO: Add here announce protocol usage NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg); - reg_arg.hop = arg.hop; reg_arg.current_blockchain_height = arg.current_blockchain_height; reg_arg.b = b; relay_block(reg_arg, context); @@ -700,7 +696,6 @@ namespace cryptonote NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; fluffy_response.b.block = t_serializable_object_to_blob(b); fluffy_response.current_blockchain_height = arg.current_blockchain_height; - fluffy_response.hop = arg.hop; for(auto& tx_idx: arg.missing_tx_indices) { if(tx_idx < b.tx_hashes.size()) @@ -936,7 +931,8 @@ namespace cryptonote } // get the last parsed block, which should be the highest one - if(m_core.have_block(cryptonote::get_block_hash(b))) + const crypto::hash last_block_hash = cryptonote::get_block_hash(b); + if(m_core.have_block(last_block_hash)) { const uint64_t subchain_height = start_height + arg.blocks.size(); LOG_DEBUG_CC(context, "These are old blocks, ignoring: blocks " << start_height << " - " << (subchain_height-1) << ", blockchain height " << m_core.get_current_blockchain_height()); @@ -954,7 +950,7 @@ namespace cryptonote MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1e3) << " kB/s, size now " << (m_block_queue.get_data_size() + blocks_size) / 1048576.f << " MB"); m_block_queue.add_blocks(start_height, arg.blocks, context.m_connection_id, rate, blocks_size); - context.m_last_known_hash = cryptonote::get_blob_hash(arg.blocks.back().block); + context.m_last_known_hash = last_block_hash; if (!m_core.get_test_drop_download() || !m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing return 1; @@ -1488,7 +1484,7 @@ skip: if (!start_from_current_chain) { // we'll want to start off from where we are on that peer, which may not be added yet - if (context.m_last_known_hash != cryptonote::null_hash && r.block_ids.front() != context.m_last_known_hash) + if (context.m_last_known_hash != crypto::null_hash && r.block_ids.front() != context.m_last_known_hash) r.block_ids.push_front(context.m_last_known_hash); } @@ -1605,7 +1601,6 @@ skip: bool t_cryptonote_protocol_handler<t_core>::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) { NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg); - fluffy_arg.hop = arg.hop; fluffy_arg.current_blockchain_height = arg.current_blockchain_height; std::list<blobdata> fluffy_txs; fluffy_arg.b = arg.b; diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 782667867..d0fc1d846 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -94,6 +94,7 @@ target_link_libraries(daemon daemonizer serialization daemon_rpc_server + version ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} @@ -102,7 +103,6 @@ target_link_libraries(daemon ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB} ${EXTRA_LIBRARIES}) -add_dependencies(daemon version) set_property(TARGET daemon PROPERTY OUTPUT_NAME "monerod") diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index f19d5cc63..7fa58f9d8 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -46,6 +46,11 @@ namespace daemon_args , "Specify log file" , "" }; + const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = { + "max-log-file-size" + , "Specify maximum log file size [B]" + , MAX_LOG_FILE_SIZE + }; const command_line::arg_descriptor<std::string> arg_log_level = { "log-level" , "" diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index f301ef14a..6443d9be0 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -36,7 +36,7 @@ #pragma once -#include <boost/optional/optional_fwd.hpp> +#include <boost/optional/optional.hpp> #include "daemon/rpc_command_executor.h" #include "common/common_fwd.h" diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 9df698547..b9f503c6b 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -241,7 +241,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "bc_dyn_stats" , std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1) - , "Print information about current blockchain dynamic state" + , "Print information about current blockchain dynamic state, bc_dyn_stats <last n blocks>" ); m_command_lookup.set_handler( "update" diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 8f6d542b6..acc23b9f9 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -88,6 +88,7 @@ int main(int argc, char const * argv[]) bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log"); command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string()); command_line::add_arg(core_settings, daemon_args::arg_log_level); + command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size); command_line::add_arg(core_settings, daemon_args::arg_max_concurrency); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); @@ -207,7 +208,7 @@ int main(int argc, char const * argv[]) if (! vm["log-file"].defaulted()) log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file); log_file_path = bf::absolute(log_file_path, relative_path_base); - mlog_configure(log_file_path.string(), true); + mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size)); // Set log level if (!vm["log-level"].defaulted()) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index cda6f3f95..11e3a2252 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -700,6 +700,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { std::string fail_message = "Problem fetching transaction"; req.txs_hashes.push_back(epee::string_tools::pod_to_hex(transaction_hash)); + req.decode_as_json = false; if (m_is_rpc) { if (!m_rpc_client->rpc_request(req, res, "/gettransactions", fail_message.c_str())) @@ -782,7 +783,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) { if (1 == res.spent_status.size()) { // first as hex - tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent"); + tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent") << (res.spent_status.front() == cryptonote::COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_POOL ? " (in pool)" : ""); } else { diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 99198dc57..7a82c12d9 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -42,8 +42,6 @@ target_link_libraries(cn_deserialize epee ${CMAKE_THREAD_LIBS_INIT}) -add_dependencies(cn_deserialize - version) set_property(TARGET cn_deserialize PROPERTY OUTPUT_NAME "monero-utils-deserialize") @@ -65,8 +63,6 @@ target_link_libraries(object_sizes epee ${CMAKE_THREAD_LIBS_INIT}) -add_dependencies(object_sizes - version) set_property(TARGET object_sizes PROPERTY OUTPUT_NAME "monero-utils-object-sizes") diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 0704940b5..123b0a272 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -39,6 +39,7 @@ monero_add_library(p2p ${P2P}) target_link_libraries(p2p PUBLIC epee + version ${UPNP_LIBRARIES} ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} @@ -47,5 +48,3 @@ target_link_libraries(p2p ${Boost_THREAD_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) -add_dependencies(p2p - version) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 22ae5ec26..8bbaa9138 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -105,7 +105,7 @@ namespace nodetool bool init(const boost::program_options::variables_map& vm); bool deinit(); bool send_stop_signal(); - uint32_t get_this_peer_port(){return m_listenning_port;} + uint32_t get_this_peer_port(){return m_listening_port;} t_payload_net_handler& get_payload_object(); template <class Archive, class t_version_type> @@ -218,6 +218,8 @@ namespace nodetool bool is_peer_used(const peerlist_entry& peer); bool is_peer_used(const anchor_peerlist_entry& peer); bool is_addr_connected(const epee::net_utils::network_address& peer); + void add_upnp_port_mapping(uint32_t port); + void delete_upnp_port_mapping(uint32_t port); template<class t_callback> bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f); @@ -287,7 +289,7 @@ namespace nodetool bool m_have_address; bool m_first_connection_maker_call; - uint32_t m_listenning_port; + uint32_t m_listening_port; uint32_t m_external_port; uint32_t m_ip_address; bool m_allow_local_ip; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 889cfaf9d..f1ca50f76 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -573,55 +573,15 @@ namespace nodetool res = m_net_server.init_server(m_port, m_bind_ip); CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); - m_listenning_port = m_net_server.get_binded_port(); - MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listenning_port); + m_listening_port = m_net_server.get_binded_port(); + MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listening_port); if(m_external_port) MDEBUG("External port defined as " << m_external_port); - // Add UPnP port mapping - if(m_no_igd == false) { - MDEBUG("Attempting to add IGD port mapping."); - int result; -#if MINIUPNPC_API_VERSION > 13 - // default according to miniupnpc.h - unsigned char ttl = 2; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result); -#else - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); -#endif - UPNPUrls urls; - IGDdatas igdData; - char lanAddress[64]; - result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); - freeUPNPDevlist(deviceList); - if (result != 0) { - if (result == 1) { - std::ostringstream portString; - portString << m_listenning_port; - - // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly - UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0); - - int portMappingResult; - portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0"); - if (portMappingResult != 0) { - LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult)); - } else { - MLOG_GREEN(el::Level::Info, "Added IGD port mapping."); - } - } else if (result == 2) { - MWARNING("IGD was found but reported as not connected."); - } else if (result == 3) { - MWARNING("UPnP device was found but not recognized as IGD."); - } else { - MWARNING("UPNP_GetValidIGD returned an unknown result code."); - } + // add UPnP port mapping + if(!m_no_igd) + add_upnp_port_mapping(m_listening_port); - FreeUPNPUrls(&urls); - } else { - MINFO("No IGD was found."); - } - } return res; } //----------------------------------------------------------------------------------- @@ -688,6 +648,9 @@ namespace nodetool kill(); m_peerlist.deinit(); m_net_server.deinit_server(); + // remove UPnP port mapping + if(!m_no_igd) + delete_upnp_port_mapping(m_listening_port); return store_config(); } //----------------------------------------------------------------------------------- @@ -1127,6 +1090,8 @@ namespace nodetool if (use_white_list) { local_peers_count = m_peerlist.get_white_peers_count(); + if (!local_peers_count) + return false; max_random_index = std::min<uint64_t>(local_peers_count -1, 20); random_index = get_random_index_with_fixed_probability(max_random_index); } else { @@ -1389,7 +1354,7 @@ namespace nodetool node_data.local_time = local_time; node_data.peer_id = m_config.m_peer_id; if(!m_hide_my_port) - node_data.my_port = m_external_port ? m_external_port : m_listenning_port; + node_data.my_port = m_external_port ? m_external_port : m_listening_port; else node_data.my_port = 0; node_data.network_id = m_network_id; @@ -1951,8 +1916,13 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::gray_peerlist_housekeeping() { + if (!m_exclusive_peers.empty()) return true; + peerlist_entry pe = AUTO_VAL_INIT(pe); + if (m_net_server.is_stop_signal_sent()) + return false; + if (!m_peerlist.get_random_gray_peer(pe)) { return false; } @@ -1973,4 +1943,93 @@ namespace nodetool return true; } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port) + { + MDEBUG("Attempting to add IGD port mapping."); + int result; +#if MINIUPNPC_API_VERSION > 13 + // default according to miniupnpc.h + unsigned char ttl = 2; + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result); +#else + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); +#endif + UPNPUrls urls; + IGDdatas igdData; + char lanAddress[64]; + result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); + freeUPNPDevlist(deviceList); + if (result != 0) { + if (result == 1) { + std::ostringstream portString; + portString << port; + + // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly + UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0); + + int portMappingResult; + portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0"); + if (portMappingResult != 0) { + LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult)); + } else { + MLOG_GREEN(el::Level::Info, "Added IGD port mapping."); + } + } else if (result == 2) { + MWARNING("IGD was found but reported as not connected."); + } else if (result == 3) { + MWARNING("UPnP device was found but not recognized as IGD."); + } else { + MWARNING("UPNP_GetValidIGD returned an unknown result code."); + } + + FreeUPNPUrls(&urls); + } else { + MINFO("No IGD was found."); + } + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port) + { + MDEBUG("Attempting to delete IGD port mapping."); + int result; +#if MINIUPNPC_API_VERSION > 13 + // default according to miniupnpc.h + unsigned char ttl = 2; + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result); +#else + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); +#endif + UPNPUrls urls; + IGDdatas igdData; + char lanAddress[64]; + result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); + freeUPNPDevlist(deviceList); + if (result != 0) { + if (result == 1) { + std::ostringstream portString; + portString << port; + + int portMappingResult; + portMappingResult = UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0); + if (portMappingResult != 0) { + LOG_ERROR("UPNP_DeletePortMapping failed, error: " << strupnperror(portMappingResult)); + } else { + MLOG_GREEN(el::Level::Info, "Deleted IGD port mapping."); + } + } else if (result == 2) { + MWARNING("IGD was found but reported as not connected."); + } else if (result == 3) { + MWARNING("UPnP device was found but not recognized as IGD."); + } else { + MWARNING("UPNP_GetValidIGD returned an unknown result code."); + } + + FreeUPNPUrls(&urls); + } else { + MINFO("No IGD was found."); + } + } } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index a30a05422..8372445aa 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -190,35 +190,16 @@ namespace nodetool if (ver < 6) return; - if(ver < 3) - return; CRITICAL_REGION_LOCAL(m_peerlist_lock); - if(ver < 4) - { - //loading data from old storage - peers_indexed_old pio; - a & pio; - peers_indexed_from_old(pio, m_peers_white); - return; - } #if 0 // trouble loading more than one peer, can't find why a & m_peers_white; a & m_peers_gray; + a & m_peers_anchor; #else serialize_peers(a, m_peers_white, peerlist_entry(), ver); serialize_peers(a, m_peers_gray, peerlist_entry(), ver); -#endif - - if(ver < 5) { - return; - } - -#if 0 - // trouble loading more than one peer, can't find why - a & m_peers_anchor; -#else serialize_peers(a, m_peers_anchor, anchor_peerlist_entry(), ver); #endif } diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index b5c38b1a8..23bb6aaae 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -117,10 +117,3 @@ target_link_libraries(daemon_rpc_server ${EXTRA_LIBRARIES}) target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) - - -add_dependencies(rpc - version) - -add_dependencies(daemon_rpc_server - version) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 1f7f4a1ff..e814dcc1e 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -50,6 +50,16 @@ using namespace epee; #define MAX_RESTRICTED_FAKE_OUTS_COUNT 40 #define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500 +namespace +{ + void add_reason(std::string &reasons, const char *reason) + { + if (!reasons.empty()) + reasons += ", "; + reasons += reason; + } +} + namespace cryptonote { @@ -128,11 +138,7 @@ namespace cryptonote { CHECK_CORE_BUSY(); crypto::hash top_hash; - if (!m_core.get_blockchain_top(res.height, top_hash)) - { - res.status = "Failed"; - return false; - } + m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); @@ -478,18 +484,30 @@ namespace cryptonote bool r = m_core.get_pool_transactions(pool_txs); if(r) { - for (std::list<transaction>::const_iterator i = pool_txs.begin(); i != pool_txs.end(); ++i) + // sort to match original request + std::list<transaction> sorted_txs; + std::list<cryptonote::transaction>::const_iterator i; + for (const crypto::hash &h: vh) { - crypto::hash tx_hash = get_transaction_hash(*i); - std::list<crypto::hash>::iterator mi = std::find(missed_txs.begin(), missed_txs.end(), tx_hash); - if (mi != missed_txs.end()) + if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end()) + { + // core returns the ones it finds in the right order + if (get_transaction_hash(txs.front()) != h) + { + res.status = "Failed: tx hash mismatch"; + return true; + } + sorted_txs.push_back(std::move(txs.front())); + txs.pop_front(); + } + else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](cryptonote::transaction &tx) { return h == cryptonote::get_transaction_hash(tx); })) != pool_txs.end()) { - pool_tx_hashes.insert(tx_hash); - missed_txs.erase(mi); - txs.push_back(*i); + sorted_txs.push_back(*i); + missed_txs.remove(h); ++found_in_pool; } } + txs = sorted_txs; } LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool"); } @@ -510,11 +528,12 @@ namespace cryptonote e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end(); if (e.in_pool) { - e.block_height = std::numeric_limits<uint64_t>::max(); + e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max(); } else { e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); + e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); } // fill up old style responses too, in case an old wallet asks @@ -623,31 +642,33 @@ namespace cryptonote 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) { - if (tvc.m_verifivation_failed) - { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); - } - else - { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); - } res.status = "Failed"; + res.reason = ""; if ((res.low_mixin = tvc.m_low_mixin)) - res.reason = "ring size too small"; + add_reason(res.reason, "ring size too small"); if ((res.double_spend = tvc.m_double_spend)) - res.reason = "double spend"; + add_reason(res.reason, "double spend"); if ((res.invalid_input = tvc.m_invalid_input)) - res.reason = "invalid input"; + add_reason(res.reason, "invalid input"); if ((res.invalid_output = tvc.m_invalid_output)) - res.reason = "invalid output"; + add_reason(res.reason, "invalid output"); if ((res.too_big = tvc.m_too_big)) - res.reason = "too big"; + add_reason(res.reason, "too big"); if ((res.overspend = tvc.m_overspend)) - res.reason = "overspend"; + add_reason(res.reason, "overspend"); if ((res.fee_too_low = tvc.m_fee_too_low)) - res.reason = "fee too low"; + add_reason(res.reason, "fee too low"); if ((res.not_rct = tvc.m_not_rct)) - res.reason = "tx is not ringct"; + add_reason(res.reason, "tx is not ringct"); + const std::string punctuation = res.reason.empty() ? "" : ": "; + if (tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << res.reason); + } + else + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << res.reason); + } return true; } @@ -948,7 +969,7 @@ namespace cryptonote LOG_ERROR("Failed to find tx pub key in blockblob"); return false; } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + 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(res.reserved_offset + req.reserve_size > block_blob.size()) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1048,13 +1069,7 @@ namespace cryptonote } uint64_t last_block_height; crypto::hash last_block_hash; - bool have_last_block_hash = m_core.get_blockchain_top(last_block_height, last_block_hash); - if (!have_last_block_hash) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't get last block hash."; - return false; - } + m_core.get_blockchain_top(last_block_height, last_block_hash); block last_block; bool have_last_block = m_core.get_block_by_hash(last_block_hash, last_block); if (!have_last_block) @@ -1287,11 +1302,7 @@ namespace cryptonote } crypto::hash top_hash; - if (!m_core.get_blockchain_top(res.height, top_hash)) - { - res.status = "Failed"; - return false; - } + m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); @@ -1703,11 +1714,7 @@ namespace cryptonote } crypto::hash top_hash; - if (!m_core.get_blockchain_top(res.height, top_hash)) - { - res.status = "Failed"; - return false; - } + m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height res.target_height = m_core.get_target_blockchain_height(); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 88327dd75..99430bf20 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 13 +#define CORE_RPC_VERSION_MINOR 14 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -215,6 +215,7 @@ namespace cryptonote std::string as_json; bool in_pool; uint64_t block_height; + uint64_t block_timestamp; std::vector<uint64_t> output_indices; BEGIN_KV_SERIALIZE_MAP() @@ -223,6 +224,7 @@ namespace cryptonote KV_SERIALIZE(as_json) KV_SERIALIZE(in_pool) KV_SERIALIZE(block_height) + KV_SERIALIZE(block_timestamp) KV_SERIALIZE(output_indices) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 53eeb5e76..9a6c61b10 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -542,7 +542,7 @@ namespace rpc { if (m_core.get_current_blockchain_height() <= req.height) { - res.hash = cryptonote::null_hash; + res.hash = crypto::null_hash; res.status = Message::STATUS_FAILED; res.error_details = "height given is higher than current chain height"; return; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index ead3fdd58..e35389f9c 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -29,6 +29,7 @@ #include "json_object.h" #include <limits> +#include <type_traits> #include "string_tools.h" namespace cryptonote @@ -52,8 +53,9 @@ namespace void convert_numeric(Source source, Type& i) { static_assert( + (std::is_same<Type, char>() && std::is_same<Source, int>()) || std::numeric_limits<Source>::is_signed == std::numeric_limits<Type>::is_signed, - "source and destination signs do not match" + "comparisons below may have undefined behavior" ); if (source < std::numeric_limits<Type>::min()) { diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 639240820..869f5d10e 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -41,6 +41,7 @@ #pragma once #include <vector> +#include <deque> #include <list> #include <string> #include <boost/type_traits/is_integral.hpp> @@ -198,6 +199,11 @@ inline bool do_serialize(Archive &ar, bool &v) #define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \ ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving()) +/*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION + */ +#define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \ + ::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive<W>::is_saving()) + /*! \macro END_SERIALIZE * \brief self-explanatory */ @@ -292,6 +298,17 @@ namespace serialization { vec.resize(size); } + template <typename T> + void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/) + { + } + + template <typename T> + void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/) + { + vec.resize(size); + } + /*! \fn do_check_stream_state * * \brief self explanatory diff --git a/src/serialization/vector.h b/src/serialization/vector.h index 598cfb92e..12fd59558 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -37,6 +37,11 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v); template <template <bool> class Archive, class T> bool do_serialize(Archive<true> &ar, std::vector<T> &v); +template <template <bool> class Archive, class T> +bool do_serialize(Archive<false> &ar, std::deque<T> &v); +template <template <bool> class Archive, class T> +bool do_serialize(Archive<true> &ar, std::deque<T> &v); + namespace serialization { namespace detail @@ -64,7 +69,7 @@ namespace serialization } template <template <bool> class Archive, class T> -bool do_serialize(Archive<false> &ar, std::vector<T> &v) +bool do_serialize_vd(Archive<false> &ar, T &v) { size_t cnt; ar.begin_array(cnt); @@ -93,7 +98,7 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v) } template <template <bool> class Archive, class T> -bool do_serialize(Archive<true> &ar, std::vector<T> &v) +bool do_serialize_vd(Archive<true> &ar, T &v) { size_t cnt = v.size(); ar.begin_array(cnt); @@ -110,3 +115,13 @@ bool do_serialize(Archive<true> &ar, std::vector<T> &v) ar.end_array(); return true; } + +template <template <bool> class Archive, class T> +bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); } +template <template <bool> class Archive, class T> +bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); } + +template <template <bool> class Archive, class T> +bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); } +template <template <bool> class Archive, class T> +bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); } diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 443e9b87e..b56085b8f 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -49,14 +49,13 @@ target_link_libraries(simplewallet common mnemonics p2p + version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) -add_dependencies(simplewallet - version) set_property(TARGET simplewallet PROPERTY OUTPUT_NAME "monero-wallet-cli") diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 857e2af6e..937403c66 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -290,7 +290,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto return true; } -bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +bool simple_wallet::print_seed(bool encrypted) { bool success = false; std::string electrum_words; @@ -311,7 +311,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st m_wallet->set_seed_language(mnemonic_language); } - success = m_wallet->get_seed(electrum_words); + std::string seed_pass; + if (encrypted) + { + auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed")); + if (std::cin.eof() || !pwd_container) + return true; + seed_pass = pwd_container->password(); + } + + success = m_wallet->get_seed(electrum_words, seed_pass); } if (success) @@ -325,6 +334,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st return true; } +bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + return print_seed(false); +} + +bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + return print_seed(true); +} + bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { if (m_wallet->watch_only()) @@ -757,6 +776,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key")); m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed")); m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-ring-size <n> - set default ring size (default is 5); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-outputs-count [n] - try to keep at least that many outputs of value at least min-outputs-value; min-outputs-value [n] - try to keep at least min-outputs-count outputs of at least that value; merge-destinations <1|0> - whether to merge multiple payments to the same destination address; confirm-backlog <1|0> - whether to warn if there is transaction backlog")); + m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display encrypted Electrum-style mnemonic seed")); m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs")); m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>")); @@ -768,6 +788,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("Set an arbitrary string note for a txid")); m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), tr("Get a string note for a txid")); m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), tr("Show wallet status information")); + m_cmd_binder.set_handler("wallet_info", boost::bind(&simple_wallet::wallet_info, this, _1), tr("Show wallet information")); m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), tr("Sign the contents of a file")); m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file")); m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images")); @@ -1026,6 +1047,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("Electrum-style word list failed verification"); return false; } + + auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none")); + if (std::cin.eof() || !pwd_container) + return false; + std::string seed_pass = pwd_container->password(); + if (!seed_pass.empty()) + m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass); } if (!m_generate_from_view_key.empty()) { @@ -1680,9 +1708,18 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) catch (const std::exception& e) { fail_msg_writer() << tr("failed to load wallet: ") << e.what(); - // only suggest removing cache if the password was actually correct - if (m_wallet && m_wallet->verify_password(password)) - fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file; + if (m_wallet) + { + // only suggest removing cache if the password was actually correct + bool password_is_correct = false; + try + { + password_is_correct = m_wallet->verify_password(password); + } + catch (...) { } // guard against I/O errors + if (password_is_correct) + fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file; + } return false; } success_msg_writer() << @@ -2329,7 +2366,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } } - size_t fake_outs_count; + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -2906,7 +2943,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a std::vector<std::string> local_args = args_; - size_t fake_outs_count; + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -3241,7 +3278,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, std::vector<tx_extra_field> tx_extra_fields; bool has_encrypted_payment_id = false; - crypto::hash8 payment_id8 = cryptonote::null_hash8; + crypto::hash8 payment_id8 = crypto::null_hash8; if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields)) { tx_extra_nonce extra_nonce; @@ -3474,6 +3511,9 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) catch (const tools::error::tx_rejected& e) { fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + std::string reason = e.reason(); + if (!reason.empty()) + fail_msg_writer() << tr("Reason: ") << reason; } catch (const tools::error::tx_sum_overflow& e) { @@ -4507,6 +4547,15 @@ bool simple_wallet::status(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::wallet_info(const std::vector<std::string> &args) +{ + message_writer() << tr("Filename: ") << m_wallet->get_wallet_file(); + message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + message_writer() << tr("Watch only: ") << (m_wallet->watch_only() ? tr("Yes") : tr("No")); + message_writer() << tr("Testnet: ") << (m_wallet->testnet() ? tr("Yes") : tr("No")); + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::sign(const std::vector<std::string> &args) { if (args.size() != 1) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 079fae9f5..3b29e3ca2 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -97,6 +97,7 @@ namespace cryptonote bool viewkey(const std::vector<std::string> &args = std::vector<std::string>()); bool spendkey(const std::vector<std::string> &args = std::vector<std::string>()); bool seed(const std::vector<std::string> &args = std::vector<std::string>()); + bool encrypted_seed(const std::vector<std::string> &args = std::vector<std::string>()); /*! * \brief Sets seed language. @@ -165,6 +166,7 @@ namespace cryptonote bool set_tx_note(const std::vector<std::string> &args); bool get_tx_note(const std::vector<std::string> &args); bool status(const std::vector<std::string> &args); + bool wallet_info(const std::vector<std::string> &args); bool set_default_priority(const std::vector<std::string> &args); bool sign(const std::vector<std::string> &args); bool verify(const std::vector<std::string> &args); @@ -185,6 +187,7 @@ namespace cryptonote bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr); std::string get_prompt() const; + bool print_seed(bool encrypted); /*! * \brief Prints the seed with a nice message diff --git a/src/version.cmake b/src/version.cmake index 623d3cf1f..45a97cd20 100644 --- a/src/version.cmake +++ b/src/version.cmake @@ -36,7 +36,7 @@ if(RET) message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.") set(VERSIONTAG "unknown") - configure_file("src/version.h.in" "${TO}") + configure_file("src/version.cpp.in" "${TO}") else() message(STATUS "You are currently on commit ${COMMIT}") @@ -59,5 +59,5 @@ else() endif() endif() - configure_file("src/version.h.in" "${TO}") + configure_file("src/version.cpp.in" "${TO}") endif() diff --git a/src/version.cpp.in b/src/version.cpp.in new file mode 100644 index 000000000..d1444f867 --- /dev/null +++ b/src/version.cpp.in @@ -0,0 +1,11 @@ +#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" +#define DEF_MONERO_VERSION "0.11.0.0" +#define DEF_MONERO_RELEASE_NAME "Helium Hydra" +#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG + +#include "version.h" + +const char* const MONERO_VERSION_TAG = DEF_MONERO_VERSION_TAG; +const char* const MONERO_VERSION = DEF_MONERO_VERSION; +const char* const MONERO_RELEASE_NAME = DEF_MONERO_RELEASE_NAME; +const char* const MONERO_VERSION_FULL = DEF_MONERO_VERSION_FULL; diff --git a/src/version.h b/src/version.h new file mode 100644 index 000000000..d1d06c790 --- /dev/null +++ b/src/version.h @@ -0,0 +1,6 @@ +#pragma once + +extern const char* const MONERO_VERSION_TAG; +extern const char* const MONERO_VERSION; +extern const char* const MONERO_RELEASE_NAME; +extern const char* const MONERO_VERSION_FULL; diff --git a/src/version.h.in b/src/version.h.in deleted file mode 100644 index 281b52db4..000000000 --- a/src/version.h.in +++ /dev/null @@ -1,4 +0,0 @@ -#define MONERO_VERSION_TAG "@VERSIONTAG@" -#define MONERO_VERSION "0.11.0.0" -#define MONERO_RELEASE_NAME "Helium Hydra" -#define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 639080051..fe87d0de1 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -84,7 +84,6 @@ target_link_libraries(wallet ${Boost_REGEX_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) -add_dependencies(wallet version) if (NOT BUILD_GUI_DEPS) set(wallet_rpc_sources @@ -110,13 +109,13 @@ if (NOT BUILD_GUI_DEPS) cryptonote_core cncrypto common + version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) - add_dependencies(wallet_rpc_server version) set_property(TARGET wallet_rpc_server PROPERTY OUTPUT_NAME "monero-wallet-rpc") diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 28f835ebd..9605047b7 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -42,7 +42,7 @@ namespace Monero { AddressBook::~AddressBook() {} AddressBookImpl::AddressBookImpl(WalletImpl *wallet) - : m_wallet(wallet) {} + : m_wallet(wallet), m_errorCode(Status_Ok) {} bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &payment_id_str, const std::string &description) { @@ -57,7 +57,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa return false; } - crypto::hash payment_id = cryptonote::null_hash; + crypto::hash payment_id = crypto::null_hash; bool has_long_pid = (payment_id_str.empty())? false : tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); // Short payment id provided @@ -106,7 +106,7 @@ void AddressBookImpl::refresh() for (size_t i = 0; i < rows.size(); ++i) { tools::wallet2::address_book_row * row = &rows.at(i); - std::string payment_id = (row->m_payment_id == cryptonote::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id); + std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id); std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(),row->m_address); // convert the zero padded short payment id to integrated address if (payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) { diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 961bd772a..5105278e4 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -230,13 +230,13 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const { std::vector<string> result; for (const auto &utx: m_unsigned_tx_set.txes) { - crypto::hash payment_id = cryptonote::null_hash; + crypto::hash payment_id = crypto::null_hash; cryptonote::tx_extra_nonce extra_nonce; std::vector<cryptonote::tx_extra_field> tx_extra_fields; cryptonote::parse_tx_extra(utx.extra, tx_extra_fields); if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - crypto::hash8 payment_id8 = cryptonote::null_hash8; + crypto::hash8 payment_id8 = crypto::null_hash8; if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) { // We can't decrypt short pid without recipient key. @@ -244,10 +244,10 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const } else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { - payment_id = cryptonote::null_hash; + payment_id = crypto::null_hash; } } - if(payment_id != cryptonote::null_hash) + if(payment_id != crypto::null_hash) result.push_back(epee::string_tools::pod_to_hex(payment_id)); else result.push_back(""); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7afc1f449..9cd72b543 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -303,7 +303,7 @@ WalletImpl::~WalletImpl() // Pause refresh thread - prevents refresh from starting again pauseRefresh(); // Close wallet - stores cache and stops ongoing refresh operation - close(); + close(false); // do not store wallet as part of the closing activities // Stop refresh thread stopRefresh(); delete m_wallet2Callback; @@ -566,19 +566,21 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed) return m_status == Status_Ok; } -bool WalletImpl::close() +bool WalletImpl::close(bool store) { bool result = false; LOG_PRINT_L1("closing wallet..."); try { - // Do not store wallet with invalid status - // Status Critical refers to errors on opening or creating wallets. - if (status() != Status_Critical) - m_wallet->store(); - else - LOG_ERROR("Status_Critical - not storing wallet"); - LOG_PRINT_L1("wallet::store done"); + if (store) { + // Do not store wallet with invalid status + // Status Critical refers to errors on opening or creating wallets. + if (status() != Status_Critical) + m_wallet->store(); + else + LOG_ERROR("Status_Critical - not storing wallet"); + LOG_PRINT_L1("wallet::store done"); + } LOG_PRINT_L1("Calling wallet::stop..."); m_wallet->stop(); LOG_PRINT_L1("wallet::stop done"); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 8190c7873..1e3d1e600 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -63,7 +63,7 @@ public: const std::string &address_string, const std::string &viewkey_string, const std::string &spendkey_string = ""); - bool close(); + bool close(bool store = true); std::string seed() const; std::string getSeedLanguage() const; void setSeedLanguage(const std::string &arg); @@ -153,7 +153,6 @@ private: std::string m_password; TransactionHistoryImpl * m_history; bool m_trustedDaemon; - WalletListener * m_walletListener; Wallet2CallbackImpl * m_wallet2Callback; AddressBookImpl * m_addressBook; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index a23533530..897137d35 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -102,10 +102,12 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, return wallet; } -bool WalletManagerImpl::closeWallet(Wallet *wallet) +bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) { WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet); - bool result = wallet_->close(); + if (!wallet_) + return false; + bool result = wallet_->close(store); if (!result) { m_errorString = wallet_->errorString(); } else { diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index aa6ea439e..8455f0f16 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -48,7 +48,7 @@ public: const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString = ""); - virtual bool closeWallet(Wallet *wallet); + virtual bool closeWallet(Wallet *wallet, bool store = true); bool walletExists(const std::string &path); bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const; std::vector<std::string> findWallets(const std::string &path); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f72d281c7..5e990d215 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -289,6 +289,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, return false; } restore_deterministic_wallet = true; + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string()); + if (field_seed_passphrase_found) + { + if (!field_seed_passphrase.empty()) + recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase); + } } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string()); @@ -376,7 +383,11 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, cryptonote::account_public_address address2; bool has_payment_id; crypto::hash8 new_payment_id; - get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address); + if (!get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address)) + { + tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address; + return false; + } address.m_spend_public_key = address2.m_spend_public_key; } wallet->generate(field_filename, field_password, address, viewkey); @@ -502,6 +513,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit) { + m_checkpoints.init_default_checkpoints(m_testnet); if(m_http_client.is_connected()) m_http_client.disconnect(); m_is_initialized = true; @@ -520,7 +532,7 @@ bool wallet2::is_deterministic() const return keys_deterministic; } //---------------------------------------------------------------------------------------------------- -bool wallet2::get_seed(std::string& electrum_words) const +bool wallet2::get_seed(std::string& electrum_words, const std::string &passphrase) const { bool keys_deterministic = is_deterministic(); if (!keys_deterministic) @@ -534,7 +546,10 @@ bool wallet2::get_seed(std::string& electrum_words) const return false; } - crypto::ElectrumWords::bytes_to_words(get_account().get_keys().m_spend_secret_key, electrum_words, seed_language); + crypto::secret_key key = get_account().get_keys().m_spend_secret_key; + if (!passphrase.empty()) + key = cryptonote::encrypt_key(key, passphrase); + crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language); return true; } @@ -577,24 +592,24 @@ void wallet2::set_unspent(size_t idx) td.m_spent_height = 0; } //---------------------------------------------------------------------------------------------------- -void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const +void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const { if (o.target.type() != typeid(txout_to_key)) { - error = true; + tx_scan_info.error = true; LOG_ERROR("wrong type id in transaction out"); return; } - received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i); - if(received) + tx_scan_info.received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i); + if(tx_scan_info.received) { - money_transfered = o.amount; // may be 0 for ringct outputs + tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs } else { - money_transfered = 0; + tx_scan_info.money_transfered = 0; } - error = false; + tx_scan_info.error = false; } //---------------------------------------------------------------------------------------------------- static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key &pub, const crypto::secret_key &sec, unsigned int i, rct::key & mask) @@ -635,6 +650,22 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a return true; } //---------------------------------------------------------------------------------------------------- +void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs) +{ + wallet_generate_key_image_helper(keys, tx_pub_key, i, tx_scan_info.in_ephemeral, tx_scan_info.ki); + THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); + + outs.push_back(i); + if (tx_scan_info.money_transfered == 0) + { + tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, i, tx_scan_info.mask); + } + tx_money_got_in_outs += tx_scan_info.money_transfered; + tx_scan_info.amount = tx_scan_info.money_transfered; + ++num_vouts_received; +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { // In this function, tx (probably) only contains the base information @@ -673,13 +704,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote int num_vouts_received = 0; tx_pub_key = pub_key_field.pub_key; bool r = true; - std::deque<cryptonote::keypair> in_ephemeral(tx.vout.size()); - std::deque<crypto::key_image> ki(tx.vout.size()); - std::deque<uint64_t> amount(tx.vout.size()); - std::deque<rct::key> mask(tx.vout.size()); tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - int threads = tpool.get_max_concurrency(); + std::unique_ptr<tx_scan_info_t[]> tx_scan_info{new tx_scan_info_t[tx.vout.size()]}; const cryptonote::account_keys& keys = m_account.get_keys(); crypto::key_derivation derivation; generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); @@ -689,105 +716,64 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { - uint64_t money_transfered = 0; - bool error = false, received = false; - check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, received, money_transfered, error); - if (error) + check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, tx_scan_info[0]); + if (tx_scan_info[0].error) { r = false; } else { // this assumes that the miner tx pays a single address - if (received) + if (tx_scan_info[0].received) { - wallet_generate_key_image_helper(keys, tx_pub_key, 0, in_ephemeral[0], ki[0]); - THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - - outs.push_back(0); - if (money_transfered == 0) - { - money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, 0, mask[0]); - } - amount[0] = money_transfered; - tx_money_got_in_outs = money_transfered; - ++num_vouts_received; + scan_output(keys, tx, tx_pub_key, 0, tx_scan_info[0], num_vouts_received, tx_money_got_in_outs, outs); // process the other outs from that tx - - std::vector<uint64_t> money_transfered(tx.vout.size()); - std::deque<bool> error(tx.vout.size()); - std::deque<bool> received(tx.vout.size()); // the first one was already checked for (size_t i = 1; i < tx.vout.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i, - std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i]))); + std::ref(tx_scan_info[i]))); } waiter.wait(); + for (size_t i = 1; i < tx.vout.size(); ++i) { - if (error[i]) + if (tx_scan_info[i].error) { r = false; break; } - if (received[i]) + if (tx_scan_info[i].received) { - wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]); - THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - - outs.push_back(i); - if (money_transfered[i] == 0) - { - money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]); - } - tx_money_got_in_outs += money_transfered[i]; - amount[i] = money_transfered[i]; - ++num_vouts_received; + scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } } } } - else if (tx.vout.size() > 1 && threads > 1) + else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1) { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::vector<uint64_t> money_transfered(tx.vout.size()); - std::deque<bool> error(tx.vout.size()); - std::deque<bool> received(tx.vout.size()); for (size_t i = 0; i < tx.vout.size(); ++i) { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i, - std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i]))); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), + std::cref(tx.vout[i]), std::cref(derivation), i, std::ref(tx_scan_info[i]))); } waiter.wait(); - tx_money_got_in_outs = 0; + for (size_t i = 0; i < tx.vout.size(); ++i) { - if (error[i]) + if (tx_scan_info[i].error) { r = false; break; } - if (received[i]) + if (tx_scan_info[i].received) { - wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]); - THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - - outs.push_back(i); - if (money_transfered[i] == 0) - { - money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]); - } - tx_money_got_in_outs += money_transfered[i]; - amount[i] = money_transfered[i]; - ++num_vouts_received; + scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } } @@ -795,31 +781,15 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { for (size_t i = 0; i < tx.vout.size(); ++i) { - uint64_t money_transfered = 0; - bool error = false, received = false; - check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, received, money_transfered, error); - if (error) + check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, tx_scan_info[i]); + if (tx_scan_info[i].error) { r = false; break; } - else + if (tx_scan_info[i].received) { - if (received) - { - wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]); - THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - - outs.push_back(i); - if (money_transfered == 0) - { - money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]); - } - amount[i] = money_transfered; - tx_money_got_in_outs += money_transfered; - ++num_vouts_received; - } + scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } } @@ -841,7 +811,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" + std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size())); - auto kit = m_pub_keys.find(in_ephemeral[o].pub); + auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub); THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(), error::wallet_internal_error, std::string("Unexpected transfer index from public key: ") + "got " + (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second)) @@ -857,14 +827,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; td.m_txid = txid; - td.m_key_image = ki[o]; + td.m_key_image = tx_scan_info[o].ki; td.m_key_image_known = !m_watch_only; td.m_amount = tx.vout[o].amount; td.m_pk_index = pk_index - 1; if (td.m_amount == 0) { - td.m_mask = mask[o]; - td.m_amount = amount[o]; + td.m_mask = tx_scan_info[o].mask; + td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version == 2) @@ -879,7 +849,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } set_unspent(m_transfers.size()-1); m_key_images[td.m_key_image] = m_transfers.size()-1; - m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1; + m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1; LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount); @@ -912,8 +882,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_pk_index = pk_index - 1; if (td.m_amount == 0) { - td.m_mask = mask[o]; - td.m_amount = amount[o]; + td.m_mask = tx_scan_info[o].mask; + td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version == 2) @@ -926,7 +896,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_mask = rct::identity(); td.m_rct = false; } - THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys"); + THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub, error::wallet_internal_error, "Inconsistent public keys"); THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); @@ -1128,16 +1098,19 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const { size_t i = 0; size_t current_multiplier = 1; - size_t sz = m_blockchain.size(); + size_t sz = m_blockchain.size() - m_blockchain.offset(); if(!sz) + { + ids.push_back(m_blockchain.genesis()); return; + } size_t current_back_offset = 1; - bool genesis_included = false; + bool base_included = false; while(current_back_offset < sz) { - ids.push_back(m_blockchain[sz-current_back_offset]); + ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]); if(sz-current_back_offset == 0) - genesis_included = true; + base_included = true; if(i < 10) { ++current_back_offset; @@ -1147,8 +1120,10 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const } ++i; } - if(!genesis_included) - ids.push_back(m_blockchain[0]); + if(!base_included) + ids.push_back(m_blockchain[m_blockchain.offset()]); + if(m_blockchain.offset()) + ids.push_back(m_blockchain.genesis()); } //---------------------------------------------------------------------------------------------------- void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const @@ -1233,6 +1208,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote:: size_t tx_o_indices_idx = 0; THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch"); + THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain"); tools::threadpool& tpool = tools::threadpool::getInstance(); int threads = tpool.get_max_concurrency(); @@ -1529,23 +1505,22 @@ void wallet2::update_pool_state(bool refreshed) { if (res.txs.size() == txids.size()) { - size_t n = 0; - for (const auto &txid: txids) + for (const auto &tx_entry: res.txs) { - // might have just been put in a block - if (res.txs[n].in_pool) + if (tx_entry.in_pool) { cryptonote::transaction tx; cryptonote::blobdata bd; crypto::hash tx_hash, tx_prefix_hash; - if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd)) + if (epee::string_tools::parse_hexstr_to_binbuff(tx_entry.as_hex, bd)) { if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) { - if (tx_hash == txid) + const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash); + if (i != txids.end()) { - process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); - m_scanned_pool_txs[0].insert(txid); + process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); + m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]); @@ -1554,7 +1529,7 @@ void wallet2::update_pool_state(bool refreshed) } else { - LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool"); + MERROR("Got txid " << tx_hash << " which we did not ask for"); } } else @@ -1564,14 +1539,13 @@ void wallet2::update_pool_state(bool refreshed) } else { - LOG_PRINT_L0("Failed to parse tx " << txid); + LOG_PRINT_L0("Failed to parse transaction from daemon"); } } else { - LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more"); + LOG_PRINT_L1("Transaction from daemon was in pool, but is no more"); } - ++n; } } else @@ -1787,6 +1761,13 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok) void wallet2::detach_blockchain(uint64_t height) { LOG_PRINT_L0("Detaching blockchain on height " << height); + + // size 1 2 3 4 5 6 7 8 9 + // block 0 1 2 3 4 5 6 7 8 + // C + THROW_WALLET_EXCEPTION_IF(height <= m_checkpoints.get_max_height() && m_blockchain.size() > m_checkpoints.get_max_height(), + error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); + size_t transfers_detached = 0; for (size_t i = 0; i < m_transfers.size(); ++i) @@ -1817,8 +1798,8 @@ void wallet2::detach_blockchain(uint64_t height) } m_transfers.erase(it, m_transfers.end()); - size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); - m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); + size_t blocks_detached = m_blockchain.size() - height; + m_blockchain.crop(height); m_local_bc_height -= blocks_detached; for (auto it = m_payments.begin(); it != m_payments.end(); ) @@ -2562,13 +2543,49 @@ void wallet2::load(const std::string& wallet_, const std::string& password) check_genesis(genesis_hash); } + trim_hashchain(); + m_local_bc_height = m_blockchain.size(); } //---------------------------------------------------------------------------------------------------- +void wallet2::trim_hashchain() +{ + uint64_t height = m_checkpoints.get_max_height(); + if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset()) + { + MINFO("Fixing empty hashchain"); + epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request> req = AUTO_VAL_INIT(req); + epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response, std::string> res = AUTO_VAL_INIT(res); + m_daemon_rpc_mutex.lock(); + req.jsonrpc = "2.0"; + req.id = epee::serialization::storage_entry(0); + req.method = "getblockheaderbyheight"; + req.params.height = m_blockchain.size() - 1; + bool r = net_utils::invoke_http_json("/json_rpc", req, res, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + if (r && res.result.status == CORE_RPC_STATUS_OK) + { + crypto::hash hash; + epee::string_tools::hex_to_pod(res.result.block_header.hash, hash); + m_blockchain.refill(hash); + } + else + { + MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon"); + } + } + if (height > 0 && m_blockchain.size() > height) + { + --height; + MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset()); + m_blockchain.trim(height); + } +} +//---------------------------------------------------------------------------------------------------- void wallet2::check_genesis(const crypto::hash& genesis_hash) const { std::string what("Genesis block mismatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); - THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain[0], error::wallet_internal_error, what); + THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what); } //---------------------------------------------------------------------------------------------------- std::string wallet2::path() const @@ -2583,6 +2600,8 @@ void wallet2::store() //---------------------------------------------------------------------------------------------------- void wallet2::store_to(const std::string &path, const std::string &password) { + trim_hashchain(); + // if file is the same, we do: // 1. save wallet to the *.new file // 2. remove old wallet file @@ -2645,10 +2664,11 @@ void wallet2::store_to(const std::string &path, const std::string &password) // if we here, main wallet file is saved and we only need to save keys and address files if (!same_file) { prepare_file_names(path); - store_keys(m_keys_file, password, false); + bool r = store_keys(m_keys_file, password, false); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); // save address to the new file const std::string address_file = m_wallet_file + ".address.txt"; - bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet)); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); // remove old wallet file r = boost::filesystem::remove(old_file); @@ -3090,7 +3110,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const { std::vector<tx_extra_field> tx_extra_fields; if(!parse_tx_extra(ptx.tx.extra, tx_extra_fields)) - return cryptonote::null_hash; + return crypto::null_hash; tx_extra_nonce extra_nonce; crypto::hash payment_id = null_hash; if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) @@ -3105,7 +3125,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const } else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { - payment_id = cryptonote::null_hash; + payment_id = crypto::null_hash; } } return payment_id; @@ -3154,7 +3174,7 @@ void wallet2::commit_tx(pending_tx& ptx) } txid = get_transaction_hash(ptx.tx); - crypto::hash payment_id = cryptonote::null_hash; + crypto::hash payment_id = crypto::null_hash; std::vector<cryptonote::tx_destination_entry> dests; uint64_t amount_in = 0; if (store_tx_info()) @@ -5164,10 +5184,9 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle for (size_t i = 0; i < td.m_tx.vout.size(); ++i) { - uint64_t money_transfered = 0; - bool error = false, received = false; - check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, received, money_transfered, error); - if (!error && received) + tx_scan_info_t tx_scan_info; + check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, tx_scan_info); + if (!tx_scan_info.error && tx_scan_info.received) return tx_pub_key; } } @@ -5175,7 +5194,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle // we found no key yielding an output THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Public key yielding at least one output wasn't found in the transaction extra"); - return cryptonote::null_pkey; + return crypto::null_pkey; } bool wallet2::export_key_images(const std::string filename) @@ -5381,6 +5400,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag } spent = 0; unspent = 0; + std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input. + std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx + // was created by sweep_all, so we can't know the spent height and other detailed info. for(size_t i = 0; i < m_transfers.size(); ++i) { transfer_details &td = m_transfers[i]; @@ -5391,8 +5413,145 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag unspent += amount; LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): " << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")"); + + if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN) + { + bool is_spent_tx_found = false; + for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it) + { + bool is_spent_tx = false; + for(const cryptonote::txin_v& in : it->m_tx.vin) + { + if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image) + { + is_spent_tx = true; + break; + } + } + if (is_spent_tx) + { + is_spent_tx_found = true; + spent_txids.insert(it->m_txid); + break; + } + } + + if (!is_spent_tx_found) + swept_transfers.push_back(i); + } } MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); + + if (check_spent) + { + // query outgoing txes + COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req; + COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res; + gettxs_req.decode_as_json = false; + for (const crypto::hash& spent_txid : spent_txids) + gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid)); + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, 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"); + THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error, + "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size())); + + // process each outgoing tx + auto spent_txid = spent_txids.begin(); + for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs) + { + THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool"); + + // parse tx + cryptonote::blobdata bd; + THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(e.as_hex, bd), error::wallet_internal_error, "parse_hexstr_to_binbuff failed"); + cryptonote::transaction spent_tx; + crypto::hash spnet_txid_parsed, spent_txid_prefix; + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, spent_tx, spnet_txid_parsed, spent_txid_prefix), error::wallet_internal_error, "parse_and_validate_tx_from_blob failed"); + THROW_WALLET_EXCEPTION_IF(*spent_txid != spnet_txid_parsed, error::wallet_internal_error, "parsed txid mismatch"); + + // get received (change) amount + uint64_t tx_money_got_in_outs = 0; + const cryptonote::account_keys& keys = m_account.get_keys(); + const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx); + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); + size_t output_index = 0; + for (const cryptonote::tx_out& out : spent_tx.vout) + { + tx_scan_info_t tx_scan_info; + check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, tx_scan_info); + THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed"); + if (tx_scan_info.received) + { + if (tx_scan_info.money_transfered == 0) + { + rct::key mask; + tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask); + } + tx_money_got_in_outs += tx_scan_info.money_transfered; + } + ++output_index; + } + + // get spent amount + uint64_t tx_money_spent_in_ins = 0; + for (const cryptonote::txin_v& in : spent_tx.vin) + { + if (in.type() != typeid(cryptonote::txin_to_key)) + continue; + auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); + if (it != m_key_images.end()) + { + const transfer_details& td = m_transfers[it->second]; + uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount; + if (amount > 0) + { + THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error, + std::string("Inconsistent amount in tx input: got ") + print_money(amount) + + std::string(", expected ") + print_money(td.amount())); + } + amount = td.amount(); + tx_money_spent_in_ins += amount; + + LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << *spent_txid); + set_spent(it->second, e.block_height); + if (m_callback) + m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx); + } + } + + // create outgoing payment + process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_money_spent_in_ins, tx_money_got_in_outs); + + // erase corresponding incoming payment + for (auto j = m_payments.begin(); j != m_payments.end(); ++j) + { + if (j->second.m_tx_hash == *spent_txid) + { + m_payments.erase(j); + break; + } + } + + ++spent_txid; + } + + for (size_t n : swept_transfers) + { + const transfer_details& td = m_transfers[n]; + confirmed_transfer_details pd; + pd.m_change = (uint64_t)-1; // cahnge is unknown + pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown + std::string err; + pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest + crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random + m_confirmed_txs.insert(std::make_pair(spent_txid, pd)); + } + } + return m_transfers[signed_key_images.size() - 1].m_block_height; } wallet2::payment_container wallet2::export_payments() const @@ -5421,20 +5580,28 @@ void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2 } } -std::vector<crypto::hash> wallet2::export_blockchain() const +std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const { - std::vector<crypto::hash> bc; - for (auto const &b : m_blockchain) + std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc; + std::get<0>(bc) = m_blockchain.offset(); + std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis(); + for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n) { - bc.push_back(b); + std::get<2>(bc).push_back(m_blockchain[n]); } return bc; } -void wallet2::import_blockchain(const std::vector<crypto::hash> &bc) +void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc) { m_blockchain.clear(); - for (auto const &b : bc) + if (std::get<0>(bc)) + { + for (size_t n = std::get<0>(bc); n > 0; ++n) + m_blockchain.push_back(std::get<1>(bc)); + m_blockchain.trim(std::get<0>(bc)); + } + for (auto const &b : std::get<2>(bc)) { m_blockchain.push_back(b); } @@ -5536,7 +5703,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret crypto::secret_key_to_public_key(skey, pkey); const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)]; THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature), - error::wallet_internal_error, "Failed to authenticate criphertext"); + error::wallet_internal_error, "Failed to authenticate ciphertext"); } crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); return plaintext; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index adf03abcc..1dff14a95 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -36,6 +36,7 @@ #include <boost/program_options/variables_map.hpp> #include <boost/serialization/list.hpp> #include <boost/serialization/vector.hpp> +#include <boost/serialization/deque.hpp> #include <atomic> #include "include_base_utils.h" @@ -52,6 +53,7 @@ #include "crypto/hash.h" #include "ringct/rctTypes.h" #include "ringct/rctOps.h" +#include "checkpoints/checkpoints.h" #include "wallet_errors.h" #include "common/password.h" @@ -91,6 +93,38 @@ namespace tools } }; + class hashchain + { + public: + hashchain(): m_genesis(crypto::null_hash), m_offset(0) {} + + size_t size() const { return m_blockchain.size() + m_offset; } + size_t offset() const { return m_offset; } + const crypto::hash &genesis() const { return m_genesis; } + void push_back(const crypto::hash &hash) { if (m_offset == 0 && m_blockchain.empty()) m_genesis = hash; m_blockchain.push_back(hash); } + bool is_in_bounds(size_t idx) const { return idx >= m_offset && idx < size(); } + const crypto::hash &operator[](size_t idx) const { return m_blockchain[idx - m_offset]; } + crypto::hash &operator[](size_t idx) { return m_blockchain[idx - m_offset]; } + void crop(size_t height) { m_blockchain.resize(height - m_offset); } + void clear() { m_offset = 0; m_blockchain.clear(); } + bool empty() const { return m_blockchain.empty() && m_offset == 0; } + void trim(size_t height) { while (height > m_offset+1 && m_blockchain.size() > 1) { m_blockchain.pop_front(); ++m_offset; } m_blockchain.shrink_to_fit(); } + void refill(const crypto::hash &hash) { m_blockchain.push_back(hash); --m_offset; } + + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int ver) + { + a & m_offset; + a & m_genesis; + a & m_blockchain; + } + + private: + size_t m_offset; + crypto::hash m_genesis; + std::deque<crypto::hash> m_blockchain; + }; + class wallet2 { friend class ::Serialization_portability_wallet_Test; @@ -133,6 +167,19 @@ namespace tools wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + struct tx_scan_info_t + { + cryptonote::keypair in_ephemeral; + crypto::key_image ki; + rct::key mask; + uint64_t amount; + uint64_t money_transfered; + bool error; + bool received; + + tx_scan_info_t(): money_transfered(0), error(true), received(false) {} + }; + struct transfer_details { uint64_t m_block_height; @@ -203,7 +250,7 @@ namespace tools uint64_t m_timestamp; uint64_t m_unlock_time; - confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash), m_timestamp(0), m_unlock_time(0) {} + confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0) {} confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height): m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time) {} }; @@ -363,7 +410,7 @@ namespace tools * \brief Checks if deterministic wallet */ bool is_deterministic() const; - bool get_seed(std::string& electrum_words) const; + bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const; /*! * \brief Gets the seed language */ @@ -439,7 +486,19 @@ namespace tools uint64_t dummy_refresh_height = 0; // moved to keys file if(ver < 5) return; - a & m_blockchain; + if (ver < 19) + { + std::vector<crypto::hash> blockchain; + a & blockchain; + for (const auto &b: blockchain) + { + m_blockchain.push_back(b); + } + } + else + { + a & m_blockchain; + } a & m_transfers; a & m_account_public_address; a & m_key_images; @@ -588,8 +647,8 @@ namespace tools payment_container export_payments() const; void import_payments(const payment_container &payments); void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments); - std::vector<crypto::hash> export_blockchain() const; - void import_blockchain(const std::vector<crypto::hash> &bc); + std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const; + void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc); bool export_key_images(const std::string filename); std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const; uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true); @@ -650,7 +709,7 @@ namespace tools bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const; crypto::hash get_payment_id(const pending_tx &ptx) const; crypto::hash8 get_short_payment_id(const pending_tx &ptx) const; - void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const; + void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_transaction_size_limit(); std::vector<uint64_t> get_unspent_amounts_vector(); @@ -664,6 +723,8 @@ namespace tools crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; + void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs); + void trim_hashchain(); cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; @@ -671,12 +732,13 @@ namespace tools std::string m_wallet_file; std::string m_keys_file; epee::net_utils::http::http_simple_client m_http_client; - std::vector<crypto::hash> m_blockchain; + hashchain m_blockchain; std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs; std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments; std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys; + cryptonote::checkpoints m_checkpoints; transfer_container m_transfers; payment_container m_payments; @@ -716,7 +778,7 @@ namespace tools std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; }; } -BOOST_CLASS_VERSION(tools::wallet2, 18) +BOOST_CLASS_VERSION(tools::wallet2, 19) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6) diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 8da8c62eb..7a5e01af7 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -663,7 +663,7 @@ struct WalletManager * \param wallet previously opened / created wallet instance * \return None */ - virtual bool closeWallet(Wallet *wallet) = 0; + virtual bool closeWallet(Wallet *wallet, bool store = true) = 0; /* * ! checks if wallet with the given name already exists diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 34c5a2a5d..22cfcc269 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -84,6 +84,7 @@ namespace wallet_args #endif const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = {"max-log-file-size", "Specify maximum log file size [B]", MAX_LOG_FILE_SIZE}; const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY}; const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""}; const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true}; @@ -99,8 +100,9 @@ namespace wallet_args command_line::add_arg(desc_general, command_line::arg_help); command_line::add_arg(desc_general, command_line::arg_version); - command_line::add_arg(desc_params, arg_log_file, ""); + command_line::add_arg(desc_params, arg_log_file); command_line::add_arg(desc_params, arg_log_level); + command_line::add_arg(desc_params, arg_max_log_file_size); command_line::add_arg(desc_params, arg_max_concurrency); command_line::add_arg(desc_params, arg_config_file); @@ -114,6 +116,21 @@ namespace wallet_args auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options); po::store(parser.run(), vm); + if (command_line::get_arg(vm, command_line::arg_help)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n" + "daemon to work correctly.") << ENDL; + tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage; + tools::msg_writer() << desc_all; + return false; + } + else if (command_line::get_arg(vm, command_line::arg_version)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + return false; + } + if(command_line::has_arg(vm, arg_config_file)) { std::string config = command_line::get_arg(vm, arg_config_file); @@ -141,27 +158,12 @@ namespace wallet_args log_path = command_line::get_arg(vm, arg_log_file); else log_path = mlog_get_default_log_path(default_log_name); - mlog_configure(log_path, log_to_console); + mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size)); if (!vm["log-level"].defaulted()) { mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); } - if (command_line::get_arg(vm, command_line::arg_help)) - { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; - tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n" - "daemon to work correctly.") << ENDL; - tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage; - tools::msg_writer() << desc_all; - return boost::none; - } - else if (command_line::get_arg(vm, command_line::arg_version)) - { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - return boost::none; - } - if(command_line::has_arg(vm, arg_max_concurrency)) tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 773d12775..46b092376 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include <boost/format.hpp> #include <boost/asio/ip/address.hpp> #include <boost/filesystem/operations.hpp> #include <cstdint> @@ -37,7 +38,6 @@ using namespace epee; #include "wallet/wallet_args.h" #include "common/command_line.h" #include "common/i18n.h" -#include "common/util.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "wallet_rpc_server_commands_defs.h" @@ -70,18 +70,12 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false) + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_vm(NULL) { } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::~wallet_rpc_server() { - try - { - boost::system::error_code ec{}; - boost::filesystem::remove(rpc_login_filename, ec); - } - catch (...) {} } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::set_wallet(wallet2 *cr) @@ -160,7 +154,15 @@ namespace tools #else #define MKDIR(path, mode) mkdir(path, mode) #endif - MKDIR(m_wallet_dir.c_str(), 0700); + if (MKDIR(m_wallet_dir.c_str(), 0700) < 0) + { +#ifdef _WIN32 + LOG_ERROR(tr("Failed to create directory ") + m_wallet_dir); +#else + LOG_ERROR((boost::format(tr("Failed to create directory %s: %s")) % m_wallet_dir % strerror(errno)).str()); +#endif + return false; + } } if (disable_auth) @@ -182,34 +184,32 @@ namespace tools default_rpc_username, string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()) ); + + std::string temp = "monero-wallet-rpc." + bind_port + ".login"; + rpc_login_file = tools::private_file::create(temp); + if (!rpc_login_file.handle()) + { + LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file")); + return false; + } + std::fputs(http_login->username.c_str(), rpc_login_file.handle()); + std::fputc(':', rpc_login_file.handle()); + std::fputs(http_login->password.c_str(), rpc_login_file.handle()); + std::fflush(rpc_login_file.handle()); + if (std::ferror(rpc_login_file.handle())) + { + LOG_ERROR(tr("Error writing to file ") << temp); + return false; + } + LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp); } - else + else // chosen user/pass { http_login.emplace( std::move(rpc_config->login->username), std::move(rpc_config->login->password).password() ); } assert(bool(http_login)); - - std::string temp = "monero-wallet-rpc." + bind_port + ".login"; - const auto cookie = tools::create_private_file(temp); - if (!cookie) - { - LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file")); - return false; - } - rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup - temp = rpc_login_filename; - std::fputs(http_login->username.c_str(), cookie.get()); - std::fputc(':', cookie.get()); - std::fputs(http_login->password.c_str(), cookie.get()); - std::fflush(cookie.get()); - if (std::ferror(cookie.get())) - { - LOG_ERROR(tr("Error writing to file ") << temp); - return false; - } - LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp); } // end auth enabled m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login()); @@ -362,7 +362,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er) { - crypto::hash8 integrated_payment_id = cryptonote::null_hash8; + crypto::hash8 integrated_payment_id = crypto::null_hash8; std::string extra_nonce; for (auto it = destinations.begin(); it != destinations.end(); it++) { @@ -395,7 +395,7 @@ namespace tools if (has_payment_id) { - if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash8) + if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8) { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; er.message = "A single payment id is allowed per transaction"; @@ -1485,7 +1485,7 @@ namespace tools cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id8; - crypto::hash payment_id = cryptonote::null_hash; + crypto::hash payment_id = crypto::null_hash; er.message = ""; if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { @@ -1545,7 +1545,7 @@ namespace tools er.message = "Failed to add address book entry"; return false; } - res.index = m_wallet->get_address_book().size(); + res.index = m_wallet->get_address_book().size() - 1; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1896,7 +1896,15 @@ just_dir: wrpc.send_stop_signal(); }); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet rpc server")); - wrpc.run(); + try + { + wrpc.run(); + } + catch (const std::exception &e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); + return 1; + } LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet rpc server")); try { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index dd54222b0..e5ed0a846 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -33,6 +33,7 @@ #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> #include <string> +#include "common/util.h" #include "net/http_server_impl_base.h" #include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" @@ -154,7 +155,7 @@ namespace tools wallet2 *m_wallet; std::string m_wallet_dir; - std::string rpc_login_filename; + tools::private_file rpc_login_file; std::atomic<bool> m_stop; bool m_trusted_daemon; epee::net_utils::http::http_simple_client m_http_client; diff --git a/tests/core_proxy/CMakeLists.txt b/tests/core_proxy/CMakeLists.txt index d22fecc9c..d2dc93cf0 100644 --- a/tests/core_proxy/CMakeLists.txt +++ b/tests/core_proxy/CMakeLists.txt @@ -40,6 +40,7 @@ target_link_libraries(core_proxy cryptonote_core cryptonote_protocol p2p + version epee ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 366937e1d..a0be3db96 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -229,10 +229,9 @@ bool tests::proxy_core::get_short_chain_history(std::list<crypto::hash>& ids) { return true; } -bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { +void tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { height = 0; top_id = get_block_hash(m_genesis); - return true; } bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { @@ -256,7 +255,7 @@ void tests::proxy_core::build_short_history(std::list<crypto::hash> &m_history, m_history.push_front(cit->first); size_t n = 1 << m_history.size(); - while (m_hash2blkidx.end() != cit && cryptonote::null_hash != cit->second.blk.prev_id && n > 0) { + while (m_hash2blkidx.end() != cit && crypto::null_hash != cit->second.blk.prev_id && n > 0) { n--; cit = m_hash2blkidx.find(cit->second.blk.prev_id); } @@ -266,7 +265,7 @@ void tests::proxy_core::build_short_history(std::list<crypto::hash> &m_history, bool tests::proxy_core::add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob) { size_t height = 0; - if (cryptonote::null_hash != _blk.prev_id) { + if (crypto::null_hash != _blk.prev_id) { std::unordered_map<crypto::hash, tests::block_index>::const_iterator cit = m_hash2blkidx.find(_blk.prev_id); if (m_hash2blkidx.end() == cit) { cerr << "ERROR: can't find previous block with id \"" << _blk.prev_id << "\"" << endl; diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 85518612a..cc35be618 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -46,7 +46,7 @@ namespace tests cryptonote::blobdata blob; std::list<cryptonote::transaction> txes; - block_index() : height(0), id(cryptonote::null_hash), longhash(cryptonote::null_hash) { } + block_index() : height(0), id(crypto::null_hash), longhash(crypto::null_hash) { } block_index(size_t _height, const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob, const std::list<cryptonote::transaction> &_txes) : height(_height), id(_id), longhash(_longhash), blk(_blk), blob(_blob), txes(_txes) { } }; @@ -74,7 +74,7 @@ namespace tests bool get_short_chain_history(std::list<crypto::hash>& ids); bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;} bool have_block(const crypto::hash& id); - bool get_blockchain_top(uint64_t& height, crypto::hash& top_id); + void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_txs(const std::list<cryptonote::blobdata>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index c1d3161bc..a24bd4fce 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -65,6 +65,7 @@ target_link_libraries(coretests PRIVATE cryptonote_core p2p + version epee ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 21638354d..b546e4407 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -484,8 +484,9 @@ bool gen_rct_tx_pre_rct_altered_extra::generate(std::vector<test_event_entry>& e const int mixin = 2; const int out_idx[] = {0, -1}; const uint64_t amount_paid = 10000; + bool failed = false; return generate_with(events, out_idx, mixin, amount_paid, false, - NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);}); + NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed; } bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& events) const @@ -493,7 +494,8 @@ bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& event const int mixin = 2; const int out_idx[] = {1, -1}; const uint64_t amount_paid = 10000; + bool failed = false; return generate_with(events, out_idx, mixin, amount_paid, false, - NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);}); + NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed; } diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index 51375440b..4c5542d91 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -40,6 +40,7 @@ add_executable(libwallet_api_tests target_link_libraries(libwallet_api_tests PRIVATE wallet + version epee ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index bf0483b0f..d08ab7c75 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -811,7 +811,7 @@ struct MyWalletListener : public Monero::WalletListener void reset() { - send_triggered = receive_triggered = update_triggered = refresh_triggered = false; + send_triggered = receive_triggered = newblock_triggered = update_triggered = refresh_triggered = false; } virtual void moneySpent(const string &txId, uint64_t amount) diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 656815db9..ed8951659 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -47,3 +47,25 @@ public: return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, 0); } }; + +class test_is_out_to_acc_precomp : public single_tx_test_base +{ +public: + static const size_t loop_count = 1000; + + bool init() + { + if (!single_tx_test_base::init()) + return false; + crypto::generate_key_derivation(m_tx_pub_key, m_bob.get_keys().m_view_secret_key, m_derivation); + return true; + } + bool test() + { + const cryptonote::txout_to_key& tx_out = boost::get<cryptonote::txout_to_key>(m_tx.vout[0].target); + return cryptonote::is_out_to_acc_precomp(m_bob.get_keys().m_account_address.m_spend_public_key, tx_out, m_derivation, 0); + } + +private: + crypto::key_derivation m_derivation; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index cc9fe86ef..3c0283eca 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -100,6 +100,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(test_check_tx_signature, 100, true); TEST_PERFORMANCE0(test_is_out_to_acc); + TEST_PERFORMANCE0(test_is_out_to_acc_precomp); TEST_PERFORMANCE0(test_generate_key_image_helper); TEST_PERFORMANCE0(test_generate_key_derivation); TEST_PERFORMANCE0(test_generate_key_image); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index f5e08b102..53d93fcce 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -27,6 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(unit_tests_sources + apply_permutation.cpp address_from_url.cpp ban.cpp base58.cpp @@ -45,6 +46,7 @@ set(unit_tests_sources epee_utils.cpp fee.cpp get_xtype_from_string.cpp + hashchain.cpp http.cpp main.cpp mnemonics.cpp @@ -78,6 +80,7 @@ target_link_libraries(unit_tests rpc wallet p2p + version epee ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} diff --git a/tests/unit_tests/apply_permutation.cpp b/tests/unit_tests/apply_permutation.cpp new file mode 100644 index 000000000..a008b74ee --- /dev/null +++ b/tests/unit_tests/apply_permutation.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2017, 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 "gtest/gtest.h" +#include "common/apply_permutation.h" + +TEST(apply_permutation, empty) +{ + std::vector<int> v = {}; + tools::apply_permutation({}, v); + ASSERT_EQ(v, std::vector<int>({})); +} + +TEST(apply_permutation, reorder) +{ + // 0 1 2 3 4 5 6 + std::vector<int> v = {8, 4, 6, 1, 7, 2, 4}; + tools::apply_permutation({3, 5, 6, 1, 2, 4, 0}, v); + ASSERT_EQ(v, std::vector<int>({1, 2, 4, 4, 6, 7, 8})); +} + +TEST(apply_permutation, bad_size) +{ + std::vector<int> v_large = {8, 4, 6, 1, 7, 2, 4, 9}; + std::vector<int> v_small = {8, 4, 6, 1, 7, 2}; + try + { + tools::apply_permutation({3, 5, 6, 1, 2, 4, 0}, v_large); + ASSERT_FALSE(true); + } + catch (const std::exception &e) {} + try + { + tools::apply_permutation({3, 5, 6, 1, 2, 4, 0}, v_small); + ASSERT_FALSE(true); + } + catch (const std::exception &e) {} +} + +TEST(apply_permutation, bad_permutation) +{ + std::vector<int> v = {8, 4, 6, 1, 7, 2, 4}; + try + { + tools::apply_permutation({3, 5, 6, 1, 2, 4, 1}, v); + ASSERT_FALSE(true); + } + catch (const std::exception &e) {} +} diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 82ff058b1..b8d57452e 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -51,7 +51,7 @@ public: bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; } bool get_stat_info(cryptonote::core_stat_info& st_inf) const {return true;} bool have_block(const crypto::hash& id) const {return true;} - bool get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=cryptonote::null_hash;return true;} + void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } bool handle_incoming_txs(const std::list<cryptonote::blobdata>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index d3c4d3b2b..f6015db2f 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "cryptonote_basic/checkpoints.cpp" +#include "checkpoints/checkpoints.cpp" using namespace cryptonote; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index c30feb461..2b0904224 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -53,7 +53,7 @@ public: virtual std::string get_db_name() const { return std::string(); } virtual bool lock() { return true; } virtual void unlock() { } - virtual bool batch_start(uint64_t batch_num_blocks=0) { return true; } + 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) {} diff --git a/tests/unit_tests/hashchain.cpp b/tests/unit_tests/hashchain.cpp new file mode 100644 index 000000000..0fa0f784a --- /dev/null +++ b/tests/unit_tests/hashchain.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2014-2017, 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. + +// FIXME: move this into a full wallet2 unit test suite, if possible + +#include "gtest/gtest.h" + +#include "wallet/wallet2.h" + +static crypto::hash make_hash(uint64_t n) +{ + union + { + crypto::hash hash; + uint64_t n; + } hash; + hash.hash = crypto::null_hash; + hash.n = n; + return hash.hash; +} + +TEST(hashchain, empty) +{ + tools::hashchain hashchain; + ASSERT_EQ(hashchain.size(), 0); + ASSERT_EQ(hashchain.offset(), 0); +} + +TEST(hashchain, genesis) +{ + tools::hashchain hashchain; + hashchain.push_back(make_hash(1)); + ASSERT_EQ(hashchain.size(), 1); + ASSERT_EQ(hashchain.genesis(), make_hash(1)); + hashchain.push_back(make_hash(2)); + ASSERT_EQ(hashchain.size(), 2); + ASSERT_EQ(hashchain.genesis(), make_hash(1)); +} + +TEST(hashchain, push_back) +{ + tools::hashchain hashchain; + hashchain.push_back(make_hash(1)); + hashchain.push_back(make_hash(2)); + hashchain.push_back(make_hash(3)); + ASSERT_EQ(hashchain[0], make_hash(1)); + ASSERT_EQ(hashchain[1], make_hash(2)); + ASSERT_EQ(hashchain[2], make_hash(3)); +} + +TEST(hashchain, clear_empty) +{ + tools::hashchain hashchain; + ASSERT_TRUE(hashchain.empty()); + hashchain.push_back(make_hash(1)); + ASSERT_FALSE(hashchain.empty()); + hashchain.push_back(make_hash(2)); + ASSERT_FALSE(hashchain.empty()); + hashchain.clear(); + ASSERT_TRUE(hashchain.empty()); +} + +TEST(hashchain, crop) +{ + tools::hashchain hashchain; + hashchain.push_back(make_hash(1)); + hashchain.push_back(make_hash(2)); + hashchain.push_back(make_hash(3)); + ASSERT_EQ(hashchain.size(), 3); + ASSERT_EQ(hashchain[0], make_hash(1)); + ASSERT_EQ(hashchain[1], make_hash(2)); + ASSERT_EQ(hashchain[2], make_hash(3)); + hashchain.crop(3); + ASSERT_EQ(hashchain.size(), 3); + hashchain.crop(2); + ASSERT_EQ(hashchain.size(), 2); + ASSERT_EQ(hashchain[0], make_hash(1)); + ASSERT_EQ(hashchain[1], make_hash(2)); + ASSERT_EQ(hashchain.genesis(), make_hash(1)); + hashchain.crop(0); + ASSERT_TRUE(hashchain.empty()); + ASSERT_EQ(hashchain.size(), 0); + hashchain.push_back(make_hash(5)); + ASSERT_EQ(hashchain.genesis(), make_hash(5)); + ASSERT_EQ(hashchain.size(), 1); +} + +TEST(hashchain, trim) +{ + tools::hashchain hashchain; + hashchain.push_back(make_hash(1)); + hashchain.push_back(make_hash(2)); + hashchain.push_back(make_hash(3)); + ASSERT_EQ(hashchain.offset(), 0); + hashchain.trim(2); + ASSERT_EQ(hashchain.offset(), 2); + ASSERT_EQ(hashchain.size(), 3); + ASSERT_EQ(hashchain[2], make_hash(3)); + hashchain.trim(3); + ASSERT_EQ(hashchain.offset(), 3); + ASSERT_EQ(hashchain.size(), 3); + ASSERT_FALSE(hashchain.empty()); + ASSERT_EQ(hashchain.genesis(), make_hash(1)); +} diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index ed436dffd..6ff73b107 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -42,7 +42,7 @@ static tools::wallet2::transfer_container make_transfers_container(size_t N) tools::wallet2::transfer_details &td = transfers.back(); td.m_block_height = 1000; td.m_spent = false; - td.m_txid = cryptonote::null_hash; + td.m_txid = crypto::null_hash; td.m_txid.data[0] = n & 0xff; td.m_txid.data[1] = (n >> 8) & 0xff; td.m_txid.data[2] = (n >> 16) & 0xff; diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index 0ff91c247..4ce62e2f5 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -141,7 +141,7 @@ TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed) cryptonote::blobdata b = "dsdsdfsdfsf"; ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, TEST_FEE, acc.get_keys().m_account_address, tx, b, 1)); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx); - ASSERT_NE(tx_pub_key, cryptonote::null_pkey); + ASSERT_NE(tx_pub_key, crypto::null_pkey); } TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce) { diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service index 12395eb8c..96e88a2d3 100644 --- a/utils/systemd/monerod.service +++ b/utils/systemd/monerod.service @@ -6,12 +6,13 @@ After=network.target User=monero Group=monero WorkingDirectory=~ +RuntimeDirectory=monero Type=forking -PIDFile=/var/run/monerod.pid +PIDFile=/run/monero/monerod.pid ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf \ - --detach --pidfile /var/run/monerod.pid + --detach --pidfile /run/monero/monerod.pid [Install] WantedBy=multi-user.target diff --git a/version.cmake b/version.cmake index 80f1c40b8..75343c381 100644 --- a/version.cmake +++ b/version.cmake @@ -1,32 +1,49 @@ +# Copyright (c) 2014-2017, 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. + function (write_static_version_header hash) set(VERSIONTAG "${hash}") - configure_file("src/version.h.in" "version/version.h") - add_custom_target(version ALL) + configure_file("src/version.cpp.in" "version.cpp") endfunction () -file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/version") find_package(Git QUIET) if ("$Format:$" STREQUAL "") # We're in a tarball; use hard-coded variables. write_static_version_header("release") elseif (GIT_FOUND OR Git_FOUND) message(STATUS "Found Git: ${GIT_EXECUTABLE}") - set(extra_output) - if (CMAKE_GENERATOR MATCHES "Ninja") - # Ninja will not rerun the command every time if the file doesn't change, - # so inject this bogus output so that it always runs. - set(extra_output "${CMAKE_SOURCE_DIR}/.force-git-version-check") - endif () - add_custom_command( - OUTPUT "${CMAKE_BINARY_DIR}/version/version.h" - ${extra_output} + add_custom_target(genversion ALL COMMAND "${CMAKE_COMMAND}" "-D" "GIT=${GIT_EXECUTABLE}" - "-D" "TO=${CMAKE_BINARY_DIR}/version/version.h" + "-D" "TO=${CMAKE_BINARY_DIR}/version.cpp" "-P" "src/version.cmake" + BYPRODUCTS "${CMAKE_BINARY_DIR}/version.cpp" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") - add_custom_target(version ALL - DEPENDS "${CMAKE_BINARY_DIR}/version/version.h") else() message(STATUS "WARNING: Git was not found!") write_static_version_header("unknown") |