diff options
123 files changed, 2087 insertions, 1179 deletions
diff --git a/.gitignore b/.gitignore index a27982af1..0bd9ba3dd 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,6 @@ CMakeCache.txt CMakeFiles cmake_install.cmake install_manifest.txt -*.cmake ### Linux ### *~ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2964e299c..357719ffd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,6 +331,10 @@ if (UNIX AND NOT APPLE) endif() # Handle OpenSSL, used for sha256sum on binary updates and light wallet ssl http +if (CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads") +endif () + if (APPLE AND NOT IOS) if (NOT OpenSSL_DIR) EXECUTE_PROCESS(COMMAND brew --prefix openssl @@ -675,6 +679,8 @@ elseif(FREEBSD) elseif(DRAGONFLY) find_library(COMPAT compat) set(EXTRA_LIBRARIES execinfo ${COMPAT}) +elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") + set(EXTRA_LIBRARIES socket nsl resolv) elseif(NOT MSVC) find_library(RT rt) set(EXTRA_LIBRARIES ${RT}) @@ -688,8 +694,8 @@ if(USE_READLINE) if(READLINE_FOUND AND GNU_READLINE_FOUND) add_definitions(-DHAVE_READLINE) include_directories(${Readline_INCLUDE_DIR}) - list(APPEND EXTRA_LIBRARIES ${Readline_LIBRARY}) message(STATUS "Found readline library at: ${Readline_ROOT_DIR}") + set(EPEE_READLINE epee_readline) else() message(STATUS "Could not find GNU readline library so building without readline support") endif() @@ -703,8 +709,6 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32" AND NOT IOS) list(APPEND EXTRA_LIBRARIES ${ATOMIC}) endif() -include(version.cmake) - find_path(ZMQ_INCLUDE_PATH zmq.hpp) find_library(ZMQ_LIB zmq) find_library(PGM_LIBRARY pgm) @@ -212,7 +212,7 @@ invokes cmake commands as needed. make release-test - *NOTE*: `coretests` test may take a few hours to complete. + *NOTE*: `core_tests` test may take a few hours to complete. * **Optional**: to build binaries suitable for debugging: @@ -350,6 +350,8 @@ We expect to add Monero into the ports tree in the near future, which will aid i ### On OpenBSD: +#### OpenBSD < 6.2 + This has been tested on OpenBSD 5.8. You will need to add a few packages to your system. `pkg_add db cmake gcc gcc-libs g++ miniupnpc gtest`. @@ -363,6 +365,82 @@ You will have to add the serialization, date_time, and regex modules to Boost wh To build: `env CC=egcc CXX=eg++ CPP=ecpp DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/path/to/the/boost/you/built make release-static-64` +#### OpenBSD >= 6.2 + +You will need to add a few packages to your system. Choose version 4 for db. `pkg_add db cmake miniupnpc zeromq`. + +The doxygen and graphviz packages are optional and require the xbase set. + + +Build the Boost library using clang. This guide is derived from: https://github.com/bitcoin/bitcoin/blob/master/doc/build-openbsd.md + +We assume you are compiling with a non-root user and you have `doas` enabled. + +Note: do not use the boost package provided by OpenBSD, as we are installing boost to `/usr/local`. + +``` +# Create boost building directory +mkdir ~/boost +cd ~/boost + +# Fetch boost source +ftp -o boost_1_64_0.tar.bz2 https://netcologne.dl.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2 + +# MUST output: (SHA256) boost_1_64_0.tar.bz2: OK +echo "7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 boost_1_64_0.tar.bz2" | sha256 -c +tar xfj boost_1_64_0.tar.bz2 + +# Fetch a boost patch, required for OpenBSD +ftp -o boost.patch https://raw.githubusercontent.com/openbsd/ports/bee9e6df517077a7269ff0dfd57995f5c6a10379/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp +cd boost_1_64_0 +patch -p0 < ../boost.patch + +# Start building boost +echo 'using clang : : c++ : <cxxflags>"-fvisibility=hidden -fPIC" <linkflags>"" <archiver>"ar" <striper>"strip" <ranlib>"ranlib" <rc>"" : ;' > user-config.jam +./bootstrap.sh --without-icu --with-libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization --with-toolset=clang +./b2 toolset=clang cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++" +doas ./b2 -d0 runtime-link=shared threadapi=pthread threading=multi link=static variant=release --layout=tagged --build-type=complete --user-config=user-config.jam -sNO_BZIP2=1 --prefix=/usr/local install +``` + +Build cppzmq + +Build the cppzmq bindings. + +We assume you are compiling with a non-root user and you have `doas` enabled. + +``` +# Create cppzmq building directory +mkdir ~/cppzmq +cd ~/cppzmq + +# Fetch cppzmq source +ftp -o cppzmq-4.2.2.tar.gz https://github.com/zeromq/cppzmq/archive/v4.2.2.tar.gz + +# MUST output: (SHA256) cppzmq-4.2.2.tar.gz: OK +echo "3ef50070ac5877c06c6bb25091028465020e181bbfd08f110294ed6bc419737d cppzmq-4.2.2.tar.gz" | sha256 -c +tar xfz cppzmq-4.2.2.tar.gz + +# Start building cppzmq +cd cppzmq-4.2.2 +mkdir build +cd build +cmake .. +doas make install +``` + +Build monero: `env DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/usr/local make release-static` + +### On Solaris: + +The default Solaris linker can't be used, you have to install GNU ld, then run cmake manually with the path to your copy of GNU ld: + + mkdir -p build/release + cd build/release + cmake -DCMAKE_LINKER=/path/to/ld -D CMAKE_BUILD_TYPE=Release ../.. + cd ../.. + +Then you can run make as usual. + ### On Linux for Android (using docker): # Build image (select android64.Dockerfile for aarch64) diff --git a/src/version.cmake b/cmake/GenVersion.cmake index 45a97cd20..45a97cd20 100644 --- a/src/version.cmake +++ b/cmake/GenVersion.cmake diff --git a/version.cmake b/cmake/Version.cmake index 75343c381..fb636c4ff 100644 --- a/version.cmake +++ b/cmake/Version.cmake @@ -41,7 +41,7 @@ elseif (GIT_FOUND OR Git_FOUND) COMMAND "${CMAKE_COMMAND}" "-D" "GIT=${GIT_EXECUTABLE}" "-D" "TO=${CMAKE_BINARY_DIR}/version.cpp" - "-P" "src/version.cmake" + "-P" "cmake/GenVersion.cmake" BYPRODUCTS "${CMAKE_BINARY_DIR}/version.cpp" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") else() diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 03f143fe4..33fec8ec5 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -213,6 +213,12 @@ namespace net_utils int get_binded_port(){return m_port;} + long get_connections_count() const + { + auto connections_count = (m_sock_count > 0) ? (m_sock_count - 1) : 0; // Socket count minus listening socket + return connections_count; + } + boost::asio::io_service& get_io_service(){return io_service_;} struct idle_callback_conext_base diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index a66fb7c23..a66fb7c23 100755..100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 80a4504e3..d77c84c28 100755..100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -66,8 +66,6 @@ namespace epee namespace net_utils { -using namespace std; - /*struct url { public: diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index 652d8ff6f..652d8ff6f 100755..100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index c3350bf73..c3350bf73 100755..100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 429e3e1af..429e3e1af 100755..100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 0788c6a4b..8b8e31b51 100755..100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -117,6 +117,11 @@ namespace epee return m_net_server.get_binded_port(); } + long get_connections_count() const + { + return m_net_server.get_connections_count(); + } + protected: net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server; }; diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h index d630bff19..7d060f5ef 100644 --- a/contrib/epee/include/net/levin_base.h +++ b/contrib/epee/include/net/levin_base.h @@ -87,6 +87,7 @@ namespace levin virtual void on_connection_new(t_connection_context& context){}; virtual void on_connection_close(t_connection_context& context){}; + virtual ~levin_commands_handler(){} }; #define LEVIN_OK 0 diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h index 337d345c4..6c8f9bcb3 100644 --- a/contrib/epee/include/net/levin_client_async.h +++ b/contrib/epee/include/net/levin_client_async.h @@ -52,6 +52,7 @@ namespace levin class levin_client_async { levin_commands_handler* m_pcommands_handler; + void (*commands_handler_destroy)(levin_commands_handler*); volatile uint32_t m_is_stop; volatile uint32_t m_threads_count; ::critical_section m_send_lock; @@ -85,9 +86,9 @@ namespace levin ::critical_section m_connection_lock; net_utils::blocked_mode_client m_transport; public: - levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + levin_client_async():m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) {} - levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) {} ~levin_client_async() { @@ -97,11 +98,16 @@ namespace levin while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count)) ::Sleep(100); + + set_handler(NULL); } - void set_handler(levin_commands_handler* phandler) + void set_handler(levin_commands_handler* phandler, void (*destroy)(levin_commands_handler*) = NULL) { + if (commands_handler_destroy && m_pcommands_handler) + (*commands_handler_destroy)(m_pcommands_handler); m_pcommands_handler = phandler; + m_pcommands_handler_destroy = destroy; } bool connect(uint32_t ip, uint32_t port, uint32_t timeout) diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h index fbc9727e2..b3a75bedc 100644 --- a/contrib/epee/include/net/levin_protocol_handler.h +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -43,6 +43,8 @@ namespace levin struct protocl_handler_config { levin_commands_handler<t_connection_context>* m_pcommands_handler; + void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*); + ~protocl_handler_config() { if (m_pcommands_handler && m_pcommands_handler_destroy) (*m_pcommands_handler_destroy)(m_pcommands_handler); } }; template<class t_connection_context = net_utils::connection_context_base> diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 779f4e78f..7ad6d198b 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -72,9 +72,11 @@ class async_protocol_handler_config friend class async_protocol_handler<t_connection_context>; + levin_commands_handler<t_connection_context>* m_pcommands_handler; + void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*); + public: typedef t_connection_context connection_context; - levin_commands_handler<t_connection_context>* m_pcommands_handler; uint64_t m_max_packet_size; uint64_t m_invoke_timeout; @@ -91,8 +93,9 @@ public: template<class callback_t> bool for_connection(const boost::uuids::uuid &connection_id, callback_t cb); size_t get_connections_count(); + void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) {} void del_out_connections(size_t count); }; @@ -832,6 +835,15 @@ size_t async_protocol_handler_config<t_connection_context>::get_connections_coun } //------------------------------------------------------------------------------------------ template<class t_connection_context> +void async_protocol_handler_config<t_connection_context>::set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*)) +{ + if (m_pcommands_handler && m_pcommands_handler_destroy) + (*m_pcommands_handler_destroy)(m_pcommands_handler); + m_pcommands_handler = handler; + m_pcommands_handler_destroy = destroy; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) { async_protocol_handler<t_connection_context>* aph; diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h index 5695143b0..38e9f9098 100644 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -47,6 +47,9 @@ namespace epee PRAGMA_WARNING_PUSH PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") +#ifdef __clang__ + PRAGMA_GCC("GCC diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") +#endif template<class t_stream> size_t pack_varint(t_stream& strm, size_t val) { //the first two bits always reserved for size information diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 3b41f415e..bd6714791 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -26,10 +26,9 @@ # 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. +add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp) if (USE_READLINE AND GNU_READLINE_FOUND) - add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp readline_buffer.cpp) -else() - add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp) + add_library(epee_readline STATIC readline_buffer.cpp) endif() # Build and install libepee if we're building for GUI @@ -41,6 +40,10 @@ if (BUILD_GUI_DEPS) endif() install(TARGETS epee ARCHIVE DESTINATION ${lib_folder}) + if (USE_READLINE AND GNU_READLINE_FOUND) + install(TARGETS epee_readline + ARCHIVE DESTINATION ${lib_folder}) + endif() endif() target_link_libraries(epee @@ -51,3 +54,11 @@ target_link_libraries(epee PRIVATE ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) + +if (USE_READLINE AND GNU_READLINE_FOUND) + target_link_libraries(epee_readline + PUBLIC + easylogging + PRIVATE + ${Readline_LIBRARY}) +endif() diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h index 5b21036bb..2e1b1e5fd 100644 --- a/contrib/epee/tests/src/net/test_net.h +++ b/contrib/epee/tests/src/net/test_net.h @@ -155,7 +155,7 @@ namespace tests bool init(const std::string& bind_port = "", const std::string& bind_ip = "0.0.0.0") { - m_net_server.get_config_object().m_pcommands_handler = this; + m_net_server.get_config_object().set_handler(this); m_net_server.get_config_object().m_invoke_timeout = 1000; LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); return m_net_server.init_server(bind_port, bind_ip); diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c index b3de9702f..87b244ce7 100644 --- a/external/db_drivers/liblmdb/mdb.c +++ b/external/db_drivers/liblmdb/mdb.c @@ -304,7 +304,8 @@ union semun { # else # define MDB_USE_ROBUST 1 /* glibc < 2.12 only provided _np API */ -# if defined(__GLIBC__) && GLIBC_VER < 0x02000c +# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \ + (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST)) # define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP # define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag) # define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex) @@ -4977,6 +4978,13 @@ mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl) #else /* MDB_USE_POSIX_MUTEX: */ pthread_mutexattr_t mattr; + /* Solaris needs this before initing a robust mutex. Otherwise + * it may skip the init and return EBUSY "seems someone already + * inited" or EINVAL "it was inited differently". + */ + memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex)); + memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex)); + if ((rc = pthread_mutexattr_init(&mattr)) || (rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) #ifdef MDB_ROBUST_SUPPORTED diff --git a/external/easylogging++/CMakeLists.txt b/external/easylogging++/CMakeLists.txt index ae7d931cf..97d0bf571 100644 --- a/external/easylogging++/CMakeLists.txt +++ b/external/easylogging++/CMakeLists.txt @@ -51,6 +51,7 @@ if (BUILD_GUI_DEPS) set(lib_folder lib) endif() install(TARGETS easylogging - ARCHIVE DESTINATION ${lib_folder}) + ARCHIVE DESTINATION ${lib_folder} + LIBRARY DESTINATION ${lib_folder}) endif() diff --git a/external/miniupnpc/CMakeLists.txt b/external/miniupnpc/CMakeLists.txt index 4c01b6d06..bc9685699 100644 --- a/external/miniupnpc/CMakeLists.txt +++ b/external/miniupnpc/CMakeLists.txt @@ -36,6 +36,9 @@ if (NOT WIN32) # add_definitions (-D_POSIX_C_SOURCE=200112L) add_definitions (-D_XOPEN_SOURCE=600) endif (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT CMAKE_SYSTEM_NAME STREQUAL "DragonFly") + if (CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") + add_definitions (-D__EXTENSIONS__ -std=c99) + endif () else (NOT WIN32) add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends endif (NOT WIN32) diff --git a/external/unbound/CMakeLists.txt b/external/unbound/CMakeLists.txt index 3dae8b425..a2e475232 100644 --- a/external/unbound/CMakeLists.txt +++ b/external/unbound/CMakeLists.txt @@ -44,7 +44,12 @@ endif () set(RETSIGTYPE void) +if(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") +add_definitions(-D_XOPEN_SOURCE=600) +else() add_definitions(-D_GNU_SOURCE) +endif() +add_definitions(-std=c99) option(USE_ECDSA "Use ECDSA algorithms" ON) option(USE_SHA2 "Enable SHA2 support" ON) @@ -60,6 +65,15 @@ if (CMAKE_USE_WIN32_THREADS_INIT) else () set(HAVE_WINDOWS_THREADS 0) endif () + +# determine if we have libressl +check_symbol_exists(LIBRESSL_VERSION_TEXT "openssl/opensslv.h" HAVE_LIBRESSL) +# check if we have found HAVE_DECL_REALLOCARRAY already, so we can safely undefine and redefine it with value 1 +if (HAVE_LIBRESSL AND HAVE_DECL_REALLOCARRAY) + unset(HAVE_DECL_REALLOCARRAY CACHE) + add_definitions(-DHAVE_DECL_REALLOCARRAY=1) +endif () + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h") diff --git a/external/unbound/configure_checks.cmake b/external/unbound/configure_checks.cmake index 258f28138..2acf556d1 100644 --- a/external/unbound/configure_checks.cmake +++ b/external/unbound/configure_checks.cmake @@ -49,6 +49,11 @@ if (WIN32) iphlpapi ws2_32) endif () +if (CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") + set(CMAKE_REQUIRED_LIBRARIES + socket + nsl) +endif () check_function_exists(_beginthreadex HAVE__BEGINTHREADEX) check_function_exists(arc4random HAVE_ARC4RANDOM) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fb08b645..0281b1df6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,26 +83,30 @@ function (monero_add_executable name) endfunction () function (monero_add_library name) - source_group("${name}" - FILES - ${ARGN}) + monero_add_library_with_deps(NAME "${name}" SOURCES ${ARGN}) +endfunction() + +function (monero_add_library_with_deps) + cmake_parse_arguments(MONERO_ADD_LIBRARY "" "NAME" "DEPENDS;SOURCES" ${ARGN}) + source_group("${MONERO_ADD_LIBRARY_NAME}" FILES ${MONERO_ADD_LIBRARY_SOURCES}) # Define a ("virtual") object library and an actual library that links those # objects together. The virtual libraries can be arbitrarily combined to link # any subset of objects into one library archive. This is used for releasing # libwallet, which combines multiple components. - set(objlib obj_${name}) - add_library(${objlib} OBJECT ${ARGN}) - add_library("${name}" $<TARGET_OBJECTS:${objlib}>) - set_property(TARGET "${name}" - PROPERTY - FOLDER "libs") + set(objlib obj_${MONERO_ADD_LIBRARY_NAME}) + add_library(${objlib} OBJECT ${MONERO_ADD_LIBRARY_SOURCES}) + add_library("${MONERO_ADD_LIBRARY_NAME}" $<TARGET_OBJECTS:${objlib}>) + if (MONERO_ADD_LIBRARY_DEPENDS) + add_dependencies(${objlib} ${MONERO_ADD_LIBRARY_DEPENDS}) + endif() + set_property(TARGET "${MONERO_ADD_LIBRARY_NAME}" PROPERTY FOLDER "libs") target_compile_definitions(${objlib} - PRIVATE $<TARGET_PROPERTY:${name},INTERFACE_COMPILE_DEFINITIONS>) + PRIVATE $<TARGET_PROPERTY:${MONERO_ADD_LIBRARY_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) +include(Version) +monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion) add_subdirectory(common) add_subdirectory(crypto) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index d62a250ff..c3f6e3d87 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -208,7 +208,7 @@ uint64_t BlockchainDB::add_block( const block& blk time1 = epee::misc_utils::get_tick_count(); add_transaction(blk_hash, blk.miner_tx); int tx_i = 0; - crypto::hash tx_hash = null_hash; + crypto::hash tx_hash = crypto::null_hash; for (const transaction& tx : txs) { tx_hash = blk.tx_hashes[tx_i]; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 838385e8a..79676b808 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -147,8 +147,9 @@ struct txpool_tx_meta_t uint8_t kept_by_block; uint8_t relayed; uint8_t do_not_relay; + uint8_t double_spend_seen: 1; - uint8_t padding[77]; // till 192 bytes + uint8_t padding[76]; // till 192 bytes }; #define DBF_SAFE 1 @@ -1314,7 +1315,7 @@ public: /** * @brief get the number of transactions in the txpool */ - virtual uint64_t get_txpool_tx_count() const = 0; + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const = 0; /** * @brief check whether a txid is in the txpool @@ -1369,7 +1370,7 @@ public: * * @return false if the function returns false for any transaction, otherwise true */ - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const = 0; + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const = 0; /** * @brief runs a function over all key images stored diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 5bd02bcf7..865558e07 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -34,6 +34,7 @@ #include <cstring> // memcpy #include <random> +#include "common/util.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" #include "profile_tools.h" @@ -48,6 +49,7 @@ #endif using epee::string_tools::pod_to_hex; +using namespace crypto; // Increase when the DB changes in a non backward compatible way, and there // is no automatic conversion, so that a full resync is needed. @@ -1135,6 +1137,11 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) if ((result = mdb_env_set_maxdbs(m_env, 20))) throw0(DB_ERROR(lmdb_error("Failed to set max number of dbs: ", result).c_str())); + int threads = tools::get_max_concurrency(); + if (threads > 110 && /* maxreaders default is 126, leave some slots for other read processes */ + (result = mdb_env_set_maxreaders(m_env, threads+16))) + throw0(DB_ERROR(lmdb_error("Failed to set max number of readers: ", result).c_str())); + size_t mapsize = DEFAULT_MAPSIZE; if (db_flags & DBF_FAST) @@ -1522,21 +1529,49 @@ void BlockchainLMDB::update_txpool_tx(const crypto::hash &txid, const txpool_tx_ } } -uint64_t BlockchainLMDB::get_txpool_tx_count() const +uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); - TXN_PREFIX_RDONLY(); int result; + uint64_t num_entries = 0; - MDB_stat db_stats; - if ((result = mdb_stat(m_txn, m_txpool_meta, &db_stats))) - throw0(DB_ERROR(lmdb_error("Failed to query m_txpool_meta: ", result).c_str())); + TXN_PREFIX_RDONLY(); + + if (include_unrelayed_txes) + { + // No filtering, we can get the number of tx the "fast" way + MDB_stat db_stats; + if ((result = mdb_stat(m_txn, m_txpool_meta, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_txpool_meta: ", result).c_str())); + num_entries = db_stats.ms_entries; + } + else + { + // Filter unrelayed tx out of the result, so we need to loop over transactions and check their meta data + RCURSOR(txpool_meta); + RCURSOR(txpool_blob); + MDB_val k; + MDB_val v; + MDB_cursor_op op = MDB_FIRST; + while (1) + { + result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, op); + op = MDB_NEXT; + if (result == MDB_NOTFOUND) + break; + if (result) + throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str())); + const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; + if (!meta.do_not_relay) + ++num_entries; + } + } TXN_POSTFIX_RDONLY(); - return db_stats.ms_entries; + return num_entries; } bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const @@ -1633,7 +1668,7 @@ cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid return bd; } -bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob) const +bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1657,6 +1692,9 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str())); const crypto::hash txid = *(const crypto::hash*)k.mv_data; const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; + if (!include_unrelayed_txes && meta.do_not_relay) + // Skipping that tx + continue; const cryptonote::blobdata *passed_bd = NULL; cryptonote::blobdata bd; if (include_blob) diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 98571a7f8..fce8f29ed 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -243,13 +243,13 @@ public: virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& meta); virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta); - virtual uint64_t get_txpool_tx_count() const; + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; virtual bool txpool_has_tx(const crypto::hash &txid) const; virtual void remove_txpool_tx(const crypto::hash& txid); virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const; virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const; virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false) const; + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const; virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const; virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const; diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 60672eeda..78bb51ab6 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -30,6 +30,7 @@ #include "blocksdat_file.h" #include "common/command_line.h" #include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/db_types.h" #include "version.h" @@ -66,21 +67,16 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; - const command_line::arg_descriptor<bool> arg_testnet_on = { - "testnet" - , "Run on testnet." - , false - }; const command_line::arg_descriptor<std::string> arg_database = { "database", available_dbs.c_str(), default_db_type }; const command_line::arg_descriptor<bool> arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat}; - command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string()); - command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string()); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir, default_data_path.string()); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_data_dir, default_testnet_data_path.string()); command_line::add_arg(desc_cmd_sett, arg_output_file); - command_line::add_arg(desc_cmd_sett, arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_database); command_line::add_arg(desc_cmd_sett, arg_block_stop); @@ -117,12 +113,12 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Starting..."); - bool opt_testnet = command_line::get_arg(vm, arg_testnet_on); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat); std::string m_config_folder; - auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto data_dir_arg = opt_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir; m_config_folder = command_line::get_arg(vm, data_dir_arg); std::string db_type = command_line::get_arg(vm, arg_database); diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 2a956dbdb..a50b0bad6 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -256,7 +256,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path seek_height = start_height; BootstrapFile bootstrap; - streampos pos; + std::streampos pos; // BootstrapFile bootstrap(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); @@ -585,11 +585,6 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size}; const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks}; const command_line::arg_descriptor<bool> arg_drop_hf = {"drop-hard-fork", "Drop hard fork subdbs", false}; - const command_line::arg_descriptor<bool> arg_testnet_on = { - "testnet" - , "Run on testnet." - , false - }; const command_line::arg_descriptor<bool> arg_count_blocks = { "count-blocks" , "Count blocks in bootstrap file and exit" @@ -674,8 +669,8 @@ int main(int argc, char* argv[]) } } - opt_testnet = command_line::get_arg(vm, arg_testnet_on); - auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + auto data_dir_arg = opt_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir; m_config_folder = command_line::get_arg(vm, data_dir_arg); db_arg_str = command_line::get_arg(vm, arg_database); diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index 25b9df58f..50b56eab9 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -220,7 +220,7 @@ void BootstrapFile::write_block(block& block) // now add all regular transactions for (const auto& tx_id : block.tx_hashes) { - if (tx_id == null_hash) + if (tx_id == crypto::null_hash) { throw std::runtime_error("Aborting: tx == null_hash"); } @@ -432,7 +432,7 @@ uint64_t BootstrapFile::count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) { - streampos dummy_pos; + std::streampos dummy_pos; uint64_t dummy_height = 0; return count_blocks(import_file_path, dummy_pos, dummy_height); } @@ -440,7 +440,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) // 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) +uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::streampos &start_pos, uint64_t& seek_height) { boost::filesystem::path raw_file_path(import_file_path); boost::system::error_code ec; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index c3969a357..0926ee2e5 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -57,7 +57,7 @@ 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, std::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/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index bea392db0..9be08958c 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -140,8 +140,8 @@ namespace cryptonote { if (testnet) { - // just use the genesis block on testnet ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); + ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); return true; } ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 666b3267f..4b9ca9559 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -36,10 +36,6 @@ #include "cryptonote_config.h" #include "string_tools.h" -#ifdef HAVE_READLINE - #include "readline_buffer.h" -#endif - namespace command_line { namespace @@ -50,29 +46,29 @@ namespace command_line } } - std::string input_line(const std::string& prompt) + bool is_yes(const std::string& str) { -#ifdef HAVE_READLINE - rdln::suspend_readline pause_readline; -#endif - std::cout << prompt; - - std::string buf; - std::getline(std::cin, buf); + if (str == "y" || str == "Y") + return true; - return epee::string_tools::trim(buf); + boost::algorithm::is_iequal ignore_case{}; + if (boost::algorithm::equals("yes", str, ignore_case)) + return true; + if (boost::algorithm::equals(command_line::tr("yes"), str, ignore_case)) + return true; + return false; } - bool is_yes(const std::string& str) + bool is_no(const std::string& str) { - if (str == "y" || str == "Y") + if (str == "n" || str == "N") return true; boost::algorithm::is_iequal ignore_case{}; - if (boost::algorithm::equals("yes", str, ignore_case)) + if (boost::algorithm::equals("no", str, ignore_case)) return true; - if (boost::algorithm::equals(command_line::tr("yes"), str, ignore_case)) + if (boost::algorithm::equals(command_line::tr("no"), str, ignore_case)) return true; return false; @@ -80,49 +76,4 @@ namespace command_line const arg_descriptor<bool> arg_help = {"help", "Produce help message"}; const arg_descriptor<bool> arg_version = {"version", "Output version information"}; - const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"}; - const arg_descriptor<std::string> arg_testnet_data_dir = {"testnet-data-dir", "Specify testnet data directory"}; - const arg_descriptor<bool> arg_test_drop_download = {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"}; - const arg_descriptor<uint64_t> arg_test_drop_download_height = {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0}; - const arg_descriptor<int> arg_test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."}; - const arg_descriptor<bool, false> arg_testnet_on = { - "testnet" - , "Run on testnet. The wallet must be launched with --testnet flag." - , false - }; - const arg_descriptor<bool> arg_dns_checkpoints = { - "enforce-dns-checkpointing" - , "checkpoints from DNS server will be enforced" - , false - }; - const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = { - "fast-block-sync" - , "Sync up most of the way by using embedded, known block hashes." - , 1 - }; - const command_line::arg_descriptor<uint64_t> arg_prep_blocks_threads = { - "prep-blocks-threads" - , "Max number of threads to use when preparing block hashes in groups." - , 4 - }; - const command_line::arg_descriptor<uint64_t> arg_show_time_stats = { - "show-time-stats" - , "Show time-stats when processing blocks/txs and disk synchronization." - , 0 - }; - const command_line::arg_descriptor<size_t> arg_block_sync_size = { - "block-sync-size" - , "How many blocks to sync at once during chain synchronization (0 = adaptive)." - , 0 - }; - const command_line::arg_descriptor<std::string> arg_check_updates = { - "check-updates" - , "Check for new versions of monero: [disabled|notify|download|update]" - , "notify" - }; - const arg_descriptor<bool> arg_fluffy_blocks = { - "fluffy-blocks" - , "Relay blocks as fluffy blocks where possible (automatic on testnet)" - , false - }; } diff --git a/src/common/command_line.h b/src/common/command_line.h index 04fd70be5..c2bac9cc8 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -41,10 +41,10 @@ namespace command_line { - std::string input_line(const std::string& prompt); - //! \return True if `str` is `is_iequal("y" || "yes" || `tr("yes"))`. bool is_yes(const std::string& str); + //! \return True if `str` is `is_iequal("n" || "no" || `tr("no"))`. + bool is_no(const std::string& str); template<typename T, bool required = false> struct arg_descriptor; @@ -211,17 +211,4 @@ namespace command_line extern const arg_descriptor<bool> arg_help; extern const arg_descriptor<bool> arg_version; - extern const arg_descriptor<std::string> arg_data_dir; - extern const arg_descriptor<std::string> arg_testnet_data_dir; - extern const arg_descriptor<bool> arg_test_drop_download; - extern const arg_descriptor<uint64_t> arg_test_drop_download_height; - extern const arg_descriptor<int> arg_test_dbg_lock_sleep; - extern const arg_descriptor<bool, false> arg_testnet_on; - extern const arg_descriptor<bool> arg_dns_checkpoints; - extern const arg_descriptor<uint64_t> arg_fast_block_sync; - extern const arg_descriptor<uint64_t> arg_prep_blocks_threads; - extern const arg_descriptor<uint64_t> arg_show_time_stats; - extern const arg_descriptor<size_t> arg_block_sync_size; - extern const arg_descriptor<std::string> arg_check_updates; - extern const arg_descriptor<bool> arg_fluffy_blocks; } diff --git a/src/common/int-util.h b/src/common/int-util.h index 34288805a..7cec571ad 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -40,6 +40,10 @@ #include <byteswap.h> #endif +#if defined(__sun) && defined(__SVR4) +#include <endian.h> +#endif + #if defined(_MSC_VER) #include <stdlib.h> diff --git a/src/common/password.cpp b/src/common/password.cpp index 5c04023f4..5d56464a5 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -88,13 +88,11 @@ namespace { pass.back() = '\0'; pass.resize(pass.size() - 1); - std::cout << "\b \b"; } } else { pass.push_back(ch); - std::cout << '*'; } } @@ -150,13 +148,11 @@ namespace { aPass.back() = '\0'; aPass.resize(aPass.size() - 1); - std::cout << "\b \b"; } } else { aPass.push_back(ch); - std::cout << '*'; } } @@ -242,9 +238,6 @@ namespace tools boost::optional<password_container> password_container::prompt(const bool verify, const char *message) { -#ifdef HAVE_READLINE - rdln::suspend_readline pause_readline; -#endif password_container pass1{}; password_container pass2{}; if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password)) @@ -253,7 +246,7 @@ namespace tools return boost::none; } - boost::optional<login> login::parse(std::string&& userpass, bool verify, const char* message) + boost::optional<login> login::parse(std::string&& userpass, bool verify, const std::function<boost::optional<password_container>(bool)> &prompt) { login out{}; password_container wipe{std::move(userpass)}; @@ -261,7 +254,7 @@ namespace tools const auto loc = wipe.password().find(':'); if (loc == std::string::npos) { - auto result = tools::password_container::prompt(verify, message); + auto result = prompt(verify); if (!result) return boost::none; diff --git a/src/common/password.h b/src/common/password.h index 12f715df4..ba1c30a28 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -82,7 +82,7 @@ namespace tools \return The username and password, or boost::none if `password_container::prompt` fails. */ - static boost::optional<login> parse(std::string&& userpass, bool verify, const char* message = "Password"); + static boost::optional<login> parse(std::string&& userpass, bool verify, const std::function<boost::optional<password_container>(bool)> &prompt); login(const login&) = delete; login(login&&) = default; diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 41d0c25e0..20c5765b0 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -39,7 +39,7 @@ namespace tools threadpool::threadpool() : running(true), active(0) { boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - max = tools::get_max_concurrency() * 2; + max = tools::get_max_concurrency(); size_t i = max; while(i--) { threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this))); @@ -74,7 +74,7 @@ void threadpool::submit(waiter *obj, std::function<void()> f) { } int threadpool::get_max_concurrency() { - return max / 2; + return max; } void threadpool::waiter::wait() { diff --git a/src/common/updates.cpp b/src/common/updates.cpp index 8a057b1cf..141330c2c 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -100,7 +100,7 @@ namespace tools { const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/"; #ifdef _WIN32 - static const char extension[] = ".zip"; + static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe"; #else static const char extension[] = ".tar.bz2"; #endif diff --git a/src/common/util.cpp b/src/common/util.cpp index 30746f680..a13ac6e50 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -405,7 +405,7 @@ namespace tools #else std::string get_nix_version_display_string() { - utsname un; + struct utsname un; if(uname(&un) < 0) return std::string("*nix: failed to get os version"); @@ -549,6 +549,13 @@ std::string get_nix_version_display_string() if (!strcmp(ver, "2.25")) MCLOG_RED(el::Level::Warning, "global", "Running with glibc " << ver << ", hangs may occur - change glibc version if possible"); #endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000 + SSL_library_init(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + return true; } void set_strict_default_file_permissions(bool strict) diff --git a/src/common/util.h b/src/common/util.h index 2e4d6e917..1aac026c1 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -148,9 +148,10 @@ namespace tools } return r; #else - /* Only blocks SIGINT and SIGTERM */ + /* Only blocks SIGINT, SIGTERM and SIGPIPE */ signal(SIGINT, posix_handler); signal(SIGTERM, posix_handler); + signal(SIGPIPE, SIG_IGN); m_handler = t; return true; #endif diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 619038ae6..eb1d1c069 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -31,8 +31,13 @@ #pragma once #if defined(__GNUC__) +#if defined(__sun) && defined(__SVR4) +#define INITIALIZER(name) __attribute__((constructor)) static void name(void) +#define FINALIZER(name) __attribute__((destructor)) static void name(void) +#else #define INITIALIZER(name) __attribute__((constructor(101))) static void name(void) #define FINALIZER(name) __attribute__((destructor(101))) static void name(void) +#endif #define REGISTER_FINALIZER(name) ((void) 0) #elif defined(_MSC_VER) diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 090d563a2..fc6d487c2 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -2,6 +2,8 @@ // 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi> // A baseline Keccak (3rd round) implementation. +#include <stdio.h> +#include <stdlib.h> #include "hash-ops.h" #include "keccak.h" @@ -73,12 +75,18 @@ void keccakf(uint64_t st[25], int rounds) // compute a keccak hash (md) of given byte length from "in" typedef uint64_t state_t[25]; -int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) +void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) { state_t st; uint8_t temp[144]; size_t i, rsiz, rsizw; + if (mdlen <= 0 || mdlen > 200 || sizeof(st) != 200) + { + fprintf(stderr, "Bad keccak use"); + abort(); + } + rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; rsizw = rsiz / 8; @@ -91,6 +99,12 @@ int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) } // last block and padding + if (inlen >= sizeof(temp) || inlen > rsiz || rsiz - inlen + inlen + 1 >= sizeof(temp) || rsiz == 0 || rsiz - 1 >= sizeof(temp) || rsizw * 8 > sizeof(temp)) + { + fprintf(stderr, "Bad keccak use"); + abort(); + } + memcpy(temp, in, inlen); temp[inlen++] = 1; memset(temp + inlen, 0, rsiz - inlen); @@ -102,8 +116,6 @@ int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) keccakf(st, KECCAK_ROUNDS); memcpy(md, st, mdlen); - - return 0; } void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h index fbd8e1904..fb9d8bd04 100644 --- a/src/crypto/keccak.h +++ b/src/crypto/keccak.h @@ -16,7 +16,7 @@ #endif // compute a keccak hash (md) of given byte length from "in" -int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); +void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); // update the state void keccakf(uint64_t st[25], int norounds); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index d09f4c432..1c28ca4d8 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -45,6 +45,8 @@ using namespace epee; // #define ENABLE_HASH_CASH_INTEGRITY_CHECK +using namespace crypto; + static const uint64_t valid_decomposed_outputs[] = { (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9d89c6280..3d586a704 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -61,6 +61,8 @@ #define FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE (100*1024*1024) // 100 MB +using namespace crypto; + //#include "serialization/json_archive.h" /* TODO: @@ -3166,9 +3168,9 @@ bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids) cryptonote::transaction tx; size_t blob_size; uint64_t fee; - bool relayed, do_not_relay; + bool relayed, do_not_relay, double_spend_seen; MINFO("Removing txid " << txid << " from the pool"); - if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay)) + if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -3351,7 +3353,7 @@ leave: transaction tx; size_t blob_size = 0; uint64_t fee = 0; - bool relayed = false, do_not_relay = false; + bool relayed = false, do_not_relay = false, double_spend_seen = false; TIME_MEASURE_START(aa); // XXX old code does not check whether tx exists @@ -3368,7 +3370,7 @@ leave: TIME_MEASURE_START(bb); // get transaction with hash <tx_id> from tx_pool - if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay)) + if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) { MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); bvc.m_verifivation_failed = true; @@ -4211,9 +4213,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid) m_db->remove_txpool_tx(txid); } -uint64_t Blockchain::get_txpool_tx_count() const +uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const { - return m_db->get_txpool_tx_count(); + return m_db->get_txpool_tx_count(include_unrelayed_txes); } txpool_tx_meta_t Blockchain::get_txpool_tx_meta(const crypto::hash& txid) const @@ -4231,9 +4233,9 @@ cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) co return m_db->get_txpool_tx_blob(txid); } -bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob) const +bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const { - return m_db->for_all_txpool_txes(f, include_blob); + return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes); } void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync) @@ -4381,12 +4383,12 @@ void Blockchain::load_compiled_in_block_hashes() size_t blob_size; uint64_t fee; - bool relayed, do_not_relay; + bool relayed, do_not_relay, double_spend_seen; transaction pool_tx; for(const transaction &tx : txs) { crypto::hash tx_hash = get_transaction_hash(tx); - m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay); + m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay, double_spend_seen); } } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index f64bd35e3..e0936da8f 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -635,16 +635,6 @@ namespace cryptonote uint64_t get_current_cumulative_blocksize_limit() const; /** - * @brief checks if the blockchain is currently being stored - * - * Note: this should be meaningless in cases where Blockchain is not - * directly managing saving the blockchain to disk. - * - * @return true if Blockchain is having the chain stored currently, else false - */ - bool is_storing_blockchain()const{return false;} - - /** * @brief gets the difficulty of the block with a given height * * @param i the height @@ -947,11 +937,11 @@ namespace cryptonote void add_txpool_tx(transaction &tx, const txpool_tx_meta_t &meta); void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta); void remove_txpool_tx(const crypto::hash &txid); - uint64_t get_txpool_tx_count() const; + uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const; bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const; cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; - bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const; + bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const; bool is_within_compiled_block_hash_area(uint64_t height) const; bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 61f844612..acc76a8d6 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -38,6 +38,7 @@ using namespace epee; #include "common/updates.h" #include "common/download.h" #include "common/threadpool.h" +#include "common/command_line.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -61,6 +62,69 @@ DISABLE_VS_WARNINGS(4355) namespace cryptonote { + const command_line::arg_descriptor<std::string> arg_data_dir = { + "data-dir" + , "Specify data directory" + }; + const command_line::arg_descriptor<std::string> arg_testnet_data_dir = { + "testnet-data-dir" + , "Specify testnet data directory" + }; + const command_line::arg_descriptor<bool, false> arg_testnet_on = { + "testnet" + , "Run on testnet. The wallet must be launched with --testnet flag." + , false + }; + + static const command_line::arg_descriptor<bool> arg_test_drop_download = { + "test-drop-download" + , "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)" + }; + static const command_line::arg_descriptor<uint64_t> arg_test_drop_download_height = { + "test-drop-download-height" + , "Like test-drop-download but disards only after around certain height" + , 0 + }; + static const command_line::arg_descriptor<int> arg_test_dbg_lock_sleep = { + "test-dbg-lock-sleep" + , "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests." + , 0 + }; + static const command_line::arg_descriptor<bool> arg_dns_checkpoints = { + "enforce-dns-checkpointing" + , "checkpoints from DNS server will be enforced" + , false + }; + static const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = { + "fast-block-sync" + , "Sync up most of the way by using embedded, known block hashes." + , 1 + }; + static const command_line::arg_descriptor<uint64_t> arg_prep_blocks_threads = { + "prep-blocks-threads" + , "Max number of threads to use when preparing block hashes in groups." + , 4 + }; + static const command_line::arg_descriptor<uint64_t> arg_show_time_stats = { + "show-time-stats" + , "Show time-stats when processing blocks/txs and disk synchronization." + , 0 + }; + static const command_line::arg_descriptor<size_t> arg_block_sync_size = { + "block-sync-size" + , "How many blocks to sync at once during chain synchronization (0 = adaptive)." + , 0 + }; + static const command_line::arg_descriptor<std::string> arg_check_updates = { + "check-updates" + , "Check for new versions of monero: [disabled|notify|download|update]" + , "notify" + }; + static const command_line::arg_descriptor<bool> arg_fluffy_blocks = { + "fluffy-blocks" + , "Relay blocks as fluffy blocks where possible (automatic on testnet)" + , false + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -148,20 +212,21 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& desc) { - command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir()); - command_line::add_arg(desc, command_line::arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string()); + command_line::add_arg(desc, arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc, arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string()); - command_line::add_arg(desc, command_line::arg_test_drop_download); - command_line::add_arg(desc, command_line::arg_test_drop_download_height); + command_line::add_arg(desc, arg_test_drop_download); + command_line::add_arg(desc, arg_test_drop_download_height); - command_line::add_arg(desc, command_line::arg_testnet_on); - command_line::add_arg(desc, command_line::arg_dns_checkpoints); - command_line::add_arg(desc, command_line::arg_prep_blocks_threads); - command_line::add_arg(desc, command_line::arg_fast_block_sync); - command_line::add_arg(desc, command_line::arg_show_time_stats); - command_line::add_arg(desc, command_line::arg_block_sync_size); - command_line::add_arg(desc, command_line::arg_check_updates); - command_line::add_arg(desc, command_line::arg_fluffy_blocks); + command_line::add_arg(desc, arg_testnet_on); + command_line::add_arg(desc, arg_dns_checkpoints); + command_line::add_arg(desc, arg_prep_blocks_threads); + command_line::add_arg(desc, arg_fast_block_sync); + command_line::add_arg(desc, arg_show_time_stats); + command_line::add_arg(desc, arg_block_sync_size); + command_line::add_arg(desc, arg_check_updates); + command_line::add_arg(desc, arg_fluffy_blocks); + command_line::add_arg(desc, arg_test_dbg_lock_sleep); // we now also need some of net_node's options (p2p bind arg, for separate data dir) command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); @@ -173,9 +238,9 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) { - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, arg_testnet_on); - auto data_dir_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto data_dir_arg = m_testnet ? arg_testnet_data_dir : arg_data_dir; m_config_folder = command_line::get_arg(vm, data_dir_arg); auto data_dir = boost::filesystem::path(m_config_folder); @@ -196,13 +261,15 @@ namespace cryptonote } - set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints)); - test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height)); - m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks); + set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); + test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); + m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks); - if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true) + if (command_line::get_arg(vm, arg_test_drop_download) == true) test_drop_download(); + epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, arg_test_dbg_lock_sleep); + return true; } //----------------------------------------------------------------------------------------------- @@ -268,7 +335,7 @@ namespace cryptonote m_fakechain = test_options != NULL; bool r = handle_command_line(vm); - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + bool testnet = command_line::get_arg(vm, arg_testnet_on); auto p2p_bind_arg = testnet ? nodetool::arg_testnet_p2p_bind_port : nodetool::arg_p2p_bind_port; std::string m_port = command_line::get_arg(vm, p2p_bind_arg); std::string m_config_folder_mempool = m_config_folder; @@ -281,9 +348,9 @@ namespace cryptonote std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0; - bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0; - uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads); - std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates); + bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; + uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads); + std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -409,11 +476,11 @@ namespace cryptonote // transactions in the pool that do not conform to the current fork m_mempool.validate(m_blockchain_storage.get_current_hard_fork_version()); - bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0; + bool show_time_stats = command_line::get_arg(vm, arg_show_time_stats) != 0; m_blockchain_storage.set_show_time_stats(show_time_stats); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - block_sync_size = command_line::get_arg(vm, command_line::arg_block_sync_size); + block_sync_size = command_line::get_arg(vm, arg_block_sync_size); MGINFO("Loading checkpoints"); @@ -498,8 +565,8 @@ namespace cryptonote return false; } - tx_hash = null_hash; - tx_prefixt_hash = null_hash; + tx_hash = crypto::null_hash; + tx_prefixt_hash = crypto::null_hash; if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { @@ -1182,21 +1249,21 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list<transaction>& txs) const + bool core::get_pool_transactions(std::list<transaction>& txs, bool include_sensitive_data) const { - m_mempool.get_transactions(txs); + m_mempool.get_transactions(txs, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const + bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_data) const { - m_mempool.get_transaction_hashes(txs); + m_mempool.get_transaction_hashes(txs, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction_stats(struct txpool_stats& stats) const + bool core::get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_data) const { - m_mempool.get_transaction_stats(stats); + m_mempool.get_transaction_stats(stats, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- @@ -1210,9 +1277,9 @@ namespace cryptonote return m_mempool.have_tx(id); } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const { - return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); + return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos, include_sensitive_data); } //----------------------------------------------------------------------------------------------- bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 7340e1024..dc014206d 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -41,6 +41,7 @@ #include "storages/portable_storage_template_helper.h" #include "common/download.h" #include "common/threadpool.h" +#include "common/command_line.h" #include "tx_pool.h" #include "blockchain.h" #include "cryptonote_basic/miner.h" @@ -58,6 +59,10 @@ namespace cryptonote const std::pair<uint8_t, uint64_t> *hard_forks; }; + extern const command_line::arg_descriptor<std::string> arg_data_dir; + extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir; + extern const command_line::arg_descriptor<bool, false> arg_testnet_on; + /************************************************************************/ /* */ /************************************************************************/ @@ -420,11 +425,12 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::list<transaction>& txs) const; - + bool get_pool_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const; + /** * @copydoc tx_memory_pool::get_txpool_backlog * @@ -434,17 +440,19 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const; + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_stats(struct txpool_stats& stats) const; + bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_transaction @@ -455,10 +463,11 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info */ - bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const; + bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_pool_for_rpc diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 96f6ee872..feefc1592 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -40,6 +40,8 @@ using namespace epee; #include "crypto/hash.h" #include "ringct/rctSigs.h" +using namespace crypto; + namespace cryptonote { //--------------------------------------------------------------- @@ -160,12 +162,6 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct) { - if (destinations.empty()) - { - LOG_ERROR("The destinations must be non-empty"); - return false; - } - std::vector<rct::key> amount_keys; tx.set_null(); amount_keys.clear(); @@ -174,9 +170,8 @@ namespace cryptonote tx.unlock_time = unlock_time; tx.extra = extra; - keypair txkey = keypair::generate(); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); + keypair txkey; + txkey.sec = rct::rct2sk(rct::skGen()); tx_key = txkey.sec; // if we have a stealth payment id, find it and encrypt it with the tx key now @@ -323,9 +318,13 @@ namespace cryptonote if (num_stdaddresses == 0 && num_subaddresses == 1) { txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec))); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); } + else + { + txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(txkey.sec))); + } + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); + add_tx_pub_key_to_extra(tx, txkey.pub); std::vector<crypto::public_key> additional_tx_public_keys; additional_tx_keys.clear(); @@ -348,9 +347,11 @@ namespace cryptonote keypair additional_txkey; if (need_additional_txkeys) { - additional_txkey = keypair::generate(); + additional_txkey.sec = rct::rct2sk(rct::skGen()); if (dst_entr.is_subaddress) additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); + else + additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec))); } bool r; @@ -393,7 +394,6 @@ namespace cryptonote } remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys)); - add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); LOG_PRINT_L2("tx pubkey: " << txkey.pub); if (need_additional_txkeys) @@ -401,6 +401,7 @@ namespace cryptonote LOG_PRINT_L2("additional tx pubkeys: "); for (size_t i = 0; i < additional_tx_public_keys.size(); ++i) LOG_PRINT_L2(additional_tx_public_keys[i]); + add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); } //check money diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 9071c330c..e6f217463 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -51,6 +51,8 @@ DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated +using namespace crypto; + namespace cryptonote { namespace @@ -187,6 +189,7 @@ namespace cryptonote { if(have_tx_keyimges_as_spent(tx)) { + mark_double_spend(tx); LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images"); tvc.m_verifivation_failed = true; tvc.m_double_spend = true; @@ -228,6 +231,7 @@ namespace cryptonote meta.last_relayed_time = time(NULL); meta.relayed = relayed; meta.do_not_relay = do_not_relay; + meta.double_spend_seen = have_tx_keyimges_as_spent(tx); memset(meta.padding, 0, sizeof(meta.padding)); try { @@ -266,6 +270,7 @@ namespace cryptonote meta.last_relayed_time = time(NULL); meta.relayed = relayed; meta.do_not_relay = do_not_relay; + meta.double_spend_seen = false; memset(meta.padding, 0, sizeof(meta.padding)); try @@ -354,7 +359,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay) + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -377,6 +382,7 @@ namespace cryptonote fee = meta.fee; relayed = meta.relayed; do_not_relay = meta.do_not_relay; + double_spend_seen = meta.double_spend_seen; // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); @@ -432,7 +438,7 @@ namespace cryptonote remove.insert(txid); } return true; - }); + }, false); if (!remove.empty()) { @@ -494,7 +500,7 @@ namespace cryptonote } } return true; - }); + }, false); return true; } //--------------------------------------------------------------------------------- @@ -521,14 +527,14 @@ namespace cryptonote } } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_transactions_count() const + size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_txpool_tx_count(); + return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::list<transaction>& txs) const + void tx_memory_pool::get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -542,20 +548,20 @@ namespace cryptonote } txs.push_back(tx); return true; - }, true); + }, true, include_unrelayed_txes); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const + void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ txs.push_back(txid); return true; - }); + }, false, include_unrelayed_txes); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const + void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -563,16 +569,16 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); return true; - }); + }, false, include_unrelayed_txes); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats) const + void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); std::map<uint64_t, txpool_histo> agebytes; - stats.txs_total = m_blockchain.get_txpool_tx_count(); + stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); std::vector<uint32_t> sizes; sizes.reserve(stats.txs_total); m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ @@ -594,8 +600,10 @@ namespace cryptonote uint64_t age = now - meta.receive_time + (now == meta.receive_time); agebytes[age].txs++; agebytes[age].bytes += meta.blob_size; + if (meta.double_spend_seen) + ++stats.num_double_spends; return true; - }); + }, false, include_unrelayed_txes); stats.bytes_med = epee::misc_utils::median(sizes); if (stats.txs_total > 1) { @@ -642,13 +650,14 @@ namespace cryptonote } //------------------------------------------------------------------ //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ + m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); + txi.tx_blob = *bd; transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) { @@ -664,14 +673,18 @@ namespace cryptonote txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(meta.max_used_block_id); txi.last_failed_height = meta.last_failed_height; txi.last_failed_id_hash = epee::string_tools::pod_to_hex(meta.last_failed_id); - txi.receive_time = meta.receive_time; + // In restricted mode we do not include this data: + txi.receive_time = include_sensitive_data ? meta.receive_time : 0; txi.relayed = meta.relayed; - txi.last_relayed_time = meta.last_relayed_time; + // In restricted mode we do not include this data: + txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0; txi.do_not_relay = meta.do_not_relay; + txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true); + }, true, include_sensitive_data); + txpool_tx_meta_t meta; for (const key_images_container::value_type& kee : m_spent_key_images) { const crypto::key_image& k_image = kee.first; const std::unordered_set<crypto::hash>& kei_image_set = kee.second; @@ -679,9 +692,26 @@ namespace cryptonote ki.id_hash = epee::string_tools::pod_to_hex(k_image); for (const crypto::hash& tx_id_hash : kei_image_set) { + if (!include_sensitive_data) + { + try + { + meta = m_blockchain.get_txpool_tx_meta(tx_id_hash); + if (!meta.relayed) + // Do not include that transaction if in restricted mode and it's not relayed + continue; + } + catch (const std::exception &e) + { + MERROR("Failed to get tx meta from txpool: " << e.what()); + return false; + } + } ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); } - key_image_infos.push_back(ki); + // Only return key images for which we have at least one tx that we can show for them + if (!ki.txs_hashes.empty()) + key_image_infos.push_back(ki); } return true; } @@ -712,9 +742,10 @@ namespace cryptonote txi.relayed = meta.relayed; txi.last_relayed_time = meta.last_relayed_time; txi.do_not_relay = meta.do_not_relay; + txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true); + }, true, false); for (const key_images_container::value_type& kee : m_spent_key_images) { std::vector<crypto::hash> tx_hashes; @@ -843,7 +874,10 @@ namespace cryptonote } //if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure if(m_blockchain.have_tx_keyimges_as_spent(tx)) + { + txd.double_spend_seen = true; return false; + } //transaction is ok. return true; @@ -871,6 +905,39 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + void tx_memory_pool::mark_double_spend(const transaction &tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + LockedTXN lock(m_blockchain); + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void()); + const key_images_container::const_iterator it = m_spent_key_images.find(itk.k_image); + if (it != m_spent_key_images.end()) + { + for (const crypto::hash &txid: it->second) + { + txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(txid); + if (!meta.double_spend_seen) + { + MDEBUG("Marking " << txid << " as double spending " << itk.k_image); + meta.double_spend_seen = true; + try + { + m_blockchain.update_txpool_tx(txid, meta); + } + catch (const std::exception &e) + { + MERROR("Failed to update tx meta: " << e.what()); + // continue, not fatal + } + } + } + } + } + } + //--------------------------------------------------------------------------------- std::string tx_memory_pool::print_pool(bool short_format) const { std::stringstream ss; @@ -890,6 +957,7 @@ namespace cryptonote ss << "blob_size: " << meta.blob_size << std::endl << "fee: " << print_money(meta.fee) << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl + << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl << "max_used_block_height: " << meta.max_used_block_height << std::endl << "max_used_block_id: " << meta.max_used_block_id << std::endl << "last_failed_height: " << meta.last_failed_height << std::endl @@ -1044,7 +1112,7 @@ namespace cryptonote remove.insert(txid); } return true; - }); + }, false); size_t n_removed = 0; if (!remove.empty()) diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3e4ccb338..d657c6554 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -137,10 +137,11 @@ namespace cryptonote * @param fee the transaction fee * @param relayed return-by-reference was transaction relayed to us by the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network? + * @param double_spend_seen return-by-reference was a double spend seen for that transaction? * * @return true unless the transaction cannot be found in the pool */ - bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay); + bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); /** * @brief checks if the pool has a transaction with the given hash @@ -233,29 +234,37 @@ namespace cryptonote * @brief get a list of all transactions in the pool * * @param txs return-by-reference the list of transactions + * @param include_unrelayed_txes include unrelayed txes in the result + * */ - void get_transactions(std::list<transaction>& txs) const; + void get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const; /** * @brief get a list of all transaction hashes in the pool * * @param txs return-by-reference the list of transactions + * @param include_unrelayed_txes include unrelayed txes in the result + * */ - void get_transaction_hashes(std::vector<crypto::hash>& txs) const; + void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; /** * @brief get (size, fee, receive time) for all transaction in the pool * * @param txs return-by-reference that data + * @param include_unrelayed_txes include unrelayed txes in the result + * */ - void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const; + void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const; /** * @brief get a summary statistics of all transaction hashes in the pool * * @param stats return-by-reference the pool statistics + * @param include_unrelayed_txes include unrelayed txes in the result + * */ - void get_transaction_stats(struct txpool_stats& stats) const; + void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; /** * @brief get information about all transactions and key images in the pool @@ -264,10 +273,11 @@ namespace cryptonote * * @param tx_infos return-by-reference the transactions' information * @param key_image_infos return-by-reference the spent key images' information + * @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node privacy * * @return true */ - bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const; + bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = true) const; /** * @brief get information about all transactions and key images in the pool @@ -308,6 +318,7 @@ namespace cryptonote * nonzero fee * hasn't been relayed too recently * isn't old enough that relaying it is considered harmful + * Note a transaction can be "relayable" even if do_not_relay is true * * @param txs return-by-reference the transactions and their hashes * @@ -327,7 +338,7 @@ namespace cryptonote * * @return the number of transactions in the pool */ - size_t get_transactions_count() const; + size_t get_transactions_count(bool include_unrelayed_txes = true) const; /** * @brief get a string containing human-readable pool information @@ -391,6 +402,8 @@ namespace cryptonote time_t last_relayed_time; //!< the last time the transaction was relayed to the network bool relayed; //!< whether or not the transaction has been relayed to the network bool do_not_relay; //!< to avoid relay this transaction to the network + + bool double_spend_seen; //!< true iff another tx was seen double spending this one }; private: @@ -478,6 +491,11 @@ namespace cryptonote */ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const; + /** + * @brief mark all transactions double spending the one passed + */ + void mark_double_spend(const transaction &tx); + //TODO: confirm the below comments and investigate whether or not this // is the desired behavior //! map key images to transactions which spent them diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index d16e5d7e5..73433a8d8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -52,6 +52,7 @@ #define BLOCK_QUEUE_SIZE_THRESHOLD (100*1024*1024) // MB #define REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD (5 * 1000000) // microseconds #define IDLE_PEER_KICK_TIME (600 * 1000000) // microseconds +#define PASSIVE_PEER_KICK_TIME (60 * 1000000) // microseconds namespace cryptonote { @@ -296,9 +297,9 @@ namespace cryptonote m_core.set_target_blockchain_height((hshd.current_height)); int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height()); uint64_t abs_diff = std::abs(diff); - uint64_t max_block_height = max(hshd.current_height,m_core.get_current_blockchain_height()); + uint64_t max_block_height = std::max(hshd.current_height,m_core.get_current_blockchain_height()); uint64_t last_block_v1 = m_core.get_testnet() ? 624633 : 1009826; - uint64_t diff_v2 = max_block_height > last_block_v1 ? min(abs_diff, max_block_height - last_block_v1) : 0; + uint64_t diff_v2 = max_block_height > last_block_v1 ? std::min(abs_diff, max_block_height - last_block_v1) : 0; MCLOG(is_inital ? el::Level::Info : el::Level::Debug, "global", context << "Sync data returned a new top block candidate: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height << " [Your node is " << abs_diff << " blocks (" << ((abs_diff - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) " << (0 <= diff ? std::string("behind") : std::string("ahead")) @@ -1184,13 +1185,15 @@ skip: std::vector<boost::uuids::uuid> kick_connections; m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool { - if (context.m_state == cryptonote_connection_context::state_synchronizing) + if (context.m_state == cryptonote_connection_context::state_synchronizing || context.m_state == cryptonote_connection_context::state_before_handshake) { + const bool passive = context.m_state == cryptonote_connection_context::state_before_handshake; const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); const boost::posix_time::time_duration dt = now - context.m_last_request_time; - if (dt.total_microseconds() > IDLE_PEER_KICK_TIME) + const int64_t threshold = passive ? PASSIVE_PEER_KICK_TIME : IDLE_PEER_KICK_TIME; + if (dt.total_microseconds() > threshold) { - MINFO(context << " kicking idle peer"); + MINFO(context << " kicking " << (passive ? "passive" : "idle") << " peer"); kick_connections.push_back(context.m_connection_id); } } diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index d0fc1d846..2f9c2b2f9 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -94,6 +94,8 @@ target_link_libraries(daemon daemonizer serialization daemon_rpc_server + epee + ${EPEE_READLINE} version ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} @@ -102,6 +104,7 @@ target_link_libraries(daemon ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB} + ${Readline_LIBRARY} ${EXTRA_LIBRARIES}) set_property(TARGET daemon PROPERTY diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index faa620c54..cf7d5f8ab 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -88,7 +88,7 @@ t_daemon::t_daemon( ) : mp_internals{new t_internals{vm}} { - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); if (testnet) { zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_testnet_rpc_bind_port); diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 5d548f410..ae83943b6 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -82,7 +82,6 @@ int main(int argc, char const * argv[]) command_line::add_arg(visible_options, daemon_args::arg_os_version); bf::path default_conf = default_data_dir / std::string(CRYPTONOTE_NAME ".conf"); command_line::add_arg(visible_options, daemon_args::arg_config_file, default_conf.string()); - command_line::add_arg(visible_options, command_line::arg_test_dbg_lock_sleep); // Settings bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log"); @@ -144,8 +143,6 @@ int main(int argc, char const * argv[]) return 0; } - epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, command_line::arg_test_dbg_lock_sleep); - std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); // verify that blockchaindb type is valid @@ -156,9 +153,9 @@ int main(int argc, char const * argv[]) return 0; } - bool testnet_mode = command_line::get_arg(vm, command_line::arg_testnet_on); + bool testnet_mode = command_line::get_arg(vm, cryptonote::arg_testnet_on); - auto data_dir_arg = testnet_mode ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto data_dir_arg = testnet_mode ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir; // data_dir // default: e.g. ~/.bitmonero/ or ~/.bitmonero/testnet @@ -250,7 +247,12 @@ int main(int argc, char const * argv[]) if (command_line::has_arg(vm, arg.rpc_login)) { login = tools::login::parse( - command_line::get_arg(vm, arg.rpc_login), false, "Daemon client password" + command_line::get_arg(vm, arg.rpc_login), false, [](bool verify) { +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + return tools::password_container::prompt(verify, "Daemon client password"); + } ); if (!login) { diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 2c11dcb31..230b9f090 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -814,7 +814,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { } else { - if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) + if (!m_rpc_server->on_get_transaction_pool(req, res, false) || res.status != CORE_RPC_STATUS_OK) { tools::fail_msg_writer() << make_error(fail_message, res.status); return true; @@ -840,6 +840,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl + << "double_spend_seen: " << (tx_info.double_spend_seen ? 'T' : 'F') << std::endl << "max_used_block_height: " << tx_info.max_used_block_height << std::endl << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl << "last_failed_height: " << tx_info.last_failed_height << std::endl @@ -898,7 +899,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { } else { - if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) + if (!m_rpc_server->on_get_transaction_pool(req, res, false) || res.status != CORE_RPC_STATUS_OK) { tools::fail_msg_writer() << make_error(fail_message, res.status); return true; @@ -922,6 +923,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl + << "double_spend_seen: " << (tx_info.double_spend_seen ? 'T' : 'F') << std::endl << "max_used_block_height: " << tx_info.max_used_block_height << std::endl << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl << "last_failed_height: " << tx_info.last_failed_height << std::endl @@ -954,7 +956,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { else { memset(&res.pool_stats, 0, sizeof(res.pool_stats)); - if (!m_rpc_server->on_get_transaction_pool_stats(req, res) || res.status != CORE_RPC_STATUS_OK) + if (!m_rpc_server->on_get_transaction_pool_stats(req, res, false) || res.status != CORE_RPC_STATUS_OK) { tools::fail_msg_writer() << make_error(fail_message, res.status); return true; @@ -984,7 +986,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { tools::msg_writer() << n_transactions << " tx(es), " << res.pool_stats.bytes_total << " bytes total (min " << res.pool_stats.bytes_min << ", max " << res.pool_stats.bytes_max << ", avg " << avg_bytes << ", median " << res.pool_stats.bytes_med << ")" << std::endl << "fees " << cryptonote::print_money(res.pool_stats.fee_total) << " (avg " << cryptonote::print_money(n_transactions ? res.pool_stats.fee_total / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(res.pool_stats.bytes_total ? res.pool_stats.fee_total / res.pool_stats.bytes_total : 0) << " per byte)" << std::endl - << res.pool_stats.num_not_relayed << " not relayed, " << res.pool_stats.num_failing << " failing, " << res.pool_stats.num_10m << " older than 10 minutes (oldest " << (res.pool_stats.oldest == 0 ? "-" : get_human_time_ago(res.pool_stats.oldest, now)) << "), " << backlog_message; + << res.pool_stats.num_double_spends << " double spends, " << res.pool_stats.num_not_relayed << " not relayed, " << res.pool_stats.num_failing << " failing, " << res.pool_stats.num_10m << " older than 10 minutes (oldest " << (res.pool_stats.oldest == 0 ? "-" : get_human_time_ago(res.pool_stats.oldest, now)) << "), " << backlog_message; if (n_transactions > 1 && res.pool_stats.histo.size()) { @@ -1009,7 +1011,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { tools::msg_writer() << " Age Txes Bytes"; for (i=0; i<n; i++) { - tools::msg_writer() << get_time_hms(times[i]) << setw(8) << res.pool_stats.histo[i].txs << setw(12) << res.pool_stats.histo[i].bytes; + tools::msg_writer() << get_time_hms(times[i]) << std::setw(8) << res.pool_stats.histo[i].txs << std::setw(12) << res.pool_stats.histo[i].bytes; } } tools::msg_writer(); diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp index b95f36b99..8edd75b3e 100644 --- a/src/p2p/connection_basic.cpp +++ b/src/p2p/connection_basic.cpp @@ -158,7 +158,7 @@ connection_basic::connection_basic(boost::asio::io_service& io_service, std::ato ++ref_sock_count; // increase the global counter mI->m_peer_number = sock_number.fetch_add(1); // use, and increase the generated number - string remote_addr_str = "?"; + std::string remote_addr_str = "?"; try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; _note("Spawned connection p2p#"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_ref_sock_count); @@ -166,7 +166,7 @@ connection_basic::connection_basic(boost::asio::io_service& io_service, std::ato } connection_basic::~connection_basic() noexcept(false) { - string remote_addr_str = "?"; + std::string remote_addr_str = "?"; m_ref_sock_count--; try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; _note("Destructing connection p2p#"<<mI->m_peer_number << " to " << remote_addr_str); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 6162d649b..f64b29c1f 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -48,6 +48,7 @@ #include "net/local_ip.h" #include "crypto/crypto.h" #include "storages/levin_abstract_invoke2.h" +#include "cryptonote_core/cryptonote_core.h" // We have to look for miniupnpc headers in different places, dependent on if its compiled or external #ifdef UPNP_STATIC @@ -434,7 +435,7 @@ namespace nodetool bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm) { std::set<std::string> full_addrs; - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); if (m_testnet) { @@ -535,7 +536,7 @@ namespace nodetool bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - auto config_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto config_arg = m_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir; m_config_folder = command_line::get_arg(vm, config_arg); if ((!m_testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) @@ -560,7 +561,7 @@ namespace nodetool //configure self m_net_server.set_threads_prefix("P2P"); - m_net_server.get_config_object().m_pcommands_handler = this; + m_net_server.get_config_object().set_handler(this); m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; m_net_server.set_connection_filter(this); diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index cb19bbbd6..412450c18 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -35,8 +35,6 @@ #define RCTOPS_H #include <cstddef> -#include <mutex> -#include <vector> #include <tuple> #include "crypto/generic-ops.h" @@ -57,9 +55,6 @@ extern "C" { #define DP(x) #endif -using namespace std; -using namespace crypto; - namespace rct { //Various key initialization functions @@ -99,13 +94,13 @@ namespace rct { key pkGen(); //generates a random secret and corresponding public key void skpkGen(key &sk, key &pk); - tuple<key, key> skpkGen(); + std::tuple<key, key> skpkGen(); //generates a <secret , public> / Pedersen commitment to the amount - tuple<ctkey, ctkey> ctskpkGen(xmr_amount amount); + std::tuple<ctkey, ctkey> ctskpkGen(xmr_amount amount); //generates C =aG + bH from b, a is random void genC(key & C, const key & a, xmr_amount amount); //this one is mainly for testing, can take arbitrary amounts.. - tuple<ctkey, ctkey> ctskpkGen(const key &bH); + std::tuple<ctkey, ctkey> ctskpkGen(const key &bH); // make a pedersen commitment with given key key commit(xmr_amount amount, const key &mask); // make a pedersen commitment with zero key diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index ca40ddd85..d158f06f0 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -36,7 +36,6 @@ #define RCTSIGS_H #include <cstddef> -#include <mutex> #include <vector> #include <tuple> @@ -61,9 +60,6 @@ extern "C" { -using namespace std; -using namespace crypto; - namespace rct { boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices); @@ -110,7 +106,7 @@ namespace rct { //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk // the return value are the key matrix, and the index where inPk was put (random). void getKeyFromBlockchain(ctkey & a, size_t reference_index); - tuple<ctkeyM, xmr_amount> populateFromBlockchain(ctkeyV inPk, int mixin); + std::tuple<ctkeyM, xmr_amount> populateFromBlockchain(ctkeyV inPk, int mixin); //RingCT protocol //genRct: @@ -122,10 +118,10 @@ namespace rct { //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); - rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSimple(const rctSig & rv, bool semantics); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index cc0000ad6..8147cb602 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -33,9 +33,7 @@ #define RCT_TYPES_H #include <cstddef> -#include <mutex> #include <vector> -#include <tuple> #include <iostream> #include <cinttypes> @@ -67,9 +65,6 @@ extern "C" { //for printing large ints -using namespace std; -using namespace crypto; - //Namespace specifically for ring ct code namespace rct { //basic ops containers @@ -89,8 +84,8 @@ namespace rct { bool operator==(const key &k) const { return !memcmp(bytes, k.bytes, sizeof(bytes)); } unsigned char bytes[32]; }; - typedef vector<key> keyV; //vector of keys - typedef vector<keyV> keyM; //matrix of keys (indexed by column first) + typedef std::vector<key> keyV; //vector of keys + typedef std::vector<keyV> keyM; //matrix of keys (indexed by column first) //containers For CT operations //if it's representing a private ctkey then "dest" contains the secret key of the address @@ -101,8 +96,8 @@ namespace rct { key dest; key mask; //C here if public }; - typedef vector<ctkey> ctkeyV; - typedef vector<ctkeyV> ctkeyM; + typedef std::vector<ctkey> ctkeyV; + typedef std::vector<ctkeyV> ctkeyM; //data for passing the amount to the receiver secretly // If the pedersen commitment to an amount is C = aG + bH, @@ -184,7 +179,7 @@ namespace rct { ctkeyM mixRing; //the set of all pubkeys / copy //pairs that you mix with keyV pseudoOuts; //C - for simple rct - vector<ecdhTuple> ecdhInfo; + std::vector<ecdhTuple> ecdhInfo; ctkeyV outPk; xmr_amount txnFee; // contains b @@ -245,8 +240,8 @@ namespace rct { } }; struct rctSigPrunable { - vector<rangeSig> rangeSigs; - vector<mgSig> MGs; // simple rct has N, full has 1 + std::vector<rangeSig> rangeSigs; + std::vector<mgSig> MGs; // simple rct has N, full has 1 template<bool W, template <bool> class Archive> bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b3ce30d0c..6e7fb7e8e 100755..100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -36,6 +36,7 @@ using namespace epee; #include "common/updates.h" #include "common/download.h" #include "common/util.h" +#include "common/perf_timer.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -84,7 +85,7 @@ namespace cryptonote const boost::program_options::variables_map& vm ) { - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); m_net_server.set_threads_prefix("RPC"); auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port; @@ -105,30 +106,20 @@ namespace cryptonote ); } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::check_core_busy() - { - if(m_p2p.get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return false; - } - return true; - } -#define CHECK_CORE_BUSY() do { if(!check_core_busy()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0) - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_ready() { if(!m_p2p.get_payload_object().is_synchronized()) { return false; } - return check_core_busy(); + return true; } #define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0) //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_height); res.height = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -136,7 +127,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_info); crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height @@ -150,6 +141,7 @@ namespace cryptonote uint64_t total_conn = m_p2p.get_connections_count(); res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.rpc_connections_count = get_connections_count(); res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); res.testnet = m_testnet; @@ -179,7 +171,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_blocks); std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) @@ -238,7 +230,7 @@ namespace cryptonote } bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_alt_blocks_hashes); std::list<block> blks; if(!m_core.get_alternative_blocks(blks)) @@ -261,7 +253,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_blocks_by_height); res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -291,7 +283,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_hashes); NOTIFY_RESPONSE_CHAIN_ENTRY::request resp; resp.start_height = req.start_height; @@ -310,7 +302,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_random_outs); res.status = "Failed"; if (m_restricted) @@ -349,7 +341,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_outs_bin); res.status = "Failed"; if (m_restricted) @@ -372,7 +364,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_outs); res.status = "Failed"; if (m_restricted) @@ -410,7 +402,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_random_rct_outs); res.status = "Failed"; if(!m_core.get_random_rct_outs(req, res)) { @@ -434,7 +426,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_indexes); bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); if(!r) { @@ -448,7 +440,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_get_transactions); std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -478,15 +470,17 @@ namespace cryptonote // try the pool for any missing txes size_t found_in_pool = 0; std::unordered_set<crypto::hash> pool_tx_hashes; + std::unordered_map<crypto::hash, bool> double_spend_seen; if (!missed_txs.empty()) { - std::list<transaction> pool_txs; - bool r = m_core.get_pool_transactions(pool_txs); + std::vector<tx_info> pool_tx_info; + std::vector<spent_key_image_info> pool_key_image_info; + bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info); if(r) { // sort to match original request std::list<transaction> sorted_txs; - std::list<cryptonote::transaction>::const_iterator i; + std::vector<tx_info>::const_iterator i; for (const crypto::hash &h: vh) { if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end()) @@ -500,11 +494,26 @@ namespace cryptonote 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()) + else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end()) { - sorted_txs.push_back(*i); + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_from_blob(i->tx_blob, tx)) + { + res.status = "Failed to parse and validate tx from blob"; + return true; + } + sorted_txs.push_back(tx); missed_txs.remove(h); pool_tx_hashes.insert(h); + const std::string hash_string = epee::string_tools::pod_to_hex(h); + for (const auto &ti: pool_tx_info) + { + if (ti.id_hash == hash_string) + { + double_spend_seen.insert(std::make_pair(h, ti.double_spend_seen)); + break; + } + } ++found_in_pool; } } @@ -530,11 +539,21 @@ namespace cryptonote if (e.in_pool) { e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max(); + if (double_spend_seen.find(tx_hash) != double_spend_seen.end()) + { + e.double_spend_seen = double_spend_seen[tx_hash]; + } + else + { + MERROR("Failed to determine double spend status for " << tx_hash); + e.double_spend_seen = false; + } } 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); + e.double_spend_seen = false; } // fill up old style responses too, in case an old wallet asks @@ -564,9 +583,9 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res) + bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_is_key_image_spent); std::vector<crypto::key_image> key_images; for(const auto& ki_hex_str: req.key_images) { @@ -596,7 +615,7 @@ namespace cryptonote // check the pool too std::vector<cryptonote::tx_info> txs; std::vector<cryptonote::spent_key_image_info> ki; - r = m_core.get_pool_transactions_and_spent_keys_info(txs, ki); + r = m_core.get_pool_transactions_and_spent_keys_info(txs, ki, !request_has_rpc_origin || !m_restricted); if(!r) { res.status = "Failed"; @@ -629,6 +648,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { + PERF_TIMER(on_send_raw_tx); CHECK_CORE_READY(); std::string tx_blob; @@ -692,6 +712,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { + PERF_TIMER(on_start_mining); CHECK_CORE_READY(); cryptonote::address_parse_info info; if(!get_account_address_from_str(info, m_testnet, req.miner_address)) @@ -739,6 +760,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { + PERF_TIMER(on_stop_mining); if(!m_core.get_miner().stop()) { res.status = "Failed, mining not stopped"; @@ -751,7 +773,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_mining_status); const miner& lMiner = m_core.get_miner(); res.active = lMiner.is_mining(); @@ -770,7 +792,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_save_bc); if( !m_core.get_blockchain_storage().store_blockchain() ) { res.status = "Error while storing blockhain"; @@ -782,6 +804,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res) { + PERF_TIMER(on_get_peer_list); std::list<nodetool::peerlist_entry> white_list; std::list<nodetool::peerlist_entry> gray_list; m_p2p.get_peerlist_manager().get_peerlist_full(gray_list, white_list); @@ -811,6 +834,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res) { + PERF_TIMER(on_set_log_hash_rate); if(m_core.get_miner().is_mining()) { m_core.get_miner().do_print_hashrate(req.visible); @@ -825,6 +849,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res) { + PERF_TIMER(on_set_log_level); if (req.level < 0 || req.level > 4) { res.status = "Error: log level not valid"; @@ -837,38 +862,40 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res) { + PERF_TIMER(on_set_log_categories); mlog_set_log(req.categories.c_str()); res.categories = mlog_get_categories(); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res) + bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin) { - CHECK_CORE_BUSY(); - m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images); + PERF_TIMER(on_get_transaction_pool); + m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res) + bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin) { - CHECK_CORE_BUSY(); - m_core.get_pool_transaction_hashes(res.tx_hashes); + PERF_TIMER(on_get_transaction_pool_hashes); + m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res) + bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin) { - CHECK_CORE_BUSY(); - m_core.get_pool_transaction_stats(res.pool_stats); + PERF_TIMER(on_get_transaction_pool_stats); + m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { + PERF_TIMER(on_stop_daemon); // FIXME: replace back to original m_p2p.send_stop_signal() after // investigating why that isn't working quite right. m_p2p.send_stop_signal(); @@ -878,7 +905,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { - CHECK_CORE_BUSY(); + PERF_TIMER(on_getblockcount); res.count = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -886,12 +913,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy"; - return false; - } + PERF_TIMER(on_getblockhash); if(req.size() != 1) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; @@ -927,6 +949,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_getblocktemplate); if(!check_core_ready()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; @@ -968,7 +991,7 @@ namespace cryptonote } blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); - if(tx_pub_key == null_pkey) + if(tx_pub_key == crypto::null_pkey) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; @@ -1001,6 +1024,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_submitblock); CHECK_CORE_READY(); if(req.size()!=1) { @@ -1057,6 +1081,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response) { + PERF_TIMER(fill_block_header_response); response.major_version = blk.major_version; response.minor_version = blk.minor_version; response.timestamp = blk.timestamp; @@ -1075,12 +1100,8 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_last_block_header); + CHECK_CORE_READY(); uint64_t last_block_height; crypto::hash last_block_hash; m_core.get_blockchain_top(last_block_height, last_block_hash); @@ -1104,12 +1125,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp){ - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_block_header_by_hash); crypto::hash block_hash; bool hash_parsed = parse_hash256(req.hash, block_hash); if(!hash_parsed) @@ -1146,12 +1162,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_headers_range(const COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request& req, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response& res, epee::json_rpc::error& error_resp){ - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_block_headers_range); const uint64_t bc_height = m_core.get_current_blockchain_height(); if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height) { @@ -1197,12 +1208,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp){ - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_block_header_by_height); if(m_core.get_current_blockchain_height() <= req.height) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; @@ -1230,12 +1236,7 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp){ - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_block); crypto::hash block_hash; if (!req.hash.empty()) { @@ -1280,6 +1281,7 @@ namespace cryptonote error_resp.message = "Internal error: can't produce valid response."; return false; } + res.miner_tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(blk.miner_tx)); for (size_t n = 0; n < blk.tx_hashes.size(); ++n) { res.tx_hashes.push_back(epee::string_tools::pod_to_hex(blk.tx_hashes[n])); @@ -1292,12 +1294,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_connections); res.connections = m_p2p.get_payload_object().get_connections(); @@ -1308,12 +1305,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_info_json); crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); @@ -1328,6 +1320,7 @@ namespace cryptonote uint64_t total_conn = m_p2p.get_connections_count(); res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.rpc_connections_count = get_connections_count(); res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); res.testnet = m_testnet; @@ -1340,12 +1333,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_hard_fork_info); const Blockchain &blockchain = m_core.get_blockchain_storage(); uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version(); @@ -1358,12 +1346,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_bans); auto now = time(nullptr); std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts(); @@ -1387,12 +1370,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_set_bans); for (auto i = req.bans.begin(); i != req.bans.end(); ++i) { @@ -1422,12 +1400,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_flush_txpool); bool failed = false; std::list<crypto::hash> txids; @@ -1476,12 +1449,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_output_histogram); std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram; try @@ -1508,6 +1476,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_get_version); res.version = CORE_RPC_VERSION; res.status = CORE_RPC_STATUS_OK; return true; @@ -1515,6 +1484,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_get_coinbase_tx_sum); std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count); res.emission_amount = amounts.first; res.fee_amount = amounts.second; @@ -1524,6 +1494,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_get_per_kb_fee_estimate); res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); res.status = CORE_RPC_STATUS_OK; return true; @@ -1531,6 +1502,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp) { + PERF_TIMER(on_get_alternate_chains); try { std::list<std::pair<Blockchain::block_extended_info, uint64_t>> chains = m_core.get_blockchain_storage().get_alternative_chains(); @@ -1549,6 +1521,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res) { + PERF_TIMER(on_get_limit); res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit(); res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit(); res.status = CORE_RPC_STATUS_OK; @@ -1557,6 +1530,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res) { + PERF_TIMER(on_set_limit); // -1 = reset to default // 0 = do not modify @@ -1596,6 +1570,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res) { + PERF_TIMER(on_out_peers); size_t n_connections = m_p2p.get_outgoing_connections_count(); size_t n_delete = (n_connections > req.out_peers) ? n_connections - req.out_peers : 0; m_p2p.m_config.m_net_config.connections_count = req.out_peers; @@ -1607,6 +1582,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res) { + PERF_TIMER(on_start_save_graph); m_p2p.set_save_graph(true); res.status = CORE_RPC_STATUS_OK; return true; @@ -1614,6 +1590,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res) { + PERF_TIMER(on_stop_save_graph); m_p2p.set_save_graph(false); res.status = CORE_RPC_STATUS_OK; return true; @@ -1621,6 +1598,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res) { + PERF_TIMER(on_update); static const char software[] = "monero"; #ifdef BUILD_TAG static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); @@ -1715,12 +1693,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_relay_tx); bool failed = false; for (const auto &str: req.txids) @@ -1761,12 +1734,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_sync_info); crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); @@ -1792,12 +1760,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp) { - if(!check_core_busy()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } + PERF_TIMER(on_get_txpool_backlog); if (!m_core.get_txpool_backlog(res.backlog)) { @@ -1825,7 +1788,7 @@ namespace cryptonote const command_line::arg_descriptor<bool> core_rpc_server::arg_restricted_rpc = { "restricted-rpc" - , "Restrict RPC to view only commands" + , "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls" , false }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 73a308a72..7f252258c 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -122,7 +122,7 @@ namespace cryptonote MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted) MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) - MAP_JON_RPC_WE("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM) + MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted) MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) @@ -137,7 +137,7 @@ namespace cryptonote bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res); bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res); bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); - bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res); + bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin = true); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res); bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); @@ -153,9 +153,9 @@ namespace cryptonote bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res); bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res); bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res); - bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res); - bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res); - bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res); + bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin = true); + bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin = true); + bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin = true); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res); bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index ee2a79eb4..15b4b503a 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 15 +#define CORE_RPC_VERSION_MINOR 16 #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) @@ -566,6 +566,7 @@ namespace cryptonote std::string as_hex; std::string as_json; bool in_pool; + bool double_spend_seen; uint64_t block_height; uint64_t block_timestamp; std::vector<uint64_t> output_indices; @@ -575,6 +576,7 @@ namespace cryptonote KV_SERIALIZE(as_hex) KV_SERIALIZE(as_json) KV_SERIALIZE(in_pool) + KV_SERIALIZE(double_spend_seen) KV_SERIALIZE(block_height) KV_SERIALIZE(block_timestamp) KV_SERIALIZE(output_indices) @@ -917,6 +919,7 @@ namespace cryptonote uint64_t alt_blocks_count; uint64_t outgoing_connections_count; uint64_t incoming_connections_count; + uint64_t rpc_connections_count; uint64_t white_peerlist_size; uint64_t grey_peerlist_size; bool testnet; @@ -936,6 +939,7 @@ namespace cryptonote KV_SERIALIZE(alt_blocks_count) KV_SERIALIZE(outgoing_connections_count) KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(rpc_connections_count) KV_SERIALIZE(white_peerlist_size) KV_SERIALIZE(grey_peerlist_size) KV_SERIALIZE(testnet) @@ -1217,12 +1221,14 @@ namespace cryptonote { std::string status; block_header_response block_header; + std::string miner_tx_hash; std::vector<std::string> tx_hashes; std::string blob; std::string json; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) + KV_SERIALIZE(miner_tx_hash) KV_SERIALIZE(tx_hashes) KV_SERIALIZE(status) KV_SERIALIZE(blob) @@ -1357,6 +1363,8 @@ namespace cryptonote bool relayed; uint64_t last_relayed_time; bool do_not_relay; + bool double_spend_seen; + std::string tx_blob; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(id_hash) @@ -1372,6 +1380,8 @@ namespace cryptonote KV_SERIALIZE(relayed) KV_SERIALIZE(last_relayed_time) KV_SERIALIZE(do_not_relay) + KV_SERIALIZE(double_spend_seen) + KV_SERIALIZE(tx_blob) END_KV_SERIALIZE_MAP() }; @@ -1480,6 +1490,7 @@ namespace cryptonote uint32_t num_not_relayed; uint64_t histo_98pc; std::vector<txpool_histo> histo; + uint32_t num_double_spends; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(bytes_total) @@ -1494,6 +1505,7 @@ namespace cryptonote KV_SERIALIZE(num_not_relayed) KV_SERIALIZE(histo_98pc) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo) + KV_SERIALIZE(num_double_spends) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 00f1e0caa..581048eaf 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -95,6 +95,7 @@ namespace rpc uint64_t last_relayed_time; bool relayed; bool do_not_relay; + bool double_spend_seen; }; typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes; diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 93309bf3c..e03c5472d 100755..100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -83,7 +83,9 @@ namespace cryptonote if (command_line::has_arg(vm, arg.rpc_login)) { - config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, "RPC server password"); + config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, [](bool verify) { + return tools::password_container::prompt(verify, "RPC server password"); + }); if (!config.login) return boost::none; diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index 72b5aa706..72b5aa706 100755..100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index a40821d19..6e6e51528 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -755,6 +755,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time); INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed); INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay); + INSERT_INTO_JSON_OBJECT(val, doc, double_spend_seen, tx.double_spend_seen); } @@ -777,6 +778,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx) GET_FROM_JSON_OBJECT(val, tx.last_relayed_time, last_relayed_time); GET_FROM_JSON_OBJECT(val, tx.relayed, relayed); GET_FROM_JSON_OBJECT(val, tx.do_not_relay, do_not_relay); + GET_FROM_JSON_OBJECT(val, tx.double_spend_seen, double_spend_seen); } void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val) diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index b56085b8f..3ff4466fc 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -48,12 +48,14 @@ target_link_libraries(simplewallet cncrypto common mnemonics - p2p + epee + ${EPEE_READLINE} version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} + ${Readline_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) set_property(TARGET simplewallet diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index c936f481e..9b5752b19 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -49,7 +49,6 @@ #include "common/dns_utils.h" #include "common/base58.h" #include "common/scoped_message_writer.h" -#include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -111,6 +110,7 @@ namespace const auto arg_wallet_file = wallet_args::arg_wallet_file(); const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; + const command_line::arg_descriptor<std::string> arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""}; const auto arg_generate_from_json = wallet_args::arg_generate_from_json(); @@ -125,6 +125,37 @@ namespace const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; + std::string input_line(const std::string& prompt) + { +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::cout << prompt; + + std::string buf; + std::getline(std::cin, buf); + + return epee::string_tools::trim(buf); + } + + boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify) + { +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + auto pwd_container = tools::password_container::prompt(verify, prompt); + if (!pwd_container) + { + tools::fail_msg_writer() << tr("failed to read wallet password"); + } + return pwd_container; + } + + boost::optional<tools::password_container> default_password_prompter(bool verify) + { + return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify); + } + inline std::string interpret_rpc_response(bool ok, const std::string& status) { std::string err; @@ -161,20 +192,50 @@ namespace return tools::scoped_message_writer(console_color_red, true, sw::tr("Error: "), el::Level::Error); } - bool is_it_true(const std::string& s) + bool parse_bool(const std::string& s, bool& result) { if (s == "1" || command_line::is_yes(s)) + { + result = true; return true; + } + if (s == "0" || command_line::is_no(s)) + { + result = false; + return true; + } boost::algorithm::is_iequal ignore_case{}; - if (boost::algorithm::equals("true", s, ignore_case)) + if (boost::algorithm::equals("true", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case)) + { + result = true; return true; - if (boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case)) + } + if (boost::algorithm::equals("false", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("false"), s, ignore_case)) + { + result = false; return true; + } return false; } + template <typename F> + bool parse_bool_and_use(const std::string& s, F func) + { + bool r; + if (parse_bool(s, r)) + { + func(r); + return true; + } + else + { + fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no"); + return false; + } + } + const struct { const char *name; @@ -240,7 +301,7 @@ namespace << tr("Is this OK? (Y/n) ") ; // prompt the user for confirmation given the dns query and dnssec status - std::string confirm_dns_ok = command_line::input_line(prompt.str()); + std::string confirm_dns_ok = input_line(prompt.str()); if (std::cin.eof()) { return {}; @@ -418,7 +479,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) } // prompts for a new password, pass true to verify the password - const auto pwd_container = tools::wallet2::password_prompt(true); + const auto pwd_container = default_password_prompter(true); try { @@ -507,8 +568,10 @@ bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->always_confirm_transfers(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->always_confirm_transfers(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -518,8 +581,10 @@ bool simple_wallet::set_print_ring_members(const std::vector<std::string> &args/ const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->print_ring_members(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->print_ring_members(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -535,8 +600,10 @@ bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = s const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->store_tx_info(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->store_tx_info(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -631,14 +698,15 @@ bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = st const auto pwd_container = get_and_verify_password(); if (pwd_container) { - const bool auto_refresh = is_it_true(args[1]); - m_wallet->auto_refresh(auto_refresh); - m_idle_mutex.lock(); - m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed); - m_idle_cond.notify_one(); - m_idle_mutex.unlock(); + parse_bool_and_use(args[1], [&](bool auto_refresh) { + m_wallet->auto_refresh(auto_refresh); + m_idle_mutex.lock(); + m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed); + m_idle_cond.notify_one(); + m_idle_mutex.unlock(); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -665,8 +733,10 @@ bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->confirm_missing_payment_id(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->confirm_missing_payment_id(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -676,8 +746,10 @@ bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = st const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->ask_password(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->ask_password(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -753,8 +825,10 @@ bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/ const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->merge_destinations(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->merge_destinations(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -764,8 +838,10 @@ bool simple_wallet::set_confirm_backlog(const std::vector<std::string> &args/* = const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->confirm_backlog(is_it_true(args[1])); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->confirm_backlog(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); } return true; } @@ -834,6 +910,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked outputs below the threshold to an address")); + m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), tr("sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] - Send a single output of the given key image to an address without change")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - Donate <amount> to the development team (donate.getmonero.org)")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file")); @@ -975,7 +1052,7 @@ bool simple_wallet::ask_wallet_create_if_needed() do{ LOG_PRINT_L3("User asked to specify wallet file name."); - wallet_path = command_line::input_line( + wallet_path = input_line( tr(m_restoring ? "Specify a new wallet file name for your restored wallet (e.g., MyWallet).\n" "Wallet file name (or Ctrl-C to quit): " : "Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" @@ -1026,7 +1103,7 @@ bool simple_wallet::ask_wallet_create_if_needed() if (!m_restoring) { message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path; - confirm_creation = command_line::input_line(tr("(Y/Yes/N/No): ")); + confirm_creation = input_line(tr("(Y/Yes/N/No): ")); if(std::cin.eof()) { LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()"); @@ -1077,12 +1154,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!handle_command_line(vm)) return false; - if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1) + if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1) { - fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\" and --generate-from-json=\"jsonfilename\""); + fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\" and --generate-from-json=\"jsonfilename\""); return false; } - else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty()) + else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty()) { if(!ask_wallet_create_if_needed()) return false; } @@ -1110,7 +1187,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) do { const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: "; - std::string electrum_seed = command_line::input_line(prompt); + std::string electrum_seed = input_line(prompt); if (std::cin.eof()) return false; if (electrum_seed.empty()) @@ -1139,7 +1216,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_view_key; // parse address - std::string address_string = command_line::input_line("Standard address: "); + std::string address_string = input_line("Standard address: "); if (std::cin.eof()) return false; if (address_string.empty()) { @@ -1159,7 +1236,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse view secret key - std::string viewkey_string = command_line::input_line("View key: "); + std::string viewkey_string = input_line("View key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { @@ -1190,11 +1267,30 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) bool r = new_wallet(vm, info.address, boost::none, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } + else if (!m_generate_from_spend_key.empty()) + { + m_wallet_file = m_generate_from_spend_key; + // parse spend secret key + std::string spendkey_string = input_line("Secret spend key: "); + if (std::cin.eof()) + return false; + if (spendkey_string.empty()) { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + if (!epee::string_tools::hex_to_pod(spendkey_string, m_recovery_key)) + { + fail_msg_writer() << tr("failed to parse spend key secret key"); + return false; + } + bool r = new_wallet(vm, m_recovery_key, true, false, ""); + CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + } else if (!m_generate_from_keys.empty()) { m_wallet_file = m_generate_from_keys; // parse address - std::string address_string = command_line::input_line("Standard address: "); + std::string address_string = input_line("Standard address: "); if (std::cin.eof()) return false; if (address_string.empty()) { @@ -1214,7 +1310,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse spend secret key - std::string spendkey_string = command_line::input_line("Secret spend key: "); + std::string spendkey_string = input_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { @@ -1230,7 +1326,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); // parse view secret key - std::string viewkey_string = command_line::input_line("Secret view key: "); + std::string viewkey_string = input_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { @@ -1277,7 +1373,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) unsigned int multisig_n; // parse multisig type - std::string multisig_type_string = command_line::input_line("Multisig type (input as M/N with M <= N and M > 1): "); + std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1): "); if (std::cin.eof()) return false; if (multisig_type_string.empty()) @@ -1303,7 +1399,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n; // parse multisig address - std::string address_string = command_line::input_line("Multisig wallet address: "); + std::string address_string = input_line("Multisig wallet address: "); if (std::cin.eof()) return false; if (address_string.empty()) { @@ -1318,7 +1414,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse secret view key - std::string viewkey_string = command_line::input_line("Secret view key: "); + std::string viewkey_string = input_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) @@ -1358,7 +1454,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) // get N secret spend keys from user for(unsigned int i=0; i<multisig_n; ++i) { - spendkey_string = command_line::input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str())); + spendkey_string = input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str())); if (std::cin.eof()) return false; if (spendkey_string.empty()) @@ -1406,7 +1502,15 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) else if (!m_generate_from_json.empty()) { m_wallet_file = m_generate_from_json; - m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file); + try + { + m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter); + } + catch (const std::exception &e) + { + fail_msg_writer() << e.what(); + return false; + } if (!m_wallet) return false; } @@ -1428,9 +1532,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { std::string heightstr; if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6)) - heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0): "); + heightstr = input_line("Restore from specific blockchain height (optional, default 0): "); else - heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): "); + heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): "); if (std::cin.eof()) return false; if (heightstr.empty()) @@ -1466,7 +1570,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2)); m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day); success_msg_writer() << tr("Restore height is: ") << m_restore_height; - std::string confirm = command_line::input_line(tr("Is this okay? (Y/Yes/N/No): ")); + std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) return false; if(command_line::is_yes(confirm)) @@ -1531,6 +1635,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_wallet_file = command_line::get_arg(vm, arg_wallet_file); m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key); + m_generate_from_spend_key = command_line::get_arg(vm, arg_generate_from_spend_key); m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys); m_generate_from_multisig_keys = command_line::get_arg(vm, arg_generate_from_multisig_keys); m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json); @@ -1543,6 +1648,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_height = command_line::get_arg(vm, arg_restore_height); m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); m_restoring = !m_generate_from_view_key.empty() || + !m_generate_from_spend_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_multisig_keys.empty() || !m_generate_from_json.empty() || @@ -1595,7 +1701,7 @@ std::string simple_wallet::get_mnemonic_language() } while (language_number < 0) { - language_choice = command_line::input_line(tr("Enter the number corresponding to the language of your choice: ")); + language_choice = input_line(tr("Enter the number corresponding to the language of your choice: ")); if (std::cin.eof()) return std::string(); try @@ -1617,7 +1723,7 @@ std::string simple_wallet::get_mnemonic_language() //---------------------------------------------------------------------------------------------------- boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const { - auto pwd_container = tools::wallet2::password_prompt(m_wallet_file.empty()); + auto pwd_container = default_password_prompter(m_wallet_file.empty()); if (!pwd_container) return boost::none; @@ -1632,7 +1738,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm); + auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -1713,7 +1819,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey) { - auto rc = tools::wallet2::make_new(vm); + auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -1755,7 +1861,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) std::string password; try { - auto rc = tools::wallet2::make_from_file(vm, m_wallet_file); + auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter); m_wallet = std::move(rc.first); password = std::move(rc.second).password(); if (!m_wallet) @@ -1904,8 +2010,16 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) bool ok = true; size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2)); size_t arg_size = args.size(); - if(arg_size >= 3) req.ignore_battery = is_it_true(args[2]); - if(arg_size >= 2) req.do_background_mining = is_it_true(args[1]); + if(arg_size >= 3) + { + if (!parse_bool_and_use(args[2], [&](bool r) { req.ignore_battery = r; })) + return true; + } + if(arg_size >= 2) + { + if (!parse_bool_and_use(args[1], [&](bool r) { req.do_background_mining = r; })) + return true; + } if(arg_size >= 1) { uint16_t num = 1; @@ -1988,7 +2102,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, message_writer(console_color_green, false) << "\r" << tr("Height ") << height << ", " << tr("txid ") << txid << ", " << - print_money(amount) << tr(" XMR, ") << + print_money(amount) << tr("idx ") << subaddr_index; if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -2478,6 +2592,101 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return true; } //---------------------------------------------------------------------------------------------------- +static void handle_transfer_exception(const std::exception_ptr &e) +{ + try + { + std::rethrow_exception(e); + } + catch (const tools::error::daemon_busy&) + { + fail_msg_writer() << tr("daemon is busy. Please try again later."); + } + catch (const tools::error::no_connection_to_daemon&) + { + fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); + } + catch (const tools::error::wallet_rpc_error& e) + { + LOG_ERROR("RPC error: " << e.to_string()); + fail_msg_writer() << tr("RPC error: ") << e.what(); + } + catch (const tools::error::get_random_outs_error &e) + { + fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); + } + catch (const tools::error::not_enough_unlocked_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in unlocked balance"); + } + catch (const tools::error::not_enough_money& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % + print_money(e.available()) % + print_money(e.tx_amount())); + fail_msg_writer() << tr("Not enough money in unlocked balance"); + } + catch (const tools::error::tx_not_possible& e) + { + LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % + print_money(e.available()) % + print_money(e.tx_amount() + e.fee()) % + print_money(e.tx_amount()) % + print_money(e.fee())); + fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); + } + catch (const tools::error::not_enough_outs_to_mix& e) + { + auto writer = fail_msg_writer(); + writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; + for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) + { + writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; + } + } + catch (const tools::error::tx_not_constructed&) + { + fail_msg_writer() << tr("transaction was not constructed"); + } + 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) + { + fail_msg_writer() << e.what(); + } + catch (const tools::error::zero_destination&) + { + fail_msg_writer() << tr("one of destinations is zero"); + } + catch (const tools::error::tx_too_big& e) + { + fail_msg_writer() << tr("failed to find a suitable way to split transactions"); + } + catch (const tools::error::transfer_error& e) + { + LOG_ERROR("unknown transfer error: " << e.to_string()); + fail_msg_writer() << tr("unknown transfer error: ") << e.what(); + } + catch (const tools::error::wallet_internal_error& e) + { + LOG_ERROR("internal error: " << e.to_string()); + fail_msg_writer() << tr("internal error: ") << e.what(); + } + catch (const std::exception& e) + { + LOG_ERROR("unexpected error: " << e.what()); + fail_msg_writer() << tr("unexpected error: ") << e.what(); + } +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" @@ -2510,12 +2719,23 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (fake_outs_count == 0) fake_outs_count = DEFAULT_MIX; } + else if (ring_size == 0) + { + fail_msg_writer() << tr("Ring size must not be 0"); + return true; + } else { fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); + if (adjusted_fake_outs_count > fake_outs_count) + { + fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); + return true; + } const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2; if(local_args.size() < min_args) @@ -2626,7 +2846,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri // prompt is there is no payment id and confirmation is required if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) { - std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); + std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -2710,7 +2930,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri std::string prompt_str = prompt.str(); if (!prompt_str.empty()) { - std::string accepted = command_line::input_line(prompt_str); + std::string accepted = input_line(prompt_str); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -2781,7 +3001,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } prompt << ENDL << tr("Is this okay? (Y/Yes/N/No): "); - std::string accepted = command_line::input_line(prompt.str()); + std::string accepted = input_line(prompt.str()); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -2810,92 +3030,9 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri commit_or_save(ptx_vector, m_do_not_relay); } } - catch (const tools::error::daemon_busy&) - { - fail_msg_writer() << tr("daemon is busy. Please try again later."); - } - catch (const tools::error::no_connection_to_daemon&) - { - fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("RPC error: " << e.to_string()); - fail_msg_writer() << tr("RPC error: ") << e.what(); - } - catch (const tools::error::get_random_outs_error &e) - { - fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); - } - catch (const tools::error::not_enough_unlocked_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); - } - catch (const tools::error::not_enough_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in overall balance"); - } - catch (const tools::error::tx_not_possible& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % - print_money(e.available()) % - print_money(e.tx_amount() + e.fee()) % - print_money(e.tx_amount()) % - print_money(e.fee())); - fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); - } - catch (const tools::error::not_enough_outs_to_mix& e) - { - auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; - for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) - { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; - } - } - catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << tr("transaction was not constructed"); - } - 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) - { - fail_msg_writer() << e.what(); - } - catch (const tools::error::zero_destination&) - { - fail_msg_writer() << tr("one of destinations is zero"); - } - catch (const tools::error::tx_too_big& e) - { - fail_msg_writer() << tr("failed to find a suitable way to split transactions"); - } - catch (const tools::error::transfer_error& e) - { - LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << tr("unknown transfer error: ") << e.what(); - } - catch (const tools::error::wallet_internal_error& e) + catch (const std::exception &e) { - LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << tr("internal error: ") << e.what(); - } - catch (const std::exception& e) - { - LOG_ERROR("unexpected error: " << e.what()); - fail_msg_writer() << tr("unexpected error: ") << e.what(); + handle_transfer_exception(std::current_exception()); } catch (...) { @@ -2961,7 +3098,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) print_money(total_unmixable) % print_money(total_fee)).str(); } - std::string accepted = command_line::input_line(prompt_str); + std::string accepted = input_line(prompt_str); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -2989,92 +3126,9 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) commit_or_save(ptx_vector, m_do_not_relay); } } - catch (const tools::error::daemon_busy&) - { - fail_msg_writer() << tr("daemon is busy. Please try again later."); - } - catch (const tools::error::no_connection_to_daemon&) - { - fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("RPC error: " << e.to_string()); - fail_msg_writer() << tr("RPC error: ") << e.what(); - } - catch (const tools::error::get_random_outs_error &e) - { - fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); - } - catch (const tools::error::not_enough_unlocked_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); - } - catch (const tools::error::not_enough_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in overall balance"); - } - catch (const tools::error::tx_not_possible& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % - print_money(e.available()) % - print_money(e.tx_amount() + e.fee()) % - print_money(e.tx_amount()) % - print_money(e.fee())); - fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); - } - catch (const tools::error::not_enough_outs_to_mix& e) - { - auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; - for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) - { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; - } - } - catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << tr("transaction was not constructed"); - } - 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) - { - fail_msg_writer() << e.what(); - } - catch (const tools::error::zero_destination&) - { - fail_msg_writer() << tr("one of destinations is zero"); - } - catch (const tools::error::tx_too_big& e) - { - fail_msg_writer() << tr("failed to find a suitable way to split transactions"); - } - catch (const tools::error::transfer_error& e) - { - LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << tr("unknown transfer error: ") << e.what(); - } - catch (const tools::error::wallet_internal_error& e) - { - LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << tr("internal error: ") << e.what(); - } - catch (const std::exception& e) + catch (const std::exception &e) { - LOG_ERROR("unexpected error: " << e.what()); - fail_msg_writer() << tr("unexpected error: ") << e.what(); + handle_transfer_exception(std::current_exception()); } catch (...) { @@ -3088,6 +3142,12 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_) { // sweep_all [index=<N1>[,<N2>,...]] [<ring_size>] <address> [<payment_id>] + if (args_.size() == 0) + { + fail_msg_writer() << tr("No address given"); + return true; + } + if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) return true; @@ -3115,12 +3175,23 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a if (fake_outs_count == 0) fake_outs_count = DEFAULT_MIX; } + else if (ring_size == 0) + { + fail_msg_writer() << tr("Ring size must not be 0"); + return true; + } else { fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); + if (adjusted_fake_outs_count > fake_outs_count) + { + fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); + return true; + } std::vector<uint8_t> extra; bool payment_id_seen = false; @@ -3159,12 +3230,6 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a local_args.pop_back(); } - if (local_args.size() == 0) - { - fail_msg_writer() << tr("No address given"); - return true; - } - cryptonote::address_parse_info info; if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), local_args[0], oa_prompter)) { @@ -3194,7 +3259,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a // prompt is there is no payment id and confirmation is required if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) { - std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); + std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -3252,7 +3317,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a print_money(total_sent) % print_money(total_fee); } - std::string accepted = command_line::input_line(prompt.str()); + std::string accepted = input_line(prompt.str()); if (std::cin.eof()) return true; if (!command_line::is_yes(accepted)) @@ -3280,6 +3345,199 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a commit_or_save(ptx_vector, m_do_not_relay); } } + catch (const std::exception& e) + { + handle_transfer_exception(std::current_exception()); + } + catch (...) + { + LOG_ERROR("unknown error"); + fail_msg_writer() << tr("unknown error"); + } + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_single(const std::vector<std::string> &args_) +{ + if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + if (!try_connect_to_daemon()) + return true; + + std::vector<std::string> local_args = args_; + + int priority = 0; + if(local_args.size() > 0) { + auto priority_pos = std::find( + allowed_priority_strings.begin(), + allowed_priority_strings.end(), + local_args[0]); + if(priority_pos != allowed_priority_strings.end()) { + local_args.erase(local_args.begin()); + priority = std::distance(allowed_priority_strings.begin(), priority_pos); + } + } + + 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])) + { + fake_outs_count = m_wallet->default_mixin(); + if (fake_outs_count == 0) + fake_outs_count = DEFAULT_MIX; + } + else + { + fake_outs_count = ring_size - 1; + local_args.erase(local_args.begin()); + } + } + + std::vector<uint8_t> extra; + bool payment_id_seen = false; + if (local_args.size() == 3) + { + crypto::hash payment_id; + crypto::hash8 payment_id8; + std::string extra_nonce; + if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id)) + { + set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + } + else if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8)) + { + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); + } + else + { + fail_msg_writer() << tr("failed to parse Payment ID"); + return true; + } + + if (!add_extra_nonce_to_tx_extra(extra, extra_nonce)) + { + fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly"); + return true; + } + + local_args.pop_back(); + payment_id_seen = true; + } + + if (local_args.size() != 2) + { + fail_msg_writer() << tr("usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>]"); + return true; + } + + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(local_args[0], ki)) + { + fail_msg_writer() << tr("failed to parse key image"); + return true; + } + + cryptonote::address_parse_info info; + if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), local_args[1], oa_prompter)) + { + fail_msg_writer() << tr("failed to parse address"); + return true; + } + + if (info.has_payment_id) + { + if (payment_id_seen) + { + fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0]; + return true; + } + + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id); + if (!add_extra_nonce_to_tx_extra(extra, extra_nonce)) + { + fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly"); + return true; + } + payment_id_seen = true; + } + + // prompt if there is no payment id and confirmation is required + if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) + { + std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); + if (std::cin.eof()) + return true; + if (!command_line::is_yes(accepted)) + { + fail_msg_writer() << tr("transaction cancelled."); + + // would like to return false, because no tx made, but everything else returns true + // and I don't know what returning false might adversely affect. *sigh* + return true; + } + } + + try + { + // figure out what tx will be necessary + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon); + + if (ptx_vector.empty()) + { + fail_msg_writer() << tr("No outputs found"); + return true; + } + if (ptx_vector.size() > 1) + { + fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen"); + return true; + } + if (ptx_vector[0].selected_transfers.size() > 1) + { + fail_msg_writer() << tr("The transaction uses multiple inputs, which is not supposed to happen"); + return true; + } + + // give user total and fee, and prompt to confirm + uint64_t total_fee = ptx_vector[0].fee; + uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount(); + std::ostringstream prompt; + if (!print_ring_members(ptx_vector, prompt)) + return true; + prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)")) % + print_money(total_sent) % + print_money(total_fee); + std::string accepted = command_line::input_line(prompt.str()); + if (std::cin.eof()) + return true; + if (!command_line::is_yes(accepted)) + { + fail_msg_writer() << tr("transaction cancelled."); + return true; + } + + // actually commit the transactions + if (m_wallet->watch_only()) + { + bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + if (!r) + { + fail_msg_writer() << tr("Failed to write transaction(s) to file"); + } + else + { + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + } + } + else + { + m_wallet->commit_tx(ptx_vector[0]); + success_msg_writer(true) << tr("Money successfully sent, transaction: ") << get_transaction_hash(ptx_vector[0].tx); + } + + } catch (const tools::error::daemon_busy&) { fail_msg_writer() << tr("daemon is busy. Please try again later."); @@ -3297,20 +3555,13 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } - catch (const tools::error::not_enough_unlocked_money& e) + catch (const tools::error::not_enough_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); } - catch (const tools::error::not_enough_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in overall balance"); - } catch (const tools::error::tx_not_possible& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % @@ -3323,10 +3574,10 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a catch (const tools::error::not_enough_outs_to_mix& e) { auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; + writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":"; for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; + writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second; } } catch (const tools::error::tx_not_constructed&) @@ -3426,7 +3677,7 @@ bool simple_wallet::donate(const std::vector<std::string> &args_) local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << tr("Donating ") << amount_str << " XMR to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A)."; + message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A)."; transfer_new(local_args); return true; } @@ -3548,7 +3799,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, uint64_t fee = amount - amount_to_dests; std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str(); - return command_line::is_yes(command_line::input_line(prompt_str)); + return command_line::is_yes(input_line(prompt_str)); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) @@ -3637,92 +3888,9 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) commit_or_save(ptx_vector, false); } - catch (const tools::error::daemon_busy&) - { - fail_msg_writer() << tr("daemon is busy. Please try later"); - } - catch (const tools::error::no_connection_to_daemon&) - { - fail_msg_writer() << tr("no connection to daemon. Please, make sure daemon is running."); - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("Unknown RPC error: " << e.to_string()); - fail_msg_writer() << tr("RPC error: ") << e.what(); - } - catch (const tools::error::get_random_outs_error &e) - { - fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); - } - catch (const tools::error::not_enough_unlocked_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); - } - catch (const tools::error::not_enough_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in overall balance"); - } - catch (const tools::error::tx_not_possible& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % - print_money(e.available()) % - print_money(e.tx_amount() + e.fee()) % - print_money(e.tx_amount()) % - print_money(e.fee())); - fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); - } - catch (const tools::error::not_enough_outs_to_mix& e) - { - auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; - for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs()) - { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; - } - } - catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << tr("transaction was not constructed"); - } - 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) - { - fail_msg_writer() << e.what(); - } - catch (const tools::error::zero_destination&) - { - fail_msg_writer() << tr("one of destinations is zero"); - } - catch (const tools::error::tx_too_big& e) - { - fail_msg_writer() << tr("Failed to find a suitable way to split transactions"); - } - catch (const tools::error::transfer_error& e) - { - LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << tr("unknown transfer error: ") << e.what(); - } - catch (const tools::error::wallet_internal_error& e) - { - LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << tr("internal error: ") << e.what(); - } catch (const std::exception& e) { - LOG_ERROR("unexpected error: " << e.what()); - fail_msg_writer() << tr("unexpected error: ") << e.what(); + handle_transfer_exception(std::current_exception()); } catch (...) { @@ -3854,7 +4022,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) // fetch tx pubkey crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if (tx_pub_key == null_pkey) + if (tx_pub_key == crypto::null_pkey) { fail_msg_writer() << tr("Tx pubkey was not found"); return true; @@ -4191,7 +4359,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args) return true; } crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if (tx_pub_key == null_pkey) + if (tx_pub_key == crypto::null_pkey) { fail_msg_writer() << tr("Tx pubkey was not found"); return true; @@ -4415,15 +4583,18 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) try { m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments; m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { - const tools::wallet2::payment_details &pd = i->second; + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { + const tools::wallet2::payment_details &pd = i->second.m_pd; std::string payment_id = string_tools::pod_to_hex(i->first); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); - message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str(); + std::string double_spend_note; + if (i->second.m_double_spend_seen) + double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); + message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); } } catch (const std::exception& e) @@ -4743,10 +4914,11 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector //---------------------------------------------------------------------------------------------------- void simple_wallet::print_accounts() { - success_msg_writer() << boost::format("%15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label"); + success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label"); for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) { - success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %21s")) + success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s")) + % (m_current_subaddress_account == account_index ? '*' : ' ') % account_index % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6) % print_money(m_wallet->balance(account_index)) @@ -4915,7 +5087,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v fail_msg_writer() << tr("failed to parse address"); return true; } - crypto::hash payment_id = null_hash; + crypto::hash payment_id = crypto::null_hash; size_t description_start = 2; if (info.has_payment_id) { @@ -5369,7 +5541,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) const uint64_t last_block_height = m_wallet->get_blockchain_current_height(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet->get_payments(payments, 0, m_current_subaddress_account); + m_wallet->get_payments(payments, 0, (uint64_t)-1, m_current_subaddress_account); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { const tools::wallet2::payment_details &pd = i->second; if (pd.m_tx_hash == txid) { @@ -5439,10 +5611,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) try { m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; - m_wallet->get_unconfirmed_payments(pool_payments); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { - const tools::wallet2::payment_details &pd = i->second; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments; + m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account); + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { + const tools::wallet2::payment_details &pd = i->second.m_pd; if (pd.m_tx_hash == txid) { std::string payment_id = string_tools::pod_to_hex(i->first); @@ -5455,6 +5627,8 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) success_msg_writer() << "Payment ID: " << payment_id; success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor; success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid); + if (i->second.m_double_spend_seen) + success_msg_writer() << tr("Double spend seen on the network: this transaction may or may not end up being mined"); return true; } } @@ -5465,7 +5639,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) } std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet->get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { if (i->first == txid) { @@ -5546,6 +5720,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_generate_new_wallet); command_line::add_arg(desc_params, arg_generate_from_view_key); + command_line::add_arg(desc_params, arg_generate_from_spend_key); command_line::add_arg(desc_params, arg_generate_from_keys); command_line::add_arg(desc_params, arg_generate_from_multisig_keys); command_line::add_arg(desc_params, arg_generate_from_json); @@ -5568,6 +5743,7 @@ int main(int argc, char* argv[]) "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", desc_params, positional_options, + [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, "monero-wallet-cli.log" ); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 639cee642..d65784828 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -141,6 +141,7 @@ namespace cryptonote bool sweep_main(uint64_t below, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); bool sweep_below(const std::vector<std::string> &args); + bool sweep_single(const std::vector<std::string> &args); bool sweep_unmixable(const std::vector<std::string> &args); bool donate(const std::vector<std::string> &args); bool sign_transfer(const std::vector<std::string> &args); @@ -281,6 +282,7 @@ namespace cryptonote std::string m_wallet_file; std::string m_generate_new; std::string m_generate_from_view_key; + std::string m_generate_from_spend_key; std::string m_generate_from_keys; std::string m_generate_from_multisig_keys; std::string m_generate_from_json; diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 24399790c..e5c79a447 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -79,7 +79,6 @@ target_link_libraries(wallet common cryptonote_core mnemonics - p2p ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} @@ -89,42 +88,40 @@ target_link_libraries(wallet PRIVATE ${EXTRA_LIBRARIES}) -if (NOT BUILD_GUI_DEPS) - set(wallet_rpc_sources - wallet_rpc_server.cpp) +set(wallet_rpc_sources + wallet_rpc_server.cpp) - set(wallet_rpc_headers) +set(wallet_rpc_headers) - set(wallet_rpc_private_headers - wallet_rpc_server.h) +set(wallet_rpc_private_headers + wallet_rpc_server.h) - monero_private_headers(wallet_rpc_server - ${wallet_rpc_private_headers}) - monero_add_executable(wallet_rpc_server - ${wallet_rpc_sources} - ${wallet_rpc_headers} - ${wallet_rpc_private_headers}) +monero_private_headers(wallet_rpc_server + ${wallet_rpc_private_headers}) +monero_add_executable(wallet_rpc_server + ${wallet_rpc_sources} + ${wallet_rpc_headers} + ${wallet_rpc_private_headers}) - target_link_libraries(wallet_rpc_server - PRIVATE - wallet - epee - rpc - cryptonote_core - cncrypto - common - version - ${Boost_CHRONO_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) - set_property(TARGET wallet_rpc_server - PROPERTY - OUTPUT_NAME "monero-wallet-rpc") - install(TARGETS wallet_rpc_server DESTINATION bin) -endif() +target_link_libraries(wallet_rpc_server + PRIVATE + wallet + epee + rpc + cryptonote_core + cncrypto + common + version + ${Boost_CHRONO_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) +set_property(TARGET wallet_rpc_server + PROPERTY + OUTPUT_NAME "monero-wallet-rpc") +install(TARGETS wallet_rpc_server DESTINATION bin) # build and install libwallet_merged only if we building for GUI diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 59eca3dd7..8a8243047 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -217,10 +217,10 @@ void TransactionHistoryImpl::refresh() // unconfirmed payments (tx pool) - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> upayments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> upayments; m_wallet->m_wallet->get_unconfirmed_payments(upayments); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { - const tools::wallet2::payment_details &pd = i->second; + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { + const tools::wallet2::payment_details &pd = i->second.m_pd; std::string payment_id = string_tools::pod_to_hex(i->first); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index a64766c84..2326f54d3 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -434,12 +434,14 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool return addresses.front(); } -std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, const std::string &subdir) +std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, std::string subdir) { #ifdef BUILD_TAG static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); #else static const char buildtag[] = "source"; + // Override the subdir string when built from source + subdir = "source"; #endif std::string version, hash; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a87803206..00b096b88 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -59,7 +59,6 @@ using namespace epee; #include "rapidjson/stringbuffer.h" #include "common/json_util.h" #include "common/base58.h" -#include "common/scoped_message_writer.h" #include "ringct/rctSigs.h" extern "C" @@ -67,6 +66,8 @@ extern "C" #include "crypto/keccak.h" #include "crypto/crypto-ops.h" } +using namespace std; +using namespace crypto; using namespace cryptonote; #undef MONERO_DEFAULT_LOG_CATEGORY @@ -135,7 +136,7 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui return calculate_fee(fee_per_kb, blob.size(), fee_multiplier); } -std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts) +std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool restricted = command_line::get_arg(vm, opts.restricted); @@ -144,17 +145,16 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl auto daemon_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); - if (!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port) - { - tools::fail_msg_writer() << tools::wallet2::tr("can't specify daemon host or port more than once"); - return nullptr; - } + THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, + tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); boost::optional<epee::net_utils::http::login> login{}; if (command_line::has_arg(vm, opts.daemon_login)) { auto parsed = tools::login::parse( - command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password" + command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) { + return password_prompter("Daemon client password", verify); + } ); if (!parsed) return nullptr; @@ -178,12 +178,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl return wallet; } -boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const bool verify) +boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char*, bool)> &password_prompter, const bool verify) { if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file)) { - tools::fail_msg_writer() << tools::wallet2::tr("can't specify more than one of --password and --password-file"); - return boost::none; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file")); } if (command_line::has_arg(vm, opts.password)) @@ -196,21 +195,17 @@ boost::optional<tools::password_container> get_password(const boost::program_opt std::string password; bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file), password); - if (!r) - { - tools::fail_msg_writer() << tools::wallet2::tr("the password file specified could not be read"); - return boost::none; - } + THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read")); // Remove line breaks the user might have inserted boost::trim_right_if(password, boost::is_any_of("\r\n")); return {tools::password_container{std::move(password)}}; } - return tools::wallet2::password_prompt(verify); + return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify); } -std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts) +std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); @@ -221,22 +216,20 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const auto do_generate = [&]() -> bool { std::string buf; if (!epee::file_io_utils::load_file_to_string(json_file, buf)) { - tools::fail_msg_writer() << tools::wallet2::tr("Failed to load file ") << json_file; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("Failed to load file ")) + json_file); return false; } rapidjson::Document json; if (json.Parse(buf.c_str()).HasParseError()) { - tools::fail_msg_writer() << tools::wallet2::tr("Failed to parse JSON"); + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Failed to parse JSON")); return false; } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0); const int current_version = 1; - if (field_version > current_version) { - tools::fail_msg_writer() << boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version; - return false; - } + THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error, + ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str()); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string()); @@ -252,14 +245,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, cryptonote::blobdata viewkey_data; if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse view key secret key")); } viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); crypto::public_key pkey; if (!crypto::secret_key_to_public_key(viewkey, pkey)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } } @@ -270,14 +261,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, cryptonote::blobdata spendkey_data; if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key")); } spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); crypto::public_key pkey; if (!crypto::secret_key_to_public_key(spendkey, pkey)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } } @@ -289,8 +278,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, { if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language)) { - tools::fail_msg_writer() << tools::wallet2::tr("Electrum-style word list failed verification"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification")); } restore_deterministic_wallet = true; @@ -305,15 +293,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string()); // compatibility checks - if (!field_seed_found && !field_viewkey_found) + if (!field_seed_found && !field_viewkey_found && !field_spendkey_found) { - tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key must be specified"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("At least one of Electrum-style word list and private view key and private spend key must be specified")); } if (field_seed_found && (field_viewkey_found || field_spendkey_found)) { - tools::fail_msg_writer() << tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified")); } // if an address was given, we check keys against it, and deduce the spend @@ -323,43 +309,36 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, cryptonote::address_parse_info info; if(!get_account_address_from_str(info, testnet, field_address)) { - tools::fail_msg_writer() << tools::wallet2::tr("invalid address"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("invalid address")); } if (field_viewkey_found) { crypto::public_key pkey; if (!crypto::secret_key_to_public_key(viewkey, pkey)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } if (info.address.m_view_public_key != pkey) { - tools::fail_msg_writer() << tools::wallet2::tr("view key does not match standard address"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address")); } } if (field_spendkey_found) { crypto::public_key pkey; if (!crypto::secret_key_to_public_key(spendkey, pkey)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } if (info.address.m_spend_public_key != pkey) { - tools::fail_msg_writer() << tools::wallet2::tr("spend key does not match standard address"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address")); } } } const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) || crypto::ElectrumWords::get_is_old_style_seed(field_seed)); - if (deprecated_wallet) { - tools::fail_msg_writer() << tools::wallet2::tr("Cannot create deprecated wallets from JSON"); - return false; - } + THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error, + tools::wallet2::tr("Cannot create deprecated wallets from JSON")); - wallet.reset(make_basic(vm, opts).release()); + wallet.reset(make_basic(vm, opts, password_prompter).release()); wallet->set_refresh_from_block_height(field_scan_from_height); try @@ -368,12 +347,15 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, { wallet->generate(field_filename, field_password, recovery_key, recover, false); } + else if (field_viewkey.empty() && !field_spendkey.empty()) + { + wallet->generate(field_filename, field_password, spendkey, recover, false); + } else { cryptonote::account_public_address address; if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } if (field_spendkey.empty()) @@ -385,18 +367,20 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, cryptonote::address_parse_info info; if(!get_account_address_from_str(info, testnet, field_address)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address; - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address); } address.m_spend_public_key = info.address.m_spend_public_key; } + else + { + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet")); + } wallet->generate(field_filename, field_password, address, viewkey); } else { if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } wallet->generate(field_filename, field_password, address, spendkey, viewkey); } @@ -404,8 +388,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, } catch (const std::exception& e) { - tools::fail_msg_writer() << tools::wallet2::tr("failed to generate new wallet: ") << e.what(); - return false; + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what()); } return true; }; @@ -444,6 +427,34 @@ std::string strjoin(const std::vector<size_t> &V, const char *sep) return ss.str(); } +static void emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container, + const crypto::hash &key, const tools::wallet2::pool_payment_details &pd) +{ + auto range = container.equal_range(key); + for (auto i = range.first; i != range.second; ++i) + { + if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash) + { + i->second = pd; + return; + } + } + container.emplace(key, pd); +} + +void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_t N) +{ + std::list<crypto::hash>::iterator right; + // drop early N off, skipping the genesis block + if (short_chain_history.size() > N) { + right = short_chain_history.end(); + std::advance(right,-1); + std::list<crypto::hash>::iterator left = right; + std::advance(left, -N); + short_chain_history.erase(left, right); + } +} + } //namespace namespace tools @@ -472,34 +483,22 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.restricted); } -boost::optional<password_container> wallet2::password_prompt(const bool new_password) -{ - auto pwd_container = tools::password_container::prompt( - new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password")) - ); - if (!pwd_container) - { - tools::fail_msg_writer() << tr("failed to read wallet password"); - } - return pwd_container; -} - -std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file) +std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const options opts{}; - return generate_from_json(json_file, vm, opts); + return generate_from_json(json_file, vm, opts, password_prompter); } std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( - const boost::program_options::variables_map& vm, const std::string& wallet_file) + const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const options opts{}; - auto pwd = get_password(vm, opts, false); + auto pwd = get_password(vm, opts, password_prompter, false); if (!pwd) { return {nullptr, password_container{}}; } - auto wallet = make_basic(vm, opts); + auto wallet = make_basic(vm, opts, password_prompter); if (wallet) { wallet->load(wallet_file, pwd->password()); @@ -507,21 +506,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( return {std::move(wallet), std::move(*pwd)}; } -std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm) +std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter) { const options opts{}; - auto pwd = get_password(vm, opts, true); + auto pwd = get_password(vm, opts, password_prompter, true); if (!pwd) { return {nullptr, password_container{}}; } - return {make_basic(vm, opts), std::move(*pwd)}; + return {make_basic(vm, opts, password_prompter), std::move(*pwd)}; } -std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm) +std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const options opts{}; - return make_basic(vm, opts); + return make_basic(vm, opts, password_prompter); } //---------------------------------------------------------------------------------------------------- @@ -793,7 +792,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote ++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) +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, bool double_spend_seen) { // In this function, tx (probably) only contains the base information // (that is, the prunable stuff may or may not be included) @@ -831,7 +830,6 @@ 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; tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; const cryptonote::account_keys& keys = m_account.get_keys(); @@ -854,46 +852,33 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]); - if (tx_scan_info[0].error) - { - r = false; - } - else + THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); + + // this assumes that the miner tx pays a single address + if (tx_scan_info[0].received) { - // this assumes that the miner tx pays a single address - if (tx_scan_info[0].received) + // process the other outs from that tx + // the first one was already checked + for (size_t i = 1; i < tx.vout.size(); ++i) { - 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 - // 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(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::ref(tx_scan_info[i]))); - } - waiter.wait(); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, + std::ref(tx_scan_info[i]))); + } + waiter.wait(); - for (size_t i = 1; i < tx.vout.size(); ++i) + // then scan all outputs from 0 + for (size_t i = 0; i < tx.vout.size(); ++i) + { + THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); + if (tx_scan_info[i].received) { - if (tx_scan_info[i].error) - { - r = false; - break; - } - if (tx_scan_info[i].received) - { - scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); - } + 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 && tools::threadpool::getInstance().get_max_concurrency() > 1) { - tools::threadpool& tpool = tools::threadpool::getInstance(); - tools::threadpool::waiter waiter; - for (size_t i = 0; i < tx.vout.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, @@ -902,11 +887,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote waiter.wait(); for (size_t i = 0; i < tx.vout.size(); ++i) { - if (tx_scan_info[i].error) - { - r = false; - break; - } + THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); @@ -918,18 +899,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) { check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, tx_scan_info[i]); - if (tx_scan_info[i].error) - { - r = false; - break; - } + THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } } - THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if(!outs.empty() && num_vouts_received > 0) { @@ -1163,7 +1139,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_timestamp = ts; payment.m_subaddr_index = i.first; if (pool) { - m_unconfirmed_payments.emplace(payment_id, payment); + emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}); if (0 != m_callback) m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index); } @@ -1241,7 +1217,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); @@ -1252,7 +1228,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry cryptonote::transaction tx; bool r = parse_and_validate_tx_base_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false); ++idx; } TIME_MEASURE_FINISH(txs_handle_time); @@ -1497,6 +1473,8 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei try { + drop_from_short_history(short_chain_history, 3); + // prepend the last 3 blocks, should be enough to guard against a block or two's reorg cryptonote::block bl; std::list<cryptonote::block_complete_entry>::const_reverse_iterator i = prev_blocks.rbegin(); @@ -1520,10 +1498,10 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes) { // remove pool txes to us that aren't in the pool anymore - std::unordered_multimap<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); + std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { - const crypto::hash &txid = uit->second.m_tx_hash; + const crypto::hash &txid = uit->second.m_pd.m_tx_hash; bool found = false; for (const auto &it2: tx_hashes) { @@ -1626,23 +1604,27 @@ void wallet2::update_pool_state(bool refreshed) MDEBUG("update_pool_state done second loop"); // gather txids of new pool txes to us - std::vector<crypto::hash> txids; + std::vector<std::pair<crypto::hash, bool>> txids; for (const auto &txid: res.tx_hashes) { - if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) - { - LOG_PRINT_L2("Already seen " << txid << ", skipped"); - continue; - } bool txid_found_in_up = false; for (const auto &up: m_unconfirmed_payments) { - if (up.second.m_tx_hash == txid) + if (up.second.m_pd.m_tx_hash == txid) { txid_found_in_up = true; break; } } + if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) + { + // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out + if (!txid_found_in_up) + { + LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped"); + continue; + } + } if (!txid_found_in_up) { LOG_PRINT_L1("Found new pool tx: " << txid); @@ -1670,7 +1652,7 @@ void wallet2::update_pool_state(bool refreshed) if (!found) { // not one of those we sent ourselves - txids.push_back(txid); + txids.push_back({txid, false}); } else { @@ -1680,6 +1662,7 @@ void wallet2::update_pool_state(bool refreshed) else { LOG_PRINT_L1("Already saw that one, it's for us"); + txids.push_back({txid, true}); } } @@ -1688,8 +1671,8 @@ void wallet2::update_pool_state(bool refreshed) { cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; - for (const auto &txid: txids) - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + for (const auto &p: txids) + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first)); MDEBUG("asking for " << txids.size() << " transactions"); req.decode_as_json = false; m_daemon_rpc_mutex.lock(); @@ -1711,10 +1694,11 @@ void wallet2::update_pool_state(bool refreshed) { if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) { - const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash); + const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(), + [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); + process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { @@ -1759,24 +1743,29 @@ void wallet2::update_pool_state(bool refreshed) void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history) { std::list<crypto::hash> hashes; - size_t current_index = m_blockchain.size(); + const uint64_t checkpoint_height = m_checkpoints.get_max_height(); + if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) + { + // we will drop all these, so don't bother getting them + uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size(); + while (missing_blocks-- > 0) + m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector + m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height)); + m_local_bc_height = m_blockchain.size(); + short_chain_history.clear(); + get_short_chain_history(short_chain_history); + } + + size_t current_index = m_blockchain.size(); while(m_run.load(std::memory_order_relaxed) && current_index < stop_height) { pull_hashes(0, blocks_start_height, short_chain_history, hashes); if (hashes.size() <= 3) return; if (hashes.size() + current_index < stop_height) { - std::list<crypto::hash>::iterator right; - // drop early 3 off, skipping the genesis block - if (short_chain_history.size() > 3) { - right = short_chain_history.end(); - std::advance(right,-1); - std::list<crypto::hash>::iterator left = right; - std::advance(left, -3); - short_chain_history.erase(left, right); - } - right = hashes.end(); + drop_from_short_history(short_chain_history, 3); + std::list<crypto::hash>::iterator right = hashes.end(); // prepend 3 more for (int i = 0; i<3; i++) { right--; @@ -3073,11 +3062,11 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall } } //---------------------------------------------------------------------------------------------------- -void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const +void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const { for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) { - if ((!subaddr_account || *subaddr_account == i->second.m_subaddr_index.major) && - (subaddr_indices.empty() || subaddr_indices.count(i->second.m_subaddr_index.minor) == 1)) + if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) && + (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1)) unconfirmed_payments.push_back(*i); } } @@ -3272,7 +3261,7 @@ float wallet2::get_output_relatedness(const transfer_details &td0, const transfe return 0.0f; } //---------------------------------------------------------------------------------------------------- -size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::list<size_t>& selected_transfers, bool smallest) const +size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const { std::vector<size_t> candidates; float best_relatedness = 1.0f; @@ -3280,7 +3269,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve { const transfer_details &candidate = transfers[unused_indices[n]]; float relatedness = 0.0f; - for (std::list<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i) + for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i) { float r = get_output_relatedness(candidate, transfers[*i]); if (r > relatedness) @@ -3321,7 +3310,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve return pop_index (unused_indices, candidates[idx]); } //---------------------------------------------------------------------------------------------------- -size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::list<size_t>& selected_transfers, bool smallest) const +size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const { return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest); } @@ -3330,9 +3319,10 @@ size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::l // returns: // direct return: amount of money found // modified reference: selected_transfers, a list of iterators/indices of input sources -uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon) +uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) { uint64_t found_money = 0; + selected_transfers.reserve(unused_transfers_indices.size()); while (found_money < needed_money && !unused_transfers_indices.empty()) { size_t idx = pop_best_value(unused_transfers_indices, selected_transfers); @@ -3950,6 +3940,19 @@ int wallet2::get_fee_algorithm() return 1; return 0; } +//------------------------------------------------------------------------------------------------------------------------------ +uint64_t wallet2::adjust_mixin(uint64_t mixin) +{ + if (mixin < 4 && use_fork_rules(6, 10)) { + MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); + mixin = 4; + } + else if (mixin < 2 && use_fork_rules(2, 10)) { + MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3"); + mixin = 2; + } + return mixin; +} //---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // @@ -4073,7 +4076,7 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out return true; } -void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) { +void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) { MDEBUG("LIGHTWALLET - Getting random outs"); @@ -4177,7 +4180,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ } } -void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -4412,7 +4415,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } template<typename T> -void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) { @@ -4566,7 +4569,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent LOG_PRINT_L2("transfer_selected done"); } -void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count, +void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx) { @@ -5129,7 +5132,7 @@ void wallet2::light_wallet_get_address_txs() payments_txs.push_back(p.second.m_tx_hash); std::vector<crypto::hash> unconfirmed_payments_txs; for(const auto &up: m_unconfirmed_payments) - unconfirmed_payments_txs.push_back(up.second.m_tx_hash); + unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash); // for balance calculation uint64_t wallet_total_sent = 0; @@ -5195,7 +5198,11 @@ void wallet2::light_wallet_get_address_txs() if (t.mempool) { if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) { pool_txs.push_back(tx_hash); - m_unconfirmed_payments.emplace(tx_hash, payment); + // assume false as we don't get that info from the light wallet server + crypto::hash payment_id; + THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id), + error::wallet_internal_error, "Failed to parse payment id"); + emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false}); if (0 != m_callback) { m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount); } @@ -5325,11 +5332,27 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, crypto::key_image calculated_key_image; cryptonote::keypair in_ephemeral; - // Subaddresses aren't supported in mymonero/openmonero yet. Using empty values. - const std::vector<crypto::public_key> additional_tx_pub_keys; - const crypto::public_key pkey = crypto::null_pkey; - - cryptonote::generate_key_image_helper(get_account().get_keys(), m_subaddresses, pkey, tx_public_key, additional_tx_pub_keys, out_index, in_ephemeral, calculated_key_image); + // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme: + // compute D = a*R + // compute P = Hs(D || i)*G + B + // compute x = Hs(D || i) + b (and check if P==x*G) + // compute I = x*Hp(P) + const account_keys& ack = get_account().get_keys(); + crypto::key_derivation derivation; + bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + + r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + + crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec); + crypto::public_key out_pkey_test; + r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test); + CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")"); + CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key"); + + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image); + index_keyimage_map.emplace(out_index, calculated_key_image); m_key_image_cache.emplace(tx_public_key, index_keyimage_map); return key_image == calculated_key_image; @@ -5361,7 +5384,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp uint64_t needed_money; uint64_t accumulated_fee, accumulated_outputs, accumulated_change; struct TX { - std::list<size_t> selected_transfers; + std::vector<size_t> selected_transfers; std::vector<cryptonote::tx_destination_entry> dsts; cryptonote::transaction tx; pending_tx ptx; @@ -5416,9 +5439,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // throw if attempting a transaction with no money THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); - std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account); + std::map<uint32_t, uint64_t> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account); + std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account); - if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked bakance + if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance { for (const auto& i : balance_per_subaddr) subaddr_indices.insert(i.first); @@ -5428,10 +5452,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // we could also check for being within FEE_PER_KB, but if the fee calculation // ever changes, this might be missed, so let this go through uint64_t balance_subtotal = 0; + uint64_t unlocked_balance_subtotal = 0; for (uint32_t index_minor : subaddr_indices) + { balance_subtotal += balance_per_subaddr[index_minor]; + unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor]; + } THROW_WALLET_EXCEPTION_IF(needed_money > balance_subtotal, error::not_enough_money, balance_subtotal, needed_money, 0); + // first check overall balance is enough, then unlocked one, so we throw distinct exceptions + THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance_subtotal, error::not_enough_unlocked_money, + unlocked_balance_subtotal, needed_money, 0); for (uint32_t i : subaddr_indices) LOG_PRINT_L2("Candidate subaddress index for spending: " << i); @@ -5475,24 +5506,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } } - // early out if we know we can't make it anyway - // we could also check for being within FEE_PER_KB, but if the fee calculation - // ever changes, this might be missed, so let this go through - // first check overall balance is enough, then unlocked one, so we throw distinct exceptions - THROW_WALLET_EXCEPTION_IF(needed_money > balance(subaddr_account), error::not_enough_money, - unlocked_balance(subaddr_account), needed_money, 0); - THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance(subaddr_account), error::not_enough_unlocked_money, - unlocked_balance(subaddr_account), needed_money, 0); - // shuffle & sort output indices { std::random_device rd; std::mt19937 g(rd()); std::shuffle(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), g); std::shuffle(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), g); - auto sort_predicate = [&balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y) + auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y) { - return balance_per_subaddr[x.first] > balance_per_subaddr[y.first]; + return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first]; }; std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate); std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate); @@ -5803,7 +5825,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below std::vector<size_t> unused_dust_indices; const bool use_rct = use_fork_rules(4, 0); - THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unclocked balance in the entire wallet"); + THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet"); std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account); @@ -5840,11 +5862,32 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon); } +std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon) +{ + std::vector<size_t> unused_transfers_indices; + std::vector<size_t> unused_dust_indices; + const bool use_rct = use_fork_rules(4, 0); + // find output with the given key image + for (size_t i = 0; i < m_transfers.size(); ++i) + { + const transfer_details& td = m_transfers[i]; + if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) + { + if (td.is_rct() || is_valid_decomposed_amount(td.amount())) + unused_transfers_indices.push_back(i); + else + unused_dust_indices.push_back(i); + break; + } + } + return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon); +} + std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon) { uint64_t accumulated_fee, accumulated_outputs, accumulated_change; struct TX { - std::list<size_t> selected_transfers; + std::vector<size_t> selected_transfers; std::vector<cryptonote::tx_destination_entry> dsts; cryptonote::transaction tx; pending_tx ptx; @@ -6446,16 +6489,12 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent std::string data; bool r = epee::file_io_utils::load_file_to_string(filename, data); - if (!r) - { - fail_msg_writer() << tr("failed to read file ") << filename; - return 0; - } + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); + const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) { - fail_msg_writer() << "Bad key image export file magic in " << filename; - return 0; + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); } try @@ -6464,31 +6503,22 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent } catch (const std::exception &e) { - fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what(); - return 0; + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); } const size_t headerlen = 2 * sizeof(crypto::public_key); - if (data.size() < headerlen) - { - fail_msg_writer() << "Bad data size from file " << filename; - return 0; - } + THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) { - fail_msg_writer() << "Key images from " << filename << " are for a different account"; - return 0; + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); } const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); - if ((data.size() - headerlen) % record_size) - { - fail_msg_writer() << "Bad data size from file " << filename; - return 0; - } + THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, + error::wallet_internal_error, std::string("Bad data size from file ") + filename); size_t nki = (data.size() - headerlen) / record_size; std::vector<std::pair<crypto::key_image, crypto::signature>> ski; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f1e12a700..6e01d4e28 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -147,7 +147,7 @@ namespace tools }; private: - wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), 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_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), 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(RefreshDefault), 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_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} public: static const char* tr(const char* str); @@ -155,25 +155,22 @@ namespace tools static bool has_testnet_option(const boost::program_options::variables_map& vm); static void init_options(boost::program_options::options_description& desc_params); - //! \return Password retrieved from prompt. Logs error on failure. - static boost::optional<password_container> password_prompt(const bool new_password); - //! Uses stdin and stdout. Returns a wallet2 if no errors. - static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file); + static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors. static std::pair<std::unique_ptr<wallet2>, password_container> - make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file); + make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. - static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm); + static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); //! Just parses variables. - static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm); + static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only); - 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), m_light_wallet(false), m_light_wallet_scanned_block_height(0), m_light_wallet_blockchain_height(0), m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0) {} + 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(RefreshDefault), 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), m_light_wallet(false), m_light_wallet_scanned_block_height(0), m_light_wallet_blockchain_height(0), m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0) {} struct tx_scan_info_t { @@ -244,6 +241,12 @@ namespace tools bool m_incoming; }; + struct pool_payment_details + { + payment_details m_pd; + bool m_double_spend_seen; + }; + struct unconfirmed_transfer_details { cryptonote::transaction_prefix m_tx; @@ -282,7 +285,7 @@ namespace tools std::vector<cryptonote::tx_source_entry> sources; cryptonote::tx_destination_entry change_dts; std::vector<cryptonote::tx_destination_entry> splitted_dsts; // split, includes change - std::list<size_t> selected_transfers; + std::vector<size_t> selected_transfers; std::vector<uint8_t> extra; uint64_t unlock_time; bool use_rct; @@ -303,7 +306,7 @@ namespace tools uint64_t dust, fee; bool dust_added_to_fee; cryptonote::tx_destination_entry change_dts; - std::list<size_t> selected_transfers; + std::vector<size_t> selected_transfers; std::string key_images; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; @@ -501,10 +504,10 @@ namespace tools void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon); void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon); template<typename T> - void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count, + void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); - void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count, + void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx); @@ -521,6 +524,7 @@ namespace tools std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); + std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon); std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); @@ -530,7 +534,7 @@ namespace tools void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; - void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; + void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; uint64_t get_blockchain_current_height() const { return m_local_bc_height; } void rescan_spent(); @@ -585,7 +589,7 @@ namespace tools std::unordered_map<crypto::hash, payment_details> m; a & m; for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i) - m_unconfirmed_payments.insert(*i); + m_unconfirmed_payments.insert(std::make_pair(i->first, pool_payment_details{i->second, false})); } if(ver < 14) return; @@ -607,7 +611,14 @@ namespace tools a & m_address_book; if(ver < 17) return; - a & m_unconfirmed_payments; + if (ver < 22) + { + // we're loading an old version, where m_unconfirmed_payments payload was payment_details + std::unordered_multimap<crypto::hash, payment_details> m; + a & m; + for (const auto &i: m) + m_unconfirmed_payments.insert(std::make_pair(i.first, pool_payment_details{i.second, false})); + } if(ver < 18) return; a & m_scanned_pool_txs[0]; @@ -621,6 +632,9 @@ namespace tools if(ver < 21) return; a & m_attributes; + if(ver < 22) + return; + a & m_unconfirmed_payments; } /*! @@ -700,8 +714,8 @@ namespace tools std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon); std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon); - size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::list<size_t>& selected_transfers, bool smallest = false) const; - size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::list<size_t>& selected_transfers, bool smallest = false) const; + size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const; + size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const; void set_tx_note(const crypto::hash &txid, const std::string ¬e); std::string get_tx_note(const crypto::hash &txid) const; @@ -744,6 +758,7 @@ namespace tools uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_per_kb_fee(); + uint64_t adjust_mixin(uint64_t mixin); // Light wallet specific functions // fetch unspent outs from lw node and store in m_transfers @@ -757,7 +772,7 @@ namespace tools // Send an import request to lw node. returns info about import fee, address and payment_id bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); // get random outputs from light wallet server - void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); + void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); // Parse rct string bool light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const; // check if key image is ours @@ -797,7 +812,7 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const std::string& password); - void 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); + void 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, bool double_spend_seen); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids) const; @@ -808,7 +823,7 @@ namespace tools void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history); void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error); void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added); - uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon); + uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon); bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices); @@ -827,7 +842,7 @@ namespace tools std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const; void set_spent(size_t idx, uint64_t height); void set_unspent(size_t idx); - void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; @@ -846,7 +861,7 @@ namespace tools 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_multimap<crypto::hash, pool_payment_details> m_unconfirmed_payments; std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys; cryptonote::checkpoints m_checkpoints; std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys; @@ -908,16 +923,17 @@ namespace tools std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache; }; } -BOOST_CLASS_VERSION(tools::wallet2, 21) +BOOST_CLASS_VERSION(tools::wallet2, 22) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2) +BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5) BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0) -BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 1) -BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 1) +BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2) +BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 2) namespace boost { @@ -1137,7 +1153,14 @@ namespace boost } a & x.m_subaddr_index; } - + + template <class Archive> + inline void serialize(Archive& a, tools::wallet2::pool_payment_details& x, const boost::serialization::version_type ver) + { + a & x.m_pd; + a & x.m_double_spend_seen; + } + template <class Archive> inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver) { @@ -1172,7 +1195,16 @@ namespace boost a & x.sources; a & x.change_dts; a & x.splitted_dsts; - a & x.selected_transfers; + if (ver < 2) + { + // load list to vector + std::list<size_t> selected_transfers; + a & selected_transfers; + x.selected_transfers.clear(); + x.selected_transfers.reserve(selected_transfers.size()); + for (size_t t: selected_transfers) + x.selected_transfers.push_back(t); + } a & x.extra; a & x.unlock_time; a & x.use_rct; @@ -1184,6 +1216,9 @@ namespace boost } a & x.subaddr_account; a & x.subaddr_indices; + if (ver < 2) + return; + a & x.selected_transfers; } template <class Archive> @@ -1194,7 +1229,16 @@ namespace boost a & x.fee; a & x.dust_added_to_fee; a & x.change_dts; - a & x.selected_transfers; + if (ver < 2) + { + // load list to vector + std::list<size_t> selected_transfers; + a & selected_transfers; + x.selected_transfers.clear(); + x.selected_transfers.reserve(selected_transfers.size()); + for (size_t t: selected_transfers) + x.selected_transfers.push_back(t); + } a & x.key_images; a & x.tx_key; a & x.dests; @@ -1202,6 +1246,9 @@ namespace boost if (ver < 1) return; a & x.additional_tx_keys; + if (ver < 2) + return; + a & x.selected_transfers; } } } @@ -1291,7 +1338,7 @@ namespace tools // randomly select inputs for transaction // throw if requested send amount is greater than (unlocked) amount available to send - std::list<size_t> selected_transfers; + std::vector<size_t> selected_transfers; uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon); THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index a8c150ca7..432c820cb 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -869,7 +869,7 @@ struct WalletManager virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; //! checks for an update and returns version, hash and url - static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); + static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, std::string subdir); }; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index df01ec238..cc6bb1de2 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -30,7 +30,6 @@ #include <boost/filesystem/path.hpp> #include <boost/format.hpp> #include "common/i18n.h" -#include "common/scoped_message_writer.h" #include "common/util.h" #include "misc_log_ex.h" #include "string_tools.h" @@ -50,6 +49,20 @@ #define DEFAULT_MAX_CONCURRENCY 0 #endif +namespace +{ + class Print + { + public: + Print(const std::function<void(const std::string&, bool)> &p, bool em = false): print(p), emphasis(em) {} + ~Print() { print(ss.str(), emphasis); } + template<typename T> std::ostream &operator<<(const T &t) { ss << t; return ss; } + private: + const std::function<void(const std::string&, bool)> &print; + std::stringstream ss; + bool emphasis; + }; +} namespace wallet_args { @@ -73,6 +86,7 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, + const std::function<void(const std::string&, bool)> &print, const char *default_log_name, bool log_to_console) @@ -118,16 +132,16 @@ namespace wallet_args 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" + Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + Print(print) << 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; + Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage; + Print(print) << 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 << ")"; + Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; return false; } @@ -142,7 +156,7 @@ namespace wallet_args } else { - tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config; + MERROR(wallet_args::tr("Can't find config file ") << config); return false; } } @@ -167,14 +181,15 @@ namespace wallet_args if(command_line::has_arg(vm, arg_max_concurrency)) tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; if (!command_line::is_arg_defaulted(vm, arg_log_level)) MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level)); else MINFO("Setting log levels = " << getenv("MONERO_LOGS")); MINFO(wallet_args::tr("Logging to: ") << log_path); - tools::scoped_message_writer(epee::console_color_white, true) << boost::format(wallet_args::tr("Logging to %s")) % log_path; + + Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path; return {std::move(vm)}; } diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index cf23ffded..8974098ad 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -50,5 +50,6 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, + const std::function<void(const std::string&, bool)> &print, const char *default_log_name, bool log_to_console = false); } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 9d66f125e..41eb77451 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -765,6 +765,12 @@ namespace tools #define STRINGIZE_DETAIL(x) #x #define STRINGIZE(x) STRINGIZE_DETAIL(x) +#define THROW_WALLET_EXCEPTION(err_type, ...) \ + do { \ + LOG_ERROR("THROW EXCEPTION: " << #err_type); \ + tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ## __VA_ARGS__); \ + } while(0) + #define THROW_WALLET_EXCEPTION_IF(cond, err_type, ...) \ if (cond) \ { \ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 9e6a97bdc..09ed205c6 100755..100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -60,6 +60,16 @@ namespace const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; constexpr const char default_rpc_username[] = "monero"; + + boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify) + { + auto pwd_container = tools::password_container::prompt(verify, prompt); + if (!pwd_container) + { + MERROR("failed to read wallet password"); + } + return pwd_container; + } } namespace tools @@ -131,7 +141,7 @@ namespace tools walvars = m_wallet; else { - tmpwal = tools::wallet2::make_dummy(*m_vm); + tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter); walvars = tmpwal.get(); } boost::optional<epee::net_utils::http::login> http_login{}; @@ -227,19 +237,6 @@ namespace tools return false; } //------------------------------------------------------------------------------------------------------------------------------ - uint64_t wallet_rpc_server::adjust_mixin(uint64_t mixin) - { - if (mixin < 4 && m_wallet->use_fork_rules(6, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); - mixin = 4; - } - else if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3"); - mixin = 2; - } - return mixin; - } - //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) { entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); @@ -299,8 +296,9 @@ namespace tools entry.subaddr_index = { pd.m_subaddr_account, 0 }; } //------------------------------------------------------------------------------------------------------------------------------ - void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) + void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd) { + const tools::wallet2::payment_details &pd = ppd.m_pd; entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); entry.payment_id = string_tools::pod_to_hex(payment_id); if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos) @@ -311,6 +309,7 @@ namespace tools entry.unlock_time = pd.m_unlock_time; entry.fee = 0; // TODO entry.note = m_wallet->get_tx_note(pd.m_tx_hash); + entry.double_spend_seen = ppd.m_double_spend_seen; entry.type = "pool"; entry.subaddr_index = pd.m_subaddr_index; } @@ -605,7 +604,7 @@ namespace tools try { - uint64_t mixin = adjust_mixin(req.mixin); + uint64_t mixin = m_wallet->adjust_mixin(req.mixin); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); // reject proposed transactions if there are more than one. see on_transfer_split below. @@ -665,7 +664,7 @@ namespace tools try { - uint64_t mixin = adjust_mixin(req.mixin); + uint64_t mixin = m_wallet->adjust_mixin(req.mixin); uint64_t ptx_amount; std::vector<wallet2::pending_tx> ptx_vector; LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); @@ -782,7 +781,7 @@ namespace tools try { - uint64_t mixin = adjust_mixin(req.mixin); + uint64_t mixin = m_wallet->adjust_mixin(req.mixin); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); if (!req.do_not_relay) @@ -813,6 +812,101 @@ namespace tools } return true; } +//------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er) + { + std::vector<cryptonote::tx_destination_entry> dsts; + std::vector<uint8_t> extra; + + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + // validate the transfer requested and populate dsts & extra + std::list<wallet_rpc::transfer_destination> destination; + destination.push_back(wallet_rpc::transfer_destination()); + destination.back().amount = 0; + destination.back().address = req.address; + if (!validate_transfer(destination, req.payment_id, dsts, extra, er)) + { + return false; + } + + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(req.key_image, ki)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "failed to parse key image"; + return false; + } + + try + { + uint64_t mixin = adjust_mixin(req.mixin); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); + + if (ptx_vector.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No outputs found"; + return false; + } + if (ptx_vector.size() > 1) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Multiple transactions are created, which is not supposed to happen"; + return false; + } + if (ptx_vector[0].selected_transfers.size() > 1) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "The transaction uses multiple inputs, which is not supposed to happen"; + return false; + } + + if (!req.do_not_relay) + m_wallet->commit_tx(ptx_vector); + + // populate response with tx hashes + const wallet2::pending_tx &ptx = ptx_vector[0]; + res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)); + if (req.get_tx_key) + { + res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key); + } + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx.tx, blob); + res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + } + + return true; + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + return false; + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = e.what(); + return false; + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + return true; + } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { @@ -1069,6 +1163,7 @@ namespace tools rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); rpc_transfers.tx_size = txBlob.size(); rpc_transfers.subaddr_index = td.m_subaddr_index.minor; + rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); } } @@ -1357,9 +1452,9 @@ namespace tools { m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments; m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); } @@ -1430,10 +1525,10 @@ namespace tools m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments; m_wallet->get_unconfirmed_payments(pool_payments); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { - if (i->second.m_tx_hash == txid) + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { + if (i->second.m_pd.m_tx_hash == txid) { fill_transfer_entry(res.transfer, i->first, i->second); return true; @@ -1809,7 +1904,7 @@ namespace tools command_line::add_arg(desc, arg_password); po::store(po::parse_command_line(argc, argv, desc), vm2); } - std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first; + std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, password_prompter).first; if (!wal) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -1883,7 +1978,7 @@ namespace tools } std::unique_ptr<tools::wallet2> wal = nullptr; try { - wal = tools::wallet2::make_from_file(vm2, wallet_file).first; + wal = tools::wallet2::make_from_file(vm2, wallet_file, password_prompter).first; } catch (const std::exception& e) { @@ -1982,6 +2077,7 @@ int main(int argc, char** argv) { "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]", desc_params, po::positional_options_description(), + [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); }, "monero-wallet-rpc.log", true ); @@ -2018,11 +2114,19 @@ int main(int argc, char** argv) { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); if(!wallet_file.empty()) { - wal = tools::wallet2::make_from_file(*vm, wallet_file).first; + wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompter).first; } else { - wal = tools::wallet2::make_from_json(*vm, from_json); + try + { + wal = tools::wallet2::make_from_json(*vm, from_json, password_prompter); + } + catch (const std::exception &e) + { + MERROR("Error creating wallet: " << e.what()); + return 1; + } } if (!wal) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b38726cb7..2a51ddf46 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -79,6 +79,7 @@ namespace tools MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL) + MAP_JON_RPC_WE("sweep_single", on_sweep_single, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE) MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS) @@ -126,6 +127,7 @@ namespace tools bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er); + bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er); bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er); bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er); @@ -163,7 +165,7 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd); - void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); + void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &pd); bool not_open(epee::json_rpc::error& er); uint64_t adjust_mixin(uint64_t mixin); void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index ffc2e2d49..9bcc5138a 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -476,6 +476,49 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_SWEEP_SINGLE + { + struct request + { + std::string address; + uint32_t priority; + uint64_t mixin; + uint64_t unlock_time; + std::string payment_id; + bool get_tx_key; + std::string key_image; + bool do_not_relay; + bool get_tx_hex; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(priority) + KV_SERIALIZE(mixin) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) + KV_SERIALIZE(get_tx_key) + KV_SERIALIZE(key_image) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string tx_hash; + std::string tx_key; + uint64_t fee; + std::string tx_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_key) + KV_SERIALIZE(fee) + KV_SERIALIZE(tx_blob) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_STORE { struct request @@ -562,6 +605,7 @@ namespace wallet_rpc std::string tx_hash; uint64_t tx_size; uint32_t subaddr_index; + std::string key_image; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) @@ -570,6 +614,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_size) KV_SERIALIZE(subaddr_index) + KV_SERIALIZE(key_image) END_KV_SERIALIZE_MAP() }; @@ -580,11 +625,13 @@ namespace wallet_rpc std::string transfer_type; uint32_t account_index; std::set<uint32_t> subaddr_indices; + bool verbose; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(transfer_type) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) + KV_SERIALIZE(verbose) END_KV_SERIALIZE_MAP() }; @@ -794,6 +841,7 @@ namespace wallet_rpc std::string type; uint64_t unlock_time; cryptonote::subaddress_index subaddr_index; + bool double_spend_seen; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -807,6 +855,7 @@ namespace wallet_rpc KV_SERIALIZE(type); KV_SERIALIZE(unlock_time) KV_SERIALIZE(subaddr_index); + KV_SERIALIZE(double_spend_seen) END_KV_SERIALIZE_MAP() }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a5f5335db..762eee776 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -111,7 +111,7 @@ add_test( COMMAND hash-target-tests) set(enabled_tests - coretests + core_tests difficulty hash performance_tests diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index a0be3db96..fd07a5d2e 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -34,12 +34,8 @@ #include "include_base_utils.h" #include "version.h" - -using namespace epee; - #include <iostream> #include <sstream> -using namespace std; #include <boost/program_options.hpp> @@ -56,6 +52,8 @@ using namespace std; #endif namespace po = boost::program_options; +using namespace std; +using namespace epee; using namespace cryptonote; using namespace crypto; @@ -71,7 +69,7 @@ int main(int argc, char* argv[]) TRY_ENTRY(); - + tools::on_startup(); string_tools::set_module_name_and_folder(argv[0]); //set up logging options @@ -81,7 +79,7 @@ int main(int argc, char* argv[]) po::options_description desc("Allowed options"); // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc, cryptonote::arg_data_dir, tools::get_default_data_dir()); nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> >::init_options(desc); po::variables_map vm; diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index a24bd4fce..68f2e9816 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -58,10 +58,10 @@ set(core_tests_headers v2_tests.h rct.h) -add_executable(coretests +add_executable(core_tests ${core_tests_sources} ${core_tests_headers}) -target_link_libraries(coretests +target_link_libraries(core_tests PRIVATE cryptonote_core p2p @@ -69,10 +69,10 @@ target_link_libraries(coretests epee ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) -set_property(TARGET coretests +set_property(TARGET core_tests PROPERTY FOLDER "tests") add_test( - NAME coretests - COMMAND coretests --generate_and_play_test_data) + NAME core_tests + COMMAND core_tests --generate_and_play_test_data) diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 9f3939652..8e68554d3 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -29,8 +29,6 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" - #include "block_reward.h" using namespace epee; diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index db44cd279..94b636f82 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "block_validation.h" using namespace epee; using namespace cryptonote; diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index eaaa3e045..79aecb1c0 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "chain_split_1.h" using namespace std; diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index b04d05219..01cf00f7a 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "chain_switch_1.h" using namespace epee; using namespace cryptonote; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 2b713cab9..4c5dc6c9a 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -47,6 +47,7 @@ using namespace std; using namespace epee; +using namespace crypto; using namespace cryptonote; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 3e5b949c8..9eba347cd 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -47,6 +47,7 @@ namespace int main(int argc, char* argv[]) { TRY_ENTRY(); + tools::on_startup(); epee::string_tools::set_module_name_and_folder(argv[0]); //set up logging options diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 58114b026..d82120254 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "double_spend.h" using namespace epee; using namespace cryptonote; diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 5a9604fc1..4abdfbff5 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -29,8 +29,6 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" - #include "integer_overflow.h" using namespace epee; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 8a38cbc22..50f65cc67 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -30,7 +30,7 @@ #include "ringct/rctSigs.h" #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "rct.h" using namespace epee; using namespace crypto; diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index f9ec68e45..43c63dc53 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "ring_signature_1.h" using namespace epee; using namespace cryptonote; diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 0e4b2e71a..9987f80d6 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "tx_validation.h" using namespace epee; using namespace crypto; diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index 6c94ac76c..6c2f91fcf 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "chaingen.h" -#include "chaingen_tests_list.h" +#include "v2_tests.h" using namespace epee; using namespace crypto; diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 4c9e073d4..35a0bb9bd 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -34,6 +34,7 @@ using namespace epee; #include "common/command_line.h" +#include "common/util.h" #include "transactions_flow_test.h" namespace po = boost::program_options; @@ -58,6 +59,7 @@ namespace int main(int argc, char* argv[]) { TRY_ENTRY(); + tools::on_startup(); string_tools::set_module_name_and_folder(argv[0]); //set up logging options diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index 099e8e3bd..03751fc14 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "include_base_utils.h" -#include "common/command_line.h" #include "file_io_utils.h" #include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index c35d604a8..25a80fc68 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "include_base_utils.h" -#include "common/command_line.h" #include "file_io_utils.h" #include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index 3edf8cd19..756a8c847 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -29,6 +29,7 @@ #include <boost/program_options.hpp> #include "include_base_utils.h" #include "common/command_line.h" +#include "common/util.h" #include "fuzzer.h" #if (!defined(__clang__) || (__clang__ < 5)) @@ -48,6 +49,7 @@ using namespace boost::program_options; int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer) { TRY_ENTRY(); + tools::on_startup(); string_tools::set_module_name_and_folder(argv[0]); //set up logging options diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index 071297ba8..42a8fa997 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "include_base_utils.h" -#include "common/command_line.h" #include "file_io_utils.h" #include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index 21cd01bef..9e2b9f2cb 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "include_base_utils.h" -#include "common/command_line.h" #include "file_io_utils.h" #include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index 853ad7c8d..3434cd530 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -33,6 +33,7 @@ #include "wallet/wallet2_api.h" #include "wallet/wallet2.h" #include "include_base_utils.h" +#include "common/util.h" #include <boost/chrono/chrono.hpp> #include <boost/filesystem.hpp> @@ -1138,6 +1139,7 @@ TEST_F(WalletManagerMainnetTest, RecoverAndRefreshWalletMainNetAsync) int main(int argc, char** argv) { + tools::on_startup(); // we can override default values for "TESTNET_DAEMON_ADDRESS" and "WALLETS_ROOT_DIR" const char * testnet_daemon_addr = std::getenv("TESTNET_DAEMON_ADDRESS"); diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 376d7ee53..a5e5b7c0c 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -193,7 +193,7 @@ namespace { m_thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2); - m_tcp_server.get_config_object().m_pcommands_handler = &m_commands_handler; + m_tcp_server.get_config_object().set_handler(&m_commands_handler); m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; ASSERT_TRUE(m_tcp_server.init_server(clt_port, "127.0.0.1")); @@ -238,9 +238,10 @@ namespace static void TearDownTestCase() { // Stop server - test_levin_commands_handler commands_handler; - test_tcp_server tcp_server(epee::net_utils::e_connection_type_NET); - tcp_server.get_config_object().m_pcommands_handler = &commands_handler; + test_levin_commands_handler *commands_handler_ptr = new test_levin_commands_handler(); + test_levin_commands_handler &commands_handler = *commands_handler_ptr; + test_tcp_server tcp_server(epee::net_utils::e_connection_type_RPC); + tcp_server.get_config_object().set_handler(commands_handler_ptr, [](epee::levin::levin_commands_handler<test_connection_context> *handler)->void { delete handler; }); tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; if (!tcp_server.init_server(clt_port, "127.0.0.1")) return; @@ -627,6 +628,7 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser int main(int argc, char** argv) { + tools::on_startup(); epee::debug::get_set_enable_assert(true, false); //set up logging options mlog_configure(mlog_get_default_log_path("net_load_tests_clt.log"), true); diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index f74282683..ce9d8b6fe 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -151,6 +151,11 @@ namespace net_load_tests bool handle_new_connection(const boost::uuids::uuid& connection_id, bool ignore_close_fails = false) { size_t idx = m_next_opened_conn_idx.fetch_add(1, std::memory_order_relaxed); + if (idx >= m_connections.size()) + { + LOG_PRINT_L0("ERROR: connections overflow"); + exit(1); + } m_connections[idx] = connection_id; size_t prev_connection_count = m_opened_connection_count.fetch_add(1, std::memory_order_relaxed); diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index e6dee1639..a987aa4e2 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -215,6 +215,7 @@ namespace int main(int argc, char** argv) { + tools::on_startup(); //set up logging options mlog_configure(mlog_get_default_log_path("net_load_tests_srv.log"), true); @@ -224,8 +225,8 @@ int main(int argc, char** argv) if (!tcp_server.init_server(srv_port, "127.0.0.1")) return 1; - srv_levin_commands_handler commands_handler(tcp_server); - tcp_server.get_config_object().m_pcommands_handler = &commands_handler; + srv_levin_commands_handler *commands_handler = new srv_levin_commands_handler(tcp_server); + tcp_server.get_config_object().set_handler(commands_handler, [](epee::levin::levin_commands_handler<test_connection_context> *handler) { delete handler; }); tcp_server.get_config_object().m_invoke_timeout = 10000; //tcp_server.get_config_object().m_max_packet_size = max_packet_size; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 3c0283eca..459eecba4 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -28,6 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include "common/util.h" #include "performance_tests.h" #include "performance_utils.h" @@ -48,6 +49,7 @@ int main(int argc, char** argv) { + tools::on_startup(); set_process_affinity(1); set_thread_high_priority(); diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 061772cbd..6bf45ff83 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -40,7 +40,7 @@ void set_process_affinity(int core) { -#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) return; #elif defined(BOOST_WINDOWS) DWORD_PTR mask = 1; @@ -62,7 +62,7 @@ void set_process_affinity(int core) void set_thread_high_priority() { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) return; #elif defined(BOOST_WINDOWS) ::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index c7efcf074..e10648d20 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -55,6 +55,7 @@ set(unit_tests_sources serialization.cpp sha256.cpp slow_memmem.cpp + subaddress.cpp test_tx_utils.cpp test_peerlist.cpp test_protocol_pack.cpp diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 51e26c2bb..d9a7b8ad5 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -74,16 +74,12 @@ namespace { size_t inlen = sizeof(source); int mdlen = (int)sizeof(md); - int ret = keccak(source, inlen, md, mdlen); + keccak(source, inlen, md, mdlen); if (md[0] != 0x00) { return true; } - else if (!ret) - { - return true; - } else { return false; diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index d2aa31555..c749c2531 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -187,9 +187,11 @@ namespace typedef std::unique_ptr<test_connection> test_connection_ptr; - async_protocol_handler_test() + async_protocol_handler_test(): + m_pcommands_handler(new test_levin_commands_handler()), + m_commands_handler(*m_pcommands_handler) { - m_handler_config.m_pcommands_handler = &m_commands_handler; + m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; }); m_handler_config.m_invoke_timeout = invoke_timeout; m_handler_config.m_max_packet_size = max_packet_size; } @@ -212,7 +214,7 @@ namespace protected: boost::asio::io_service m_io_service; test_levin_protocol_handler_config m_handler_config; - test_levin_commands_handler m_commands_handler; + test_levin_commands_handler *m_pcommands_handler, &m_commands_handler; }; class positive_test_connection_to_levin_protocol_handler_calls : public async_protocol_handler_test diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 2b0904224..c235f49fd 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -115,13 +115,13 @@ public: virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& details) {} virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {} - virtual uint64_t get_txpool_tx_count() const { return 0; } + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } virtual void remove_txpool_tx(const crypto::hash& txid) {} virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const { return txpool_tx_meta_t(); } virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const { return false; } + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } virtual void add_block( const block& blk , const size_t& block_size diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 1706c43c9..86e7bcef3 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -35,6 +35,7 @@ #include "include_base_utils.h" #include "common/command_line.h" +#include "common/util.h" #include "unit_tests_utils.h" namespace po = boost::program_options; @@ -43,6 +44,7 @@ boost::filesystem::path unit_test::data_dir; int main(int argc, char** argv) { + tools::on_startup(); epee::string_tools::set_module_name_and_folder(argv[0]); mlog_configure(mlog_get_default_log_path("unit_tests.log"), true); epee::debug::get_set_enable_assert(true, false); @@ -51,7 +53,7 @@ int main(int argc, char** argv) po::options_description desc_options("Command line options"); const command_line::arg_descriptor<std::string> arg_data_dir = {"data-dir", "Data files directory", "", true}; - command_line::add_arg(desc_options, command_line::arg_data_dir, ""); + command_line::add_arg(desc_options, arg_data_dir, ""); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -64,7 +66,7 @@ int main(int argc, char** argv) return 1; if (vm["data-dir"].defaulted()) - unit_test::data_dir = boost::filesystem::path(epee::string_tools::get_current_module_folder()) + unit_test::data_dir = boost::filesystem::canonical(boost::filesystem::path(epee::string_tools::get_current_module_folder())) .parent_path().parent_path().parent_path().parent_path() .append("tests").append("data"); else diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index 6ff73b107..ccca0b799 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -76,7 +76,7 @@ TEST(select_outputs, one_out_of_N) tools::wallet2::transfer_container transfers = make_transfers_container(10); transfers[6].m_block_height = 700; std::vector<size_t> unused_indices({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); - std::list<size_t> selected; + std::vector<size_t> selected; SELECT(2); PICK(6); } @@ -93,7 +93,7 @@ TEST(select_outputs, order) transfers[3].m_block_height = 716; transfers[4].m_block_height = 701; std::vector<size_t> unused_indices({0, 1, 2, 3, 4}); - std::list<size_t> selected; + std::vector<size_t> selected; SELECT(0); PICK(3); // first the one that's far away PICK(2); // then the one that's close diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index af6afa636..ef6151efb 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -38,6 +38,7 @@ #include "ringct/rctSigs.h" #include "ringct/rctOps.h" +using namespace std; using namespace crypto; using namespace rct; diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 011082d1c..0750ab7d1 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -49,6 +49,7 @@ #include "gtest/gtest.h" #include "unit_tests_utils.h" using namespace std; +using namespace crypto; struct Struct { diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp new file mode 100644 index 000000000..c304b7347 --- /dev/null +++ b/tests/unit_tests/subaddress.cpp @@ -0,0 +1,118 @@ +// 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include <boost/filesystem.hpp> +#include "gtest/gtest.h" + +#include "include_base_utils.h" +#include "wallet/wallet2.h" +#include "crypto/crypto.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "wallet/api/subaddress.h" + +class WalletSubaddress : public ::testing::Test +{ + protected: + virtual void SetUp() + { + try + { + w1.generate(wallet_name, password, recovery_key, true, false); + } + catch (const std::exception& e) + { + LOG_ERROR("failed to generate wallet: " << e.what()); + throw e; + } + + w1.add_subaddress_account(test_label); + w1.set_subaddress_label(subaddress_index, test_label); + } + + virtual void TearDown() + { + boost::filesystem::wpath wallet_file(wallet_name); + boost::filesystem::wpath wallet_address_file(wallet_name + ".address.txt"); + boost::filesystem::wpath wallet_keys_file(wallet_name + ".keys"); + + if ( boost::filesystem::exists(wallet_file) ) + boost::filesystem::remove(wallet_file); + + if ( boost::filesystem::exists(wallet_address_file) ) + boost::filesystem::remove(wallet_address_file); + + if ( boost::filesystem::exists(wallet_keys_file) ) + boost::filesystem::remove(wallet_keys_file); + } + + tools::wallet2 w1; + std::string path_working_dir = "."; + std::string path_test_wallet = "test_wallet"; + const std::string wallet_name = path_working_dir + "/" + path_test_wallet; + const std::string password = "testpass"; + crypto::secret_key recovery_key = crypto::secret_key(); + const std::string test_label = "subaddress test label"; + + uint32_t major_index = 0; + uint32_t minor_index = 0; + const cryptonote::subaddress_index subaddress_index = {major_index, minor_index}; +}; + +TEST_F(WalletSubaddress, GetSubaddressLabel) +{ + EXPECT_EQ(test_label, w1.get_subaddress_label(subaddress_index)); +} + +TEST_F(WalletSubaddress, AddSubaddress) +{ + std::string label = "test adding subaddress"; + w1.add_subaddress(0, label); + EXPECT_EQ(label, w1.get_subaddress_label({0, 1})); +} + +TEST_F(WalletSubaddress, OutOfBoundsIndexes) +{ + try + { + w1.get_subaddress_label({1,0}); + } + catch(const std::exception& e) + { + EXPECT_STREQ("index_major is out of bound", e.what()); + } + try + { + w1.get_subaddress_label({0,2}); + } + catch(const std::exception& e) + { + EXPECT_STREQ("index.minor is out of bound", e.what()); + } +} diff --git a/utils/gpg_keys/stoffu.asc b/utils/gpg_keys/stoffu.asc new file mode 100644 index 000000000..5067ccf90 --- /dev/null +++ b/utils/gpg_keys/stoffu.asc @@ -0,0 +1,62 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFi08i4BEADYSPbRfJ4JEopmiYEzc4ipaEVu8yPJhX1sDy2Bad5FrzIFSuxU +ppcOxxsKjDBpd/OlDEAcf4RoCnWWBd7W9dEYxjHHl3rCCvQkgkD+kTXKu8piNzki +N6rCHNvpnbibicl/FYlNJsVCicLv2XS0GXEJnQeLEOvXLfl6iq3G31YcyRFKVKoE +t8+dR3B4pXIjgM4aRDcFiZcLqdzO4PIvouE6E/yqjuVDH4Y1MkEwFTw+q4+UPpzB +z6OpM+3we89me4OwEcj073N71QwneRl1I5Hltx/rJhBrHxHk0JEboj080QwGl3iw +a+weFLtWuqvwt+AsBOyiZpBOCEeiihZ6W5fWAKvVwSwQEimRF6loQdWrXriM8vfh +txZB0AqT/W/lNHVK/6pg8c3p+GREoEC0OySNymPYN6p8QEFCt1RhrhiUKTczkMFJ +g7t0rm06mPdy2Dm4OfkAeMmUPdlF5R8PSoQMmXcNu/Y0JayG5yuwROVndPiZnX6u +t/sjWpYavMuBoXwtQzP7PTNr1FDAIi/S7pwCdBSY+lqIG+u2qNimL6zXgay8WT6f +1CisFzqx0n81uhJc0buprsKKKZFaeHIs8ZJPj9Y3JI5jBHALAgJX/VIFOIN+4cWa +vaB+wB1sCenq2GvkCljdb8/XXuEtT5macaUAFkUem8U8JuotAdC5myaqKwARAQAB +tB1zdG9mZnUgPHN0b2ZmdUBwcm90b25tYWlsLmNoPokCOAQTAQIAIgUCWLTyLgIb +AwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQQdq4NDqewBLY4hAAkaIiEuhi +MDJ8R186OP8rk508gfz7Lw9Usj7MibLN8WWOvaqD+fWozH+J/ThyrWvBAfzr93xC +CWYYXunmUkeUHRkoY6djnJBjFfGQl5Yhil+bzE5foZjjs7nh4oaMc77TvaZqZfAg +r/DO79Qf12M7t4vR1EhRmSfmeo1IvgwxJqhnCiYrr+M/cSk9ywnsdgGa9zdHyHyl +IthYPrqPg9kQrJPfv6NWnesYULa1GVhC/HLbeO/TIn4c+G97FF7RSm8Z/o9oAZbR +Acka56HIDqM9kM6pWeDHzZalzOiAII8+izx7KbijltWTdjns0BeWuW1/ylq+kFQ+ +NhDxe8XIBvKc3EiypLrjND8JinY9Vq5TVSYGqw6BM+0PE+38El7a81Qg2saPWqDO +rPBx31CsDC88EeCTA/FgJKLOBdMjg6YHEjU9FioRQuifNis6hvfLhQJAPB2SgQl+ +ql7jShdIpPmwWaK5t/lRtGiaIyAMd4/PK3tjEV9CwB8hQhmUykJ/OjK0sLXg7aOX +nBTBhvZtrHUCI5tFgKDQMV6lHq1WFPIYj2Ah7Zn2MuyxVk+LB8aDPqIODMVO1ehO +U23lW2Xo64nEZS2+KwRk7A+o8/FDF1j2iDbCKm8xqOrmj2lu4nPU0MIdP3Y6xYUm +Y4C57G2tFelV6k//H4ZACRfpgvvLkrzaDhGJAhwEEAECAAYFAljtzhcACgkQCF0J +Lx9D1Royvw/7BBfxl9fvq+UtNu4SK+GD8C8pnznoRL1A1t6CtboG7c1TEhpCPxIu +XHnBs74D5ZR+kDDsJegH+gYsvX9JlB+Il75DA5IYTByvijhSBXHFHKxdXnmBa2l3 +hb1yn8sxkU0jT5HpgrdY2GH93NPnfrJ4KcvINQtDEIcLwYRcQA4i4kujK55w9Q71 +s/+23wkldmSub9PDX62S3DoK37Wqn+EIUbvRrXvU9GmHLgnNiyoTy3IhrdiXRRAm +Kei999ooJe8q3LgCcRg5mUshIV8RvcfJZOWjk4TCHNxRTZ2aaOakPrj87dH53LYS +Zl3q86CvcVk8tujY+YSGb6RviKydHDZRQkvNk5WQReVa5C39qakF98lK+5DGXqNi +mdtQKGO62Dr6OS/85ATzMJ8LZK6CkGMq8lwigtnJkgRFbZqhQ1/GtlKf3Qdrf9uh +1Ds1U/hzeXLWskIXNpYcMmFISaYlEu7fyU7JAPq7otuQWMJPJHvNVHt3OTMqz3kR +LfJxW8gq175GeJagOwjeBNQOTvvfyRqO1QtmZnoovvciH7MTLfYRPhScbtfAeTmx +UR8Lqd7OssmeD95tGmF9fG9U4JsCfSE78x5mtAWdKj725T4I4QHdX7LSxeFOrCn/ +IjrM9NMW+iJpH5RguPPRyYRlgR2UvFkA3Y8b2qIA40ZXzdAc0slwDpy5Ag0EWLTy +LgEQAL8SMWnZfiNXIaGe/yi8dI2gyAtafClcleSRwk3ehaBsb6jvYlpHdn8F7kSi +jwyqu55I8fub0gtQ57GYz9rwar0E7l7USWC2GbpEs0AITCB8zDzNObW2yTmm12lS +Vj+G5YvoqIH0ktf1I6QYZSV+E36EqX09SlPdkNk08JjSzafGrs1i3ImKDsdge4ca +OelIDTm/x44sUi4AuG9fdaNzigUzTZxAZWTeH1FK55WJCacUizAFvNoDo5PcL0eB +yLuh276f3Lj6PzMN94uLJQPsf52hfr41tZI3S7+9F1hX0hBdh8N11VKwx27lMRnt +iOvkeFCGjta5VX/QSAShX1FPUqG0eyAd00RgiaBLJIoqiO6Z0VU6aMnC3Ce/MWPh +oenYSjm7vVGoxGs192tW9WDsvgYINn/6GHMF5pXwZP1JIlt13l3TqUcuduafyu+B +KseIUCnsxvW0DG1jVipc6rrEFusAfNRDHChxdnMKjcA87dLJ22tsbVE85dBXiMy5 +Setsa/mJC5w9/dUc2IOsOB+y4GVP/aTiRZRfcWHSPCQlytqKf1CT52ySctCb4LWZ ++lKbnIvQBr6CS+BKYnBsT/6QPdNfjT4HPIT8S2DOow7Ggrwaxp5kmzKU0dbqu8Hy +s1f5lIjnr1pFWUL/fQKrIz/E5o/LfNhHXxVuwdKZmC+Lt8aRABEBAAGJAh8EGAEC +AAkFAli08i4CGwwACgkQQdq4NDqewBLlqw/+N1zKL3m/UMRZOYMHGzkkQXwLIN3i +u2XE+iLb11Lh6jKLUMb9k/NG4tX/xmXSJZhA/tHR0tSRH2s4Yv02k4pFQbABZjAD +tUZly4TJ2OlBWCFE/Jr2HZFtL8xJpus0Qqv8eJFehsc1X/W6obl2sj6DLcglYOWL +0WiSi4ITQvIyg2JKxwe2wea1RWIINpMBi1JfL/wl8P61y3HouEeDVcDXvKR3FC/7 +h7YGKqvfMWwZEu/f6kjA49PGiFOYhGGW3D4fBuVUKMmsoAhrUtrMEu+7wCA/Aekx +ZO90IcPk/733KkfPpAkvre3o50JgJnXUBe0ZYXYZn/yrSE0rnF4ydfsQ7d2y+YEf +8Iwmd7zulP750mGoMDJp09jqkrrj5VL9VAQn00cSFb4TPtdgcQyHWJ50SX8uuDJx +e5ECxj+5hFntjWpFG+p4v1xedTfIQLhPtO4OXljOFzUYVzhMk/pX2oFcFoT6+AOt +BPX5Rxw0bTsUnsnkD24OEhOaxiK/aAkummNT8hAK09NV/+rXHZZUL1n93SobnGJ/ +X4DlBRYA/tbn/Ol3vYzhWFzCDLrTwVsm8cTgY4bp+8I9ysUee5Rx4rw0qiyMxz6Z +zRBUCxarimrjaoaejm6pm1yckEj2gBlf0nOaSiWgQhSTOq82Og0oVZT4ol8aDUv/ +hnbUrKukLHjFgd8= +=GKYE +-----END PGP PUBLIC KEY BLOCK----- |