diff options
63 files changed, 1751 insertions, 510 deletions
diff --git a/ANONYMITY_NETWORKS.md b/ANONYMITY_NETWORKS.md index a5f18010e..feb8528da 100644 --- a/ANONYMITY_NETWORKS.md +++ b/ANONYMITY_NETWORKS.md @@ -19,6 +19,11 @@ network. The transaction will not be broadcast unless an anonymity connection is made or until `monerod` is shutdown and restarted with only public connections enabled. +Anonymity networks can also be used with `monero-wallet-cli` and +`monero-wallet-rpc` - the wallets will connect to a daemon through a proxy. The +daemon must provide a hidden service for the RPC itself, which is separate from +the hidden service for P2P connections. + ## P2P Commands @@ -74,6 +79,35 @@ forwarded to `monerod` localhost port 30000. These addresses will be shared with outgoing peers, over the same network type, otherwise the peer will not be notified of the peer address by the proxy. +### Wallet RPC + +An anonymity network can be configured to forward incoming connections to a +`monerod` RPC port - which is independent from the configuration for incoming +P2P anonymity connections. The anonymity network (Tor/i2p) is +[configured in the same manner](#configuration), except the localhost port +must be the RPC port (typically 18081 for mainnet) instead of the p2p port: + +> HiddenServiceDir /var/lib/tor/data/monero +> HiddenServicePort 18081 127.0.0.1:18081 + +Then the wallet will be configured to use a Tor/i2p address: +> `--proxy 127.0.0.1:9050` +> `--daemon-address rveahdfho7wo4b2m.onion` + +The proxy must match the address type - a Tor proxy will not work properly with +i2p addresses, etc. + +i2p and onion addresses provide the information necessary to authenticate and +encrypt the connection from end-to-end. If desired, SSL can also be applied to +the connection with `--daemon-address https://rveahdfho7wo4b2m.onion` which +requires a server certificate that is signed by a "root" certificate on the +machine running the wallet. Alternatively, `--daemon-cert-file` can be used to +specify a certificate to authenticate the server. + +Proxies can also be used to connect to "clearnet" (ipv4 addresses or ICANN +domains), but `--daemon-cert-file` _must_ be used for authentication and +encryption. + ### Network Types #### Tor & I2P diff --git a/Dockerfile b/Dockerfile index d932e0173..999290c95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,10 +25,13 @@ RUN set -ex && \ WORKDIR /usr/local +ENV CFLAGS='-fPIC' +ENV CXXFLAGS='-fPIC' + #Cmake -ARG CMAKE_VERSION=3.13.0 -ARG CMAKE_VERSION_DOT=v3.13 -ARG CMAKE_HASH=4058b2f1a53c026564e8936698d56c3b352d90df067b195cb749a97a3d273c90 +ARG CMAKE_VERSION=3.14.0 +ARG CMAKE_VERSION_DOT=v3.14 +ARG CMAKE_HASH=aa76ba67b3c2af1946701f847073f4652af5cbd9f141f221c97af99127e75502 RUN set -ex \ && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ @@ -39,41 +42,41 @@ RUN set -ex \ && make install ## Boost -ARG BOOST_VERSION=1_68_0 -ARG BOOST_VERSION_DOT=1.68.0 -ARG BOOST_HASH=7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7 +ARG BOOST_VERSION=1_69_0 +ARG BOOST_VERSION_DOT=1.69.0 +ARG BOOST_HASH=8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406 RUN set -ex \ && curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ && 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 --with-locale threading=multi threadapi=pthread cflags="-fPIC" cxxflags="-fPIC" stage + && ./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 --with-locale threading=multi threadapi=pthread cflags="$CFLAGS" cxxflags="$CXXFLAGS" stage ENV BOOST_ROOT /usr/local/boost_${BOOST_VERSION} # OpenSSL -ARG OPENSSL_VERSION=1.1.0j -ARG OPENSSL_HASH=31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246 +ARG OPENSSL_VERSION=1.1.1b +ARG OPENSSL_HASH=5c557b023230413dfb0756f3137a13e6d726838ccd1430888ad15bfb2b43ea4b RUN set -ex \ && 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 \ + && ./Configure linux-x86_64 no-shared --static "$CFLAGS" \ && make build_generated \ && make libcrypto.a \ && make install ENV OPENSSL_ROOT_DIR=/usr/local/openssl-${OPENSSL_VERSION} # ZMQ -ARG ZMQ_VERSION=v4.2.5 -ARG ZMQ_HASH=d062edd8c142384792955796329baf1e5a3377cd +ARG ZMQ_VERSION=v4.3.1 +ARG ZMQ_HASH=2cb1240db64ce1ea299e00474c646a2453a8435b RUN set -ex \ && 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 \ + && ./configure --enable-static --disable-shared \ && make \ && make install \ && ldconfig @@ -88,39 +91,39 @@ RUN set -ex \ && mv *.hpp /usr/local/include # Readline -ARG READLINE_VERSION=7.0 -ARG READLINE_HASH=750d437185286f40a369e1e4f4764eda932b9459b5ec9a731628393dd3d32334 +ARG READLINE_VERSION=8.0 +ARG READLINE_HASH=e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461 RUN set -ex \ && 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 \ + && ./configure \ && make \ && make install # Sodium -ARG SODIUM_VERSION=1.0.16 -ARG SODIUM_HASH=675149b9b8b66ff44152553fb3ebf9858128363d +ARG SODIUM_VERSION=1.0.17 +ARG SODIUM_HASH=b732443c442239c2e0184820e9b23cca0de0828c RUN set -ex \ && 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 \ + && ./configure \ && make \ && make check \ && make install # Udev -ARG UDEV_VERSION=v3.2.6 -ARG UDEV_HASH=0c35b136c08d64064efa55087c54364608e65ed6 +ARG UDEV_VERSION=v3.2.7 +ARG UDEV_HASH=4758e346a14126fc3a964de5831e411c27ebe487 RUN set -ex \ && git clone https://github.com/gentoo/eudev -b ${UDEV_VERSION} \ && cd eudev \ && test `git rev-parse HEAD` = ${UDEV_HASH} || exit 1 \ && ./autogen.sh \ - && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared \ + && ./configure --disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared \ && make \ && make install @@ -132,7 +135,7 @@ RUN set -ex \ && cd libusb \ && test `git rev-parse HEAD` = ${USB_HASH} || exit 1 \ && ./autogen.sh \ - && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-shared \ + && ./configure --disable-shared \ && make \ && make install @@ -144,20 +147,20 @@ RUN set -ex \ && cd hidapi \ && test `git rev-parse HEAD` = ${HIDAPI_HASH} || exit 1 \ && ./bootstrap \ - && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --enable-static --disable-shared \ + && ./configure --enable-static --disable-shared \ && make \ && make install # Protobuf -ARG PROTOBUF_VERSION=v3.6.1 -ARG PROTOBUF_HASH=48cb18e5c419ddd23d9badcfe4e9df7bde1979b2 +ARG PROTOBUF_VERSION=v3.7.0 +ARG PROTOBUF_HASH=582743bf40c5d3639a70f98f183914a2c0cd0680 RUN set -ex \ && git clone https://github.com/protocolbuffers/protobuf -b ${PROTOBUF_VERSION} \ && cd protobuf \ && test `git rev-parse HEAD` = ${PROTOBUF_HASH} || exit 1 \ && git submodule update --init --recursive \ && ./autogen.sh \ - && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --enable-static --disable-shared \ + && ./configure --enable-static --disable-shared \ && make \ && make install \ && ldconfig @@ -29,3 +29,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers + +Parts of the project are originally copyright (c) 2014 The Boolberry +developers, distributed under the MIT licence: + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index e4b504466..67c63cca5 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -58,11 +58,6 @@ #define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes #define TIMEOUT_EXTRA_MS_PER_BYTE 0.2 -#if BOOST_VERSION >= 107000 -#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) -#else -#define GET_IO_SERVICE(s) ((s).get_io_service()) -#endif PRAGMA_WARNING_PUSH namespace epee diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 58a8e6888..f0425278d 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -327,10 +327,17 @@ namespace net_utils m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_allowed_certificates, m_ssl_allowed_fingerprints, m_ssl_allow_any_cert); } + template<typename F> + void set_connector(F connector) + { + CRITICAL_REGION_LOCAL(m_lock); + m_net_client.set_connector(std::move(connector)); + } + bool connect(std::chrono::milliseconds timeout) { CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.connect(m_host_buff, m_port, timeout, "0.0.0.0"); + return m_net_client.connect(m_host_buff, m_port, timeout); } //--------------------------------------------------------------------------- bool disconnect() diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 742cf916e..aa3df7160 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -33,12 +33,17 @@ //#include <Ws2tcpip.h> #include <string> #include <boost/version.hpp> -#include <boost/asio.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/read.hpp> #include <boost/asio/ssl.hpp> #include <boost/asio/steady_timer.hpp> +#include <boost/thread/future.hpp> #include <boost/lambda/bind.hpp> #include <boost/lambda/lambda.hpp> #include <boost/interprocess/detail/atomic.hpp> +#include <boost/system/error_code.hpp> +#include <functional> #include "net/net_utils_base.h" #include "net/net_ssl.h" #include "misc_language.h" @@ -55,6 +60,12 @@ namespace epee { namespace net_utils { + struct direct_connect + { + boost::unique_future<boost::asio::ip::tcp::socket> + operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer&) const; + }; + class blocked_mode_client { @@ -85,31 +96,38 @@ namespace net_utils ref_bytes_transferred = bytes_transferred; } }; - + public: inline - blocked_mode_client():m_initialized(false), - m_connected(false), - m_deadline(m_io_service), - m_shutdowned(0), - m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), - m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}), - m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service,m_ctx.context)) + blocked_mode_client() : + m_io_service(), + m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}), + m_connector(direct_connect{}), + m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx.context)), + m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), + m_initialized(true), + m_connected(false), + m_deadline(m_io_service), + m_shutdowned(0) { - - - m_initialized = true; + } + /*! The first/second parameters are host/port respectively. The third + parameter is for setting the timeout callback - the timer is + already set by the caller, the callee only needs to set the + behavior. - // No deadline is required until the first socket operation is started. We - // set the deadline to positive infinity so that the actor takes no action - // until a specific deadline is set. - m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); + Additional asynchronous operations should be queued using the + `io_service` from the timer. The implementation should assume + multi-threaded I/O processing. - // Start the persistent actor that checks for deadline expiry. - check_deadline(); + If the callee cannot start an asynchronous operation, an exception + should be thrown to signal an immediate failure. + + The return value is a future to a connected socket. Asynchronous + failures should use the `set_exception` method. */ + using connect_func = boost::unique_future<boost::asio::ip::tcp::socket>(const std::string&, const std::string&, boost::asio::steady_timer&); - } inline ~blocked_mode_client() { @@ -128,33 +146,28 @@ namespace net_utils } inline - bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout) { - return connect(addr, std::to_string(port), timeout, bind_ip); + return connect(addr, std::to_string(port), timeout); } inline - try_connect_result_t try_connect(const std::string& addr, const std::string& port, const boost::asio::ip::tcp::endpoint &remote_endpoint, std::chrono::milliseconds timeout, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) + try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, epee::net_utils::ssl_support_t ssl_support) { - m_ssl_socket->next_layer().open(remote_endpoint.protocol()); - if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) - { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); - m_ssl_socket->next_layer().bind(local_endpoint); - } - - m_deadline.expires_from_now(timeout); + boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline); + for (;;) + { + m_io_service.reset(); + m_io_service.run_one(); - boost::system::error_code ec = boost::asio::error::would_block; - - m_ssl_socket->next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); - while (ec == boost::asio::error::would_block) - { - m_io_service.run_one(); + if (connection.is_ready()) + break; } - - if (!ec && m_ssl_socket->next_layer().is_open()) + + m_ssl_socket->next_layer() = connection.get(); + m_deadline.cancel(); + if (m_ssl_socket->next_layer().is_open()) { m_connected = true; m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); @@ -183,14 +196,14 @@ namespace net_utils return CONNECT_SUCCESS; }else { - MWARNING("Some problems at connect, message: " << ec.message()); + MWARNING("Some problems at connect, expected open socket"); return CONNECT_FAILURE; } } inline - bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout) { m_connected = false; try @@ -205,25 +218,7 @@ namespace net_utils // Get a list of endpoints corresponding to the server name. - ////////////////////////////////////////////////////////////////////////// - - boost::asio::ip::tcp::resolver resolver(m_io_service); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name); - boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) - { - LOG_ERROR("Failed to resolve " << addr); - return false; - } - - ////////////////////////////////////////////////////////////////////////// - - - //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - - try_connect_result_t try_connect_result = try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support); + try_connect_result_t try_connect_result = try_connect(addr, port, timeout, m_ssl_support); if (try_connect_result == CONNECT_FAILURE) return false; if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) @@ -233,7 +228,7 @@ namespace net_utils { MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL"); m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; - if (try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support) != CONNECT_SUCCESS) + if (try_connect(addr, port, timeout, m_ssl_support) != CONNECT_SUCCESS) return false; } } @@ -251,6 +246,11 @@ namespace net_utils return true; } + //! Change the connection routine (proxy, etc.) + void set_connector(std::function<connect_func> connector) + { + m_connector = std::move(connector); + } inline bool disconnect() @@ -265,7 +265,6 @@ namespace net_utils m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); } } - catch(const boost::system::system_error& /*er*/) { //LOG_ERROR("Some problems at disconnect, message: " << er.what()); @@ -304,6 +303,7 @@ namespace net_utils // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block) { + m_io_service.reset(); m_io_service.run_one(); } @@ -433,6 +433,7 @@ namespace net_utils // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) { + m_io_service.reset(); m_io_service.run_one(); } @@ -573,10 +574,6 @@ namespace net_utils return true; } - void set_connected(bool connected) - { - m_connected = connected; - } boost::asio::io_service& get_io_service() { return m_io_service; @@ -619,6 +616,7 @@ namespace net_utils m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); while (ec == boost::asio::error::would_block) { + m_io_service.reset(); m_io_service.run_one(); } // Ignore "short read" error @@ -665,11 +663,8 @@ namespace net_utils boost::asio::io_service m_io_service; epee::net_utils::ssl_context_t m_ctx; std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket; + std::function<connect_func> m_connector; epee::net_utils::ssl_support_t m_ssl_support; - std::string m_ssl_private_key; - std::string m_ssl_certificate; - std::list<std::string> m_ssl_allowed_certificates; - bool m_ssl_allow_any_cerl; bool m_initialized; bool m_connected; boost::asio::steady_timer m_deadline; @@ -790,3 +785,4 @@ namespace net_utils }; } } + diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 7b5b07ef2..50536f63b 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -44,6 +44,12 @@ #define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) #endif +#if BOOST_VERSION >= 107000 +#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) +#else +#define GET_IO_SERVICE(s) ((s).get_io_service()) +#endif + namespace net { class tor_address; diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h index da84fd8ea..ca7c81ddc 100644 --- a/contrib/epee/include/storages/portable_storage_base.h +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -82,6 +82,7 @@ namespace epee struct array_entry_t { array_entry_t():m_it(m_array.end()){} + array_entry_t(const array_entry_t& other):m_array(other.m_array), m_it(m_array.end()){} const t_entry_type* get_first_val() const { diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 0787a9d08..2465afebb 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -26,8 +26,9 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c +add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp) + if (USE_READLINE AND GNU_READLINE_FOUND) add_library(epee_readline STATIC readline_buffer.cpp) endif() diff --git a/contrib/epee/src/net_helper.cpp b/contrib/epee/src/net_helper.cpp new file mode 100644 index 000000000..3543f5716 --- /dev/null +++ b/contrib/epee/src/net_helper.cpp @@ -0,0 +1,54 @@ +#include "net/net_helper.h" + +namespace epee +{ +namespace net_utils +{ + boost::unique_future<boost::asio::ip::tcp::socket> + direct_connect::operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer& timeout) const + { + // Get a list of endpoints corresponding to the server name. + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout)); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; + if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty + throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr}; + + ////////////////////////////////////////////////////////////////////////// + + struct new_connection + { + boost::promise<boost::asio::ip::tcp::socket> result_; + boost::asio::ip::tcp::socket socket_; + + explicit new_connection(boost::asio::io_service& io_service) + : result_(), socket_(io_service) + {} + }; + + const auto shared = std::make_shared<new_connection>(GET_IO_SERVICE(timeout)); + timeout.async_wait([shared] (boost::system::error_code error) + { + if (error != boost::system::errc::operation_canceled && shared && shared->socket_.is_open()) + { + shared->socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + shared->socket_.close(); + } + }); + shared->socket_.async_connect(*iterator, [shared] (boost::system::error_code error) + { + if (shared) + { + if (error) + shared->result_.set_exception(boost::system::system_error{error}); + else + shared->result_.set_value(std::move(shared->socket_)); + } + }); + return shared->result_.get_future(); + } +} +} + diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 3391d9bff..9f71fd068 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -54,7 +54,7 @@ using epee::string_tools::pod_to_hex; using namespace crypto; // Increase when the DB structure changes -#define VERSION 4 +#define VERSION 5 namespace { @@ -274,7 +274,7 @@ typedef struct mdb_block_info_1 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; } mdb_block_info_1; @@ -284,7 +284,7 @@ typedef struct mdb_block_info_2 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; } mdb_block_info_2; @@ -295,13 +295,26 @@ typedef struct mdb_block_info_3 uint64_t bi_timestamp; uint64_t bi_coins; uint64_t bi_weight; // a size_t really but we need 32-bit compat - difficulty_type bi_diff; + uint64_t bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; uint64_t bi_long_term_block_weight; } mdb_block_info_3; -typedef mdb_block_info_3 mdb_block_info; +typedef struct mdb_block_info_4 +{ + uint64_t bi_height; + uint64_t bi_timestamp; + uint64_t bi_coins; + uint64_t bi_weight; // a size_t really but we need 32-bit compat + uint64_t bi_diff_lo; + uint64_t bi_diff_hi; + crypto::hash bi_hash; + uint64_t bi_cum_rct; + uint64_t bi_long_term_block_weight; +} mdb_block_info_4; + +typedef mdb_block_info_4 mdb_block_info; typedef struct blk_height { crypto::hash bh_hash; @@ -757,7 +770,8 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l bi.bi_timestamp = blk.timestamp; bi.bi_coins = coins_generated; bi.bi_weight = block_weight; - bi.bi_diff = cumulative_difficulty; + bi.bi_diff_hi = (cumulative_difficulty >> 64).convert_to<uint64_t>(); + bi.bi_diff_lo = (cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); bi.bi_hash = blk_hash; bi.bi_cum_rct = num_rct_outs; if (blk.major_version >= 4) @@ -2527,7 +2541,9 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db")); mdb_block_info *bi = (mdb_block_info *)result.mv_data; - difficulty_type ret = bi->bi_diff; + difficulty_type ret = bi->bi_diff_hi; + ret <<= 64; + ret |= bi->bi_diff_lo; TXN_POSTFIX_RDONLY(); return ret; } @@ -5040,6 +5056,133 @@ void BlockchainLMDB::migrate_3_4() txn.commit(); } +void BlockchainLMDB::migrate_4_5() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + + MGINFO_YELLOW("Migrating blockchain from DB version 4 to 5 - this may take a while:"); + + do { + LOG_PRINT_L1("migrating block info:"); + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + + MDB_stat db_stats; + if ((result = mdb_stat(txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + const uint64_t blockchain_height = db_stats.ms_entries; + + /* the block_info table name is the same but the old version and new version + * have incompatible data. Create a new table. We want the name to be similar + * to the old name so that it will occupy the same location in the DB. + */ + MDB_dbi o_block_info = m_block_info; + lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + + MDB_cursor *c_blocks; + result = mdb_cursor_open(txn, m_blocks, &c_blocks); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str())); + + MDB_cursor *c_old, *c_cur; + i = 0; + while(1) { + if (!(i % 1000)) { + if (i) { + LOGIF(el::Level::Info) { + std::cout << i << " / " << blockchain_height << " \r" << std::flush; + } + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + } + result = mdb_cursor_open(txn, m_block_info, &c_cur); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str())); + result = mdb_cursor_open(txn, o_block_info, &c_old); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str())); + if (!i) { + MDB_stat db_stat; + result = mdb_stat(txn, m_block_info, &db_stats); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str())); + i = db_stats.ms_entries; + } + } + result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT); + if (result == MDB_NOTFOUND) { + txn.commit(); + break; + } + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); + const mdb_block_info_3 *bi_old = (const mdb_block_info_3*)v.mv_data; + mdb_block_info_4 bi; + bi.bi_height = bi_old->bi_height; + bi.bi_timestamp = bi_old->bi_timestamp; + bi.bi_coins = bi_old->bi_coins; + bi.bi_weight = bi_old->bi_weight; + bi.bi_diff_lo = bi_old->bi_diff; + bi.bi_diff_hi = 0; + bi.bi_hash = bi_old->bi_hash; + bi.bi_cum_rct = bi_old->bi_cum_rct; + bi.bi_long_term_block_weight = bi_old->bi_long_term_block_weight; + + MDB_val_set(nv, bi); + result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str())); + /* we delete the old records immediately, so the overall DB and mapsize should not grow. + * This is a little slower than just letting mdb_drop() delete it all at the end, but + * it saves a significant amount of disk space. + */ + result = mdb_cursor_del(c_old, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str())); + i++; + } + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + /* Delete the old table */ + result = mdb_drop(txn, o_block_info, 1); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); + + RENAME_DB("block_infn"); + mdb_dbi_close(m_env, m_block_info); + + lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + txn.commit(); + } while(0); + + uint32_t version = 5; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_str(vk, "version"); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { if (oldversion < 1) @@ -5050,6 +5193,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion) migrate_2_3(); if (oldversion < 4) migrate_3_4(); + if (oldversion < 5) + migrate_4_5(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 9185bd409..2f89b77ac 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -418,6 +418,9 @@ private: // migrate from DB version 3 to 4 void migrate_3_4(); + // migrate from DB version 4 to 5 + void migrate_4_5(); + void cleanup_batch(); private: diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index e4efdc3cb..8454595ac 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -294,7 +294,8 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path } // 4 byte magic + (currently) 1024 byte header structures - bootstrap.seek_to_first_chunk(import_file); + uint8_t major_version, minor_version; + bootstrap.seek_to_first_chunk(import_file, major_version, minor_version); std::string str1; char buffer1[1024]; @@ -415,7 +416,23 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { str1.assign(buffer_block, chunk_size); bootstrap::block_package bp; - if (! ::serialization::parse_binary(str1, bp)) + bool res; + if (major_version == 0) + { + bootstrap::block_package_1 bp1; + res = ::serialization::parse_binary(str1, bp1); + if (res) + { + bp.block = std::move(bp1.block); + bp.txs = std::move(bp1.txs); + bp.block_weight = bp1.block_weight; + bp.cumulative_difficulty = bp1.cumulative_difficulty; + bp.coins_generated = bp1.coins_generated; + } + } + else + res = ::serialization::parse_binary(str1, bp); + if (!res) throw std::runtime_error("Error in deserialization of chunk"); int display_interval = 1000; diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index fb9a24f5d..252c79776 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -124,8 +124,8 @@ bool BootstrapFile::initialize_file() *m_raw_data_file << blob; bootstrap::file_info bfi; - bfi.major_version = 0; - bfi.minor_version = 1; + bfi.major_version = 1; + bfi.minor_version = 0; bfi.header_size = header_size; bootstrap::blocks_info bbi; @@ -323,7 +323,7 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem return BootstrapFile::close(); } -uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file) +uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version) { uint32_t file_magic; @@ -371,6 +371,8 @@ uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file) uint64_t full_header_size = sizeof(file_magic) + bfi.header_size; import_file.seekg(full_header_size); + major_version = bfi.major_version; + minor_version = bfi.minor_version; return full_header_size; } @@ -461,7 +463,8 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::s } uint64_t full_header_size; // 4 byte magic + length of header structures - full_header_size = seek_to_first_chunk(import_file); + uint8_t major_version, minor_version; + full_header_size = seek_to_first_chunk(import_file, major_version, minor_version); MINFO("Scanning blockchain from bootstrap file..."); bool quit = false; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 5fb2cf366..1e6ef5d81 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -60,7 +60,7 @@ public: uint64_t count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit); uint64_t count_blocks(const std::string& dir_path, std::streampos& start_pos, uint64_t& seek_height); uint64_t count_blocks(const std::string& dir_path); - uint64_t seek_to_first_chunk(std::ifstream& import_file); + uint64_t seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version); bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, boost::filesystem::path& output_file, uint64_t use_block_height=0); diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 554c6d56e..70b3eea7e 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -29,7 +29,7 @@ #pragma once #include "cryptonote_basic/cryptonote_boost_serialization.h" -#include "cryptonote_basic/difficulty.h" +#include "serialization/difficulty_type.h" namespace cryptonote @@ -66,6 +66,23 @@ namespace cryptonote END_SERIALIZE() }; + struct block_package_1 + { + cryptonote::block block; + std::vector<transaction> txs; + size_t block_weight; + uint64_t cumulative_difficulty; + uint64_t coins_generated; + + BEGIN_SERIALIZE() + FIELD(block) + FIELD(txs) + VARINT_FIELD(block_weight) + VARINT_FIELD(cumulative_difficulty) + VARINT_FIELD(coins_generated) + END_SERIALIZE() + }; + struct block_package { cryptonote::block block; @@ -78,7 +95,7 @@ namespace cryptonote FIELD(block) FIELD(txs) VARINT_FIELD(block_weight) - VARINT_FIELD(cumulative_difficulty) + FIELD(cumulative_difficulty) VARINT_FIELD(coins_generated) END_SERIALIZE() }; diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index a341a8c81..1a1155c7c 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -32,9 +32,9 @@ #include <stdlib.h> #include "include_base_utils.h" +#include "common/threadpool.h" #include <random> #include <boost/thread/mutex.hpp> -#include <boost/thread/thread.hpp> #include <boost/algorithm/string/join.hpp> #include <boost/optional.hpp> using namespace epee; @@ -523,16 +523,16 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std size_t first_index = dis(gen); // send all requests in parallel - std::vector<boost::thread> threads(dns_urls.size()); std::deque<bool> avail(dns_urls.size(), false), valid(dns_urls.size(), false); + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter; for (size_t n = 0; n < dns_urls.size(); ++n) { - threads[n] = boost::thread([n, dns_urls, &records, &avail, &valid](){ + tpool.submit(&waiter,[n, dns_urls, &records, &avail, &valid](){ records[n] = tools::DNSResolver::instance().get_txt_record(dns_urls[n], avail[n], valid[n]); }); } - for (size_t n = 0; n < dns_urls.size(); ++n) - threads[n].join(); + waiter.wait(&tpool); size_t cur_index = first_index; do diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 1840b6d2b..3dd98f0c6 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -40,6 +40,7 @@ #include <boost/archive/portable_binary_iarchive.hpp> #include <boost/archive/portable_binary_oarchive.hpp> #include "cryptonote_basic.h" +#include "difficulty.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" #include "ringct/rctTypes.h" @@ -346,6 +347,34 @@ namespace boost a & x.range_proof_type; a & x.bp_version; } + + template <class Archive> + inline void serialize(Archive &a, cryptonote::difficulty_type &x, const boost::serialization::version_type ver) + { + if (Archive::is_loading::value) + { + // load high part + uint64_t v = 0; + a & v; + x = v; + // load low part + x = x << 64; + a & v; + x += v; + } + else + { + // store high part + cryptonote::difficulty_type x_ = x >> 64; + uint64_t v = x_.convert_to<uint64_t>(); + a & v; + // store low part + x_ = x << 64 >> 64; + v = x_.convert_to<uint64_t>(); + a & v; + } + } + } } diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 89b748a83..5162e53e6 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -102,7 +102,7 @@ namespace cryptonote { return a + b < a || (c && a + b == (uint64_t) -1); } - bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + bool check_hash_64(const crypto::hash &hash, uint64_t difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); @@ -119,7 +119,7 @@ namespace cryptonote { return !carry; } - difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) { + uint64_t next_difficulty_64(std::vector<std::uint64_t> timestamps, std::vector<uint64_t> cumulative_difficulties, size_t target_seconds) { if(timestamps.size() > DIFFICULTY_WINDOW) { @@ -150,7 +150,7 @@ namespace cryptonote { if (time_span == 0) { time_span = 1; } - difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + uint64_t total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; assert(total_work > 0); uint64_t low, high; mul(total_work, target_seconds, low, high); @@ -162,4 +162,81 @@ namespace cryptonote { return (low + time_span - 1) / time_span; } +#if defined(_MSC_VER) +#ifdef max +#undef max +#endif +#endif + + const difficulty_type max64bit(std::numeric_limits<std::uint64_t>::max()); + const boost::multiprecision::uint256_t max128bit(std::numeric_limits<boost::multiprecision::uint128_t>::max()); + const boost::multiprecision::uint512_t max256bit(std::numeric_limits<boost::multiprecision::uint256_t>::max()); + +#define FORCE_FULL_128_BITS + + bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty) { +#ifndef FORCE_FULL_128_BITS + // fast check + if (difficulty >= max64bit && ((const uint64_t *) &hash)[3] > 0) + return false; +#endif + // usual slow check + boost::multiprecision::uint512_t hashVal = 0; +#ifdef FORCE_FULL_128_BITS + for(int i = 0; i < 4; i++) { // highest word is zero +#else + for(int i = 1; i < 4; i++) { // highest word is zero +#endif + hashVal <<= 64; + hashVal |= swap64le(((const uint64_t *) &hash)[3 - i]); + } + return hashVal * difficulty <= max256bit; + } + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + if (difficulty <= max64bit) // if can convert to small difficulty - do it + return check_hash_64(hash, difficulty.convert_to<std::uint64_t>()); + else + return check_hash_128(hash, difficulty); + } + + difficulty_type next_difficulty(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) { + //cutoff DIFFICULTY_LAG + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); + if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { + cut_begin = 0; + cut_end = length; + } else { + cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; + cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span; + if(res > max128bit) + return 0; // to behave like previous implementation, may be better return max128bit? + return res.convert_to<difficulty_type>(); + } + } diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index 8da355b22..f7a9376fb 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -32,12 +32,13 @@ #include <cstdint> #include <vector> +#include <boost/multiprecision/cpp_int.hpp> #include "crypto/hash.h" namespace cryptonote { - typedef std::uint64_t difficulty_type; + typedef boost::multiprecision::uint128_t difficulty_type; /** * @brief checks if a hash fits the given difficulty @@ -51,6 +52,10 @@ namespace cryptonote * * @return true if valid, else false */ + bool check_hash_64(const crypto::hash &hash, uint64_t difficulty); + uint64_t next_difficulty_64(std::vector<std::uint64_t> timestamps, std::vector<uint64_t> cumulative_difficulties, size_t target_seconds); + + bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty); bool check_hash(const crypto::hash &hash, difficulty_type difficulty); difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds); } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 6628c8448..4e2edc20f 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -435,14 +435,15 @@ namespace cryptonote { MTRACE("Miner has received stop signal"); - if (!is_mining()) + CRITICAL_REGION_LOCAL(m_threads_lock); + bool mining = !m_threads.empty(); + if (!mining) { MTRACE("Not mining - nothing to stop" ); return true; } send_stop_signal(); - CRITICAL_REGION_LOCAL(m_threads_lock); // In case background mining was active and the miner threads are waiting // on the background miner to signal start. diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f5bd9bbb5..263227148 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -645,6 +645,8 @@ block Blockchain::pop_block_from_blockchain() block popped_block; std::vector<transaction> popped_txs; + CHECK_AND_ASSERT_THROW_MES(m_db->height() > 1, "Cannot pop the genesis block"); + try { m_db->pop_block(popped_block, popped_txs); @@ -1997,7 +1999,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return true; } //------------------------------------------------------------------ -uint64_t Blockchain::block_difficulty(uint64_t i) const +difficulty_type Blockchain::block_difficulty(uint64_t i) const { LOG_PRINT_L3("Blockchain::" << __func__); // WARNING: this function does not take m_blockchain_lock, and thus should only call read only @@ -2196,7 +2198,11 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); if (result) - resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); + { + cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); + resp.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); + resp.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>(); + } return result; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3b8169764..89d8e7572 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -653,7 +653,7 @@ namespace cryptonote * * @return the difficulty */ - uint64_t block_difficulty(uint64_t i) const; + difficulty_type block_difficulty(uint64_t i) const; /** * @brief gets blocks based on a list of block hashes diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index d582e3e9c..3083a5b4c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -34,6 +34,7 @@ #include "serialization/keyvalue_serialization.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/blobdatatype.h" + namespace cryptonote { @@ -208,6 +209,7 @@ namespace cryptonote { uint64_t current_height; uint64_t cumulative_difficulty; + uint64_t cumulative_difficulty_top64; crypto::hash top_id; uint8_t top_version; uint32_t pruning_seed; @@ -215,6 +217,7 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(current_height) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) KV_SERIALIZE_OPT(top_version, (uint8_t)0) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) @@ -245,12 +248,14 @@ namespace cryptonote uint64_t start_height; uint64_t total_height; uint64_t cumulative_difficulty; + uint64_t cumulative_difficulty_top64; std::vector<crypto::hash> m_block_ids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(start_height) KV_SERIALIZE(total_height) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) END_KV_SERIALIZE_MAP() }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b33867e8b..32f0afceb 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -398,7 +398,9 @@ namespace cryptonote { m_core.get_blockchain_top(hshd.current_height, hshd.top_id); hshd.top_version = m_core.get_ideal_hard_fork_version(hshd.current_height); - hshd.cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height); + difficulty_type wide_cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height); + hshd.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); + hshd.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>(); hshd.current_height +=1; hshd.pruning_seed = m_core.get_blockchain_pruning_seed(); return true; diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 48c9ab1ba..2dd40cc9a 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -340,9 +340,7 @@ namespace crypto const size_t expected = len * 3 / 32; if (seed.size() == expected/2) { - dst += ' '; // if electrum 12-word seed, duplicate - dst += dst; // if electrum 12-word seed, duplicate - dst.pop_back(); // trailing space + dst.append(dst.data(), dst.size()); // if electrum 12-word seed, duplicate } } diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 8a3ee9e6f..738f858f0 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -26,8 +26,8 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp i2p_address.cpp) -set(net_headers error.h parse.h socks.h tor_address.h i2p_address.h) +set(net_sources error.cpp i2p_address.cpp parse.cpp socks.cpp socks_connect.cpp tor_address.cpp) +set(net_headers error.h i2p_address.h parse.h socks.h socks_connect.h tor_address.h) monero_add_library(net ${net_sources} ${net_headers}) target_link_libraries(net common epee ${Boost_ASIO_LIBRARY}) diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 53154369b..5a27e16f4 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018, The Monero Project +// Copyright (c) 2018-2019, The Monero Project // // All rights reserved. // @@ -193,7 +193,7 @@ namespace socks else if (bytes < self.buffer().size()) self.done(socks::error::bad_write, std::move(self_)); else - boost::asio::async_read(self.proxy_, get_buffer(self), completed{std::move(self_)}); + boost::asio::async_read(self.proxy_, get_buffer(self), self.strand_.wrap(completed{std::move(self_)})); } } }; @@ -215,13 +215,13 @@ namespace socks if (error) self.done(error, std::move(self_)); else - boost::asio::async_write(self.proxy_, get_buffer(self), read{std::move(self_)}); + boost::asio::async_write(self.proxy_, get_buffer(self), self.strand_.wrap(read{std::move(self_)})); } } }; client::client(stream_type::socket&& proxy, socks::version ver) - : proxy_(std::move(proxy)), buffer_size_(0), buffer_(), ver_(ver) + : proxy_(std::move(proxy)), strand_(GET_IO_SERVICE(proxy_)), buffer_size_(0), buffer_(), ver_(ver) {} client::~client() {} @@ -296,7 +296,7 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - alias.proxy_.async_connect(proxy_address, write{std::move(self)}); + alias.proxy_.async_connect(proxy_address, alias.strand_.wrap(write{std::move(self)})); return true; } return false; @@ -307,10 +307,26 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - boost::asio::async_write(alias.proxy_, write::get_buffer(alias), read{std::move(self)}); + boost::asio::async_write(alias.proxy_, write::get_buffer(alias), alias.strand_.wrap(read{std::move(self)})); return true; } return false; } + + void client::async_close::operator()(boost::system::error_code error) + { + if (self_ && error != boost::system::errc::operation_canceled) + { + const std::shared_ptr<client> self = std::move(self_); + self->strand_.dispatch([self] () + { + if (self && self->proxy_.is_open()) + { + self->proxy_.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + self->proxy_.close(); + } + }); + } + } } // socks } // net diff --git a/src/net/socks.h b/src/net/socks.h index 825937792..4d1d34e9e 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018, The Monero Project +// Copyright (c) 2018-2019, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <cstdint> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/io_service.hpp> +#include <boost/asio/strand.hpp> #include <boost/system/error_code.hpp> #include <boost/type_traits/integral_constant.hpp> #include <boost/utility/string_ref.hpp> @@ -92,6 +93,7 @@ namespace socks class client { boost::asio::ip::tcp::socket proxy_; + boost::asio::io_service::strand strand_; std::uint16_t buffer_size_; std::uint8_t buffer_[1024]; socks::version ver_; @@ -168,6 +170,8 @@ namespace socks \note Must use one of the `self->set_*_command` calls before using this function. + \note Only `async_close` can be invoked on `self` until the `done` + callback is invoked. \param self ownership of object is given to function. \param proxy_address of the socks server. @@ -182,11 +186,21 @@ namespace socks \note Must use one of the `self->set_*_command` calls before using the function. + \note Only `async_close` can be invoked on `self` until the `done` + callback is invoked. \param self ownership of object is given to function. \return False if `self->buffer().empty()` (no command set). */ static bool send(std::shared_ptr<client> self); + + /*! Callback for closing socket. Thread-safe with `*send` functions; + never blocks (uses strands). */ + struct async_close + { + std::shared_ptr<client> self_; + void operator()(boost::system::error_code error = boost::system::error_code{}); + }; }; template<typename Handler> diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp new file mode 100644 index 000000000..a5557f6f8 --- /dev/null +++ b/src/net/socks_connect.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "socks_connect.h" + +#include <boost/system/error_code.hpp> +#include <boost/system/system_error.hpp> +#include <cstdint> +#include <memory> +#include <system_error> + +#include "net/error.h" +#include "net/net_utils_base.h" +#include "net/socks.h" +#include "string_tools.h" + +namespace net +{ +namespace socks +{ + boost::unique_future<boost::asio::ip::tcp::socket> + connector::operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const + { + struct future_socket + { + boost::promise<boost::asio::ip::tcp::socket> result_; + + void operator()(boost::system::error_code error, boost::asio::ip::tcp::socket&& socket) + { + if (error) + result_.set_exception(boost::system::system_error{error}); + else + result_.set_value(std::move(socket)); + } + }; + + boost::unique_future<boost::asio::ip::tcp::socket> out{}; + { + std::uint16_t port = 0; + if (!epee::string_tools::get_xtype_from_string(port, remote_port)) + throw std::system_error{net::error::invalid_port, "Remote port for socks proxy"}; + + bool is_set = false; + std::uint32_t ip_address = 0; + boost::promise<boost::asio::ip::tcp::socket> result{}; + out = result.get_future(); + const auto proxy = net::socks::make_connect_client( + boost::asio::ip::tcp::socket{GET_IO_SERVICE(timeout)}, net::socks::version::v4a, future_socket{std::move(result)} + ); + + if (epee::string_tools::get_ip_int32_from_string(ip_address, remote_host)) + is_set = proxy->set_connect_command(epee::net_utils::ipv4_network_address{ip_address, port}); + else + is_set = proxy->set_connect_command(remote_host, port); + + if (!is_set || !net::socks::client::connect_and_send(proxy, proxy_address)) + throw std::system_error{net::error::invalid_host, "Address for socks proxy"}; + + timeout.async_wait(net::socks::client::async_close{std::move(proxy)}); + } + + return out; + } +} // socks +} // net diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h new file mode 100644 index 000000000..44b0fa2b3 --- /dev/null +++ b/src/net/socks_connect.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/thread/future.hpp> +#include <string> + +namespace net +{ +namespace socks +{ + //! Primarily for use with `epee::net_utils::http_client`. + struct connector + { + boost::asio::ip::tcp::endpoint proxy_address; + + /*! Creates a new socket, asynchronously connects to `proxy_address`, + and requests a connection to `remote_host` on `remote_port`. Sets + socket as closed if `timeout` is reached. + + \return The socket if successful, and exception in the future with + error otherwise. */ + boost::unique_future<boost::asio::ip::tcp::socket> + operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const; + }; +} // socks +} // net diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index e39ba16fd..b5499262f 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -408,10 +408,10 @@ namespace rct { return res; } - //Computes aL where L is the curve order - bool isInMainSubgroup(const key & a) { + //Computes lA where l is the curve order + bool isInMainSubgroup(const key & A) { ge_p3 p3; - return toPointCheckOrder(&p3, a.bytes); + return toPointCheckOrder(&p3, A.bytes); } //Curve addition / subtractions diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 56b0361a7..e6a09606b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -70,6 +70,13 @@ namespace { return (value + quantum - 1) / quantum * quantum; } + + void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64) + { + sdiff = (difficulty << 64 >> 64).convert_to<uint64_t>(); + swdiff = difficulty.convert_to<std::string>(); + stop64 = (difficulty >> 64).convert_to<uint64_t>(); + } } namespace cryptonote @@ -219,7 +226,7 @@ namespace cryptonote ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), res.difficulty, res.wide_difficulty, res.difficulty_top64); res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); @@ -236,7 +243,8 @@ namespace cryptonote res.testnet = net_type == TESTNET; res.stagenet = net_type == STAGENET; res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1), + res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; @@ -1196,13 +1204,15 @@ namespace cryptonote block b; cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve)) + cryptonote::difficulty_type wdiff; + if(!m_core.get_block_template(b, info.address, wdiff, res.height, res.expected_reward, blob_reserve)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; LOG_ERROR("Failed to create block template"); return false; } + store_difficulty(wdiff, res.difficulty, res.wide_difficulty, res.difficulty_top64); blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); if(tx_pub_key == crypto::null_pkey) @@ -1375,8 +1385,10 @@ namespace cryptonote response.height = height; response.depth = m_core.get_current_blockchain_height() - height - 1; response.hash = string_tools::pod_to_hex(hash); - response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); - response.cumulative_difficulty = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(height); + store_difficulty(m_core.get_blockchain_storage().block_difficulty(height), + response.difficulty, response.wide_difficulty, response.difficulty_top64); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(height), + response.cumulative_difficulty, response.wide_cumulative_difficulty, response.cumulative_difficulty_top64); response.reward = get_block_reward(blk); response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); @@ -1707,7 +1719,8 @@ namespace cryptonote ++res.height; // turn top block height into blockchain height res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), + res.difficulty, res.wide_difficulty, res.difficulty_top64); res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); @@ -1725,7 +1738,8 @@ namespace cryptonote res.stagenet = net_type == STAGENET; res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); + store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1), + res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; @@ -1741,7 +1755,9 @@ namespace cryptonote boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); + if (restricted) + res.database_size = round_up(res.database_size, 5ull * 1024 * 1024 * 1024); res.update_available = restricted ? false : m_core.is_update_available(); res.version = restricted ? "" : MONERO_VERSION; return true; @@ -1947,7 +1963,9 @@ namespace cryptonote std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains(); for (const auto &i: chains) { - res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()}); + difficulty_type wdiff = i.first.cumulative_difficulty; + res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), 0, "", 0, {}, std::string()}); + store_difficulty(wdiff, res.chains.back().difficulty, res.chains.back().wide_difficulty, res.chains.back().difficulty_top64); res.chains.back().block_hashes.reserve(i.second.size()); for (const crypto::hash &block_id: i.second) res.chains.back().block_hashes.push_back(epee::string_tools::pod_to_hex(block_id)); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f65c7c8dd..4786f7a9b 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -252,223 +252,6 @@ namespace cryptonote }; //----------------------------------------------- - struct COMMAND_RPC_GET_ADDRESS_TXS - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct spent_output { - uint64_t amount; - std::string key_image; - std::string tx_pub_key; - uint64_t out_index; - uint32_t mixin; - - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(key_image) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(out_index) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - struct transaction - { - uint64_t id; - std::string hash; - uint64_t timestamp; - uint64_t total_received; - uint64_t total_sent; - uint64_t unlock_time; - uint64_t height; - std::list<spent_output> spent_outputs; - std::string payment_id; - bool coinbase; - bool mempool; - uint32_t mixin; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) - KV_SERIALIZE(hash) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_sent) - KV_SERIALIZE(unlock_time) - KV_SERIALIZE(height) - KV_SERIALIZE(spent_outputs) - KV_SERIALIZE(payment_id) - KV_SERIALIZE(coinbase) - KV_SERIALIZE(mempool) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - - struct response_t - { - //std::list<std::string> txs_as_json; - uint64_t total_received; - uint64_t total_received_unlocked = 0; // OpenMonero only - uint64_t scanned_height; - std::vector<transaction> transactions; - uint64_t blockchain_height; - uint64_t scanned_block_height; - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_received_unlocked) - KV_SERIALIZE(scanned_height) - KV_SERIALIZE(transactions) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(scanned_block_height) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- - struct COMMAND_RPC_GET_ADDRESS_INFO - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct spent_output - { - uint64_t amount; - std::string key_image; - std::string tx_pub_key; - uint64_t out_index; - uint32_t mixin; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(key_image) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(out_index) - KV_SERIALIZE(mixin) - END_KV_SERIALIZE_MAP() - }; - - struct response_t - { - uint64_t locked_funds; - uint64_t total_received; - uint64_t total_sent; - uint64_t scanned_height; - uint64_t scanned_block_height; - uint64_t start_height; - uint64_t transaction_height; - uint64_t blockchain_height; - std::list<spent_output> spent_outputs; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(locked_funds) - KV_SERIALIZE(total_received) - KV_SERIALIZE(total_sent) - KV_SERIALIZE(scanned_height) - KV_SERIALIZE(scanned_block_height) - KV_SERIALIZE(start_height) - KV_SERIALIZE(transaction_height) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(spent_outputs) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- - struct COMMAND_RPC_GET_UNSPENT_OUTS - { - struct request_t - { - std::string amount; - std::string address; - std::string view_key; - // OpenMonero specific - uint64_t mixin; - bool use_dust; - std::string dust_threshold; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - KV_SERIALIZE(mixin) - KV_SERIALIZE(use_dust) - KV_SERIALIZE(dust_threshold) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - - struct output { - uint64_t amount; - std::string public_key; - uint64_t index; - uint64_t global_index; - std::string rct; - std::string tx_hash; - std::string tx_pub_key; - std::string tx_prefix_hash; - std::vector<std::string> spend_key_images; - uint64_t timestamp; - uint64_t height; - - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(public_key) - KV_SERIALIZE(index) - KV_SERIALIZE(global_index) - KV_SERIALIZE(rct) - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_pub_key) - KV_SERIALIZE(tx_prefix_hash) - KV_SERIALIZE(spend_key_images) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(height) - END_KV_SERIALIZE_MAP() - }; - - struct response_t - { - uint64_t amount; - std::list<output> outputs; - uint64_t per_kb_fee; - std::string status; - std::string reason; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(outputs) - KV_SERIALIZE(per_kb_fee) - KV_SERIALIZE(status) - KV_SERIALIZE(reason) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - //----------------------------------------------- struct COMMAND_RPC_GET_RANDOM_OUTS { struct request_t @@ -548,72 +331,6 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; //----------------------------------------------- - struct COMMAND_RPC_LOGIN - { - struct request_t - { - std::string address; - std::string view_key; - bool create_account; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - KV_SERIALIZE(create_account) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - std::string reason; - bool new_address; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(reason) - KV_SERIALIZE(new_address) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - //----------------------------------------------- - struct COMMAND_RPC_IMPORT_WALLET_REQUEST - { - struct request_t - { - std::string address; - std::string view_key; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - KV_SERIALIZE(view_key) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string payment_id; - uint64_t import_fee; - bool new_request; - bool request_fulfilled; - std::string payment_address; - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - KV_SERIALIZE(import_fee) - KV_SERIALIZE(new_request) - KV_SERIALIZE(request_fulfilled) - KV_SERIALIZE(payment_address) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { struct request_t @@ -943,6 +660,8 @@ namespace cryptonote uint64_t height; uint64_t target_height; uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; uint64_t target; uint64_t tx_count; uint64_t tx_pool_size; @@ -958,6 +677,8 @@ namespace cryptonote std::string nettype; std::string top_block_hash; uint64_t cumulative_difficulty; + std::string wide_cumulative_difficulty; + uint64_t cumulative_difficulty_top64; uint64_t block_size_limit; uint64_t block_weight_limit; uint64_t block_size_median; @@ -978,6 +699,8 @@ namespace cryptonote KV_SERIALIZE(height) KV_SERIALIZE(target_height) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(target) KV_SERIALIZE(tx_count) KV_SERIALIZE(tx_pool_size) @@ -993,6 +716,8 @@ namespace cryptonote KV_SERIALIZE(nettype) KV_SERIALIZE(top_block_hash) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(wide_cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE(block_size_limit) KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0) KV_SERIALIZE(block_size_median) @@ -1149,6 +874,8 @@ namespace cryptonote struct response_t { uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; uint64_t height; uint64_t reserved_offset; uint64_t expected_reward; @@ -1160,6 +887,8 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(height) KV_SERIALIZE(reserved_offset) KV_SERIALIZE(expected_reward) @@ -1226,8 +955,12 @@ namespace cryptonote uint64_t height; uint64_t depth; std::string hash; - difficulty_type difficulty; - difficulty_type cumulative_difficulty; + uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; + uint64_t cumulative_difficulty; + std::string wide_cumulative_difficulty; + uint64_t cumulative_difficulty_top64; uint64_t reward; uint64_t block_size; uint64_t block_weight; @@ -1246,7 +979,11 @@ namespace cryptonote KV_SERIALIZE(depth) KV_SERIALIZE(hash) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(cumulative_difficulty) + KV_SERIALIZE(wide_cumulative_difficulty) + KV_SERIALIZE(cumulative_difficulty_top64) KV_SERIALIZE(reward) KV_SERIALIZE(block_size) KV_SERIALIZE_OPT(block_weight, (uint64_t)0) @@ -2248,6 +1985,8 @@ namespace cryptonote uint64_t height; uint64_t length; uint64_t difficulty; + std::string wide_difficulty; + uint64_t difficulty_top64; std::vector<std::string> block_hashes; std::string main_chain_parent_block; @@ -2256,6 +1995,8 @@ namespace cryptonote KV_SERIALIZE(height) KV_SERIALIZE(length) KV_SERIALIZE(difficulty) + KV_SERIALIZE(wide_difficulty) + KV_SERIALIZE(difficulty_top64) KV_SERIALIZE(block_hashes) KV_SERIALIZE(main_chain_parent_block) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index bde2339bc..540afe6b9 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -436,7 +436,8 @@ namespace rpc auto& chain = m_core.get_blockchain_storage(); - res.info.difficulty = chain.get_difficulty_for_next_block(); + res.info.wide_difficulty = chain.get_difficulty_for_next_block(); + res.info.difficulty = (res.info.wide_difficulty << 64 >> 64).convert_to<uint64_t>(); res.info.target = chain.get_difficulty_target(); @@ -457,7 +458,8 @@ namespace rpc res.info.mainnet = m_core.get_nettype() == MAINNET; res.info.testnet = m_core.get_nettype() == TESTNET; res.info.stagenet = m_core.get_nettype() == STAGENET; - res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.wide_cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>(); res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.info.start_time = (uint64_t)m_core.get_start_time(); @@ -826,7 +828,8 @@ namespace rpc header.reward += out.amount; } - header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + header.wide_difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + header.difficulty = (header.wide_difficulty << 64 >> 64).convert_to<uint64_t>(); return true; } diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 26c5038f6..2a43811cf 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -30,6 +30,7 @@ #include "crypto/hash.h" #include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" #include "ringct/rctSigs.h" #include "rpc/rpc_handler.h" @@ -165,6 +166,7 @@ namespace rpc uint64_t height; uint64_t depth; crypto::hash hash; + cryptonote::difficulty_type wide_difficulty; uint64_t difficulty; uint64_t reward; }; @@ -173,6 +175,7 @@ namespace rpc { uint64_t height; uint64_t target_height; + cryptonote::difficulty_type wide_difficulty; uint64_t difficulty; uint64_t target; uint64_t tx_count; @@ -187,6 +190,7 @@ namespace rpc bool stagenet; std::string nettype; crypto::hash top_block_hash; + cryptonote::difficulty_type wide_cumulative_difficulty; uint64_t cumulative_difficulty; uint64_t block_size_limit; uint64_t block_weight_limit; diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h new file mode 100644 index 000000000..e32e24b78 --- /dev/null +++ b/src/serialization/difficulty_type.h @@ -0,0 +1,65 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "cryptonote_basic/difficulty.h" +#include "serialization.h" + +template<> struct is_basic_type<cryptonote::difficulty_type> { typedef boost::true_type type; }; + +template <template <bool> class Archive> +inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) +{ + uint64_t hi, lo; + ar.serialize_varint(hi); + if (!ar.stream().good()) + return false; + ar.serialize_varint(lo); + if (!ar.stream().good()) + return false; + diff = hi; + diff <<= 64; + diff += lo; + return true; +} + +template <template <bool> class Archive> +inline bool do_serialize(Archive<true>& ar, cryptonote::difficulty_type &diff) +{ + if (!ar.stream().good()) + return false; + const uint64_t hi = (diff >> 64).convert_to<uint64_t>(); + const uint64_t lo = (diff << 64 >> 64).convert_to<uint64_t>(); + ar.serialize_varint(hi); + ar.serialize_varint(lo); + if (!ar.stream().good()) + return false; + return true; +} + diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d5835609a..91a8f6348 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -116,7 +116,9 @@ typedef cryptonote::simple_wallet sw; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ if (!m_long_payment_id_support) { \ - fail_msg_writer() << tr("Long payment IDs are obsolete. Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ + fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \ + fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \ + fail_msg_writer() << tr("Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ return true; \ } \ } while(0) @@ -149,7 +151,7 @@ namespace const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; - const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support", sw::tr("Support obsolete long (unencrypted) payment ids"), false}; + const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support-bad-for-privacy", sw::tr("Support obsolete long (unencrypted) payment ids (using them harms your privacy)"), false}; const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; @@ -5355,7 +5357,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri r = add_extra_nonce_to_tx_extra(extra, extra_nonce); local_args.pop_back(); payment_id_seen = true; - message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); + message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } if(!r) { @@ -5465,7 +5467,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { LONG_PAYMENT_ID_SUPPORT_CHECK(); set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); + message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } else { diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index efd61cb5a..def23aff0 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -63,6 +63,7 @@ target_link_libraries(wallet cryptonote_core mnemonics device_trezor + net ${LMDB_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index d7226b656..82986ba2d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2173,8 +2173,7 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { - // claim RPC so there's no in-memory encryption for now - if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) + if (!m_wallet->init(daemon_address, m_daemon_login, tcp::endpoint{}, upper_transaction_size_limit)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 53388d659..439873932 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -38,6 +38,7 @@ #include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/join.hpp> +#include <boost/asio/ip/address.hpp> #include <boost/range/adaptor/transformed.hpp> #include "include_base_utils.h" using namespace epee; @@ -75,6 +76,7 @@ using namespace epee; #include "ringdb.h" #include "device/device_cold.hpp" #include "device_trezor/device_trezor.hpp" +#include "net/socks_connect.h" extern "C" { @@ -231,6 +233,7 @@ namespace struct options { const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""}; const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""}; + const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true}; const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true}; @@ -303,6 +306,8 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { + namespace ip = boost::asio::ip; + const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; @@ -352,6 +357,44 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + boost::asio::ip::tcp::endpoint proxy{}; + if (command_line::has_arg(vm, opts.proxy)) + { + namespace ip = boost::asio::ip; + const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':')); + + // onion and i2p addresses contain information about the server cert + // which both authenticates and encrypts + const bool unencrypted_proxy = + !real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") && + daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty(); + THROW_WALLET_EXCEPTION_IF( + unencrypted_proxy, + tools::error::wallet_internal_error, + std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain" + ); + + const auto proxy_address = command_line::get_arg(vm, opts.proxy); + + boost::string_ref proxy_port{proxy_address}; + boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":")); + if (proxy_port.size() == proxy_host.size()) + proxy_host = "127.0.0.1"; + else + proxy_port = proxy_port.substr(proxy_host.size() + 1); + + uint16_t port_value = 0; + THROW_WALLET_EXCEPTION_IF( + !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}), + tools::error::wallet_internal_error, + std::string{"Invalid port specified for --"} + opts.proxy.name + ); + + boost::system::error_code error{}; + proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value}; + THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name); + } + boost::optional<bool> trusted_daemon; if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon)) trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon); @@ -388,8 +431,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); - wallet->init(std::move(daemon_address), std::move(login), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); - + wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->get_message_store().set_options(vm); @@ -470,7 +512,7 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string()); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0); - const bool recover = field_scan_from_height_found; + const bool recover = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string()); @@ -580,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f wallet.reset(make_basic(vm, unattended, 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); + if (!old_language.empty()) + wallet->set_seed_language(old_language); try { @@ -1046,6 +1090,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par const options opts{}; command_line::add_arg(desc_params, opts.daemon_address); command_line::add_arg(desc_params, opts.daemon_host); + command_line::add_arg(desc_params, opts.proxy); command_line::add_arg(desc_params, opts.trusted_daemon); command_line::add_arg(desc_params, opts.untrusted_daemon); command_line::add_arg(desc_params, opts.password); @@ -1109,7 +1154,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) { m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) @@ -1119,6 +1164,10 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils:: m_daemon_address = std::move(daemon_address); m_daemon_login = std::move(daemon_login); m_trusted_daemon = trusted_daemon; + if (proxy != boost::asio::ip::tcp::endpoint{}) + m_http_client.set_connector(net::socks::connector{std::move(proxy)}); + + // When switching from light wallet to full wallet, we need to reset the height we got from lw node. return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); } //---------------------------------------------------------------------------------------------------- @@ -2406,7 +2455,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry " (height " + std::to_string(start_height) + "), local block id at this height: " + string_tools::pod_to_hex(m_blockchain[current_index])); - detach_blockchain(current_index); + detach_blockchain(current_index, output_tracker_cache); process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); } else @@ -2850,7 +2899,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. // This call is not really needed for other purposes and can be removed if mymonero changes their backend. - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response res; + tools::COMMAND_RPC_GET_ADDRESS_INFO::response res; // Get basic info if(light_wallet_get_address_info(res)) { @@ -2998,7 +3047,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // if we've got at least 10 blocks to refresh, assume we're starting // a long refresh, and setup a tracking output cache if we need to - if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10) + if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10) output_tracker_cache = create_output_tracker_cache(); // switch to the new blocks from the daemon @@ -3139,7 +3188,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::detach_blockchain(uint64_t height) +void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { LOG_PRINT_L0("Detaching blockchain on height " << height); @@ -3161,6 +3210,15 @@ void wallet2::detach_blockchain(uint64_t height) } } + for (transfer_details &td: m_transfers) + { + while (!td.m_uses.empty() && td.m_uses.back().first >= height) + td.m_uses.pop_back(); + } + + if (output_tracker_cache) + output_tracker_cache->clear(); + auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;}); size_t i_start = it - m_transfers.begin(); @@ -3490,7 +3548,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_ decrypt_keys(original_password); setup_keys(new_password); rewrite(filename, new_password); - store(); + if (!filename.empty()) + store(); } //---------------------------------------------------------------------------------------------------- /*! @@ -5179,7 +5238,8 @@ std::string wallet2::path() const //---------------------------------------------------------------------------------------------------- void wallet2::store() { - store_to("", epee::wipeable_string()); + if (!m_wallet_file.empty()) + store_to("", epee::wipeable_string()); } //---------------------------------------------------------------------------------------------------- void wallet2::store_to(const std::string &path, const epee::wipeable_string &password) @@ -8361,8 +8421,8 @@ bool wallet2::light_wallet_login(bool &new_address) { MDEBUG("Light wallet login request"); m_light_wallet_connected = false; - cryptonote::COMMAND_RPC_LOGIN::request request; - cryptonote::COMMAND_RPC_LOGIN::response response; + tools::COMMAND_RPC_LOGIN::request request; + tools::COMMAND_RPC_LOGIN::response response; request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); // Always create account if it doesn't exist. @@ -8386,10 +8446,10 @@ bool wallet2::light_wallet_login(bool &new_address) return m_light_wallet_connected; } -bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) +bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) { MDEBUG("Light wallet import wallet request"); - cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; + tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); @@ -8405,8 +8465,8 @@ void wallet2::light_wallet_get_unspent_outs() { MDEBUG("Getting unspent outs"); - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; + tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; + tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; oreq.amount = "0"; oreq.address = get_account().get_public_address_str(m_nettype); @@ -8554,11 +8614,11 @@ void wallet2::light_wallet_get_unspent_outs() } } -bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response) +bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response) { MTRACE(__FUNCTION__); - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request; + tools::COMMAND_RPC_GET_ADDRESS_INFO::request request; request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); @@ -8574,8 +8634,8 @@ void wallet2::light_wallet_get_address_txs() { MDEBUG("Refreshing light wallet"); - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires; + tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; + tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires; ireq.address = get_account().get_public_address_str(m_nettype); ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); @@ -9809,7 +9869,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); throw_on_rpc_response_error(result, "get_hard_fork_info"); - bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand + bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand if (close_enough) LOG_PRINT_L2("Using v" << (unsigned)version << " rules"); else diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b879362e2..28ebd6704 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -62,6 +62,7 @@ #include "common/password.h" #include "node_rpc_proxy.h" #include "message_store.h" +#include "wallet_light_rpc.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" @@ -680,7 +681,9 @@ namespace tools bool deinit(); bool init(std::string daemon_address = "http://localhost:8080", - boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, + boost::optional<epee::net_utils::http::login> daemon_login = boost::none, + boost::asio::ip::tcp::endpoint proxy = {}, + uint64_t upper_transaction_weight_limit = 0, bool trusted_daemon = true, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, @@ -1176,11 +1179,11 @@ namespace tools // fetch txs and store in m_payments void light_wallet_get_address_txs(); // get_address_info - bool light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response); + bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response); // Login. new_address is true if address hasn't been used on lw node before. bool light_wallet_login(bool &new_address); // Send an import request to lw node. returns info about import fee, address and payment_id - bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); + bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); // get random outputs from light wallet server void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); // Parse rct string @@ -1278,7 +1281,7 @@ namespace tools bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); - void detach_blockchain(uint64_t height); + void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; bool clear(); void clear_soft(bool keep_key_images=false); diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h new file mode 100644 index 000000000..1d35cec33 --- /dev/null +++ b/src/wallet/wallet_light_rpc.h @@ -0,0 +1,320 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "cryptonote_basic/cryptonote_basic.h" +#include "crypto/hash.h" + +namespace tools +{ + //----------------------------------------------- + struct COMMAND_RPC_GET_ADDRESS_TXS + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct spent_output { + uint64_t amount; + std::string key_image; + std::string tx_pub_key; + uint64_t out_index; + uint32_t mixin; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(key_image) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(out_index) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + struct transaction + { + uint64_t id; + std::string hash; + uint64_t timestamp; + uint64_t total_received; + uint64_t total_sent; + uint64_t unlock_time; + uint64_t height; + std::list<spent_output> spent_outputs; + std::string payment_id; + bool coinbase; + bool mempool; + uint32_t mixin; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(hash) + KV_SERIALIZE(timestamp) + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_sent) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(height) + KV_SERIALIZE(spent_outputs) + KV_SERIALIZE(payment_id) + KV_SERIALIZE(coinbase) + KV_SERIALIZE(mempool) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + + struct response_t + { + //std::list<std::string> txs_as_json; + uint64_t total_received; + uint64_t total_received_unlocked = 0; // OpenMonero only + uint64_t scanned_height; + std::vector<transaction> transactions; + uint64_t blockchain_height; + uint64_t scanned_block_height; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_received_unlocked) + KV_SERIALIZE(scanned_height) + KV_SERIALIZE(transactions) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(scanned_block_height) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_ADDRESS_INFO + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct spent_output + { + uint64_t amount; + std::string key_image; + std::string tx_pub_key; + uint64_t out_index; + uint32_t mixin; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(key_image) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(out_index) + KV_SERIALIZE(mixin) + END_KV_SERIALIZE_MAP() + }; + + struct response_t + { + uint64_t locked_funds; + uint64_t total_received; + uint64_t total_sent; + uint64_t scanned_height; + uint64_t scanned_block_height; + uint64_t start_height; + uint64_t transaction_height; + uint64_t blockchain_height; + std::list<spent_output> spent_outputs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(locked_funds) + KV_SERIALIZE(total_received) + KV_SERIALIZE(total_sent) + KV_SERIALIZE(scanned_height) + KV_SERIALIZE(scanned_block_height) + KV_SERIALIZE(start_height) + KV_SERIALIZE(transaction_height) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(spent_outputs) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_UNSPENT_OUTS + { + struct request_t + { + std::string amount; + std::string address; + std::string view_key; + // OpenMonero specific + uint64_t mixin; + bool use_dust; + std::string dust_threshold; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + KV_SERIALIZE(mixin) + KV_SERIALIZE(use_dust) + KV_SERIALIZE(dust_threshold) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + + struct output { + uint64_t amount; + std::string public_key; + uint64_t index; + uint64_t global_index; + std::string rct; + std::string tx_hash; + std::string tx_pub_key; + std::string tx_prefix_hash; + std::vector<std::string> spend_key_images; + uint64_t timestamp; + uint64_t height; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(public_key) + KV_SERIALIZE(index) + KV_SERIALIZE(global_index) + KV_SERIALIZE(rct) + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_pub_key) + KV_SERIALIZE(tx_prefix_hash) + KV_SERIALIZE(spend_key_images) + KV_SERIALIZE(timestamp) + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + + struct response_t + { + uint64_t amount; + std::list<output> outputs; + uint64_t per_kb_fee; + std::string status; + std::string reason; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(outputs) + KV_SERIALIZE(per_kb_fee) + KV_SERIALIZE(status) + KV_SERIALIZE(reason) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- + struct COMMAND_RPC_LOGIN + { + struct request_t + { + std::string address; + std::string view_key; + bool create_account; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + KV_SERIALIZE(create_account) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string status; + std::string reason; + bool new_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(reason) + KV_SERIALIZE(new_address) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- + struct COMMAND_RPC_IMPORT_WALLET_REQUEST + { + struct request_t + { + std::string address; + std::string view_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(view_key) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string payment_id; + uint64_t import_fee; + bool new_request; + bool request_fulfilled; + std::string payment_address; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payment_id) + KV_SERIALIZE(import_fee) + KV_SERIALIZE(new_request) + KV_SERIALIZE(request_fulfilled) + KV_SERIALIZE(payment_address) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + //----------------------------------------------- +} diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a1f60ea01..18b2de33f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -56,6 +56,8 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" +#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds + namespace { const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; @@ -124,13 +126,18 @@ namespace tools { m_stop = false; m_net_server.add_idle_handler([this](){ + if (m_auto_refresh_period == 0) // disabled + return true; + if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period)) + return true; try { if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon()); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } + m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time(); return true; - }, 20000); + }, 1000); m_net_server.add_idle_handler([this](){ if (m_stop.load(std::memory_order_relaxed)) { @@ -263,6 +270,9 @@ namespace tools std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() }; std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); + m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD; + m_last_auto_refresh_time = boost::posix_time::min_date_time; + m_net_server.set_threads_prefix("RPC"); auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( @@ -2834,6 +2844,28 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try + { + m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0; + MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled"))); + return true; + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx) { if (!m_wallet) return not_open(er); @@ -2936,7 +2968,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); { std::vector<std::string> languages; crypto::ElectrumWords::get_language_list(languages); @@ -3221,12 +3253,6 @@ namespace tools } // early check for mandatory fields - if (req.filename.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; - return false; - } if (req.viewkey.empty()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -3255,7 +3281,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); // check if wallet file already exists if (!wallet_file.empty()) { @@ -3365,7 +3391,8 @@ namespace tools { try { - m_wallet->store(); + if (!wallet_file.empty()) + m_wallet->store(); } catch (const std::exception &e) { @@ -3389,12 +3416,6 @@ namespace tools } // early check for mandatory fields - if (req.filename.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; - return false; - } if (req.seed.empty()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -3417,7 +3438,7 @@ namespace tools er.message = "Invalid filename"; return false; } - std::string wallet_file = m_wallet_dir + "/" + req.filename; + std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); // check if wallet file already exists if (!wallet_file.empty()) { @@ -3533,7 +3554,7 @@ namespace tools er.message = "Failed to encode seed"; return false; } - res.seed = electrum_words.data(); + res.seed = std::string(electrum_words.data(), electrum_words.size()); if (!wal) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index affaf10f7..2b52275b8 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -129,6 +129,7 @@ namespace tools MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH) + MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH) MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT) MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING) MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING) @@ -210,6 +211,7 @@ namespace tools bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); @@ -256,5 +258,7 @@ namespace tools std::atomic<bool> m_stop; bool m_restricted; const boost::program_options::variables_map *m_vm; + uint32_t m_auto_refresh_period; + boost::posix_time::ptime m_last_auto_refresh_time; }; } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 36775fa1e..df4370949 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1932,6 +1932,28 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_AUTO_REFRESH + { + struct request_t + { + bool enable; + uint32_t period; // seconds + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(enable, true) + KV_SERIALIZE_OPT(period, (uint32_t)0) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_START_MINING { struct request_t diff --git a/tests/README.md b/tests/README.md index 001ab6154..053dd2244 100644 --- a/tests/README.md +++ b/tests/README.md @@ -18,7 +18,7 @@ Tests are located in `tests/core_tests/`, and follow a straightforward naming co To run only Monero's core tests (after building): ``` -cd build/debug/tests/core +cd build/debug/tests/core_tests ctest ``` @@ -103,6 +103,8 @@ cd build/debug/tests/performance_tests ./performance_tests ``` +The path may be build/Linux/master/debug (adapt as necessary for your platform). + If the `performance_tests` binary does not exist, try running `make` in the `build/debug/tests/performance_tests` directory. To run the same tests on a release build, replace `debug` with `release`. diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index 2ed495806..fb0dd6b9e 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -45,3 +45,6 @@ set_property(TARGET difficulty-tests add_test( NAME difficulty COMMAND difficulty-tests "${CMAKE_CURRENT_SOURCE_DIR}/data.txt") +add_test( + NAME wide_difficulty + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/wide_difficulty.py" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_wide_data.py" "${CMAKE_CURRENT_BINARY_DIR}/difficulty-tests" "${CMAKE_CURRENT_BINARY_DIR}/wide_data.txt") diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index ee20e27e4..9985b8710 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -43,16 +43,62 @@ using namespace std; #define DEFAULT_TEST_DIFFICULTY_TARGET 120 +static int test_wide_difficulty(const char *filename) +{ + std::vector<uint64_t> timestamps; + std::vector<cryptonote::difficulty_type> cumulative_difficulties; + fstream data(filename, fstream::in); + data.exceptions(fstream::badbit); + data.clear(data.rdstate()); + uint64_t timestamp; + cryptonote::difficulty_type difficulty, cumulative_difficulty = 0; + size_t n = 0; + while (data >> timestamp >> difficulty) { + size_t begin, end; + if (n < DIFFICULTY_WINDOW + DIFFICULTY_LAG) { + begin = 0; + end = min(n, (size_t) DIFFICULTY_WINDOW); + } else { + end = n - DIFFICULTY_LAG; + begin = end - DIFFICULTY_WINDOW; + } + cryptonote::difficulty_type res = cryptonote::next_difficulty( + std::vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end), + std::vector<cryptonote::difficulty_type>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); + if (res != difficulty) { + cerr << "Wrong wide difficulty for block " << n << endl + << "Expected: " << difficulty << endl + << "Found: " << res << endl; + return 1; + } + timestamps.push_back(timestamp); + cumulative_difficulties.push_back(cumulative_difficulty += difficulty); + ++n; + } + if (!data.eof()) { + data.clear(fstream::badbit); + } + return 0; +} + int main(int argc, char *argv[]) { - if (argc != 2) { + if (argc < 2) { cerr << "Wrong arguments" << endl; return 1; } + if (argc == 3 && strcmp(argv[1], "--wide") == 0) + { + return test_wide_difficulty(argv[2]); + } + vector<uint64_t> timestamps, cumulative_difficulties; + std::vector<cryptonote::difficulty_type> wide_cumulative_difficulties; fstream data(argv[1], fstream::in); data.exceptions(fstream::badbit); data.clear(data.rdstate()); - uint64_t timestamp, difficulty, cumulative_difficulty = 0; + uint64_t timestamp; + uint64_t difficulty, cumulative_difficulty = 0; + cryptonote::difficulty_type wide_cumulative_difficulty = 0; size_t n = 0; while (data >> timestamp >> difficulty) { size_t begin, end; @@ -63,17 +109,27 @@ int main(int argc, char *argv[]) { end = n - DIFFICULTY_LAG; begin = end - DIFFICULTY_WINDOW; } - uint64_t res = cryptonote::next_difficulty( + uint64_t res = cryptonote::next_difficulty_64( vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end), - vector<uint64_t>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); + std::vector<uint64_t>(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); if (res != difficulty) { cerr << "Wrong difficulty for block " << n << endl << "Expected: " << difficulty << endl << "Found: " << res << endl; return 1; } + cryptonote::difficulty_type wide_res = cryptonote::next_difficulty( + std::vector<uint64_t>(timestamps.begin() + begin, timestamps.begin() + end), + std::vector<cryptonote::difficulty_type>(wide_cumulative_difficulties.begin() + begin, wide_cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); + if (wide_res.convert_to<uint64_t>() != res) { + cerr << "Wrong wide difficulty for block " << n << endl + << "Expected: " << res << endl + << "Found: " << wide_res << endl; + return 1; + } timestamps.push_back(timestamp); cumulative_difficulties.push_back(cumulative_difficulty += difficulty); + wide_cumulative_difficulties.push_back(wide_cumulative_difficulty += difficulty); ++n; } if (!data.eof()) { diff --git a/tests/difficulty/gen_wide_data.py b/tests/difficulty/gen_wide_data.py new file mode 100644 index 000000000..64af4e208 --- /dev/null +++ b/tests/difficulty/gen_wide_data.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +from __future__ import print_function +import random + +DIFFICULTY_TARGET = 120 +DIFFICULTY_WINDOW = 720 +DIFFICULTY_LAG = 15 +DIFFICULTY_CUT = 60 + +def difficulty(): + times = [] + diffs = [] + while True: + if len(times) <= 1: + diff = 1 + else: + begin = max(len(times) - DIFFICULTY_WINDOW - DIFFICULTY_LAG, 0) + end = min(begin + DIFFICULTY_WINDOW, len(times)) + length = end - begin + assert length >= 2 + if length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT: + cut_begin = 0 + cut_end = length + else: + excess = length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + cut_begin = (excess + 1) // 2 + cut_end = length - excess // 2 + assert cut_begin + 2 <= cut_end + wnd = times[begin:end] + wnd.sort() + dtime = wnd[cut_end - 1] - wnd[cut_begin] + dtime = max(dtime, 1) + ddiff = sum(diffs[begin + cut_begin + 1:begin + cut_end]) + diff = (ddiff * DIFFICULTY_TARGET + dtime - 1) // dtime + times.append((yield diff)) + diffs.append(diff) + +random.seed(1) +time = 1000 +gen = difficulty() +diff = next(gen) +for i in range(100000): + power = 100 if i < 10000 else 100000000 if i < 500 else 1000000000000 if i < 1000 else 1000000000000000 if i < 2000 else 10000000000000000000 if i < 4000 else 1000000000000000000000000 + time += random.randint(-diff // power - 10, 3 * diff // power + 10) + print(time, diff) + diff = gen.send(time) diff --git a/tests/difficulty/wide_difficulty.py b/tests/difficulty/wide_difficulty.py new file mode 100755 index 000000000..41a2a632d --- /dev/null +++ b/tests/difficulty/wide_difficulty.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +from __future__ import print_function +import sys +import subprocess + +python = sys.argv[1] +py = sys.argv[2] +c = sys.argv[3] +data = sys.argv[4] + +first = python + " " + py + " > " + data +second = [c, '--wide', data] + +try: + print('running: ', first) + subprocess.check_call(first, shell=True) + print('running: ', second) + subprocess.check_call(second) +except: + sys.exit(1) + diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index 0a034bcd5..f4050c948 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -53,7 +53,7 @@ int ColdOutputsFuzzer::init() try { - wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); } diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index fc0cfa2bd..08117281b 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -54,7 +54,7 @@ int ColdTransactionFuzzer::init() try { - wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); } diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index 3f0ada0c9..038378ae2 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -54,7 +54,7 @@ int SignatureFuzzer::init() try { - wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 70368ce24..12acc5a67 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -40,7 +40,7 @@ using cryptonote::check_hash; int main(int argc, char *argv[]) { crypto::hash h; - for (uint64_t diff = 1;; diff += 1 + (diff >> 8)) { + for (cryptonote::difficulty_type diff = 1;; diff += 1 + (diff >> 8)) { for (uint16_t b = 0; b < 256; b++) { memset(&h, b, sizeof(crypto::hash)); if (check_hash(h, diff) != (b == 0 || diff <= 255 / b)) { @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) { memset(&h, 0, sizeof(crypto::hash)); ((char *) &h)[31] = b; if (check_hash(h, diff) != (diff <= 255 / b)) { - return 1; + return 2; } } } @@ -58,11 +58,11 @@ int main(int argc, char *argv[]) { uint64_t val = 0; for (int i = 31; i >= 0; i--) { val = val * 256 + 255; - ((char *) &h)[i] = static_cast<char>(val / diff); - val %= diff; + ((char *) &h)[i] = static_cast<char>(static_cast<uint64_t>(val / diff)); + val %= diff.convert_to<uint64_t>(); } if (check_hash(h, diff) != true) { - return 1; + return 3; } if (diff > 1) { for (int i = 0;; i++) { @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) { } } if (check_hash(h, diff) != false) { - return 1; + return 4; } } } diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index aa424da80..b36df10dc 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -31,6 +31,7 @@ set(performance_tests_sources set(performance_tests_headers check_tx_signature.h + check_hash.h cn_slow_hash.h construct_tx.h derive_public_key.h diff --git a/tests/performance_tests/check_hash.h b/tests/performance_tests/check_hash.h new file mode 100644 index 000000000..d24001903 --- /dev/null +++ b/tests/performance_tests/check_hash.h @@ -0,0 +1,66 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "string_tools.h" +#include "cryptonote_basic/difficulty.h" + +template<uint64_t hash_target_high, uint64_t hash_target_low, uint64_t difficulty_high, uint64_t difficulty_low> +class test_check_hash +{ +public: + static const size_t loop_count = 100000; + + bool init() + { + cryptonote::difficulty_type hash_target = hash_target_high; + hash_target = (hash_target << 64) | hash_target_low; + difficulty = difficulty_high; + difficulty = (difficulty << 64) | difficulty_low; + boost::multiprecision::uint256_t hash_value = std::numeric_limits<boost::multiprecision::uint256_t>::max() / hash_target; + ((uint64_t*)&hash)[0] = (hash_value << 64 >> 64).convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&hash)[1] = (hash_value << 64 >> 64).convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&hash)[2] = (hash_value << 64 >> 64).convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&hash)[3] = (hash_value << 64 >> 64).convert_to<uint64_t>(); + return true; + } + + bool test() + { + cryptonote::check_hash_128(hash, difficulty); + return true; + } + +private: + crypto::hash hash; + cryptonote::difficulty_type difficulty; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index e6558a364..c32e0df20 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -38,6 +38,7 @@ // tests #include "construct_tx.h" #include "check_tx_signature.h" +#include "check_hash.h" #include "cn_slow_hash.h" #include "derive_public_key.h" #include "derive_secret_key.h" @@ -181,6 +182,14 @@ int main(int argc, char** argv) TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16); TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16); + TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 1, 0, 1); + TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 0xffffffffffffffff); + TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 1); + TEST_PERFORMANCE4(filter, p, test_check_hash, 1, 0, 1, 0); + TEST_PERFORMANCE4(filter, p, test_check_hash, 1, 0, 0, 1); + TEST_PERFORMANCE4(filter, p, test_check_hash, 0xffffffffffffffff, 0xffffffffffffffff, 0, 1); + TEST_PERFORMANCE4(filter, p, test_check_hash, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff); + TEST_PERFORMANCE0(filter, p, test_is_out_to_acc); TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp); TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index bfaea6add..56a1f8c4d 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -43,6 +43,7 @@ set(unit_tests_sources crypto.cpp decompose_amount_into_digits.cpp device.cpp + difficulty.cpp dns_resolver.cpp epee_boosted_tcp_server.cpp epee_levin_protocol_handler_async.cpp diff --git a/tests/unit_tests/difficulty.cpp b/tests/unit_tests/difficulty.cpp new file mode 100644 index 000000000..090fecc84 --- /dev/null +++ b/tests/unit_tests/difficulty.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" +#include "cryptonote_basic/difficulty.h" + +static cryptonote::difficulty_type MKDIFF(uint64_t high, uint64_t low) +{ + cryptonote::difficulty_type d = high; + d = (d << 64) | low; + return d; +} + +static crypto::hash MKHASH(uint64_t high, uint64_t low) +{ + cryptonote::difficulty_type hash_target = high; + hash_target = (hash_target << 64) | low; + boost::multiprecision::uint256_t hash_value = std::numeric_limits<boost::multiprecision::uint256_t>::max() / hash_target; + crypto::hash h; + ((uint64_t*)&h)[0] = hash_value.convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&h)[1] = hash_value.convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&h)[2] = hash_value.convert_to<uint64_t>(); + hash_value >>= 64; + ((uint64_t*)&h)[3] = hash_value.convert_to<uint64_t>(); + return h; +} + +TEST(difficulty, check_hash) +{ + ASSERT_TRUE(cryptonote::check_hash(MKHASH(0, 1), MKDIFF(0, 1))); + ASSERT_FALSE(cryptonote::check_hash(MKHASH(0, 1), MKDIFF(0, 2))); + + ASSERT_TRUE(cryptonote::check_hash(MKHASH(0, 0xffffffffffffffff), MKDIFF(0, 0xffffffffffffffff))); + ASSERT_FALSE(cryptonote::check_hash(MKHASH(0, 0xffffffffffffffff), MKDIFF(1, 0))); + + ASSERT_TRUE(cryptonote::check_hash(MKHASH(1, 1), MKDIFF(1, 1))); + ASSERT_FALSE(cryptonote::check_hash(MKHASH(1, 1), MKDIFF(1, 2))); + + ASSERT_TRUE(cryptonote::check_hash(MKHASH(0xffffffffffffffff, 1), MKDIFF(0xffffffffffffffff, 1))); + ASSERT_FALSE(cryptonote::check_hash(MKHASH(0xffffffffffffffff, 1), MKDIFF(0xffffffffffffffff, 2))); +} diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index c8e60200c..c5917200e 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -71,7 +71,7 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) try { - wallet.init("", boost::none, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET)); diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index a38ecfe81..77fb71d96 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -33,6 +33,7 @@ #include <boost/asio/io_service.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/read.hpp> +#include <boost/asio/steady_timer.hpp> #include <boost/asio/write.hpp> #include <boost/endian/conversion.hpp> #include <boost/system/error_code.hpp> @@ -45,6 +46,7 @@ #include "net/error.h" #include "net/net_utils_base.h" #include "net/socks.h" +#include "net/socks_connect.h" #include "net/parse.h" #include "net/tor_address.h" #include "p2p/net_peerlist_boost_serialization.h" @@ -742,4 +744,92 @@ TEST(socks_client, resolve_command) while (test_client->called_ == 1); } +TEST(socks_connector, host) +{ + io_thread io{}; + boost::asio::steady_timer timeout{io.io_service}; + timeout.expires_from_now(std::chrono::seconds{5}); + + boost::unique_future<boost::asio::ip::tcp::socket> sock = + net::socks::connector{io.acceptor.local_endpoint()}("example.com", "8080", timeout); + + while (!io.connected); + const std::uint8_t expected_bytes[] = { + 4, 1, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, + 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm', 0x00 + }; + + std::uint8_t actual_bytes[sizeof(expected_bytes)]; + boost::asio::read(io.server, boost::asio::buffer(actual_bytes)); + EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0); + + const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0}; + boost::asio::write(io.server, boost::asio::buffer(reply_bytes)); + + ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3})); + EXPECT_TRUE(sock.get().is_open()); +} + +TEST(socks_connector, ipv4) +{ + io_thread io{}; + boost::asio::steady_timer timeout{io.io_service}; + timeout.expires_from_now(std::chrono::seconds{5}); + + boost::unique_future<boost::asio::ip::tcp::socket> sock = + net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout); + + while (!io.connected); + const std::uint8_t expected_bytes[] = { + 4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00 + }; + + std::uint8_t actual_bytes[sizeof(expected_bytes)]; + boost::asio::read(io.server, boost::asio::buffer(actual_bytes)); + EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0); + + const std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0, 0, 0, 0}; + boost::asio::write(io.server, boost::asio::buffer(reply_bytes)); + + ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3})); + EXPECT_TRUE(sock.get().is_open()); +} + +TEST(socks_connector, error) +{ + io_thread io{}; + boost::asio::steady_timer timeout{io.io_service}; + timeout.expires_from_now(std::chrono::seconds{5}); + + boost::unique_future<boost::asio::ip::tcp::socket> sock = + net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout); + + while (!io.connected); + const std::uint8_t expected_bytes[] = { + 4, 1, 0x1f, 0x90, 0xfa, 0x58, 0x7d, 0x63, 0x00 + }; + + std::uint8_t actual_bytes[sizeof(expected_bytes)]; + boost::asio::read(io.server, boost::asio::buffer(actual_bytes)); + EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0); + + const std::uint8_t reply_bytes[] = {0, 91, 0, 0, 0, 0, 0, 0}; + boost::asio::write(io.server, boost::asio::buffer(reply_bytes)); + + ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3})); + EXPECT_THROW(sock.get().is_open(), boost::system::system_error); +} + +TEST(socks_connector, timeout) +{ + io_thread io{}; + boost::asio::steady_timer timeout{io.io_service}; + timeout.expires_from_now(std::chrono::milliseconds{10}); + + boost::unique_future<boost::asio::ip::tcp::socket> sock = + net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout); + + ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3})); + EXPECT_THROW(sock.get().is_open(), boost::system::system_error); +} diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index eb70caefc..27b14ffff 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1187,3 +1187,26 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(epee::string_tools::pod_to_hex(ki1) == "d54cbd435a8d636ad9b01b8d4f3eb13bd0cf1ce98eddf53ab1617f9b763e66c0"); ASSERT_TRUE(epee::string_tools::pod_to_hex(ki2) == "6c3cd6af97c4070a7aef9b1344e7463e29c7cd245076fdb65da447a34da3ca76"); } + +TEST(Serialization, difficulty_type) +{ + std::vector<cryptonote::difficulty_type> v_original; + + for(int i = 0; i != 100; i++) + { + v_original.push_back(cryptonote::difficulty_type("117868131154734361989189100")); + if(v_original.size() > 1) + v_original.back() *= v_original[v_original.size()-2]; + } + + std::stringstream ss; + boost::archive::portable_binary_oarchive a(ss); + a << v_original; + + std::vector<cryptonote::difficulty_type> v_unserialized; + + boost::archive::portable_binary_iarchive a2(ss); + a2 >> v_unserialized; + + ASSERT_EQ(v_original, v_unserialized); +} |