diff options
68 files changed, 1250 insertions, 351 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 730a0f875..f0be28845 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckLinkerFlag) include(CheckLibraryExists) +include(CheckFunctionExists) if (IOS) INCLUDE(CmakeLists_IOS.txt) @@ -80,6 +81,22 @@ function (add_linker_flag_if_supported flag var) endif() endfunction() +function (add_definition_if_function_found function var) + string(REPLACE "-" "_" supported ${function}_function) + check_function_exists(${function} ${supported}) + if(${${supported}}) + add_definitions("-D${var}") + endif() +endfunction() + +function (add_definition_if_library_exists library function header var) + string(REPLACE "-" "_" supported ${function}_library) + check_library_exists(${library} ${function} ${header} ${supported}) + if(${${supported}}) + add_definitions("-D${var}") + endif() +endfunction() + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) message(STATUS "Setting default build type: ${CMAKE_BUILD_TYPE}") @@ -395,6 +412,10 @@ if(STATIC AND NOT IOS) endif() endif() +add_definition_if_library_exists(c memset_s "string.h" HAVE_MEMSET_S) +add_definition_if_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZERO) +add_definition_if_function_found(strptime HAVE_STRPTIME) + add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) # Generate header for embedded translations @@ -867,10 +888,3 @@ option(INSTALL_VENDORED_LIBUNBOUND "Install libunbound binary built from source CHECK_C_COMPILER_FLAG(-std=c11 HAVE_C11) - -include(CheckLibraryExists) -include(CheckFunctionExists) - -check_library_exists(c memset_s "string.h" HAVE_MEMSET_S) -check_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZERO) -check_function_exists(strptime HAVE_STRPTIME) diff --git a/Dockerfile b/Dockerfile index 138dec4c7..f5d670633 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,16 +8,83 @@ RUN apt-get update && \ ca-certificates \ cmake \ g++ \ - libboost1.58-all-dev \ - libssl-dev \ - libzmq3-dev \ - libreadline-dev \ - libsodium-dev \ make \ pkg-config \ graphviz \ doxygen \ - git + git \ + curl \ + libtool-bin \ + autoconf \ + automake + +WORKDIR /usr/local + +## Boost +ARG BOOST_VERSION=1_66_0 +ARG BOOST_VERSION_DOT=1.66.0 +ARG BOOST_HASH=5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9 +RUN curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION_DOT}/boost_${BOOST_VERSION}.tar.bz2/download \ + && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ + && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ + && cd boost_${BOOST_VERSION} \ + && ./bootstrap.sh \ + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread threading=multi threadapi=pthread cflags="-fPIC" cxxflags="-fPIC" stage +ENV BOOST_ROOT /usr/local/boost_${BOOST_VERSION} + +# OpenSSL +ARG OPENSSL_VERSION=1.0.2n +ARG OPENSSL_HASH=370babb75f278c39e0c50e8c4e7493bc0f18db6867478341a832a982fd15a8fe +RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ + && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ + && cd openssl-${OPENSSL_VERSION} \ + && ./Configure linux-x86_64 no-shared --static -fPIC \ + && make build_crypto build_ssl \ + && make install +ENV OPENSSL_ROOT_DIR=/usr/local/openssl-${OPENSSL_VERSION} + +# ZMQ +ARG ZMQ_VERSION=v4.2.3 +ARG ZMQ_HASH=3226b8ebddd9c6c738ba42986822c26418a49afb +RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ + && cd libzmq \ + && test `git rev-parse HEAD` = ${ZMQ_HASH} || exit 1 \ + && ./autogen.sh \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --enable-static --disable-shared \ + && make \ + && make install \ + && ldconfig + +# zmq.hpp +ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 +RUN git clone https://github.com/zeromq/cppzmq.git -b ${ZMQ_VERSION} \ + && cd cppzmq \ + && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \ + && mv *.hpp /usr/local/include + +# Readline +ARG READLINE_VERSION=7.0 +ARG READLINE_HASH=750d437185286f40a369e1e4f4764eda932b9459b5ec9a731628393dd3d32334 +RUN curl -s -O https://ftp.gnu.org/gnu/readline/readline-${READLINE_VERSION}.tar.gz \ + && echo "${READLINE_HASH} readline-${READLINE_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf readline-${READLINE_VERSION}.tar.gz \ + && cd readline-${READLINE_VERSION} \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure \ + && make \ + && make install + +# Sodium +ARG SODIUM_VERSION=1.0.16 +ARG SODIUM_HASH=675149b9b8b66ff44152553fb3ebf9858128363d +RUN git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} \ + && cd libsodium \ + && test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \ + && ./autogen.sh \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure \ + && make \ + && make check \ + && make install WORKDIR /src COPY . . @@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.11.0.0 + git checkout tags/v0.11.1.0 ``` * Build: ``` diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index ee64da5d8..de270bfd0 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -77,6 +77,8 @@ class async_protocol_handler_config levin_commands_handler<t_connection_context>* m_pcommands_handler; void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*); + void delete_connections (size_t count, bool incoming); + public: typedef t_connection_context connection_context; uint64_t m_max_packet_size; @@ -101,6 +103,7 @@ public: {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); + void del_in_connections(size_t count); }; @@ -731,41 +734,50 @@ void async_protocol_handler_config<t_connection_context>::del_connection(async_p } //------------------------------------------------------------------------------------------ template<class t_connection_context> +void async_protocol_handler_config<t_connection_context>::delete_connections(size_t count, bool incoming) +{ + std::vector <boost::uuids::uuid> connections; + CRITICAL_REGION_BEGIN(m_connects_lock); + for (auto& c: m_connects) + { + if (c.second->m_connection_context.m_is_income == incoming) + connections.push_back(c.first); + } + + // close random connections from the provided set + // TODO or better just keep removing random elements (performance) + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + shuffle(connections.begin(), connections.end(), std::default_random_engine(seed)); + while (count > 0 && connections.size() > 0) + { + try + { + auto i = connections.end() - 1; + async_protocol_handler<t_connection_context> *conn = m_connects.at(*i); + del_connection(conn); + close(*i); + connections.erase(i); + } + catch (const std::out_of_range &e) + { + MWARNING("Connection not found in m_connects, continuing"); + } + --count; + } + + CRITICAL_REGION_END(); +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> void async_protocol_handler_config<t_connection_context>::del_out_connections(size_t count) { - std::vector <boost::uuids::uuid> out_connections; - CRITICAL_REGION_BEGIN(m_connects_lock); - for (auto& c: m_connects) - { - if (!c.second->m_connection_context.m_is_income) - out_connections.push_back(c.first); - } - - if (out_connections.size() == 0) - return; - - // close random out connections - // TODO or better just keep removing random elements (performance) - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - shuffle(out_connections.begin(), out_connections.end(), std::default_random_engine(seed)); - while (count > 0 && out_connections.size() > 0) - { - try - { - auto i = out_connections.end() - 1; - async_protocol_handler<t_connection_context> *conn = m_connects.at(*i); - del_connection(conn); - close(*i); - out_connections.erase(i); - } - catch (const std::out_of_range &e) - { - MWARNING("Connection not found in m_connects, continuing"); - } - --count; - } - - CRITICAL_REGION_END(); + delete_connections(count, false); +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +void async_protocol_handler_config<t_connection_context>::del_in_connections(size_t count) +{ + delete_connections(count, true); } //------------------------------------------------------------------------------------------ template<class t_connection_context> diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index 6517f1253..d93084ab0 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -115,7 +115,7 @@ namespace epee } if(resp_t.error.code || resp_t.error.message.size()) { - LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); return false; } result_struct = resp_t.result; diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h index d120112a6..70d1a9586 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -58,13 +58,10 @@ namespace epee wipeable_string &operator=(wipeable_string &&other); wipeable_string &operator=(const wipeable_string &other); - static void set_wipe(void *(*f)(void*, size_t)) { wipefunc = f; } - private: void grow(size_t sz, size_t reserved = 0); private: std::vector<char> buffer; - static void *(*wipefunc)(void*, size_t); }; } diff --git a/contrib/epee/src/memwipe.c b/contrib/epee/src/memwipe.c index 870c69757..9a83e67e8 100644 --- a/contrib/epee/src/memwipe.c +++ b/contrib/epee/src/memwipe.c @@ -31,6 +31,7 @@ #define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> #include <stdlib.h> +#include <unistd.h> #ifdef HAVE_EXPLICIT_BZERO #include <strings.h> #endif @@ -50,7 +51,12 @@ void *memwipe(void *ptr, size_t n) { if (memset_s(ptr, n, 0, n)) { +#ifdef NDEBUG + fprintf(stderr, "Error: memset_s failed\n"); + _exit(1); +#else abort(); +#endif } SCARECROW // might as well... return ptr; diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 5671ed9d9..cc43b8988 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -27,14 +27,13 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string.h> +#include "memwipe.h" #include "misc_log_ex.h" #include "wipeable_string.h" namespace epee { -void *(*wipeable_string::wipefunc)(void*, size_t) = NULL; - wipeable_string::wipeable_string(const wipeable_string &other): buffer(other.buffer) { @@ -55,12 +54,11 @@ wipeable_string::wipeable_string(const std::string &other) wipeable_string::wipeable_string(std::string &&other) { - CHECK_AND_ASSERT_THROW_MES(wipefunc, "wipefunc is not set"); grow(other.size()); memcpy(buffer.data(), other.c_str(), size()); if (!other.empty()) { - wipefunc(&other[0], other.size()); // we're kinda left with this again aren't we + memwipe(&other[0], other.size()); // we're kinda left with this again aren't we other = std::string(); } } @@ -78,30 +76,28 @@ wipeable_string::~wipeable_string() void wipeable_string::wipe() { - CHECK_AND_ASSERT_THROW_MES(wipefunc, "wipefunc is not set"); - wipefunc(buffer.data(), buffer.size() * sizeof(char)); + memwipe(buffer.data(), buffer.size() * sizeof(char)); } void wipeable_string::grow(size_t sz, size_t reserved) { - CHECK_AND_ASSERT_THROW_MES(wipefunc, "wipefunc is not set"); if (reserved < sz) reserved = sz; if (reserved <= buffer.capacity()) { if (sz < buffer.size()) - wipefunc(buffer.data() + sz, buffer.size() - sz); + memwipe(buffer.data() + sz, buffer.size() - sz); buffer.resize(sz); return; } size_t old_sz = buffer.size(); std::unique_ptr<char[]> tmp{new char[old_sz]}; memcpy(tmp.get(), buffer.data(), old_sz * sizeof(char)); - wipefunc(buffer.data(), old_sz * sizeof(char)); + memwipe(buffer.data(), old_sz * sizeof(char)); buffer.reserve(reserved); buffer.resize(sz); memcpy(buffer.data(), tmp.get(), old_sz * sizeof(char)); - wipefunc(tmp.get(), old_sz * sizeof(char)); + memwipe(tmp.get(), old_sz * sizeof(char)); } void wipeable_string::push_back(char c) diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c index 87b244ce7..bb0420453 100644 --- a/external/db_drivers/liblmdb/mdb.c +++ b/external/db_drivers/liblmdb/mdb.c @@ -1635,7 +1635,11 @@ mdb_assert_fail(MDB_env *env, const char *expr_txt, if (env->me_assert_func) env->me_assert_func(env, buf); fprintf(stderr, "%s\n", buf); +#ifdef NDEBUG + _exit(); +#else abort(); +#endif } #else # define mdb_assert0(env, expr, expr_txt) ((void) 0) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 57742b2e5..212a1822d 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -17,6 +17,8 @@ #define EASYLOGGING_CC #include "easylogging++.h" +#include <unistd.h> + #if defined(AUTO_INITIALIZE_EASYLOGGINGPP) INITIALIZE_EASYLOGGINGPP #endif @@ -36,7 +38,11 @@ static void abort(int status, const std::string& reason) { // Ignore msvc critical error dialog - break instead (on debug mode) _asm int 3 #else +#ifdef NDEBUG + ::_exit(1); +#else ::abort(); +#endif #endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) } diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 6b81a4c90..5210fc3cd 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -338,6 +338,12 @@ mdb_txn_safe::~mdb_txn_safe() num_active_txns--; } +void mdb_txn_safe::uncheck() +{ + num_active_txns--; + m_check = false; +} + void mdb_txn_safe::commit(std::string message) { if (message.size() == 0) @@ -1439,9 +1445,10 @@ void BlockchainLMDB::unlock() #define TXN_PREFIX_RDONLY() \ MDB_txn *m_txn; \ mdb_txn_cursors *m_cursors; \ + mdb_txn_safe auto_txn; \ bool my_rtxn = block_rtxn_start(&m_txn, &m_cursors); \ - mdb_txn_safe auto_txn(my_rtxn); \ - if (my_rtxn) auto_txn.m_tinfo = m_tinfo.get() + if (my_rtxn) auto_txn.m_tinfo = m_tinfo.get(); \ + else auto_txn.uncheck() #define TXN_POSTFIX_RDONLY() #define TXN_POSTFIX_SUCCESS() \ diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index ceae2f084..1b76abf35 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -112,6 +112,7 @@ struct mdb_txn_safe // BlockchainLMDB destructor to call mdb_txn_safe destructor, as that's too late // to properly abort, since mdb_env_close would have been called earlier. void abort(); + void uncheck(); operator MDB_txn*() { diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 06f127c25..1ecdae8ec 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -36,13 +36,21 @@ #include <boost/filesystem/fstream.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> +#include <boost/algorithm/string/join.hpp> using namespace epee; namespace bf = boost::filesystem; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.dns" -#define DEFAULT_DNS_PUBLIC_ADDR "8.8.4.4" +static const char *DEFAULT_DNS_PUBLIC_ADDR[] = +{ + "194.150.168.168", // CCC (Germany) + "81.3.27.54", // Lightning Wire Labs (Germany) + "31.3.135.232", // OpenNIC (Switzerland) + "80.67.169.40", // FDN (France) + "209.58.179.186", // Cyberghost (Singapore) +}; static boost::mutex instance_lock; @@ -201,13 +209,13 @@ public: DNSResolver::DNSResolver() : m_data(new DNSResolverData()) { int use_dns_public = 0; - std::string dns_public_addr = DEFAULT_DNS_PUBLIC_ADDR; + std::vector<std::string> dns_public_addr; if (auto res = getenv("DNS_PUBLIC")) { dns_public_addr = tools::dns_utils::parse_dns_public(res); if (!dns_public_addr.empty()) { - MGINFO("Using public DNS server: " << dns_public_addr << " (TCP)"); + MGINFO("Using public DNS server(s): " << boost::join(dns_public_addr, ", ") << " (TCP)"); use_dns_public = 1; } else @@ -221,7 +229,8 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData()) if (use_dns_public) { - ub_ctx_set_fwd(m_data->m_ub_context, dns_public_addr.c_str()); + for (const auto &ip: dns_public_addr) + ub_ctx_set_fwd(m_data->m_ub_context, ip.c_str()); ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no")); ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes")); } @@ -526,15 +535,16 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std return true; } -std::string parse_dns_public(const char *s) +std::vector<std::string> parse_dns_public(const char *s) { unsigned ip0, ip1, ip2, ip3; char c; - std::string dns_public_addr; + std::vector<std::string> dns_public_addr; if (!strcmp(s, "tcp")) { - dns_public_addr = DEFAULT_DNS_PUBLIC_ADDR; - LOG_PRINT_L0("Using default public DNS server: " << dns_public_addr << " (TCP)"); + for (size_t i = 0; i < sizeof(DEFAULT_DNS_PUBLIC_ADDR) / sizeof(DEFAULT_DNS_PUBLIC_ADDR[0]); ++i) + dns_public_addr.push_back(DEFAULT_DNS_PUBLIC_ADDR[i]); + LOG_PRINT_L0("Using default public DNS server(s): " << boost::join(dns_public_addr, ", ") << " (TCP)"); } else if (sscanf(s, "tcp://%u.%u.%u.%u%c", &ip0, &ip1, &ip2, &ip3, &c) == 4) { @@ -544,7 +554,7 @@ std::string parse_dns_public(const char *s) } else { - dns_public_addr = std::string(s + strlen("tcp://")); + dns_public_addr.push_back(std::string(s + strlen("tcp://"))); } } else diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index d5dc03283..f46bca3dd 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -167,7 +167,7 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls); -std::string parse_dns_public(const char *s); +std::vector<std::string> parse_dns_public(const char *s); } // namespace tools::dns_utils diff --git a/src/common/int-util.h b/src/common/int-util.h index 11e39895e..3bcc085e2 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -34,7 +34,10 @@ #include <stdbool.h> #include <stdint.h> #include <string.h> + +#ifndef _MSC_VER #include <sys/param.h> +#endif #if defined(__ANDROID__) #include <byteswap.h> @@ -206,6 +209,12 @@ static inline void memcpy_swap64(void *dst, const void *src, size_t n) { } } +#ifdef _MSC_VER +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +#endif + #if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); #endif diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index 64c84ed19..9665966ae 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -72,10 +72,10 @@ namespace tools fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { - fail_msg_writer() << "Daemon request failed"; + fail_msg_writer() << "basic_json_rpc_request: Daemon request failed"; return false; } else @@ -95,15 +95,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << " -- json_rpc_request: " << res.status; return false; } else @@ -123,15 +123,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << "-- rpc_request: " << res.status; return false; } else diff --git a/src/common/util.cpp b/src/common/util.cpp index 659ea31b8..e0f3cd655 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -552,8 +552,6 @@ std::string get_nix_version_display_string() } bool on_startup() { - wipeable_string::set_wipe(&memwipe); - mlog_configure("", true); sanitize_locale(); diff --git a/src/crypto/chacha.c b/src/crypto/chacha.c index f573083be..5d3edb98d 100644 --- a/src/crypto/chacha.c +++ b/src/crypto/chacha.c @@ -6,7 +6,9 @@ Public domain. #include <memory.h> #include <stdio.h> +#ifndef _MSC_VER #include <sys/param.h> +#endif #include "chacha.h" #include "common/int-util.h" diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index b28854a13..d9b8b6787 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -28,6 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include <unistd.h> #include <cassert> #include <cstddef> #include <cstdint> @@ -43,6 +44,18 @@ #include "crypto.h" #include "hash.h" +namespace { + static void local_abort(const char *msg) + { + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif + } +} + namespace crypto { using std::abort; @@ -467,7 +480,7 @@ POP_WARNINGS ec_scalar sum, k, h; boost::shared_ptr<rs_comm> buf(reinterpret_cast<rs_comm *>(malloc(rs_comm_size(pubs_count))), free); if (!buf) - abort(); + local_abort("malloc failure"); assert(sec_index < pubs_count); #if !defined(NDEBUG) { @@ -486,7 +499,7 @@ POP_WARNINGS } #endif if (ge_frombytes_vartime(&image_unp, &image) != 0) { - abort(); + local_abort("invalid key image"); } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); @@ -505,7 +518,7 @@ POP_WARNINGS random_scalar(sig[i].c); random_scalar(sig[i].r); if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { - abort(); + local_abort("invalid pubkey"); } ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); ge_tobytes(&buf->ab[i].a, &tmp2); diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index fc6d487c2..528a5406b 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -4,9 +4,20 @@ #include <stdio.h> #include <stdlib.h> +#include <unistd.h> #include "hash-ops.h" #include "keccak.h" +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + const uint64_t keccakf_rndc[24] = { 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, @@ -83,8 +94,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) if (mdlen <= 0 || mdlen > 200 || sizeof(st) != 200) { - fprintf(stderr, "Bad keccak use"); - abort(); + local_abort("Bad keccak use"); } rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; @@ -101,8 +111,7 @@ void 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(); + local_abort("Bad keccak use"); } memcpy(temp, in, inlen); diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index 0afec6212..9e31ebf46 100644 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -53,6 +53,12 @@ #include <unistd.h> #endif +#ifdef _MSC_VER +#define GETPID() _getpid() +#else +#define GETPID() getpid() +#endif + #include "oaes_config.h" #include "oaes_lib.h" @@ -478,7 +484,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, - _test + timer.millitm, getpid() ); + _test + timer.millitm, GETPID() ); #else struct timeval timer; struct tm *gmTimer; @@ -490,7 +496,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.tv_usec/1000, - _test + timer.tv_usec/1000, getpid() ); + _test + timer.tv_usec/1000, GETPID() ); #endif if( _test ) @@ -510,7 +516,7 @@ static uint32_t oaes_get_seed(void) _test = (char *) calloc( sizeof( char ), timer.millitm ); _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + - (uintptr_t) ( _test + timer.millitm ) + getpid(); + (uintptr_t) ( _test + timer.millitm ) + GETPID(); #else struct timeval timer; struct tm *gmTimer; @@ -522,7 +528,7 @@ static uint32_t oaes_get_seed(void) _test = (char *) calloc( sizeof( char ), timer.tv_usec/1000 ); _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.tv_usec/1000 + - (uintptr_t) ( _test + timer.tv_usec/1000 ) + getpid(); + (uintptr_t) ( _test + timer.tv_usec/1000 ) + GETPID(); #endif if( _test ) diff --git a/src/crypto/random.c b/src/crypto/random.c index cd46a1362..929377943 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -45,7 +45,11 @@ static void generate_system_random_bytes(size_t n, void *result); static void generate_system_random_bytes(size_t n, void *result) { HCRYPTPROV prov; +#ifdef NDEBUG +#define must_succeed(x) do if (!(x)) { fprintf(stderr, "Failed: " #x); _exit(1); } while (0) +#else #define must_succeed(x) do if (!(x)) abort(); while (0) +#endif must_succeed(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)); must_succeed(CryptGenRandom(prov, (DWORD)n, result)); must_succeed(CryptReleaseContext(prov, 0)); diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 59fd20bf9..e6d6a267c 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -34,7 +34,9 @@ #include "hash-ops.h" -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) +#ifdef _MSC_VER +#include <malloc.h> +#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) #include <alloca.h> #else #include <stdlib.h> diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 705af978a..5cd1709ab 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -40,7 +40,7 @@ namespace cryptonote struct cryptonote_connection_context: public epee::net_utils::connection_context_base { cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), - m_callback_request_count(0), m_last_known_hash(crypto::null_hash) {} + m_last_request_time(boost::posix_time::microsec_clock::universal_time()), m_callback_request_count(0), m_last_known_hash(crypto::null_hash) {} enum state { diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 80bd2fdc9..143133163 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -299,7 +299,7 @@ namespace boost throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) + if (x.type == rct::RCTTypeSimple) // moved to prunable with bulletproofs a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -313,6 +313,8 @@ namespace boost if (x.rangeSigs.empty()) a & x.bulletproofs; a & x.MGs; + if (x.rangeSigs.empty()) + a & x.pseudoOuts; } template <class Archive> @@ -325,7 +327,7 @@ namespace boost throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) + if (x.type == rct::RCTTypeSimple) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -335,6 +337,8 @@ namespace boost if (x.p.rangeSigs.empty()) a & x.p.bulletproofs; a & x.p.MGs; + if (x.type == rct::RCTTypeSimpleBulletproof) + a & x.p.pseudoOuts; } } } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 0ece65028..f9460e7db 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -92,7 +92,7 @@ #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading -#define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day +#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 @@ -138,6 +138,7 @@ #define HASH_OF_HASHES_STEP 256 +#define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes // New constants are intended to go here namespace config diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 178479f3c..f165aa46c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -127,7 +127,8 @@ static const struct { { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 { 6, 971400, 0, 1501709789 }, - { 7, 1057028, 0, 1512211236 }, + { 7, 1057027, 0, 1512211236 }, + { 8, 1057058, 0, 1515967497 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -2395,11 +2396,11 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v7, allow bulletproofs - if (hf_version < 7 || !m_testnet) { + // from v8, allow bulletproofs + if (hf_version < 8) { if (!tx.rct_signatures.p.bulletproofs.empty()) { - MERROR("Bulletproofs are not allowed before v7 or on mainnet"); + MERROR("Bulletproofs are not allowed before v8"); tvc.m_invalid_output = true; return false; } @@ -3753,6 +3754,10 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list<c if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP) return hashes.size(); + // if we're getting old blocks, we might have jettisoned the hashes already + if (m_blocks_hash_check.empty()) + return hashes.size(); + // find hashes encompassing those block size_t first_index = height / HASH_OF_HASHES_STEP; size_t last_index = (height + hashes.size() - 1) / HASH_OF_HASHES_STEP; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 81a7b4724..aaa5ccdbc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -137,6 +137,11 @@ namespace cryptonote , "Relay blocks as fluffy blocks where possible (automatic on testnet)" , false }; + static const command_line::arg_descriptor<size_t> arg_max_txpool_size = { + "max-txpool-size" + , "Set maximum txpool size in bytes." + , DEFAULT_TXPOOL_MAX_SIZE + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -241,6 +246,7 @@ namespace cryptonote command_line::add_arg(desc, arg_test_dbg_lock_sleep); command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); + command_line::add_arg(desc, arg_max_txpool_size); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -359,6 +365,7 @@ namespace cryptonote 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); + size_t max_txpool_size = command_line::get_arg(vm, arg_max_txpool_size); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -477,7 +484,7 @@ namespace cryptonote r = m_blockchain_storage.init(db.release(), m_testnet, m_offline, test_options); - r = m_mempool.init(); + r = m_mempool.init(max_txpool_size); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); // now that we have a valid m_blockchain_storage, we can clean out any diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 431d71556..4a10f7133 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -172,20 +172,24 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys) + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr) { - if (destinations.empty()) - return null_pkey; - for (size_t n = 1; n < destinations.size(); ++n) + account_public_address addr = {null_pkey, null_pkey}; + size_t count = 0; + for (const auto &i : destinations) { - if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr))) + if (i.amount == 0) continue; - if (destinations[n].amount == 0) + if (change_addr && i.addr == *change_addr) continue; - if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr))) + if (i.addr == addr) + continue; + if (count > 0) return null_pkey; + addr = i.addr; + ++count; } - return destinations[0].addr.m_view_public_key; + return addr.m_view_public_key; } //--------------------------------------------------------------- bool construct_tx_with_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, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) @@ -221,7 +225,7 @@ namespace cryptonote if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { LOG_PRINT_L2("Encrypting payment id " << payment_id); - crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys); + crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr); if (view_key_pub == null_pkey) { LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index e3b7a4f8c..1c390078d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -88,7 +88,7 @@ namespace cryptonote }; //--------------------------------------------------------------- - crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr); bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); bool construct_tx_with_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, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e75584bce..5f54e93f1 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -102,7 +102,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0) { } @@ -151,13 +151,20 @@ namespace cryptonote } uint64_t outputs_amount = get_outs_money_amount(tx); - if(outputs_amount >= inputs_amount) + if(outputs_amount > inputs_amount) { LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); tvc.m_verifivation_failed = true; tvc.m_overspend = true; return false; } + else if(outputs_amount == inputs_amount) + { + LOG_PRINT_L1("transaction fee is zero: outputs_amount == inputs_amount, rejecting."); + tvc.m_verifivation_failed = true; + tvc.m_fee_too_low = true; + return false; + } fee = inputs_amount - outputs_amount; } @@ -295,8 +302,12 @@ namespace cryptonote } tvc.m_verifivation_failed = false; + m_txpool_size += blob_size; MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); + + prune(m_txpool_max_size); + return true; } //--------------------------------------------------------------------------------- @@ -309,6 +320,72 @@ namespace cryptonote return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); } //--------------------------------------------------------------------------------- + size_t tx_memory_pool::get_txpool_size() const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_txpool_size; + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::set_txpool_max_size(size_t bytes) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + m_txpool_max_size = bytes; + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::prune(size_t bytes) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + if (bytes == 0) + bytes = m_txpool_max_size; + CRITICAL_REGION_LOCAL1(m_blockchain); + LockedTXN lock(m_blockchain); + + // this will never remove the first one, but we don't care + auto it = --m_txs_by_fee_and_receive_time.end(); + while (it != m_txs_by_fee_and_receive_time.begin()) + { + if (m_txpool_size <= bytes) + break; + try + { + const crypto::hash &txid = it->second; + txpool_tx_meta_t meta; + if (!m_blockchain.get_txpool_tx_meta(txid, meta)) + { + MERROR("Failed to find tx in txpool"); + return; + } + // don't prune the kept_by_block ones, they're likely added because we're adding a block with those + if (meta.kept_by_block) + { + --it; + continue; + } + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::transaction tx; + if (!parse_and_validate_tx_from_blob(txblob, tx)) + { + MERROR("Failed to parse tx from txpool"); + return; + } + // remove first, in case this throws, so key images aren't removed + MINFO("Pruning tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); + m_blockchain.remove_txpool_tx(txid); + m_txpool_size -= txblob.size(); + remove_transaction_keyimages(tx); + MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); + m_txs_by_fee_and_receive_time.erase(it--); + } + catch (const std::exception &e) + { + MERROR("Error while pruning txpool: " << e.what()); + return; + } + } + if (m_txpool_size > bytes) + MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); + } + //--------------------------------------------------------------------------------- bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block) { for(const auto& in: tx.vin) @@ -391,6 +468,7 @@ namespace cryptonote // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); + m_txpool_size -= blob_size; remove_transaction_keyimages(tx); } catch (const std::exception &e) @@ -463,6 +541,7 @@ namespace cryptonote { // remove first, so we only remove key images if the tx removal succeeds m_blockchain.remove_txpool_tx(txid); + m_txpool_size -= bd.size(); remove_transaction_keyimages(tx); } } @@ -1125,7 +1204,9 @@ namespace cryptonote size_t tx_size_limit = get_transaction_size_limit(version); std::unordered_set<crypto::hash> remove; + m_txpool_size = 0; m_blockchain.for_all_txpool_txes([this, &remove, tx_size_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { + m_txpool_size += meta.blob_size; if (meta.blob_size >= tx_size_limit) { LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.blob_size << " bytes), removing it from pool"); remove.insert(txid); @@ -1154,6 +1235,7 @@ namespace cryptonote } // remove tx from db first m_blockchain.remove_txpool_tx(txid); + m_txpool_size -= txblob.size(); remove_transaction_keyimages(tx); auto sorted_it = find_tx_in_sorted_container(txid); if (sorted_it == m_txs_by_fee_and_receive_time.end()) @@ -1176,13 +1258,15 @@ namespace cryptonote return n_removed; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init() + bool tx_memory_pool::init(size_t max_txpool_size) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + m_txpool_max_size = max_txpool_size ? max_txpool_size : DEFAULT_TXPOOL_MAX_SIZE; m_txs_by_fee_and_receive_time.clear(); m_spent_key_images.clear(); + m_txpool_size = 0; std::vector<crypto::hash> remove; bool r = m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) { cryptonote::transaction tx; @@ -1197,6 +1281,7 @@ namespace cryptonote return false; } m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid); + m_txpool_size += meta.blob_size; return true; }, true); if (!r) diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index b4ea5a8f4..19cd83ed9 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -198,11 +198,11 @@ namespace cryptonote /** * @brief loads pool state (if any) from disk, and initializes pool * - * @param config_folder folder name where pool state will be + * @param max_txpool_size the max size in bytes * * @return true */ - bool init(); + bool init(size_t max_txpool_size = 0); /** * @brief attempts to save the transaction pool state to disk @@ -362,6 +362,19 @@ namespace cryptonote */ size_t validate(uint8_t version); + /** + * @brief get the cumulative txpool size in bytes + * + * @return the cumulative txpool size in bytes + */ + size_t get_txpool_size() const; + + /** + * @brief set the max cumulative txpool size in bytes + * + * @param bytes the max cumulative txpool size in bytes + */ + void set_txpool_max_size(size_t bytes); #define CURRENT_MEMPOOL_ARCHIVE_VER 11 #define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12 @@ -496,6 +509,13 @@ namespace cryptonote */ void mark_double_spend(const transaction &tx); + /** + * @brief prune lowest fee/byte txes till we're not above bytes + * + * if bytes is 0, use m_txpool_max_size + */ + void prune(size_t bytes = 0); + //TODO: confirm the below comments and investigate whether or not this // is the desired behavior //! map key images to transactions which spent them @@ -542,6 +562,9 @@ private: std::unordered_set<crypto::hash> m_timed_out_transactions; Blockchain& m_blockchain; //!< reference to the Blockchain object + + size_t m_txpool_max_size; + size_t m_txpool_size; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index cbb8273e9..e24c964a5 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -79,7 +79,7 @@ namespace cryptonote typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler; typedef CORE_SYNC_DATA payload_type; - t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout); + t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout, bool offline = false); BEGIN_INVOKE_MAP2(cryptonote_protocol_handler) HANDLE_NOTIFY_T2(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index bc11ab6e4..ff187e8ae 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -61,10 +61,10 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------------------------- template<class t_core> - t_cryptonote_protocol_handler<t_core>::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout):m_core(rcore), + t_cryptonote_protocol_handler<t_core>::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout, bool offline):m_core(rcore), m_p2p(p_net_layout), m_syncronized_connections_count(0), - m_synchronized(false), + m_synchronized(offline), m_stopping(false) { @@ -1333,6 +1333,15 @@ skip: break; } + // this one triggers if all threads are in standby, which should not happen, + // but happened at least once, so we unblock at least one thread if so + const boost::unique_lock<boost::mutex> sync{m_sync_lock, boost::try_to_lock}; + if (sync.owns_lock()) + { + LOG_DEBUG_CC(context, "No other thread is adding blocks, resuming"); + break; + } + if (should_download_next_span(context)) { MDEBUG(context << " we should try for that next span too, we think we could get it faster, resuming"); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 3ec74ff79..09e425dd1 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -428,6 +428,23 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) return m_executor.out_peers(limit); } +bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) +{ + if (args.empty()) return false; + + unsigned int limit; + try { + limit = std::stoi(args[0]); + } + + catch(const std::exception& ex) { + _erro("stoi exception"); + return false; + } + + return m_executor.in_peers(limit); +} + bool t_command_parser_executor::start_save_graph(const std::vector<std::string>& args) { if (!args.empty()) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 37e900b8f..2c09a4748 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -108,7 +108,9 @@ public: bool set_limit_down(const std::vector<std::string>& args); bool out_peers(const std::vector<std::string>& args); - + + bool in_peers(const std::vector<std::string>& args); + bool start_save_graph(const std::vector<std::string>& args); bool stop_save_graph(const std::vector<std::string>& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 1f8981fa2..a50dbea69 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -197,6 +197,12 @@ t_command_server::t_command_server( , "Set the <max_number> of out peers." ); m_command_lookup.set_handler( + "in_peers" + , std::bind(&t_command_parser_executor::in_peers, &m_parser, p::_1) + , "in_peers <max_number>" + , "Set the <max_number> of in peers." + ); + m_command_lookup.set_handler( "start_save_graph" , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) , "Start saving data for dr monero." diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 2d662b7d3..9b15e62ca 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -68,7 +68,7 @@ public: boost::program_options::variables_map const & vm ) : core{vm} - , protocol{vm, core} + , protocol{vm, core, command_line::get_arg(vm, cryptonote::arg_offline)} , p2p{vm, protocol} { // Handle circular dependencies diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index f4d8fad5e..7bac2d3d8 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -287,7 +287,7 @@ int main(int argc, char const * argv[]) MINFO("Moving from main() into the daemonize now."); - return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm); + return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm) ? 0 : 1; } catch (std::exception const & ex) { diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index a251ae47c..fd1d1b638 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -46,9 +46,9 @@ private: public: t_protocol( boost::program_options::variables_map const & vm - , t_core & core + , t_core & core, bool offline = false ) - : m_protocol{core.get(), nullptr} + : m_protocol{core.get(), nullptr, offline} { MGINFO("Initializing cryptonote protocol..."); if (!m_protocol.init(vm)) diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index c1c2329ac..17f6c7f67 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -60,28 +60,28 @@ public: ) : m_server{core.get(), p2p.get()}, m_description{description} { - MGINFO("Initializing " << m_description << " rpc server..."); + MGINFO("Initializing " << m_description << " RPC server..."); if (!m_server.init(vm, restricted, testnet, port)) { - throw std::runtime_error("Failed to initialize " + m_description + " rpc server."); + throw std::runtime_error("Failed to initialize " + m_description + " RPC server."); } - MGINFO(m_description << " rpc server initialized OK on port: " << m_server.get_binded_port()); + MGINFO(m_description << " RPC server initialized OK on port: " << m_server.get_binded_port()); } void run() { - MGINFO("Starting " << m_description << " rpc server..."); + MGINFO("Starting " << m_description << " RPC server..."); if (!m_server.run(2, false)) { - throw std::runtime_error("Failed to start " + m_description + " rpc server."); + throw std::runtime_error("Failed to start " + m_description + " RPC server."); } - MGINFO(m_description << " rpc server started ok"); + MGINFO(m_description << " RPC server started ok"); } void stop() { - MGINFO("Stopping " << m_description << " rpc server..."); + MGINFO("Stopping " << m_description << " RPC server..."); m_server.send_stop_signal(); m_server.timed_wait_server_stop(5000); } @@ -93,11 +93,11 @@ public: ~t_rpc() { - MGINFO("Deinitializing " << m_description << " rpc server..."); + MGINFO("Deinitializing " << m_description << " RPC server..."); try { m_server.deinit(); } catch (...) { - MERROR("Failed to deinitialize " << m_description << " rpc server..."); + MERROR("Failed to deinitialize " << m_description << " RPC server..."); } } }; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 2da4f3e6e..5ef799d40 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -353,15 +353,18 @@ static std::string get_fork_extra_info(uint64_t t, uint64_t now, uint64_t block_ return ""; } -static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires) +static float get_sync_percentage(uint64_t height, uint64_t target_height) { - uint64_t height = ires.height; - uint64_t target_height = ires.target_height ? ires.target_height < ires.height ? ires.height : ires.target_height : ires.height; + target_height = target_height ? target_height < height ? height : target_height : height; float pc = 100.0f * height / target_height; if (height < target_height && pc > 99.9f) return 99.9f; // to avoid 100% when not fully synced return pc; } +static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires) +{ + return get_sync_percentage(ires.height, ires.target_height); +} bool t_rpc_command_executor::show_status() { cryptonote::COMMAND_RPC_GET_INFO::request ireq; @@ -421,12 +424,26 @@ bool t_rpc_command_executor::show_status() { std::time_t uptime = std::time(nullptr) - ires.start_time; uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height; + std::string bootstrap_msg; + if (ires.was_bootstrap_ever_used) + { + bootstrap_msg = ", bootstrapping from " + ires.bootstrap_daemon_address; + if (ires.untrusted) + { + bootstrap_msg += (boost::format(", local height: %llu (%.1f%%)") % ires.height_without_bootstrap % get_sync_percentage(ires.height_without_bootstrap, net_height)).str(); + } + else + { + bootstrap_msg += " was used before"; + } + } - tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") + tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) % (ires.testnet ? "testnet" : "mainnet") + % bootstrap_msg % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version @@ -1262,7 +1279,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) if (m_is_rpc) { - if (!m_rpc_client->json_rpc_request(req, res, "out_peers", fail_message.c_str())) + if (!m_rpc_client->rpc_request(req, res, "/out_peers", fail_message.c_str())) { return true; } @@ -1281,6 +1298,38 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) return true; } +bool t_rpc_command_executor::in_peers(uint64_t limit) +{ + cryptonote::COMMAND_RPC_IN_PEERS::request req; + cryptonote::COMMAND_RPC_IN_PEERS::response res; + + epee::json_rpc::error error_resp; + + req.in_peers = limit; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/in_peers", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_in_peers(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + std::cout << "Max number of in peers set to " << limit << std::endl; + + return true; +} + bool t_rpc_command_executor::start_save_graph() { cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index f0781180a..fa83d8988 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -122,7 +122,9 @@ public: bool set_limit(int64_t limit_down, int64_t limit_up); bool out_peers(uint64_t limit); - + + bool in_peers(uint64_t limit); + bool start_save_graph(); bool stop_save_graph(); diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 121e72392..994941168 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -55,6 +55,7 @@ namespace nodetool const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1}; + const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1}; const command_line::arg_descriptor<int64_t> arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", -1}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 20520f83c..568c650cc 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -81,6 +81,7 @@ namespace nodetool node_server(t_payload_net_handler& payload_handler) :m_payload_handler(payload_handler), m_current_number_of_out_peers(0), + m_current_number_of_in_peers(0), m_allow_local_ip(false), m_hide_my_port(false), m_no_igd(false), @@ -117,8 +118,10 @@ namespace nodetool bool log_connections(); virtual uint64_t get_connections_count(); size_t get_outgoing_connections_count(); + size_t get_incoming_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} - void delete_connections(size_t count); + void delete_out_connections(size_t count); + void delete_in_connections(size_t count); virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_host(const epee::net_utils::network_address &address); virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } @@ -230,6 +233,7 @@ namespace nodetool bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container); bool set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max); + bool set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max); bool set_tos_flag(const boost::program_options::variables_map& vm, int limit); bool set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit); @@ -271,6 +275,7 @@ namespace nodetool public: config m_config; // TODO was private, add getters? std::atomic<unsigned int> m_current_number_of_out_peers; + std::atomic<unsigned int> m_current_number_of_in_peers; void set_save_graph(bool save_graph) { @@ -345,6 +350,7 @@ namespace nodetool extern const command_line::arg_descriptor<bool> arg_no_igd; extern const command_line::arg_descriptor<bool> arg_offline; extern const command_line::arg_descriptor<int64_t> arg_out_peers; + extern const command_line::arg_descriptor<int64_t> arg_in_peers; extern const command_line::arg_descriptor<int> arg_tos_flag; extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 152dba942..ee7e62768 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -85,6 +85,7 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_out_peers); + command_line::add_arg(desc, arg_in_peers); command_line::add_arg(desc, arg_tos_flag); command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); @@ -315,6 +316,9 @@ namespace nodetool if ( !set_max_out_peers(vm, command_line::get_arg(vm, arg_out_peers) ) ) return false; + if ( !set_max_in_peers(vm, command_line::get_arg(vm, arg_in_peers) ) ) + return false; + if ( !set_tos_flag(vm, command_line::get_arg(vm, arg_tos_flag) ) ) return false; @@ -565,14 +569,23 @@ namespace nodetool while (!is_closing && !m_net_server.is_stop_signal_sent()) { // main loop of thread //number_of_peers = m_net_server.get_config_object().get_connections_count(); - unsigned int number_of_peers = 0; + unsigned int number_of_in_peers = 0; + unsigned int number_of_out_peers = 0; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if (!cntxt.m_is_income) ++number_of_peers; + if (cntxt.m_is_income) + { + ++number_of_in_peers; + } + else + { + ++number_of_out_peers; + } return true; }); // lambda - m_current_number_of_out_peers = number_of_peers; + m_current_number_of_in_peers = number_of_in_peers; + m_current_number_of_out_peers = number_of_out_peers; boost::this_thread::sleep_for(boost::chrono::seconds(1)); } // main loop of thread @@ -871,11 +884,11 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { - if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit + if (m_current_number_of_out_peers == m_config.m_net_config.max_out_connection_count) // out peers limit { return false; } - else if (m_current_number_of_out_peers > m_config.m_net_config.connections_count) + else if (m_current_number_of_out_peers > m_config.m_net_config.max_out_connection_count) { m_net_server.get_config_object().del_out_connections(1); m_current_number_of_out_peers --; // atomic variable, update time = 1s @@ -1151,6 +1164,7 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::connections_maker() { + if (m_offline) return true; if (!connect_to_peerlist(m_exclusive_peers)) return false; if (!m_exclusive_peers.empty()) return true; @@ -1164,10 +1178,10 @@ namespace nodetool if (!connect_to_peerlist(m_priority_peers)) return false; - size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + size_t expected_white_connections = (m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; size_t conn_count = get_outgoing_connections_count(); - if(conn_count < m_config.m_net_config.connections_count) + if(conn_count < m_config.m_net_config.max_out_connection_count) { if(conn_count < expected_white_connections) { @@ -1178,20 +1192,20 @@ namespace nodetool if(!make_expected_connections_count(white, expected_white_connections)) return false; //then do grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; }else { //start from grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; //and then do white list - if(!make_expected_connections_count(white, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(white, m_config.m_net_config.max_out_connection_count)) return false; } } - if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.connections_count) + if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.max_out_connection_count) { MINFO("Failed to connect to any, trying seeds"); if (!connect_to_seed()) @@ -1253,6 +1267,20 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + size_t node_server<t_payload_net_handler>::get_incoming_connections_count() + { + size_t count = 0; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.m_is_income) + ++count; + return true; + }); + + return count; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::idle_worker() { m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this)); @@ -1618,6 +1646,13 @@ namespace nodetool return 1; } + if (m_current_number_of_in_peers >= m_config.m_net_config.max_in_connection_count) // in peers limit + { + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but already have max incoming connections, so dropping this one."); + drop_connection(context); + return 1; + } + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); @@ -1779,20 +1814,37 @@ namespace nodetool bool node_server<t_payload_net_handler>::set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max) { if(max == -1) { - m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.max_out_connection_count = P2P_DEFAULT_CONNECTIONS_COUNT; + return true; + } + m_config.m_net_config.max_out_connection_count = max; + return true; + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max) + { + if(max == -1) { + m_config.m_net_config.max_in_connection_count = -1; return true; } - m_config.m_net_config.connections_count = max; + m_config.m_net_config.max_in_connection_count = max; return true; } template<class t_payload_net_handler> - void node_server<t_payload_net_handler>::delete_connections(size_t count) + void node_server<t_payload_net_handler>::delete_out_connections(size_t count) { m_net_server.get_config_object().del_out_connections(count); } template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_in_connections(size_t count) + { + m_net_server.get_config_object().del_in_connections(count); + } + + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { if(flag==-1){ @@ -1884,6 +1936,7 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::gray_peerlist_housekeeping() { + if (m_offline) return true; if (!m_exclusive_peers.empty()) return true; peerlist_entry pe = AUTO_VAL_INIT(pe); diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 348a8b978..e793e19b6 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -131,13 +131,15 @@ namespace nodetool struct network_config { BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(connections_count) + KV_SERIALIZE(max_out_connection_count) + KV_SERIALIZE(max_in_connection_count) KV_SERIALIZE(handshake_interval) KV_SERIALIZE(packet_max_size) KV_SERIALIZE(config_id) END_KV_SERIALIZE_MAP() - uint32_t connections_count; + uint32_t max_out_connection_count; + uint32_t max_in_connection_count; uint32_t connection_timeout; uint32_t ping_connection_timeout; uint32_t handshake_interval; diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 67e877326..fd15ffbc4 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -297,6 +297,39 @@ static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) return res; } +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) +{ + rct::keyV data; + data.reserve(3); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2) +{ + rct::keyV data; + data.reserve(4); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2, const rct::key &mash3) +{ + rct::keyV data; + data.reserve(5); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + data.push_back(mash3); + return hash_cache = rct::hash_to_scalar(data); +} + /* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) { @@ -329,6 +362,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) } PERF_TIMER_STOP(PROVE_aLaR); + rct::key hash_cache = rct::hash_to_scalar(V); // DEBUG: Test to ensure this recovers the value #ifdef DEBUG_BP @@ -361,11 +395,8 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::addKeys(S, ve, rct::scalarmultBase(rho)); // PAPER LINES 43-45 - rct::keyV hashed; - hashed.push_back(A); - hashed.push_back(S); - rct::key y = rct::hash_to_scalar(hashed); - rct::key z = rct::hash_to_scalar(y); + rct::key y = hash_cache_mash(hash_cache, A, S); + rct::key z = hash_cache = rct::hash_to_scalar(y); // Polynomial construction before PAPER LINE 46 rct::key t0 = rct::zero(); @@ -427,11 +458,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); // PAPER LINES 49-51 - hashed.clear(); - hashed.push_back(z); - hashed.push_back(T1); - hashed.push_back(T2); - rct::key x = rct::hash_to_scalar(hashed); + rct::key x = hash_cache_mash(hash_cache, z, T1, T2); // PAPER LINES 52-53 rct::key taux = rct::zero(); @@ -460,12 +487,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) #endif // PAPER LINES 32-33 - hashed.clear(); - hashed.push_back(x); - hashed.push_back(taux); - hashed.push_back(mu); - hashed.push_back(t); - rct::key x_ip = rct::hash_to_scalar(hashed); + rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); // These are used in the inner product rounds size_t nprime = N; @@ -509,20 +531,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); // PAPER LINES 21-22 - hashed.clear(); - if (round == 0) - { - hashed.push_back(L[0]); - hashed.push_back(R[0]); - w[0] = rct::hash_to_scalar(hashed); - } - else - { - hashed.push_back(w[round - 1]); - hashed.push_back(L[round]); - hashed.push_back(R[round]); - w[round] = rct::hash_to_scalar(hashed); - } + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); // PAPER LINES 24-25 const rct::key winv = invert(w[round]); @@ -563,6 +572,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) { init_exponents(); + CHECK_AND_ASSERT_MES(proof.V.size() == 1, false, "V does not have exactly one element"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); CHECK_AND_ASSERT_MES(proof.L.size() == 6, false, "Proof is not for 64 bits"); @@ -573,26 +583,15 @@ bool bulletproof_VERIFY(const Bulletproof &proof) // Reconstruct the challenges PERF_TIMER_START_BP(VERIFY); PERF_TIMER_START_BP(VERIFY_start); - rct::keyV hashed; - hashed.push_back(proof.A); - hashed.push_back(proof.S); - rct::key y = rct::hash_to_scalar(hashed); - rct::key z = rct::hash_to_scalar(y); - hashed.clear(); - hashed.push_back(z); - hashed.push_back(proof.T1); - hashed.push_back(proof.T2); - rct::key x = rct::hash_to_scalar(hashed); + rct::key hash_cache = rct::hash_to_scalar(proof.V[0]); + rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S); + rct::key z = hash_cache = rct::hash_to_scalar(y); + rct::key x = hash_cache_mash(hash_cache, z, proof.T1, proof.T2); PERF_TIMER_STOP(VERIFY_start); PERF_TIMER_START_BP(VERIFY_line_60); // Reconstruct the challenges - hashed.clear(); - hashed.push_back(x); - hashed.push_back(proof.taux); - hashed.push_back(proof.mu); - hashed.push_back(proof.t); - rct::key x_ip = hash_to_scalar(hashed); + rct::key x_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t); PERF_TIMER_STOP(VERIFY_line_60); PERF_TIMER_START_BP(VERIFY_line_61); @@ -647,17 +646,9 @@ bool bulletproof_VERIFY(const Bulletproof &proof) // PAPER LINES 21-22 // The inner product challenges are computed per round rct::keyV w(rounds); - hashed.clear(); - hashed.push_back(proof.L[0]); - hashed.push_back(proof.R[0]); - w[0] = rct::hash_to_scalar(hashed); - for (size_t i = 1; i < rounds; ++i) + for (size_t i = 0; i < rounds; ++i) { - hashed.clear(); - hashed.push_back(w[i-1]); - hashed.push_back(proof.L[i]); - hashed.push_back(proof.R[i]); - w[i] = rct::hash_to_scalar(hashed); + w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); } PERF_TIMER_STOP(VERIFY_line_21_22); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 3c34a5637..0c2be5add 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -43,6 +43,30 @@ using namespace std; #define MONERO_DEFAULT_LOG_CATEGORY "ringct" namespace rct { + bool is_simple(int type) + { + switch (type) + { + case RCTTypeSimple: + case RCTTypeSimpleBulletproof: + return true; + default: + return false; + } + } + + bool is_bulletproof(int type) + { + switch (type) + { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: + return true; + default: + return false; + } + } + Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) { mask = rct::skGen(); @@ -357,7 +381,8 @@ namespace rct { std::stringstream ss; binary_archive<true> ba(ss); - const size_t inputs = rv.pseudoOuts.size(); + CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing"); + const size_t inputs = is_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size(); const size_t outputs = rv.ecdhInfo.size(); CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs), "Failed to serialize rctSigBase"); @@ -750,25 +775,26 @@ namespace rct { // TODO: unused ?? // key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.pseudoOuts.resize(inamounts.size()); + keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + pseudoOuts.resize(inamounts.size()); rv.p.MGs.resize(inamounts.size()); key sumpouts = zero(); //sum pseudoOut masks keyV a(inamounts.size()); for (i = 0 ; i < inamounts.size() - 1; i++) { skGen(a[i]); sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); - genC(rv.pseudoOuts[i], a[i], inamounts[i]); + genC(pseudoOuts[i], a[i], inamounts[i]); } rv.mixRing = mixRing; sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); - genC(rv.pseudoOuts[i], a[i], inamounts[i]); - DP(rv.pseudoOuts[i]); + genC(pseudoOuts[i], a[i], inamounts[i]); + DP(pseudoOuts[i]); key full_message = get_pre_mlsag_hash(rv); if (msout) msout->c.resize(inamounts.size()); for (i = 0 ; i < inamounts.size(); i++) { - rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i]); + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i]); } return rv; } @@ -876,16 +902,26 @@ namespace rct { if (semantics) { if (rv.type == RCTTypeSimpleBulletproof) + { CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); + } else + { CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); + } CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); } else { // semantics check is early, and mixRing/MGs aren't resolved yet - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + if (rv.type == RCTTypeSimpleBulletproof) + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); + else + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); } const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); @@ -894,6 +930,8 @@ namespace rct { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; + const keyV &pseudoOuts = is_bulletproof(rv.type) ? rv.p.pseudoOuts : rv.pseudoOuts; + if (semantics) { key sumOutpks = identity(); for (size_t i = 0; i < rv.outPk.size(); i++) { @@ -904,8 +942,8 @@ namespace rct { addKeys(sumOutpks, txnFeeKey, sumOutpks); key sumPseudoOuts = identity(); - for (size_t i = 0 ; i < rv.pseudoOuts.size() ; i++) { - addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + for (size_t i = 0 ; i < pseudoOuts.size() ; i++) { + addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]); } DP(sumPseudoOuts); @@ -941,7 +979,7 @@ namespace rct { results.resize(rv.mixRing.size()); for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); }); } waiter.wait(); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 2df797360..eba1e3d93 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -246,7 +246,7 @@ namespace rct { // inputs/outputs not saved, only here for serialization help // FIELD(message) - not serialized, it can be reconstructed // FIELD(mixRing) - not serialized, it can be reconstructed - if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) + if (type == RCTTypeSimple) // moved to prunable with bulletproofs { ar.tag("pseudoOuts"); ar.begin_array(); @@ -294,6 +294,7 @@ namespace rct { std::vector<rangeSig> rangeSigs; std::vector<Bulletproof> bulletproofs; std::vector<mgSig> MGs; // simple rct has N, full has 1 + keyV pseudoOuts; //C - for simple rct 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) @@ -381,6 +382,21 @@ namespace rct { ar.delimit_array(); } ar.end_array(); + if (type == RCTTypeSimpleBulletproof) + { + ar.tag("pseudoOuts"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts); + if (pseudoOuts.size() != inputs) + return false; + for (size_t i = 0; i < inputs; ++i) + { + FIELDS(pseudoOuts[i]) + if (inputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } return true; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 140094faa..da2e79dfa 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -42,6 +42,7 @@ using namespace epee; #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "misc_language.h" +#include "storages/http_abstract_invoke.h" #include "crypto/hash.h" #include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" @@ -75,6 +76,8 @@ namespace cryptonote command_line::add_arg(desc, arg_testnet_rpc_bind_port); command_line::add_arg(desc, arg_testnet_rpc_restricted_bind_port); command_line::add_arg(desc, arg_restricted_rpc); + command_line::add_arg(desc, arg_bootstrap_daemon_address); + command_line::add_arg(desc, arg_bootstrap_daemon_login); cryptonote::rpc_args::init_options(desc); } //------------------------------------------------------------------------------------------------------------------------------ @@ -101,6 +104,30 @@ namespace cryptonote if (!rpc_config) return false; + m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address); + if (!m_bootstrap_daemon_address.empty()) + { + const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login); + const auto loc = bootstrap_daemon_login.find(':'); + if (!bootstrap_daemon_login.empty() && loc != std::string::npos) + { + epee::net_utils::http::login login; + login.username = bootstrap_daemon_login.substr(0, loc); + login.password = bootstrap_daemon_login.substr(loc + 1); + m_http_client.set_server(m_bootstrap_daemon_address, login, false); + } + else + { + m_http_client.set_server(m_bootstrap_daemon_address, boost::none, false); + } + m_should_use_bootstrap_daemon = true; + } + else + { + m_should_use_bootstrap_daemon = false; + } + m_was_bootstrap_ever_used = false; + boost::optional<epee::net_utils::http::login> http_login{}; if (rpc_config->login) @@ -126,6 +153,10 @@ namespace cryptonote bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { PERF_TIMER(on_get_height); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HEIGHT>(invoke_http_mode::JON, "/getheight", req, res, r)) + return r; + res.height = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -134,6 +165,17 @@ namespace cryptonote bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { PERF_TIMER(on_get_info); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r)) + { + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + crypto::hash top_hash; + m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); + ++res.height_without_bootstrap; // turn top block height into blockchain height + res.was_bootstrap_ever_used = true; + return r; + } + crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height @@ -158,6 +200,12 @@ namespace cryptonote res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + res.height_without_bootstrap = res.height; + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; + } return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -181,6 +229,10 @@ namespace cryptonote bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { PERF_TIMER(on_get_blocks); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r)) + return r; + 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)) @@ -240,6 +292,10 @@ 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) { PERF_TIMER(on_get_alt_blocks_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_ALT_BLOCKS_HASHES>(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r)) + return r; + std::list<block> blks; if(!m_core.get_alternative_blocks(blks)) @@ -263,6 +319,10 @@ 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) { PERF_TIMER(on_get_blocks_by_height); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r)) + return r; + res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -293,6 +353,10 @@ namespace cryptonote bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res) { PERF_TIMER(on_get_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r)) + return r; + NOTIFY_RESPONSE_CHAIN_ENTRY::request resp; resp.start_height = req.start_height; @@ -312,6 +376,10 @@ 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) { PERF_TIMER(on_get_random_outs); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -351,6 +419,10 @@ 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) { PERF_TIMER(on_get_outs_bin); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS_BIN>(invoke_http_mode::BIN, "/get_outs.bin", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -374,6 +446,10 @@ namespace cryptonote bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) { PERF_TIMER(on_get_outs); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS>(invoke_http_mode::JON, "/get_outs", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -412,6 +488,10 @@ 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) { PERF_TIMER(on_get_random_rct_outs); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS>(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r)) + return r; + res.status = "Failed"; if(!m_core.get_random_rct_outs(req, res)) { @@ -436,6 +516,10 @@ 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) { PERF_TIMER(on_get_indexes); + bool ok; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(invoke_http_mode::BIN, "/get_o_indexes.bin", req, res, ok)) + return ok; + bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); if(!r) { @@ -450,6 +534,10 @@ namespace cryptonote bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { PERF_TIMER(on_get_transactions); + bool ok; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok)) + return ok; + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -600,6 +688,10 @@ namespace cryptonote 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) { PERF_TIMER(on_is_key_image_spent); + bool ok; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok)) + return ok; + std::vector<crypto::key_image> key_images; for(const auto& ki_hex_str: req.key_images) { @@ -663,6 +755,10 @@ 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); + bool ok; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_SEND_RAW_TX>(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok)) + return ok; + CHECK_CORE_READY(); std::string tx_blob; @@ -886,6 +982,10 @@ namespace cryptonote 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) { PERF_TIMER(on_get_transaction_pool); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL>(invoke_http_mode::JON, "/get_transaction_pool", req, res, r)) + return r; + 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; @@ -894,6 +994,10 @@ namespace cryptonote 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) { PERF_TIMER(on_get_transaction_pool_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES>(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r)) + return r; + m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; @@ -902,6 +1006,10 @@ namespace cryptonote 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) { PERF_TIMER(on_get_transaction_pool_stats); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_STATS>(invoke_http_mode::JON, "/get_transaction_pool_stats", req, res, r)) + return r; + m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; @@ -920,6 +1028,14 @@ namespace cryptonote bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { PERF_TIMER(on_getblockcount); + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res.status = "This command is unsupported for bootstrap daemon"; + return false; + } + } res.count = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -928,6 +1044,14 @@ 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) { PERF_TIMER(on_getblockhash); + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res = "This command is unsupported for bootstrap daemon"; + return false; + } + } if(req.size() != 1) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; @@ -964,6 +1088,10 @@ 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); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GETBLOCKTEMPLATE>(invoke_http_mode::JON_RPC, "getblocktemplate", req, res, r)) + return r; + if(!check_core_ready()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; @@ -1039,6 +1167,14 @@ 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); + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res.status = "This command is unsupported for bootstrap daemon"; + return false; + } + } CHECK_CORE_READY(); if(req.size()!=1) { @@ -1112,9 +1248,80 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + template <typename COMMAND_TYPE> + bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r) + { + res.untrusted = false; + if (m_bootstrap_daemon_address.empty()) + return false; + + boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + if (!m_should_use_bootstrap_daemon) + { + MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon"); + return false; + } + + auto current_time = std::chrono::system_clock::now(); + if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s + { + m_bootstrap_height_check_time = current_time; + + uint64_t top_height; + crypto::hash top_hash; + m_core.get_blockchain_top(top_height, top_hash); + ++top_height; // turn top block height into blockchain height + + // query bootstrap daemon's height + cryptonote::COMMAND_RPC_GET_HEIGHT::request getheight_req; + cryptonote::COMMAND_RPC_GET_HEIGHT::response getheight_res; + bool ok = epee::net_utils::invoke_http_json("/getheight", getheight_req, getheight_res, m_http_client); + ok = ok && getheight_res.status == CORE_RPC_STATUS_OK; + + m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height; + MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")"); + } + if (!m_should_use_bootstrap_daemon) + return false; + + if (mode == invoke_http_mode::JON) + { + r = epee::net_utils::invoke_http_json(command_name, req, res, m_http_client); + } + else if (mode == invoke_http_mode::BIN) + { + r = epee::net_utils::invoke_http_bin(command_name, req, res, m_http_client); + } + else if (mode == invoke_http_mode::JON_RPC) + { + epee::json_rpc::request<typename COMMAND_TYPE::request> json_req = AUTO_VAL_INIT(json_req); + epee::json_rpc::response<typename COMMAND_TYPE::response, std::string> json_resp = AUTO_VAL_INIT(json_resp); + json_req.jsonrpc = "2.0"; + json_req.id = epee::serialization::storage_entry(0); + json_req.method = command_name; + json_req.params = req; + r = net_utils::invoke_http_json("/json_rpc", json_req, json_resp, m_http_client); + if (r) + res = json_resp.result; + } + else + { + MERROR("Unknown invoke_http_mode: " << mode); + return false; + } + m_was_bootstrap_ever_used = true; + r = r && res.status == CORE_RPC_STATUS_OK; + res.untrusted = true; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ 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) { PERF_TIMER(on_get_last_block_header); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LAST_BLOCK_HEADER>(invoke_http_mode::JON_RPC, "getlastblockheader", req, res, r)) + return r; + CHECK_CORE_READY(); uint64_t last_block_height; crypto::hash last_block_hash; @@ -1140,6 +1347,10 @@ 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){ PERF_TIMER(on_get_block_header_by_hash); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r)) + return r; + crypto::hash block_hash; bool hash_parsed = parse_hash256(req.hash, block_hash); if(!hash_parsed) @@ -1177,6 +1388,10 @@ 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){ PERF_TIMER(on_get_block_headers_range); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADERS_RANGE>(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r)) + return r; + 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) { @@ -1223,6 +1438,10 @@ 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){ PERF_TIMER(on_get_block_header_by_height); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT>(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r)) + return r; + if(m_core.get_current_blockchain_height() <= req.height) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; @@ -1251,6 +1470,10 @@ 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){ PERF_TIMER(on_get_block); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK>(invoke_http_mode::JON_RPC, "getblock", req, res, r)) + return r; + crypto::hash block_hash; if (!req.hash.empty()) { @@ -1320,6 +1543,16 @@ 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) { PERF_TIMER(on_get_info_json); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON_RPC, "get_info", req, res, r)) + { + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + crypto::hash top_hash; + m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); + ++res.height_without_bootstrap; // turn top block height into blockchain height + res.was_bootstrap_ever_used = true; + return r; + } crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); @@ -1345,12 +1578,21 @@ namespace cryptonote res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + res.height_without_bootstrap = res.height; + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; + } return true; } //------------------------------------------------------------------------------------------------------------------------------ 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) { PERF_TIMER(on_hard_fork_info); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_HARD_FORK_INFO>(invoke_http_mode::JON_RPC, "hard_fork_info", req, res, r)) + return r; const Blockchain &blockchain = m_core.get_blockchain_storage(); uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version(); @@ -1473,6 +1715,9 @@ 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) { PERF_TIMER(on_get_output_histogram); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r)) + return r; std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram; try @@ -1500,6 +1745,10 @@ 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); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_VERSION>(invoke_http_mode::JON_RPC, "get_version", req, res, r)) + return r; + res.version = CORE_RPC_VERSION; res.status = CORE_RPC_STATUS_OK; return true; @@ -1518,6 +1767,10 @@ 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); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) + return r; + res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); res.status = CORE_RPC_STATUS_OK; return true; @@ -1545,6 +1798,10 @@ 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); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LIMIT>(invoke_http_mode::JON, "/get_limit", req, res, r)) + return r; + 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; @@ -1596,9 +1853,21 @@ namespace cryptonote 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; + m_p2p.m_config.m_net_config.max_out_connection_count = req.out_peers; if (n_delete) - m_p2p.delete_connections(n_delete); + m_p2p.delete_out_connections(n_delete); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res) + { + PERF_TIMER(on_in_peers); + size_t n_connections = m_p2p.get_incoming_connections_count(); + size_t n_delete = (n_connections > req.in_peers) ? n_connections - req.in_peers : 0; + m_p2p.m_config.m_net_config.max_in_connection_count = req.in_peers; + if (n_delete) + m_p2p.delete_in_connections(n_delete); res.status = CORE_RPC_STATUS_OK; return true; } @@ -1790,6 +2059,9 @@ 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) { PERF_TIMER(on_get_txpool_backlog); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG>(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r)) + return r; if (!m_core.get_txpool_backlog(res.backlog)) { @@ -1832,4 +2104,16 @@ namespace cryptonote , "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls" , false }; + + const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_address = { + "bootstrap-daemon-address" + , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced" + , "" + }; + + const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_login = { + "bootstrap-daemon-login" + , "Specify username:password for the bootstrap daemon login" + , "" + }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 0c7028719..650e738bd 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -34,6 +34,7 @@ #include <boost/program_options/variables_map.hpp> #include "net/http_server_impl_base.h" +#include "net/http_client.h" #include "core_rpc_server_commands_defs.h" #include "cryptonote_core/cryptonote_core.h" #include "p2p/net_node.h" @@ -57,6 +58,8 @@ namespace cryptonote static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_testnet_rpc_restricted_bind_port; static const command_line::arg_descriptor<bool> arg_restricted_rpc; + static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address; + static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login; typedef epee::net_utils::connection_context_base connection_context; @@ -114,6 +117,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT) MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) + MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) @@ -170,7 +174,7 @@ namespace cryptonote bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); - bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res); @@ -183,6 +187,7 @@ namespace cryptonote 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); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); + bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); @@ -220,9 +225,18 @@ private: //utils uint64_t get_block_reward(const block& blk); bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response); + enum invoke_http_mode { JON, BIN, JON_RPC }; + template <typename COMMAND_TYPE> + bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r); core& m_core; nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p; + std::string m_bootstrap_daemon_address; + epee::net_utils::http::http_simple_client m_http_client; + boost::shared_mutex m_bootstrap_daemon_mutex; + bool m_should_use_bootstrap_daemon; + std::chrono::system_clock::time_point m_bootstrap_height_check_time; + bool m_was_bootstrap_ever_used; bool m_testnet; bool m_restricted; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9b9a8f949..64a97f8a3 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -65,10 +65,12 @@ namespace cryptonote { uint64_t height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -113,6 +115,7 @@ namespace cryptonote uint64_t current_height; std::string status; std::vector<block_output_indices> output_indices; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) @@ -120,6 +123,7 @@ namespace cryptonote KV_SERIALIZE(current_height) KV_SERIALIZE(status) KV_SERIALIZE(output_indices) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -138,10 +142,12 @@ namespace cryptonote { std::vector<block_complete_entry> blocks; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -158,10 +164,12 @@ namespace cryptonote { std::vector<std::string> blks_hashes; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blks_hashes) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -184,12 +192,14 @@ namespace cryptonote uint64_t start_height; uint64_t current_height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -595,6 +605,7 @@ namespace cryptonote // new style std::vector<entry> txs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs_as_hex) @@ -602,6 +613,7 @@ namespace cryptonote KV_SERIALIZE(txs) KV_SERIALIZE(missed_tx) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -629,10 +641,12 @@ namespace cryptonote { std::vector<int> spent_status; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(spent_status) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -653,9 +667,11 @@ namespace cryptonote { std::vector<uint64_t> o_indexes; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(o_indexes) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -695,9 +711,11 @@ namespace cryptonote { std::vector<outs_for_amount> outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -745,10 +763,12 @@ namespace cryptonote { std::vector<outkey> outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -785,10 +805,12 @@ namespace cryptonote { std::vector<outkey> outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -817,9 +839,11 @@ namespace cryptonote { std::list<out_entry> outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -854,6 +878,7 @@ namespace cryptonote bool overspend; bool fee_too_low; bool not_rct; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -867,6 +892,7 @@ namespace cryptonote KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) KV_SERIALIZE(not_rct) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -930,6 +956,10 @@ namespace cryptonote uint64_t start_time; uint64_t free_space; bool offline; + bool untrusted; + std::string bootstrap_daemon_address; + uint64_t height_without_bootstrap; + bool was_bootstrap_ever_used; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -953,6 +983,10 @@ namespace cryptonote KV_SERIALIZE(start_time) KV_SERIALIZE(free_space) KV_SERIALIZE(offline) + KV_SERIALIZE(untrusted) + KV_SERIALIZE(bootstrap_daemon_address) + KV_SERIALIZE(height_without_bootstrap) + KV_SERIALIZE(was_bootstrap_ever_used) END_KV_SERIALIZE_MAP() }; }; @@ -1080,6 +1114,7 @@ namespace cryptonote blobdata blocktemplate_blob; blobdata blockhashing_blob; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(difficulty) @@ -1090,6 +1125,7 @@ namespace cryptonote KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1153,10 +1189,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1177,10 +1215,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1201,10 +1241,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1231,6 +1273,7 @@ namespace cryptonote std::vector<std::string> tx_hashes; std::string blob; std::string json; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) @@ -1239,6 +1282,7 @@ namespace cryptonote KV_SERIALIZE(status) KV_SERIALIZE(blob) KV_SERIALIZE(json) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1415,11 +1459,13 @@ namespace cryptonote std::string status; std::vector<tx_info> transactions; std::vector<spent_key_image_info> spent_key_images; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(transactions) KV_SERIALIZE(spent_key_images) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1436,10 +1482,12 @@ namespace cryptonote { std::string status; std::vector<crypto::hash> tx_hashes; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1463,10 +1511,12 @@ namespace cryptonote { std::string status; std::vector<tx_backlog_entry> backlog; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1527,10 +1577,12 @@ namespace cryptonote { std::string status; txpool_stats pool_stats; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(pool_stats) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1573,10 +1625,12 @@ namespace cryptonote { std::string status; std::vector<block_header_response> headers; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(headers) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1630,11 +1684,13 @@ namespace cryptonote std::string status; uint64_t limit_up; uint64_t limit_down; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(limit_up) KV_SERIALIZE(limit_down) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1678,8 +1734,28 @@ namespace cryptonote struct response { - std::string status; - + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_IN_PEERS + { + struct request + { + uint64_t in_peers; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(in_peers) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() @@ -1744,6 +1820,7 @@ namespace cryptonote uint32_t state; uint64_t earliest_height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(version) @@ -1755,6 +1832,7 @@ namespace cryptonote KV_SERIALIZE(state) KV_SERIALIZE(earliest_height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1891,10 +1969,12 @@ namespace cryptonote { std::string status; std::vector<entry> histogram; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(histogram) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1911,10 +1991,12 @@ namespace cryptonote { std::string status; uint32_t version; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(version) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1961,10 +2043,12 @@ namespace cryptonote { std::string status; uint64_t fee; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(fee) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 6559db9b2..d4a6138ba 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -37,7 +37,7 @@ namespace cryptonote { rpc_args::descriptors::descriptors() - : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"}) + : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"}) , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")}) , rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""}) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a6cef1bb9..4ad1b83ec 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -158,7 +158,7 @@ namespace boost::optional<tools::password_container> default_password_prompter(bool verify) { - return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify); + return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); } inline std::string interpret_rpc_response(bool ok, const std::string& status) @@ -397,6 +397,7 @@ namespace { writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; } + writer << tr("Please use sweep_unmixable."); } catch (const tools::error::tx_not_constructed&) { @@ -1293,7 +1294,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* priority = boost::lexical_cast<int>(args[1]); if (priority < 1 || priority > 4) { - fail_msg_writer() << tr("priority must be 0, 1, 2, 3,or 4"); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4"); return true; } } @@ -1308,7 +1309,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* } catch(const boost::bad_lexical_cast &) { - fail_msg_writer() << tr("priority must be 0, 1, 2 3,or 4"); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4"); return true; } catch(...) @@ -1589,14 +1590,14 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer_original [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"), - tr("Transfer <amount> to <address> using an older transaction building algorithm. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); + tr("Transfer <amount> to <address> using an older transaction building algorithm. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"), - tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); + tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>]"), - tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); + tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); @@ -2474,7 +2475,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } - if (!m_restore_height && m_restoring) + if (!m_wallet->explicit_refresh_from_block_height() && m_restoring) { uint32_t version; bool connected = try_connect_to_daemon(false, &version); @@ -2672,12 +2673,12 @@ std::string simple_wallet::get_mnemonic_language() if (!((language_number >= 0) && (static_cast<unsigned int>(language_number) < language_list.size()))) { language_number = -1; - fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); + fail_msg_writer() << tr("invalid language choice entered. Please try again.\n"); } } catch (const std::exception &e) { - fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); + fail_msg_writer() << tr("invalid language choice entered. Please try again.\n"); } } return language_list[language_number]; @@ -3434,7 +3435,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args) { if(args.empty()) { - fail_msg_writer() << tr("expected at least one payment_id"); + fail_msg_writer() << tr("expected at least one payment ID"); return true; } @@ -4578,7 +4579,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) uint64_t below = 0; if (args_.size() < 1) { - fail_msg_writer() << tr("missing amount threshold"); + fail_msg_writer() << tr("missing threshold amount"); return true; } if (!cryptonote::parse_amount(below, args_[0])) diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 8089a6a05..38c34a912 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -74,7 +74,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa // integrated + long payment id provided if(has_long_pid && info.has_payment_id) { - m_errorString = tr("Integrated address and long payment id can't be used at the same time"); + m_errorString = tr("Integrated address and long payment ID can't be used at the same time"); m_errorCode = Invalid_Payment_Id; return false; } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 82948081e..5ce8ede8d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -626,7 +626,7 @@ bool WalletImpl::close(bool store) if (status() != Status_Critical) m_wallet->store(); else - LOG_ERROR("Status_Critical - not storing wallet"); + LOG_ERROR("Status_Critical - not saving wallet"); LOG_PRINT_L1("wallet::store done"); } LOG_PRINT_L1("Calling wallet::stop..."); @@ -732,7 +732,7 @@ bool WalletImpl::store(const std::string &path) m_wallet->store_to(path, m_password); } } catch (const std::exception &e) { - LOG_ERROR("Error storing wallet: " << e.what()); + LOG_ERROR("Error saving wallet: " << e.what()); m_status = Status_Error; m_errorString = e.what(); } @@ -1198,6 +1198,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const for (const 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("Please sweep unmixable outputs."); m_errorString = writer.str(); m_status = Status_Error; } catch (const tools::error::tx_not_constructed&) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7dc8a1e47..d97e53011 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -223,7 +223,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password")); - return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify); + return password_prompter(verify ? tr("Enter a new password for the wallet") : 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, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) @@ -361,6 +361,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, wallet.reset(make_basic(vm, opts, password_prompter).release()); wallet->set_refresh_from_block_height(field_scan_from_height); + wallet->explicit_refresh_from_block_height(field_scan_from_height_found); try { @@ -532,12 +533,9 @@ size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, si return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; } -uint8_t get_bulletproof_fork(bool testnet) +uint8_t get_bulletproof_fork() { - if (testnet) - return 7; - else - return 255; // TODO + return 8; } crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx) @@ -604,6 +602,7 @@ wallet2::wallet2(bool testnet, bool restricted): m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), + m_explicit_refresh_from_block_height(true), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), @@ -6642,7 +6641,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); const bool use_rct = use_fork_rules(4, 0); - const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); + const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -7151,7 +7150,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton std::vector<std::vector<get_outs_entry>> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); - const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); + const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -8354,8 +8353,9 @@ uint64_t wallet2::get_approximate_blockchain_height() const // Calculated blockchain height uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; // testnet got some huge rollbacks, so the estimation is way off - if (m_testnet && approx_blockchain_height > 105000) - approx_blockchain_height -= 105000; + static const uint64_t approximate_testnet_rolled_back_blocks = 148540; + if (m_testnet && approx_blockchain_height > approximate_testnet_rolled_back_blocks) + approx_blockchain_height -= approximate_testnet_rolled_back_blocks; LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); return approx_blockchain_height; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f768581b2..f9995c2ee 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -561,6 +561,9 @@ namespace tools void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;} uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;} + void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} + bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} + // upper_transaction_size_limit as defined below is set to // approximately 125% of the fixed minimum allowable penalty // free block size. TODO: fix this so that it actually takes @@ -1117,6 +1120,9 @@ namespace tools RefreshType m_refresh_type; bool m_auto_refresh; uint64_t m_refresh_from_block_height; + // If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that + // m_refresh_from_block_height was defaulted to zero.*/ + bool m_explicit_refresh_from_block_height; bool m_confirm_missing_payment_id; bool m_ask_password; uint32_t m_min_output_count; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 97faf0b56..5c644983b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1889,8 +1889,15 @@ namespace tools return false; } + if (req.account_index >= m_wallet->get_num_subaddress_accounts()) + { + er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS; + er.message = "Account index is out of bound"; + return false; + } + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet->get_payments(payments, 0); + m_wallet->get_payments(payments, 0, (uint64_t)-1, req.account_index); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { if (i->second.m_tx_hash == txid) { @@ -1900,7 +1907,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out; - m_wallet->get_payments_out(payments_out, 0); + m_wallet->get_payments_out(payments_out, 0, (uint64_t)-1, req.account_index); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) { if (i->first == txid) { @@ -1910,7 +1917,7 @@ namespace tools } 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, req.account_index); 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) { @@ -1922,7 +1929,7 @@ namespace tools m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments; - m_wallet->get_unconfirmed_payments(pool_payments); + m_wallet->get_unconfirmed_payments(pool_payments, req.account_index); 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) { @@ -2397,6 +2404,11 @@ namespace tools { std::rethrow_exception(e); } + catch (const tools::error::no_connection_to_daemon& e) + { + er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION; + er.message = e.what(); + } catch (const tools::error::daemon_busy& e) { er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; @@ -2412,6 +2424,11 @@ namespace tools er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY; er.message = e.what(); } + catch (const tools::error::not_enough_unlocked_money& e) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_MONEY; + er.message = e.what(); + } catch (const tools::error::tx_not_possible& e) { er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; @@ -2425,7 +2442,7 @@ namespace tools catch (const tools::error::not_enough_outs_to_mix& e) { er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX; - er.message = e.what(); + er.message = e.what() + std::string(" Please use sweep_dust."); } catch (const error::file_exists& e) { @@ -2439,12 +2456,12 @@ namespace tools } catch (const error::account_index_outofbound& e) { - er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND; + er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS; er.message = e.what(); } catch (const error::address_index_outofbound& e) { - er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND; + er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS; er.message = e.what(); } catch (const std::exception& e) @@ -2922,12 +2939,12 @@ int main(int argc, char** argv) { // if we ^C during potentially length load/refresh, there's no server loop yet if (quit) { - MINFO(tools::wallet_rpc_server::tr("Storing wallet...")); + MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); wal->store(); - MINFO(tools::wallet_rpc_server::tr("Stored ok")); + MINFO(tools::wallet_rpc_server::tr("Successfully saved")); return 1; } - MINFO(tools::wallet_rpc_server::tr("Loaded ok")); + MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); } catch (const std::exception& e) { @@ -2938,11 +2955,11 @@ just_dir: tools::wallet_rpc_server wrpc; if (wal) wrpc.set_wallet(wal.release()); bool r = wrpc.init(&(vm.get())); - CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server")); + CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); tools::signal_handler::install([&wrpc](int) { wrpc.send_stop_signal(); }); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet rpc server")); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); try { wrpc.run(); @@ -2952,16 +2969,16 @@ just_dir: LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); return 1; } - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet rpc server")); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); try { - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); wrpc.stop(); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok")); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); } catch (const std::exception& e) { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to store wallet: ") << e.what()); + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); return 1; } return 0; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index e9f112b63..82b6b78f9 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1291,9 +1291,11 @@ namespace wallet_rpc struct request { std::string txid; + uint32_t account_index; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); + KV_SERIALIZE_OPT(account_index, (uint32_t)0) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 311556657..d47467940 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -44,8 +44,8 @@ #define WALLET_RPC_ERROR_CODE_WRONG_URI -11 #define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12 #define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 -#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND -14 -#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND -15 +#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS -14 +#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS -15 #define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE -16 #define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -17 #define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE -18 @@ -67,3 +67,5 @@ #define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA -34 #define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE -35 #define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION -36 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_MONEY -37 +#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION -38 diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index d1b79d026..efa405160 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -75,6 +75,7 @@ std::string glob_to_regex(const std::string &val) int main(int argc, char** argv) { + TRY_ENTRY(); tools::on_startup(); set_process_affinity(1); set_thread_high_priority(); @@ -172,4 +173,5 @@ int main(int argc, char** argv) std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index c3807315e..2b3627f02 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -158,16 +158,18 @@ TEST(DNSResolver, GetTXTRecord) EXPECT_STREQ("donate.getmonero.org", addr.c_str()); } -TEST(DNS_PUBLIC, empty) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("").c_str()); } -TEST(DNS_PUBLIC, default) { EXPECT_STREQ("8.8.4.4", tools::dns_utils::parse_dns_public("tcp").c_str()); } -TEST(DNS_PUBLIC, invalid_scheme) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("invalid").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_alpha) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://invalid").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_num1) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_num3) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4.5").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_num4_extra) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4.5.6x").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_num4_range) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4.542.6").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_dot) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4.5.6.").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_num5) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4.5.6.7").c_str()); } -TEST(DNS_PUBLIC, invalid_ip_4_missing) { EXPECT_STREQ("", tools::dns_utils::parse_dns_public("tcp://3.4..7").c_str()); } -TEST(DNS_PUBLIC, valid_ip_lo) { EXPECT_STREQ("127.0.0.1", tools::dns_utils::parse_dns_public("tcp://127.0.0.1").c_str()); } -TEST(DNS_PUBLIC, valid_ip) { EXPECT_STREQ("3.4.5.6", tools::dns_utils::parse_dns_public("tcp://3.4.5.6").c_str()); } +bool is_equal(const char *s, const std::vector<std::string> &v) { return v.size() == 1 && v[0] == s; } + +TEST(DNS_PUBLIC, empty) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("").empty()); } +TEST(DNS_PUBLIC, default) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp").size() > 0); } +TEST(DNS_PUBLIC, invalid_scheme) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("invalid").empty()); } +TEST(DNS_PUBLIC, invalid_ip_alpha) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://invalid").empty()); } +TEST(DNS_PUBLIC, invalid_ip_num1) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3").empty()); } +TEST(DNS_PUBLIC, invalid_ip_num3) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4.5").empty()); } +TEST(DNS_PUBLIC, invalid_ip_num4_extra) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4.5.6x").empty()); } +TEST(DNS_PUBLIC, invalid_ip_num4_range) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4.542.6").empty()); } +TEST(DNS_PUBLIC, invalid_ip_dot) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4.5.6.").empty()); } +TEST(DNS_PUBLIC, invalid_ip_num5) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4.5.6.7").empty()); } +TEST(DNS_PUBLIC, invalid_ip_4_missing) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("tcp://3.4..7").empty()); } +TEST(DNS_PUBLIC, valid_ip_lo) { EXPECT_TRUE(is_equal("127.0.0.1", tools::dns_utils::parse_dns_public("tcp://127.0.0.1"))); } +TEST(DNS_PUBLIC, valid_ip) { EXPECT_TRUE(is_equal("3.4.5.6", tools::dns_utils::parse_dns_public("tcp://3.4.5.6"))); } diff --git a/translations/monero.ts b/translations/monero.ts index 27f5e1c52..5ca882bb3 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -414,7 +414,7 @@ <name>cryptonote::rpc_args</name> <message> <location filename="../src/rpc/rpc_args.cpp" line="38"/> - <source>Specify ip to bind rpc server</source> + <source>Specify IP to bind RPC server</source> <translation type="unfinished"></translation> </message> <message> @@ -527,7 +527,7 @@ </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="710"/> - <source>sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address</source> + <source>sweep_below <amount_threshold> [mixin] address [<payment_id>] - Send all unlocked outputs below the threshold to an address</source> <translation type="unfinished"></translation> </message> <message> @@ -851,7 +851,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2921"/> - <source>missing amount threshold</source> + <source>missing threshold amount</source> <translation type="unfinished"></translation> </message> <message> @@ -896,7 +896,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3137"/> - <source>daemon is busy. Please try later</source> + <source>daemon is busy. Please try again later</source> <translation type="unfinished"></translation> </message> <message> @@ -931,12 +931,12 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="525"/> - <source>priority must be 0, 1, 2, 3,or 4</source> + <source>priority must be 0, 1, 2, 3, or 4</source> <translation type="unfinished"></translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="540"/> - <source>priority must be 0, 1, 2 3,or 4</source> + <source>priority must be 0, 1, 2, 3, or 4</source> <translation type="unfinished"></translation> </message> <message> @@ -961,7 +961,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="709"/> - <source>sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address</source> + <source>sweep_all [mixin] address [<payment_id>] - Send all unlocked balance to an address</source> <translation type="unfinished"></translation> </message> <message> @@ -1124,7 +1124,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1887"/> - <source>expected at least one payment_id</source> + <source>expected at least one payment ID</source> <translation type="unfinished"></translation> </message> <message> @@ -1340,7 +1340,7 @@ Warning: Some input keys being spent are from </source> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="706"/> - <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> + <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> <translation type="unfinished"></translation> </message> <message> @@ -1612,7 +1612,7 @@ Warning: Some input keys being spent are from </source> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1306"/> <location filename="../src/simplewallet/simplewallet.cpp" line="1311"/> - <source>invalid language choice passed. Please try again. + <source>invalid language choice entered. Please try again. </source> <translation type="unfinished"></translation> </message> @@ -2493,7 +2493,7 @@ Outputs per *: </source> </message> <message> <location filename="../src/wallet/wallet2.cpp" line="460"/> - <source>Enter new wallet password</source> + <source>Enter new password for the wallet</source> <translation type="unfinished"></translation> </message> <message> @@ -2651,13 +2651,13 @@ Outputs per *: </source> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1789"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1814"/> - <source>Storing wallet...</source> + <source>Saving wallet...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1791"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1816"/> - <source>Stored ok</source> + <source>Saved ok</source> <translation type="unfinished"></translation> </message> <message> @@ -2672,22 +2672,22 @@ Outputs per *: </source> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1805"/> - <source>Failed to initialize wallet rpc server</source> + <source>Failed to initialize wallet RPC server</source> <translation type="unfinished"></translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1809"/> - <source>Starting wallet rpc server</source> + <source>Starting wallet RPC server</source> <translation type="unfinished"></translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1811"/> - <source>Stopped wallet rpc server</source> + <source>Stopped wallet RPC server</source> <translation type="unfinished"></translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1820"/> - <source>Failed to store wallet: </source> + <source>Failed to save wallet: </source> <translation type="unfinished"></translation> </message> </context> diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index 5cef15a92..c4e9a0f36 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -414,7 +414,7 @@ <name>cryptonote::rpc_args</name> <message> <location filename="../src/rpc/rpc_args.cpp" line="38"/> - <source>Specify ip to bind rpc server</source> + <source>Specify IP to bind RPC server</source> <translation>Spécifier l'IP à laquelle lier le serveur RPC</translation> </message> <message> @@ -527,8 +527,8 @@ </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="710"/> - <source>sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address</source> - <translation>sweep_below <montant_seuil> [mixin] adresse [ID_paiement] - Envoyer toutes les sorties débloquées sous le seuil vers une adresse</translation> + <source>sweep_below <amount_threshold> [mixin] address [<payment_id>] - Send all unlocked outputs below the threshold to an address</source> + <translation>sweep_below <montant_seuil> [mixin] adresse [<ID_paiement>] - Envoyer toutes les sorties débloquées sous le seuil vers une adresse</translation> </message> <message> <source>Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-outputs-count [n] - try to keep at least that many outputs of value at least min-outputs-value; min-outputs-value [n] - try to keep at least min-outputs-count outputs of at least that value - merge-destinations <1|0> - whether to merge multiple payments to the same destination address</source> @@ -872,7 +872,7 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2921"/> - <source>missing amount threshold</source> + <source>missing threshold amount</source> <translation>montant seuil manquant</translation> </message> <message> @@ -917,7 +917,7 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3137"/> - <source>daemon is busy. Please try later</source> + <source>daemon is busy. Please try again later</source> <translation>le démon est occupé. Veuillez réessayer plus tard</translation> </message> <message> @@ -952,12 +952,12 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="525"/> - <source>priority must be 0, 1, 2, 3,or 4</source> + <source>priority must be 0, 1, 2, 3, or 4</source> <translation>la priorité doit être 0, 1, 2, 3 ou 4</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="540"/> - <source>priority must be 0, 1, 2 3,or 4</source> + <source>priority must be 0, 1, 2, 3, or 4</source> <translation>la priorité doit être 0, 1, 2, 3 ou 4</translation> </message> <message> @@ -982,8 +982,8 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="709"/> - <source>sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address</source> - <translation>sweep_all [mixin] adresse [ID_paiement] - Envoyer tout le solde débloqué à une adresse</translation> + <source>sweep_all [mixin] address [<payment_id>] - Send all unlocked balance to an address</source> + <translation>sweep_all [mixin] adresse [<ID_paiement>] - Envoyer tout le solde débloqué à une adresse</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="711"/> @@ -1149,7 +1149,7 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1887"/> - <source>expected at least one payment_id</source> + <source>expected at least one payment ID</source> <translation>au moins un ID de paiement attendu</translation> </message> <message> @@ -1371,7 +1371,7 @@ Attention : Certaines clés d'entrées étant dépensées sont issues de < </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="706"/> - <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> + <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> <translation>transfer [<priorité>] [<mixin>] <adresse> <montant> [<ID_paiement>] - Transférer <montant> à <adresse>. <priorité> est la priorité de la transaction. Plus la priorité est élevée, plues les frais de transaction seront élévés. Les valeurs de priorité valies sont dans l'ordre (de la plus basse à la plus élevée) : unimportant, normal, elevated, priority. Si ce paramètre est omis, la valeur par défaut (voir la commande "set priority") est utilisée. <mixin> est le nombre d'entrées supplémentaires à inclure pour l'intraçabilité. De multiples paiements peuvent être effectués d'un coup en ajoutant <adresse_2> <montant_2> et cetera (avant l'ID de paiement, s'il est inclus)</translation> </message> <message> @@ -1643,7 +1643,7 @@ Attention : Certaines clés d'entrées étant dépensées sont issues de < <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1306"/> <location filename="../src/simplewallet/simplewallet.cpp" line="1311"/> - <source>invalid language choice passed. Please try again. + <source>invalid language choice entered. Please try again. </source> <translation>choix de langue passé invalide. Veuillez réessayer. </translation> @@ -2543,7 +2543,7 @@ Sorties par * : </translation> </message> <message> <location filename="../src/wallet/wallet2.cpp" line="460"/> - <source>Enter new wallet password</source> + <source>Enter new password for the wallet</source> <translation>Entrez le mot de passe du portefeuille</translation> </message> <message> @@ -2701,14 +2701,14 @@ Sorties par * : </translation> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1789"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1814"/> - <source>Storing wallet...</source> + <source>Saving wallet...</source> <translation>Sauvegarde du portefeuille...</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1791"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1816"/> - <source>Stored ok</source> - <translation>Sauvegarde OK</translation> + <source>Saved ok</source> + <translation>Sauvegarde réussie</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1794"/> @@ -2722,22 +2722,22 @@ Sorties par * : </translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1805"/> - <source>Failed to initialize wallet rpc server</source> + <source>Failed to initialize wallet RPC server</source> <translation>Échec de l'initialisation du serveur RPC du portefeuille</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1809"/> - <source>Starting wallet rpc server</source> + <source>Starting wallet RPC server</source> <translation>Démarrage du serveur RPC du portefeuille</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1811"/> - <source>Stopped wallet rpc server</source> + <source>Stopped wallet RPC server</source> <translation>Arrêt du serveur RPC du portefeuille</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1820"/> - <source>Failed to store wallet: </source> + <source>Failed to save wallet: </source> <translation>Échec de la sauvegarde du portefeuille : </translation> </message> </context> diff --git a/translations/monero_it.ts b/translations/monero_it.ts index 3674b2a32..d0cc40304 100644 --- a/translations/monero_it.ts +++ b/translations/monero_it.ts @@ -414,8 +414,8 @@ <name>cryptonote::rpc_args</name> <message> <location filename="../src/rpc/rpc_args.cpp" line="38"/> - <source>Specify ip to bind rpc server</source> - <translation>Specificare ip da associare al server rpc</translation> + <source>Specify IP to bind RPC server</source> + <translation>Specificare IP da associare al server RPC</translation> </message> <message> <location filename="../src/rpc/rpc_args.cpp" line="39"/> @@ -527,8 +527,8 @@ </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="710"/> - <source>sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address</source> - <translation>sweep_below <amount_threshold> [mixin] address [payment_id] - Invia tutti gli outputs sbloccati sotto la soglia specificata a un indirizzo</translation> + <source>sweep_below <amount_threshold> [mixin] address [<payment_id>] - Send all unlocked outputs below the threshold to an address</source> + <translation>sweep_below <amount_threshold> [mixin] address [<payment_id>] - Invia tutti gli outputs sbloccati sotto la soglia specificata a un indirizzo</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="727"/> @@ -887,7 +887,7 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="3137"/> - <source>daemon is busy. Please try later</source> + <source>daemon is busy. Please try again later</source> <translation>il daemon è occupato. Prova più tardi</translation> </message> <message> @@ -922,13 +922,13 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="525"/> - <source>priority must be 0, 1, 2, 3,or 4</source> - <translation>la priorità deve essere 0, 1, 2, 3,or 4</translation> + <source>priority must be 0, 1, 2, 3, or 4</source> + <translation>la priorità deve essere 0, 1, 2, 3, or 4</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="540"/> - <source>priority must be 0, 1, 2 3,or 4</source> - <translation>la priorità deve essere 0, 1, 2 3, or 4</translation> + <source>priority must be 0, 1, 2, 3, or 4</source> + <translation>la priorità deve essere 0, 1, 2, 3, or 4</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="623"/> @@ -952,8 +952,8 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="709"/> - <source>sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address</source> - <translation>sweep_all [mixin] address [payment_id] - Manda tutto il bilancio sbloccato ad un indirizzo</translation> + <source>sweep_all [mixin] address [<payment_id>] - Send all unlocked balance to an address</source> + <translation>sweep_all [mixin] address [<payment_id>] - Manda tutto il bilancio sbloccato ad un indirizzo</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="711"/> @@ -1115,8 +1115,8 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1887"/> - <source>expected at least one payment_id</source> - <translation>deve esserci almeno un payment_id</translation> + <source>expected at least one payment ID</source> + <translation>deve esserci almeno un payment ID</translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1896"/> @@ -1336,7 +1336,7 @@ Avviso: alcune chiavi di input spese vengono da </translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="706"/> - <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> + <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> <translation>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> è la priorità della transazione. Maggiore è la priorità, maggiori saranno le tasse per la transazione. Valori validi in ordini di priorità (dal più basso al più alto) sono:unimportant, normal, elevated, priority. se omesso, verrà usato il valore standard (vedi il comando "set priority"). <mixin_count> è il numero di inputs extra da inludere per intracciabilità. Puoi eseguire pagamenti multipli in una volta aggiungendo <address_2> <amount_2> etcetera (prima dell' ID pagamento, se incluso)</translation> </message> <message> @@ -1608,7 +1608,7 @@ Avviso: alcune chiavi di input spese vengono da </translation> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="1306"/> <location filename="../src/simplewallet/simplewallet.cpp" line="1311"/> - <source>invalid language choice passed. Please try again. + <source>invalid language choice entered. Please try again. </source> <translation>linguaggio selezionato scorretto. Prova di nuovo.</translation> </message> @@ -1755,7 +1755,7 @@ di nuovo il tuo portafoglio (le chiavi del tuo portafoglio NON sono a rischio in </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="2921"/> - <source>missing amount threshold</source> + <source>missing threshold amount</source> <translation>manca la soglia massima dell'ammontare</translation> </message> <message> @@ -2512,7 +2512,7 @@ Outputs per *: </source> </message> <message> <location filename="../src/wallet/wallet2.cpp" line="460"/> - <source>Enter new wallet password</source> + <source>Enter new password for the wallet</source> <translation>Inserisci una nuova password per il portafoglio</translation> </message> <message> @@ -2670,13 +2670,13 @@ Outputs per *: </source> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1789"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1814"/> - <source>Storing wallet...</source> + <source>Saving wallet...</source> <translation>Sto salvando il portafoglio...</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1791"/> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1816"/> - <source>Stored ok</source> + <source>Saved ok</source> <translation>Salvato con successo</translation> </message> <message> @@ -2691,22 +2691,22 @@ Outputs per *: </source> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1805"/> - <source>Failed to initialize wallet rpc server</source> - <translation>Inizializzazione server rpc portafoglio fallita</translation> + <source>Failed to initialize wallet RPC server</source> + <translation>Inizializzazione server RPC portafoglio fallita</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1809"/> - <source>Starting wallet rpc server</source> + <source>Starting wallet RPC server</source> <translation>Server RPC portafoglio in avvio</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1811"/> - <source>Stopped wallet rpc server</source> + <source>Stopped wallet RPC server</source> <translation>Server RPC portafoglio arrestato</translation> </message> <message> <location filename="../src/wallet/wallet_rpc_server.cpp" line="1820"/> - <source>Failed to store wallet: </source> + <source>Failed to save wallet: </source> <translation>Impossibile salvare portafoglio: </translation> </message> </context> diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts index 0090857c0..7decd2f5a 100644 --- a/translations/monero_sv.ts +++ b/translations/monero_sv.ts @@ -2397,7 +2397,7 @@ Utgångar per *: </translation> </message> <message> <location filename="../src/simplewallet/simplewallet.cpp" line="122"/> - <source>Create non-deterministic view and spend keys</source> + <source>Generate non-deterministic view and spend keys</source> <translation>Skapa non-deterministic visnings- och spendernyckel</translation> </message> <message> @@ -2612,7 +2612,7 @@ Utgångar per *: </translation> </message> <message> <location filename="../src/wallet/wallet2.cpp" line="356"/> - <source>Cannot create deprecated wallets from JSON</source> + <source>Cannot generate deprecated wallets from JSON</source> <translation>Det går inte att skapa inaktuella plånböcker från JSON</translation> </message> <message> |