diff options
37 files changed, 1041 insertions, 65 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f43f9d1a..49ac18c66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -522,7 +522,7 @@ ExternalProject_Add(generate_translations_header BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations" STAMP_DIR ${LRELEASE_PATH} CMAKE_ARGS -DLRELEASE_PATH=${LRELEASE_PATH} - INSTALL_COMMAND cmake -E echo "") + INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "") include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") add_subdirectory(external) @@ -165,7 +165,7 @@ library archives (`.a`). | pkg-config | any | NO | `pkg-config` | `base-devel` | `base-devel` | `pkgconf` | NO | | | Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries | | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `libressl-devel` | `openssl-devel` | NO | sha256 sum | -| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library | +| libzmq | 4.2.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | | `openpgm-devel` | NO | For ZeroMQ | | libnorm[2] | ? | NO | `libnorm-dev` | | | | YES | For ZeroMQ | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | `unbound-devel` | NO | DNS resolver | diff --git a/cmake/FindCcache.cmake b/cmake/FindCcache.cmake index a0734a312..f7b1b8425 100644 --- a/cmake/FindCcache.cmake +++ b/cmake/FindCcache.cmake @@ -42,12 +42,19 @@ find_program(CCACHE_FOUND ccache) if (CCACHE_FOUND) # Try to compile a test program with ccache, in order to verify if it really works. (needed on exotic setups) - # Create a temporary file with a simple program. - set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp") - file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }") - # And run the found ccache on it. - execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET) - if (${RET} EQUAL 0) + set(TEST_PROJECT "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeTmp") + file(WRITE "${TEST_PROJECT}/CMakeLists.txt" [=[ +cmake_minimum_required(VERSION 3.1) +project(test) +option (CCACHE "") +file(WRITE "${CMAKE_SOURCE_DIR}/test.cpp" "int main() { return 0; }") +set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}") +set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE}") +add_executable(test test.cpp) +]=]) + try_compile(RET "${TEST_PROJECT}/build" "${TEST_PROJECT}" "test" CMAKE_FLAGS -DCCACHE="${CCACHE_FOUND}") + unset(TEST_PROJECT) + if (${RET}) # Success message(STATUS "Found usable ccache: ${CCACHE_FOUND}") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}") diff --git a/contrib/epee/include/byte_slice.h b/contrib/epee/include/byte_slice.h index 6b79f6d92..18d60e088 100644 --- a/contrib/epee/include/byte_slice.h +++ b/contrib/epee/include/byte_slice.h @@ -112,7 +112,7 @@ namespace epee explicit byte_slice(std::string&& buffer); //! Convert `stream` into a slice with zero allocations. - explicit byte_slice(byte_stream&& stream) noexcept; + explicit byte_slice(byte_stream&& stream, bool shrink = true); byte_slice(byte_slice&& source) noexcept; ~byte_slice() noexcept = default; diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 3c31cf22b..f40cd108a 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -265,6 +265,12 @@ namespace net_utils template<class t_callback> bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + boost::asio::ssl::context& get_ssl_context() noexcept + { + assert(m_state != nullptr); + return m_state->ssl_context; + } + typename t_protocol_handler::config_type& get_config_object() { assert(m_state != nullptr); // always set in constructor diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index b03a03cad..61e2b30fe 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -561,7 +561,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { // LOCK: chunking epee::critical_region_t<decltype(m_chunking_lock)> send_guard(m_chunking_lock); // *** critical *** - MDEBUG("do_send() will SPLIT into small chunks, from packet="<<message_size<<" B for ptr="<<message_data); + MDEBUG("do_send() will SPLIT into small chunks, from packet="<<message_size<<" B for ptr="<<(const void*)message_data); // 01234567890 // ^^^^ (pos=0, len=4) ; pos:=pos+len, pos=4 // ^^^^ (pos=4, len=4) ; pos:=pos+len, pos=8 @@ -574,14 +574,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) while (!message.empty()) { byte_slice chunk = message.take_slice(chunksize_good); - MDEBUG("chunk_start="<<(void*)chunk.data()<<" ptr="<<message_data<<" pos="<<(chunk.data() - message_data)); + MDEBUG("chunk_start="<<(void*)chunk.data()<<" ptr="<<(const void*)message_data<<" pos="<<(chunk.data() - message_data)); MDEBUG("part of " << message.size() << ": pos="<<(chunk.data() - message_data) << " len="<<chunk.size()); bool ok = do_send_chunk(std::move(chunk)); // <====== *** all_ok = all_ok && ok; if (!all_ok) { - MDEBUG("do_send() DONE ***FAILED*** from packet="<<message_size<<" B for ptr="<<message_data); + MDEBUG("do_send() DONE ***FAILED*** from packet="<<message_size<<" B for ptr="<<(const void*)message_data); MDEBUG("do_send() SEND was aborted in middle of big package - this is mostly harmless " << " (e.g. peer closed connection) but if it causes trouble tell us at #monero-dev. " << message_size); return false; // partial failure in sending @@ -589,7 +589,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) // (in catch block, or uniq pointer) delete buf; } // each chunk - MDEBUG("do_send() DONE SPLIT from packet="<<message_size<<" B for ptr="<<message_data); + MDEBUG("do_send() DONE SPLIT from packet="<<message_size<<" B for ptr="<<(const void*)message_data); MDEBUG("do_send() m_connection_type = " << m_connection_type); diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 1665fdac7..ffb3f3b7e 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -74,7 +74,13 @@ uint64_t ticks = misc_utils::get_tick_count(); \ boost::value_initialized<command_type::request> req; \ bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \ - CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ + if (!parse_res) \ + { \ + MERROR("Failed to parse json: \r\n" << query_info.m_body); \ + response_info.m_response_code = 400; \ + response_info.m_response_comment = "Bad request"; \ + return true; \ + } \ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ boost::value_initialized<command_type::response> resp;\ MINFO(m_conn_context << "calling " << s_pattern); \ @@ -104,7 +110,13 @@ uint64_t ticks = misc_utils::get_tick_count(); \ boost::value_initialized<command_type::request> req; \ bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), epee::strspan<uint8_t>(query_info.m_body)); \ - CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ + if (!parse_res) \ + { \ + MERROR("Failed to parse bin body data, body size=" << query_info.m_body.size()); \ + response_info.m_response_code = 400; \ + response_info.m_response_comment = "Bad request"; \ + return true; \ + } \ uint64_t ticks1 = misc_utils::get_tick_count(); \ boost::value_initialized<command_type::response> resp;\ MINFO(m_conn_context << "calling " << s_pattern); \ diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 635876589..f6b73a2d5 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -787,7 +787,7 @@ void async_protocol_handler_config<t_connection_context>::delete_connections(siz { auto i = connections.end() - 1; async_protocol_handler<t_connection_context> *conn = m_connects.at(*i); - del_connection(conn); + m_connects.erase(*i); conn->close(); connections.erase(i); } diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index 1b1577e77..58cd7e45f 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -36,6 +36,7 @@ #include <boost/utility/string_ref.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ssl.hpp> +#include <boost/filesystem/path.hpp> #include <boost/system/error_code.hpp> #define SSL_FINGERPRINT_SIZE 32 @@ -144,6 +145,9 @@ namespace net_utils bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert); bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert); + + //! Store private key for `ssl` at `base + ".key"` unencrypted and certificate for `ssl` at `base + ".crt"`. + boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base); } } diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index fd343865c..2e4a0faad 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -73,7 +73,8 @@ public: \ template<bool is_store, class t_storage> \ bool serialize_map(t_storage& stg, typename t_storage::hsection hparent_section) \ { \ - decltype(*this) &this_ref = *this; + decltype(*this) &this_ref = *this; \ + (void) this_ref; // Suppress unused var warnings. Sometimes this var is used, sometimes not. #define KV_SERIALIZE_N(varialble, val_name) \ epee::serialization::selector<is_store>::serialize(this_ref.varialble, stg, hparent_section, val_name); diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 641d4718e..881ae5746 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -26,11 +26,20 @@ # 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(EPEE_INCLUDE_DIR_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../include") + +# Adding headers to the file list, to be able to search for them in IDEs. +file(GLOB EPEE_HEADERS_PUBLIC + "${EPEE_INCLUDE_DIR_BASE}/*.h*" # h* will include hpps as well. + "${EPEE_INCLUDE_DIR_BASE}/**/*.h*" # Any number of subdirs will be included. +) add_library(epee STATIC byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp int-util.cpp portable_storage.cpp misc_language.cpp + + ${EPEE_HEADERS_PUBLIC} ) if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW))) @@ -74,5 +83,5 @@ if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW))) ${GNU_READLINE_LIBRARY}) endif() -target_include_directories(epee PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include") +target_include_directories(epee PUBLIC "${EPEE_INCLUDE_DIR_BASE}") diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp index faf7689be..453b63a4c 100644 --- a/contrib/epee/src/byte_slice.cpp +++ b/contrib/epee/src/byte_slice.cpp @@ -36,6 +36,11 @@ #include "byte_slice.h" #include "byte_stream.h" +namespace +{ + const std::size_t page_size = 4096; +} + namespace epee { struct byte_slice_data @@ -173,16 +178,27 @@ namespace epee : byte_slice(adapt_buffer{}, std::move(buffer)) {} - byte_slice::byte_slice(byte_stream&& stream) noexcept + byte_slice::byte_slice(byte_stream&& stream, const bool shrink) : storage_(nullptr), portion_(stream.data(), stream.size()) { - if (stream.size()) + if (portion_.size()) { - std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice); + byte_buffer buf; + if (shrink && page_size <= stream.available()) + { + buf = byte_buffer_resize(stream.take_buffer(), portion_.size()); + if (!buf) + throw std::bad_alloc{}; + portion_ = {buf.get(), portion_.size()}; + } + else // no need to shrink buffer + buf = stream.take_buffer(); + + std::uint8_t* const data = buf.release() - sizeof(raw_byte_slice); new (data) raw_byte_slice{}; storage_.reset(reinterpret_cast<raw_byte_slice*>(data)); } - else + else // empty stream portion_ = nullptr; } diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 6ed27efa9..765dadce3 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -29,6 +29,8 @@ #include <string.h> #include <thread> #include <boost/asio/ssl.hpp> +#include <boost/cerrno.hpp> +#include <boost/filesystem/operations.hpp> #include <boost/lambda/lambda.hpp> #include <openssl/ssl.h> #include <openssl/pem.h> @@ -567,6 +569,51 @@ bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s) return true; } +boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base) +{ + EVP_PKEY* ssl_key = nullptr; + X509* ssl_cert = nullptr; + const auto ctx = ssl.native_handle(); + CHECK_AND_ASSERT_MES(ctx, boost::system::error_code(EINVAL, boost::system::system_category()), "Context is null"); + CHECK_AND_ASSERT_MES(base.has_filename(), boost::system::error_code(EINVAL, boost::system::system_category()), "Need filename"); + if (!(ssl_key = SSL_CTX_get0_privatekey(ctx)) || !(ssl_cert = SSL_CTX_get0_certificate(ctx))) + return {EINVAL, boost::system::system_category()}; + + using file_closer = int(std::FILE*); + boost::system::error_code error{}; + std::unique_ptr<std::FILE, file_closer*> file{nullptr, std::fclose}; + + // write key file unencrypted + { + const boost::filesystem::path key_file{base.string() + ".key"}; + file.reset(std::fopen(key_file.string().c_str(), "wb")); + if (!file) + return {errno, boost::system::system_category()}; + boost::filesystem::permissions(key_file, boost::filesystem::owner_read, error); + if (error) + return error; + if (!PEM_write_PrivateKey(file.get(), ssl_key, nullptr, nullptr, 0, nullptr, nullptr)) + return boost::asio::error::ssl_errors(ERR_get_error()); + if (std::fclose(file.release()) != 0) + return {errno, boost::system::system_category()}; + } + + // write certificate file in standard SSL X.509 unencrypted + const boost::filesystem::path cert_file{base.string() + ".crt"}; + file.reset(std::fopen(cert_file.string().c_str(), "wb")); + if (!file) + return {errno, boost::system::system_category()}; + const auto cert_perms = (boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read); + boost::filesystem::permissions(cert_file, cert_perms, error); + if (error) + return error; + if (!PEM_write_X509(file.get(), ssl_cert)) + return boost::asio::error::ssl_errors(ERR_get_error()); + if (std::fclose(file.release()) != 0) + return {errno, boost::system::system_category()}; + return error; +} + } // namespace } // namespace diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 7dfc5151d..1cd502994 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -87,6 +87,10 @@ void hash_extra_jh(const void *data, size_t length, char *hash); void hash_extra_skein(const void *data, size_t length, char *hash); void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); +bool tree_path(size_t count, size_t idx, uint32_t *path); +bool tree_branch(const char (*hashes)[HASH_SIZE], size_t count, const char *hash, char (*branch)[HASH_SIZE], size_t *depth, uint32_t *path); +bool tree_branch_hash(const char hash[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path, char root[HASH_SIZE]); +bool is_branch_in_tree(const char hash[HASH_SIZE], const char root[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path); #define RX_BLOCK_VERSION 12 void rx_slow_hash_allocate_state(void); diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 643e95121..8f3ea3339 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -104,3 +104,154 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { free(ints); } } + +bool tree_path(size_t count, size_t idx, uint32_t *path) +{ + if (count == 0) + return false; + + if (count == 1) { + *path = 0; + } else if (count == 2) { + *path = idx == 0 ? 0 : 1; + } else { + size_t i, j; + + *path = 0; + size_t cnt = tree_hash_cnt( count ); + + for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { + if (idx == i || idx == i+1) + { + *path = (*path << 1) | (idx == i ? 0 : 1); + idx = j; + } + } + assert(i == count); + + while (cnt > 2) { + cnt >>= 1; + for (i = 0, j = 0; j < cnt; i += 2, ++j) { + if (idx == i || idx == i + 1) + { + *path = (*path << 1) | (idx == i ? 0 : 1); + idx = j; + } + } + } + + if (idx == 0 || idx == 1) + { + *path = (*path << 1) | (idx == 0 ? 0 : 1); + idx = 0; + } + } + return true; +} + +bool tree_branch(const char (*hashes)[HASH_SIZE], size_t count, const char *hash, char (*branch)[HASH_SIZE], size_t *depth, uint32_t *path) +{ + size_t idx; + + if (count == 0) + return false; + + for (idx = 0; idx < count; ++idx) + if (!memcmp(hash, hashes[idx], HASH_SIZE)) + break; + if (idx == count) + return false; + + assert(count > 0); + if (count == 1) { + *depth = 0; + *path = 0; + } else if (count == 2) { + *depth = 1; + *path = idx == 0 ? 0 : 1; + memcpy(branch[0], hashes[idx ^ 1], HASH_SIZE); + } else { + size_t i, j; + + *depth = 0; + *path = 0; + size_t cnt = tree_hash_cnt( count ); + + char *ints = calloc(cnt, HASH_SIZE); // zero out as extra protection for using uninitialized mem + assert(ints); + + memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); + + for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { + if (idx == i || idx == i+1) + { + memcpy(branch[*depth], hashes[idx == i ? i + 1 : i], HASH_SIZE); + ++*depth; + *path = (*path << 1) | (idx == i ? 0 : 1); + idx = j; + } + cn_fast_hash(hashes[i], 64, ints + j * HASH_SIZE); + } + assert(i == count); + + while (cnt > 2) { + cnt >>= 1; + for (i = 0, j = 0; j < cnt; i += 2, ++j) { + if (idx == i || idx == i + 1) + { + memcpy(branch[*depth], ints + (idx == i ? i + 1 : i) * HASH_SIZE, HASH_SIZE); + ++*depth; + *path = (*path << 1) | (idx == i ? 0 : 1); + idx = j; + } + cn_fast_hash(ints + i * HASH_SIZE, 64, ints + j * HASH_SIZE); + } + } + + if (idx == 0 || idx == 1) + { + memcpy(branch[*depth], ints + (idx == 0 ? 1 : 0) * HASH_SIZE, HASH_SIZE); + ++*depth; + *path = (*path << 1) | (idx == 0 ? 0 : 1); + idx = 0; + } + + free(ints); + } + return true; +} + +bool tree_branch_hash(const char hash[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path, char root[HASH_SIZE]) +{ + size_t d; + char partial[HASH_SIZE]; + + memcpy(partial, hash, HASH_SIZE); + + for (d = 0; d < depth; ++d) + { + char buffer[2 * HASH_SIZE]; + if ((path >> (depth - d - 1)) & 1) + { + memcpy(buffer, branch[d], HASH_SIZE); + memcpy(buffer + HASH_SIZE, partial, HASH_SIZE); + } + else + { + memcpy(buffer, partial, HASH_SIZE); + memcpy(buffer + HASH_SIZE, branch[d], HASH_SIZE); + } + cn_fast_hash(buffer, 2 * HASH_SIZE, partial); + } + + memcpy(root, partial, HASH_SIZE); + return true; +} + +bool is_branch_in_tree(const char hash[HASH_SIZE], const char root[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path) +{ + char res[HASH_SIZE]; + if (!tree_branch_hash(hash, branch, depth, path, res)) + return false; + return memcmp(res, root, HASH_SIZE) == 0; +} diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 5286256c7..c9fb1433c 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -43,6 +43,7 @@ set(cryptonote_basic_sources cryptonote_format_utils.cpp difficulty.cpp hardfork.cpp + merge_mining.cpp miner.cpp) set(cryptonote_basic_headers) @@ -57,6 +58,7 @@ set(cryptonote_basic_private_headers cryptonote_format_utils.h difficulty.h hardfork.h + merge_mining.h miner.h tx_extra.h verification_context.h) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 1ef6590eb..3e4532d4e 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -727,6 +727,25 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, size_t mm_merkle_tree_depth) + { + CHECK_AND_ASSERT_MES(mm_merkle_tree_depth < 32, false, "merge mining merkle tree depth should be less than 32"); + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 3 + 32); + //write tag + tx_extra[start_pos] = TX_EXTRA_MERGE_MINING_TAG; + //write data size + ++start_pos; + tx_extra[start_pos] = 33; + //write depth varint (always one byte here) + ++start_pos; + tx_extra[start_pos] = mm_merkle_tree_depth; + //write data + ++start_pos; + memcpy(&tx_extra[start_pos], &mm_merkle_root, 32); + return true; + } + //--------------------------------------------------------------- bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type) { if (tx_extra.empty()) diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 636a88b9a..b311bd2b2 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -83,6 +83,7 @@ namespace cryptonote std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx); bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys); bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce); + bool add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, size_t mm_merkle_tree_depth); bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id); diff --git a/src/cryptonote_basic/merge_mining.cpp b/src/cryptonote_basic/merge_mining.cpp new file mode 100644 index 000000000..fcc74859f --- /dev/null +++ b/src/cryptonote_basic/merge_mining.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2020, 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 <string.h> +#include "misc_log_ex.h" +#include "int-util.h" +#include "crypto/crypto.h" +#include "common/util.h" +#include "merge_mining.h" + +using namespace epee; + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "cn.mm" + +using namespace crypto; + +namespace cryptonote +{ + +//--------------------------------------------------------------- +uint32_t get_aux_slot(const crypto::hash &id, uint32_t nonce, uint32_t n_aux_chains) +{ + CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0"); + + uint8_t buf[HASH_SIZE + sizeof(uint32_t) + 1]; + memcpy(buf, &id, HASH_SIZE); + uint32_t v = SWAP32LE(nonce); + memcpy(buf + HASH_SIZE, &v, sizeof(uint32_t)); + buf[HASH_SIZE + sizeof(uint32_t)] = config::HASH_KEY_MM_SLOT; + + crypto::hash res; + tools::sha256sum(buf, sizeof(buf), res); + v = *((const uint32_t*)&res); + return SWAP32LE(v) % n_aux_chains; +} +//--------------------------------------------------------------- +uint32_t get_path_from_aux_slot(uint32_t slot, uint32_t n_aux_chains) +{ + CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0"); + CHECK_AND_ASSERT_THROW_MES(slot < n_aux_chains, "slot >= n_aux_chains"); + + uint32_t path = 0; + CHECK_AND_ASSERT_THROW_MES(tree_path(n_aux_chains, slot, &path), "Failed to get path from aux slot"); + return path; +} +//--------------------------------------------------------------- +uint32_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce) +{ + CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0"); + + // how many bits to we need to representing n_aux_chains - 1 + uint32_t n_bits = 1; + while ((1u << n_bits) < n_aux_chains && n_bits < 16) + ++n_bits; + CHECK_AND_ASSERT_THROW_MES(n_bits <= 16, "Way too many bits required"); + + const uint32_t depth = (n_bits - 1) | ((n_aux_chains - 1) << 3) | (nonce << (3 + n_bits)); + return depth; +} +//--------------------------------------------------------------- +bool decode_mm_depth(uint32_t depth, uint32_t &n_aux_chains, uint32_t &nonce) +{ + const uint32_t n_bits = 1 + (depth & 7); + n_aux_chains = 1 + (depth >> 3 & ((1 << n_bits) - 1)); + nonce = depth >> (3 + n_bits); + return true; +} +//--------------------------------------------------------------- +} diff --git a/src/cryptonote_basic/merge_mining.h b/src/cryptonote_basic/merge_mining.h new file mode 100644 index 000000000..378438f7c --- /dev/null +++ b/src/cryptonote_basic/merge_mining.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020, 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 <stdint.h> +#include "crypto/crypto.h" + +namespace cryptonote +{ + uint32_t get_aux_slot(const crypto::hash &id, uint32_t nonce, uint32_t n_aux_chains); + uint32_t get_path_from_aux_slot(uint32_t slot, uint32_t n_aux_chains); + uint32_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce); + bool decode_mm_depth(uint32_t depth, uint32_t &n_aux_chains, uint32_t &nonce); +} diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6327f7379..915835d1b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -234,6 +234,7 @@ namespace config const unsigned char HASH_KEY_CLSAG_AGG_0[] = "CLSAG_agg_0"; const unsigned char HASH_KEY_CLSAG_AGG_1[] = "CLSAG_agg_1"; const char HASH_KEY_MESSAGE_SIGNING[] = "MoneroMessageSignature"; + const unsigned char HASH_KEY_MM_SLOT = 'm'; namespace testnet { diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index dd4701e4f..c039b93c5 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -133,6 +133,18 @@ int main(int argc, char* argv[]) { std::cout << "Parsed block:" << std::endl; std::cout << cryptonote::obj_to_json_str(block) << std::endl; + bool parsed = cryptonote::parse_tx_extra(block.miner_tx.extra, fields); + if (!parsed) + std::cout << "Failed to parse tx_extra" << std::endl; + + if (!fields.empty()) + { + print_extra_fields(fields); + } + else + { + std::cout << "No fields were found in tx_extra" << std::endl; + } } else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx) || cryptonote::parse_and_validate_tx_base_from_blob(blob, tx)) { diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index f59be1573..70dc7f539 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -365,15 +365,14 @@ namespace trezor { void device_trezor_base::device_state_initialize_unsafe() { require_connected(); - std::string tmp_session_id; auto initMsg = std::make_shared<messages::management::Initialize>(); const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { - memwipe(&tmp_session_id[0], tmp_session_id.size()); + if (initMsg->has_session_id()) + memwipe(&(*initMsg->mutable_session_id())[0], initMsg->mutable_session_id()->size()); }); if(!m_device_session_id.empty()) { - tmp_session_id.assign(m_device_session_id.data(), m_device_session_id.size()); - initMsg->set_allocated_session_id(&tmp_session_id); + initMsg->set_allocated_session_id(new std::string(m_device_session_id.data(), m_device_session_id.size())); } m_features = this->client_exchange<messages::management::Features>(initMsg); @@ -382,8 +381,6 @@ namespace trezor { } else { m_device_session_id.clear(); } - - initMsg->release_session_id(); } void device_trezor_base::device_state_reset() @@ -453,18 +450,14 @@ namespace trezor { pin = m_pin; } - std::string pin_field; messages::common::PinMatrixAck m; if (pin) { - pin_field.assign(pin->data(), pin->size()); - m.set_allocated_pin(&pin_field); + m.set_allocated_pin(new std::string(pin->data(), pin->size())); } const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { - m.release_pin(); - if (!pin_field.empty()){ - memwipe(&pin_field[0], pin_field.size()); - } + if (m.has_pin()) + memwipe(&(*m.mutable_pin())[0], m.mutable_pin()->size()); }); resp = call_raw(&m); @@ -499,7 +492,6 @@ namespace trezor { boost::optional<epee::wipeable_string> passphrase; TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, on_device); - std::string passphrase_field; messages::common::PassphraseAck m; m.set_on_device(on_device); if (!on_device) { @@ -512,16 +504,13 @@ namespace trezor { } if (passphrase) { - passphrase_field.assign(passphrase->data(), passphrase->size()); - m.set_allocated_passphrase(&passphrase_field); + m.set_allocated_passphrase(new std::string(passphrase->data(), passphrase->size())); } } const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() { - m.release_passphrase(); - if (!passphrase_field.empty()){ - memwipe(&passphrase_field[0], passphrase_field.size()); - } + if (m.has_passphrase()) + memwipe(&(m.mutable_passphrase())[0], m.mutable_passphrase()->size()); }); resp = call_raw(&m); diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 4db8f0c8e..0162b23df 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -165,7 +165,7 @@ namespace trezor { // Scoped session closer BOOST_SCOPE_EXIT_ALL(&, this) { - if (open_session){ + if (open_session && this->get_transport()){ this->get_transport()->close(); } }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 2bb4969e9..15b2a9bfd 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -44,6 +44,7 @@ using namespace epee; #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/merge_mining.h" #include "cryptonote_core/tx_sanity_check.h" #include "misc_language.h" #include "net/parse.h" @@ -278,6 +279,7 @@ namespace cryptonote } } disable_rpc_ban = rpc_config->disable_rpc_ban; + const std::string data_dir{command_line::get_arg(vm, cryptonote::arg_data_dir)}; std::string address = command_line::get_arg(vm, arg_rpc_payment_address); if (!address.empty() && allow_rpc_payment) { @@ -306,7 +308,7 @@ namespace cryptonote } m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); m_rpc_payment.reset(new rpc_payment(info.address, diff, credits)); - m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir)); + m_rpc_payment->load(data_dir); m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff)); } @@ -333,12 +335,32 @@ namespace cryptonote if (m_rpc_payment) m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000); + bool store_ssl_key = !restricted && rpc_config->ssl_options.auth.certificate_path.empty(); + const auto ssl_base_path = (boost::filesystem::path{data_dir} / "rpc_ssl").string(); + if (store_ssl_key && boost::filesystem::exists(ssl_base_path + ".crt")) + { + // load key from previous run, password prompted by OpenSSL + store_ssl_key = false; + rpc_config->ssl_options.auth = + epee::net_utils::ssl_authentication_t{ssl_base_path + ".key", ssl_base_path + ".crt"}; + } + auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; - return epee::http_server_impl_base<core_rpc_server, connection_context>::init( + const bool inited = epee::http_server_impl_base<core_rpc_server, connection_context>::init( rng, std::move(port), std::move(bind_ip_str), std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options) ); + + if (store_ssl_key && inited) + { + // new keys were generated, store for next run + const auto error = epee::net_utils::store_ssl_keys(m_net_server.get_ssl_context(), ssl_base_path); + if (error) + MFATAL("Failed to store HTTP SSL cert/key for " << (restricted ? "restricted " : "") << "RPC server: " << error.message()); + return !bool(error); + } + return inited; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash) @@ -1826,6 +1848,125 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_add_aux_pow(const COMMAND_RPC_ADD_AUX_POW::request& req, COMMAND_RPC_ADD_AUX_POW::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) + { + RPC_TRACKER(add_aux_pow); + bool r; + if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ADD_AUX_POW>(invoke_http_mode::JON_RPC, "add_aux_pow", req, res, r)) + return r; + + if (req.aux_pow.empty()) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Empty aux pow hash vector"; + return false; + } + + crypto::hash merkle_root; + size_t merkle_tree_depth = 0; + std::vector<std::pair<crypto::hash, crypto::hash>> aux_pow; + std::vector<crypto::hash> aux_pow_raw; + aux_pow.reserve(req.aux_pow.size()); + aux_pow_raw.reserve(req.aux_pow.size()); + for (const auto &s: req.aux_pow) + { + aux_pow.push_back({}); + if (!epee::string_tools::hex_to_pod(s.id, aux_pow.back().first)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Invalid aux pow id"; + return false; + } + if (!epee::string_tools::hex_to_pod(s.hash, aux_pow.back().second)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Invalid aux pow hash"; + return false; + } + aux_pow_raw.push_back(aux_pow.back().second); + } + + size_t path_domain = 1; + while ((1u << path_domain) < aux_pow.size()) + ++path_domain; + uint32_t nonce; + const uint32_t max_nonce = 65535; + bool collision = true; + for (nonce = 0; nonce <= max_nonce; ++nonce) + { + std::vector<bool> slots(aux_pow.size(), false); + collision = false; + for (size_t idx = 0; idx < aux_pow.size(); ++idx) + { + const uint32_t slot = cryptonote::get_aux_slot(aux_pow[idx].first, nonce, aux_pow.size()); + if (slot >= aux_pow.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Computed slot is out of range"; + return false; + } + if (slots[slot]) + { + collision = true; + break; + } + slots[slot] = true; + } + if (!collision) + break; + } + if (collision) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Failed to find a suitable nonce"; + return false; + } + + crypto::tree_hash((const char(*)[crypto::HASH_SIZE])aux_pow_raw.data(), aux_pow_raw.size(), merkle_root.data); + res.merkle_root = epee::string_tools::pod_to_hex(merkle_root); + res.merkle_tree_depth = cryptonote::encode_mm_depth(aux_pow.size(), nonce); + + blobdata blocktemplate_blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.blocktemplate_blob, blocktemplate_blob)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Invalid blocktemplate_blob"; + return false; + } + + block b; + if (!parse_and_validate_block_from_blob(blocktemplate_blob, b)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_resp.message = "Wrong blocktemplate_blob"; + return false; + } + + if (!remove_field_from_tx_extra(b.miner_tx.extra, typeid(cryptonote::tx_extra_merge_mining_tag))) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Error removing existing merkle root"; + return false; + } + if (!add_mm_merkle_root_to_tx_extra(b.miner_tx.extra, merkle_root, merkle_tree_depth)) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Error adding merkle root"; + return false; + } + b.invalidate_hashes(); + b.miner_tx.invalidate_hashes(); + + const blobdata block_blob = t_serializable_object_to_blob(b); + const blobdata hashing_blob = get_block_hashing_blob(b); + + res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + res.blockhashing_blob = string_tools::buff_to_hex_nodelimer(hashing_blob); + res.aux_pow = req.aux_pow; + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) { RPC_TRACKER(submitblock); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index dcf6b4e4b..6736a6b7f 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -146,6 +146,7 @@ namespace cryptonote MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) MAP_JON_RPC_WE("get_block_template", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) + MAP_JON_RPC_WE("add_aux_pow", on_add_aux_pow, COMMAND_RPC_ADD_AUX_POW) MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK) MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted) @@ -226,6 +227,7 @@ namespace cryptonote bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx = NULL); bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); + bool on_add_aux_pow(const COMMAND_RPC_ADD_AUX_POW::request& req, COMMAND_RPC_ADD_AUX_POW::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index bbcb27f1c..e7bcb5570 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 5 +#define CORE_RPC_VERSION_MINOR 6 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -938,6 +938,52 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_ADD_AUX_POW + { + struct aux_pow_t + { + std::string id; + std::string hash; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(hash) + END_KV_SERIALIZE_MAP() + }; + + struct request_t: public rpc_request_base + { + blobdata blocktemplate_blob; + std::vector<aux_pow_t> aux_pow; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(blocktemplate_blob) + KV_SERIALIZE(aux_pow) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t: public rpc_response_base + { + blobdata blocktemplate_blob; + blobdata blockhashing_blob; + std::string merkle_root; + uint32_t merkle_tree_depth; + std::vector<aux_pow_t> aux_pow; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) + KV_SERIALIZE(blocktemplate_blob) + KV_SERIALIZE(blockhashing_blob) + KV_SERIALIZE(merkle_root) + KV_SERIALIZE(merkle_tree_depth) + KV_SERIALIZE(aux_pow) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_SUBMITBLOCK { typedef std::vector<std::string> request; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 327a189ca..70d2d20cf 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3013,6 +3013,41 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_scan_tx(const wallet_rpc::COMMAND_RPC_SCAN_TX::request& req, wallet_rpc::COMMAND_RPC_SCAN_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + std::vector<crypto::hash> txids; + std::list<std::string>::const_iterator i = req.txids.begin(); + while (i != req.txids.end()) + { + cryptonote::blobdata txid_blob; + if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; + er.message = "TX ID has invalid format"; + return false; + } + + crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data()); + txids.push_back(txid); + } + + try { + m_wallet->scan_tx(txids); + } 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); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 6e39eca1e..9f9e3c134 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -131,6 +131,7 @@ namespace tools 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("scan_tx", on_scan_tx, wallet_rpc::COMMAND_RPC_SCAN_TX) 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) @@ -218,6 +219,7 @@ namespace tools 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_scan_tx(const wallet_rpc::COMMAND_RPC_SCAN_TX::request& req, wallet_rpc::COMMAND_RPC_SCAN_TX::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); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 81f83fb18..d6c117fe7 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 20 +#define WALLET_RPC_VERSION_MINOR 21 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -2028,6 +2028,26 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_SCAN_TX + { + struct request_t + { + std::list<std::string> txids; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txids) + 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/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 462fd4b77..7a50e9855 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -69,11 +69,10 @@ if (REQUESTS_OUTPUT STREQUAL "OK") add_test( NAME functional_tests_rpc COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/functional_tests_rpc.py" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" all) + add_test( + NAME check_missing_rpc_methods + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}") else() - message(WARNING "functional_tests_rpc skipped, needs the 'requests' python module") - set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc) + message(WARNING "functional_tests_rpc and check_missing_rpc_methods skipped, needs the 'requests' python module") + set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc check_missing_rpc_methods) endif() - -add_test( - NAME check_missing_rpc_methods - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}") diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 7ac87ca7c..b550b35b0 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -33,6 +33,7 @@ #include <string> #include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/merge_mining.h" namespace { @@ -99,3 +100,215 @@ TEST(Crypto, verify_32) } } } + +TEST(Crypto, tree_branch) +{ + crypto::hash inputs[6]; + crypto::hash branch[8]; + crypto::hash root, root2; + size_t depth; + uint32_t path, path2; + + auto hasher = [](const crypto::hash &h0, const crypto::hash &h1) -> crypto::hash + { + char buffer[64]; + memcpy(buffer, &h0, 32); + memcpy(buffer + 32, &h1, 32); + crypto::hash res; + cn_fast_hash(buffer, 64, res); + return res; + }; + + for (int n = 0; n < 6; ++n) + { + memset(&inputs[n], 0, 32); + inputs[n].data[0] = n + 1; + } + + // empty + ASSERT_FALSE(crypto::tree_branch((const char(*)[32])inputs, 0, crypto::null_hash.data, (char(*)[32])branch, &depth, &path)); + + // one, matching + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 1, inputs[0].data, (char(*)[32])branch, &depth, &path)); + ASSERT_EQ(depth, 0); + ASSERT_EQ(path, 0); + ASSERT_TRUE(crypto::tree_path(1, 0, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 1, root.data); + ASSERT_EQ(root, inputs[0]); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // one, not found + ASSERT_FALSE(crypto::tree_branch((const char(*)[32])inputs, 1, inputs[1].data, (char(*)[32])branch, &depth, &path)); + + // two, index 0 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 2, inputs[0].data, (char(*)[32])branch, &depth, &path)); + ASSERT_EQ(depth, 1); + ASSERT_EQ(path, 0); + ASSERT_TRUE(crypto::tree_path(2, 0, &path2)); + ASSERT_EQ(path, path2); + ASSERT_EQ(branch[0], inputs[1]); + crypto::tree_hash((const char(*)[32])inputs, 2, root.data); + ASSERT_EQ(root, hasher(inputs[0], inputs[1])); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // two, index 1 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 2, inputs[1].data, (char(*)[32])branch, &depth, &path)); + ASSERT_EQ(depth, 1); + ASSERT_EQ(path, 1); + ASSERT_TRUE(crypto::tree_path(2, 1, &path2)); + ASSERT_EQ(path, path2); + ASSERT_EQ(branch[0], inputs[0]); + crypto::tree_hash((const char(*)[32])inputs, 2, root.data); + ASSERT_EQ(root, hasher(inputs[0], inputs[1])); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // two, not found + ASSERT_FALSE(crypto::tree_branch((const char(*)[32])inputs, 2, inputs[2].data, (char(*)[32])branch, &depth, &path)); + + // a b c 0 + // x y + // z + + // three, index 0 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 3, inputs[0].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 1); + ASSERT_LE(depth, 2); + ASSERT_TRUE(crypto::tree_path(3, 0, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 3, root.data); + ASSERT_EQ(root, hasher(inputs[0], hasher(inputs[1], inputs[2]))); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // three, index 1 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 3, inputs[1].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 1); + ASSERT_LE(depth, 2); + ASSERT_TRUE(crypto::tree_path(3, 1, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 3, root.data); + ASSERT_EQ(root, hasher(inputs[0], hasher(inputs[1], inputs[2]))); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // three, index 2 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 3, inputs[2].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 1); + ASSERT_LE(depth, 2); + ASSERT_TRUE(crypto::tree_path(3, 2, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 3, root.data); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::tree_branch_hash(inputs[2].data, (const char(*)[32])branch, depth, path, root2.data)); + ASSERT_EQ(root, root2); + + // three, not found + ASSERT_FALSE(crypto::tree_branch((const char(*)[32])inputs, 3, inputs[3].data, (char(*)[32])branch, &depth, &path)); + + // a b c d e 0 0 0 + // x y + // z + // w + + // five, index 0 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 5, inputs[0].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 2); + ASSERT_LE(depth, 3); + ASSERT_TRUE(crypto::tree_path(5, 0, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 5, root.data); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[5].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // five, index 1 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 5, inputs[1].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 2); + ASSERT_LE(depth, 3); + ASSERT_TRUE(crypto::tree_path(5, 1, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 5, root.data); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[5].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // five, index 2 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 5, inputs[2].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 2); + ASSERT_LE(depth, 3); + ASSERT_TRUE(crypto::tree_path(5, 2, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 5, root.data); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[5].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + // five, index 4 + ASSERT_TRUE(crypto::tree_branch((const char(*)[32])inputs, 5, inputs[4].data, (char(*)[32])branch, &depth, &path)); + ASSERT_GE(depth, 2); + ASSERT_LE(depth, 3); + ASSERT_TRUE(crypto::tree_path(5, 4, &path2)); + ASSERT_EQ(path, path2); + crypto::tree_hash((const char(*)[32])inputs, 5, root.data); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[0].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[1].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[2].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[3].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_TRUE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[5].data, root.data, (const char(*)[32])branch, depth, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(crypto::null_hash.data, root.data, (const char(*)[32])branch, depth, path)); + + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth - 1, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth + 1, path)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path ^ 1)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path ^ 2)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])branch, depth, path ^ 3)); + ASSERT_FALSE(crypto::is_branch_in_tree(inputs[4].data, root.data, (const char(*)[32])(branch + 1), depth, path)); + + // five, not found + ASSERT_FALSE(crypto::tree_branch((const char(*)[32])inputs, 5, crypto::null_hash.data, (char(*)[32])branch, &depth, &path)); + + // depth encoding roundtrip + for (uint32_t n_chains = 1; n_chains <= 65; ++n_chains) + { + for (uint32_t nonce = 0; nonce < 1024; ++nonce) + { + const uint32_t depth = cryptonote::encode_mm_depth(n_chains, nonce); + uint32_t n_chains_2, nonce_2; + ASSERT_TRUE(cryptonote::decode_mm_depth(depth, n_chains_2, nonce_2)); + ASSERT_EQ(n_chains, n_chains_2); + ASSERT_EQ(nonce, nonce_2); + } + } +} diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index e99666eb1..06e076a3a 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -143,14 +143,24 @@ TEST(test_epee_connection, test_lifetime) static constexpr bool handshake_complete() noexcept { return true; } }; + using functional_obj_t = std::function<void ()>; struct command_handler_t: epee::levin::levin_commands_handler<context_t> { size_t delay; - command_handler_t(size_t delay = 0): delay(delay) {} + functional_obj_t on_connection_close_f; + command_handler_t(size_t delay = 0, + functional_obj_t on_connection_close_f = nullptr + ): + delay(delay), + on_connection_close_f(on_connection_close_f) + {} virtual int invoke(int, const epee::span<const uint8_t>, epee::byte_slice&, context_t&) override { epee::misc_utils::sleep_no_w(delay); return {}; } virtual int notify(int, const epee::span<const uint8_t>, context_t&) override { return {}; } virtual void callback(context_t&) override {} virtual void on_connection_new(context_t&) override {} - virtual void on_connection_close(context_t&) override {} + virtual void on_connection_close(context_t&) override { + if (on_connection_close_f) + on_connection_close_f(); + } virtual ~command_handler_t() override {} static void destroy(epee::levin::levin_commands_handler<context_t>* ptr) { delete ptr; } }; @@ -168,6 +178,14 @@ TEST(test_epee_connection, test_lifetime) using work_ptr = std::shared_ptr<work_t>; using workers_t = std::vector<std::thread>; using server_t = epee::net_utils::boosted_tcp_server<handler_t>; + using lock_t = std::mutex; + using lock_guard_t = std::lock_guard<lock_t>; + using connection_weak_ptr = boost::weak_ptr<connection_t>; + struct shared_conn_t { + lock_t lock; + connection_weak_ptr conn; + }; + using shared_conn_ptr = std::shared_ptr<shared_conn_t>; io_context_t io_context; work_ptr work(std::make_shared<work_t>(io_context)); @@ -255,6 +273,7 @@ TEST(test_epee_connection, test_lifetime) ASSERT_TRUE(index == N * N); ASSERT_TRUE(shared_state->get_connections_count() == 0); + while (shared_state->sock_count); ASSERT_TRUE(shared_state->get_connections_count() == 0); constexpr auto DELAY = 30; constexpr auto TIMEOUT = 1; @@ -273,6 +292,58 @@ TEST(test_epee_connection, test_lifetime) shared_state->close(tag); ASSERT_TRUE(shared_state->get_connections_count() == 0); } + + while (shared_state->sock_count); + constexpr auto ZERO_DELAY = 0; + size_t counter = 0; + shared_state->set_handler(new command_handler_t(ZERO_DELAY, + [&counter]{ + ASSERT_TRUE(counter++ == 0); + } + ), + &command_handler_t::destroy + ); + connection_ptr conn(new connection_t(io_context, shared_state, {}, {})); + conn->socket().connect(endpoint); + conn->start({}, {}); + ASSERT_TRUE(shared_state->get_connections_count() == 1); + shared_state->del_out_connections(1); + ASSERT_TRUE(shared_state->get_connections_count() == 0); + conn.reset(); + + while (shared_state->sock_count); + shared_conn_ptr shared_conn(std::make_shared<shared_conn_t>()); + shared_state->set_handler(new command_handler_t(ZERO_DELAY, + [shared_state, shared_conn]{ + { + connection_ptr conn; + { + lock_guard_t guard(shared_conn->lock); + conn = std::move(shared_conn->conn.lock()); + } + if (conn) + conn->cancel(); + } + const auto success = shared_state->foreach_connection([](context_t&){ + return true; + }); + ASSERT_TRUE(success); + } + ), + &command_handler_t::destroy + ); + for (auto i = 0; i < N; ++i) { + { + connection_ptr conn(new connection_t(io_context, shared_state, {}, {})); + conn->socket().connect(endpoint); + conn->start({}, {}); + lock_guard_t guard(shared_conn->lock); + shared_conn->conn = conn; + } + ASSERT_TRUE(shared_state->get_connections_count() == 1); + shared_state->del_out_connections(1); + ASSERT_TRUE(shared_state->get_connections_count() == 0); + } }); for (auto& w: workers) { diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 256f8c3c2..cbe3c61b1 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1115,11 +1115,13 @@ TEST(ByteStream, ToByteSlice) epee::byte_stream stream; + stream.reserve(128*1024); stream.write(source); EXPECT_EQ(sizeof(source), stream.size()); + EXPECT_EQ(128*1024, stream.capacity()); EXPECT_TRUE(equal(source, byte_span{stream.data(), stream.size()})); - const epee::byte_slice slice{std::move(stream)}; + const epee::byte_slice slice{std::move(stream), true}; EXPECT_EQ(0u, stream.size()); EXPECT_EQ(0u, stream.available()); EXPECT_EQ(0u, stream.capacity()); diff --git a/utils/health/README.md b/utils/health/README.md index 8fadd3908..05eb057a1 100644 --- a/utils/health/README.md +++ b/utils/health/README.md @@ -1,26 +1,32 @@ -#Intro +# Intro + This directory contains tools, which can be used for checking the health of the project, like build/run time analyzers, lints, etc. -#Usage +# Usage + Unless it's stated differently, these scripts should be called from a given source directory, where you want the checks to be performed, for instance: `og@ghetto:~/dev/monero$ utils/health/clang-build-time-analyzer-run.sh` -##ClangBuildAnalyzer +## ClangBuildAnalyzer + `utils/health/clang-build-time-analyzer-run.sh` The CBA helps in finding culprints of slow compilation. On the first run, the script will complain about the missing ClangBuildAnalyzer binary and will point you to another script, which is able to clone and build the required binary. -##clang-tidy +## clang-tidy + `utils/health/clang-tidy-run.sh` Performs Lint checks on the source code and stores the result in the build directory. More information on the [home page](https://clang.llvm.org/extra/clang-tidy/). -##include-what-you-use +## include-what-you-use + `utils/health/clang-include-what-you-use-run.sh` Analyses the header file hierarchy and delivers hints on how to reduce their complexity. More information on the [home page](https://include-what-you-use.org/). -##Valgrind checks +## Valgrind checks + `utils/health/valgrind-tests.sh` This script is able to run valgrind's callgrind, cachegrind and memcheck for a given set of executables. It expects ONE PARAMETER, which points to a file with paths to executables and their arguments, written line by line. For example: @@ -33,6 +39,7 @@ build/tests/unit_tests/unit_tests The `*.out` results can be interpreted with the `kcachegrind` tool. The memcheck output is just a readable text file with a summary at the end. -#Footer +# Footer + Responsible: mj-xmr diff --git a/utils/python-rpc/console.py b/utils/python-rpc/console.py index c94e07eab..4b3c18f5d 100755 --- a/utils/python-rpc/console.py +++ b/utils/python-rpc/console.py @@ -4,7 +4,16 @@ from __future__ import print_function import sys import subprocess import socket -import urlparse +try: + import urllib.parse + url_parser = urllib.parse.urlparse +except: + try: + import urlparse + url_parser = urlparse.urlparse + except: + print('urllib or urlparse is needed') + sys.exit(1) import framework.rpc import framework.daemon import framework.wallet @@ -21,7 +30,7 @@ for n in range(1, len(sys.argv)): try: port = int(sys.argv[n]) except: - t = urlparse.urlparse(sys.argv[n], allow_fragments = False) + t = url_parser(sys.argv[n], allow_fragments = False) scheme = t.scheme or scheme host = t.hostname or host port = t.port or port diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py index 435bc93f8..207565e6f 100644 --- a/utils/python-rpc/framework/daemon.py +++ b/utils/python-rpc/framework/daemon.py @@ -53,6 +53,19 @@ class Daemon(object): return self.rpc.send_json_rpc_request(getblocktemplate) get_block_template = getblocktemplate + def add_aux_pow(self, blocktemplate_blob, aux_pow, client = ""): + add_aux_pow = { + 'method': 'add_aux_pow', + 'params': { + 'blocktemplate_blob': blocktemplate_blob, + 'aux_pow' : aux_pow, + 'client' : client, + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_json_rpc_request(add_aux_pow) + def send_raw_transaction(self, tx_as_hex, do_not_relay = False, do_sanity_checks = True, client = ""): send_raw_transaction = { 'client': client, |