diff options
47 files changed, 870 insertions, 318 deletions
diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml index 4827bb51d..c534965e8 100644 --- a/.github/workflows/depends.yml +++ b/.github/workflows/depends.yml @@ -105,7 +105,7 @@ jobs: ${{env.CCACHE_SETTINGS}} make depends target=${{ matrix.toolchain.host }} -j2 - uses: actions/upload-artifact@v3 - if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin11' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }} + if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }} with: name: ${{ matrix.toolchain.name }} path: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f96017c6..3c35a225a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,9 @@ # # Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +cmake_minimum_required(VERSION 3.5) +message(STATUS "CMake version ${CMAKE_VERSION}") + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(INSERT CMAKE_MODULE_PATH 0 @@ -37,15 +40,14 @@ include(CheckCXXCompilerFlag) include(CheckLinkerFlag) include(CheckLibraryExists) include(CheckFunctionExists) + +cmake_policy(SET CMP0148 OLD) include(FindPythonInterp) if (IOS) INCLUDE(CmakeLists_IOS.txt) endif() -cmake_minimum_required(VERSION 3.5) -message(STATUS "CMake version ${CMAKE_VERSION}") - project(monero) option (USE_CCACHE "Use ccache if a usable instance is found" ON) @@ -96,7 +98,7 @@ enable_language(C ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -219,7 +221,7 @@ function(forbid_undefined_symbols) file(MAKE_DIRECTORY "${TEST_PROJECT}") file(WRITE "${TEST_PROJECT}/CMakeLists.txt" [=[ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(test) option(EXPECT_SUCCESS "" ON) file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") @@ -999,6 +1001,9 @@ else() if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0") + if(ARM) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-aligned-allocation") + endif() endif() set(DEBUG_FLAGS "-g3") @@ -1077,6 +1082,7 @@ if (WIN32) endif() find_package(Boost 1.58 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) +add_definitions(-DBOOST_NO_AUTO_PTR) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) if(NOT Boost_FOUND) @@ -166,7 +166,7 @@ library archives (`.a`). | Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Void pkg | Fedora pkg | Optional | Purpose | | ------------ | ------------- | -------- | -------------------- | ------------ | ------------------ | ------------------- | -------- | --------------- | -| GCC | 5 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | | +| GCC | 7 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | | | CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | `cmake` | NO | | | 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 | diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index e5c0cbbb8..6fe4618ae 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -92,7 +92,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") SET(PORT OFF) SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") - SET(CMAKE_CXX_STANDARD 14) + SET(CMAKE_CXX_STANDARD 17) SET(LLVM_ENABLE_PIC OFF) SET(LLVM_ENABLE_PIE OFF) elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index 6a759b515..00d695179 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -24,213 +24,20 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - - - #pragma once +#include <cstdint> // uint64_t -#include <list> -#include <numeric> -#include <random> -#include <boost/timer/timer.hpp> -#include <boost/uuid/uuid.hpp> -#include <boost/uuid/random_generator.hpp> - -#include "syncobj.h" -#include "time_helper.h" +#ifdef _WIN32 +#include <sysinfoapi.h> // GetSystemTimeAsFileTime +#else +#include <sys/time.h> // gettimeofday +#endif namespace epee { namespace math_helper { - - template<typename val, int default_base> - class average - { - public: - - average() - { - m_base = default_base; - m_last_avg_val = 0; - } - - bool set_base() - { - CRITICAL_REGION_LOCAL(m_lock); - - m_base = default_base; - if(m_list.size() > m_base) - m_list.resize(m_base); - - return true; - } - - typedef val value_type; - - void push(const value_type& vl) - { - CRITICAL_REGION_LOCAL(m_lock); - -//#ifndef DEBUG_STUB - m_list.push_back(vl); - if(m_list.size() > m_base ) - m_list.pop_front(); -//#endif - } - - double update(const value_type& vl) - { - CRITICAL_REGION_LOCAL(m_lock); -//#ifndef DEBUG_STUB - push(vl); -//#endif - - return get_avg(); - } - - double get_avg() - { - CRITICAL_REGION_LOCAL(m_lock); - - value_type vl = std::accumulate(m_list.begin(), m_list.end(), value_type(0)); - if(m_list.size()) - return m_last_avg_val = (double)(vl/m_list.size()); - - return m_last_avg_val = (double)vl; - } - - value_type get_last_val() - { - CRITICAL_REGION_LOCAL(m_lock); - if(m_list.size()) - return m_list.back(); - - return 0; - } - - private: - unsigned int m_base; - double m_last_avg_val; - std::list<value_type> m_list; - critical_section m_lock; - }; - - -#ifdef WINDOWS_PLATFORM - - /************************************************************************/ - /* */ - /************************************************************************/ - class timing_guard_base - { - public: - virtual ~timing_guard_base(){}; - }; - - template<class T> - class timing_guard: public timing_guard_base - { - public: - timing_guard(T& avrg):m_avrg(avrg) - { - m_start_ticks = ::GetTickCount(); - } - - ~timing_guard() - { - m_avrg.push(::GetTickCount()-m_start_ticks); - } - - private: - T& m_avrg; - DWORD m_start_ticks; - }; - - template<class t_timing> - timing_guard_base* create_timing_guard(t_timing& timing){return new timing_guard<t_timing>(timing);} - -#define BEGIN_TIMING_ZONE(timing_var) { boost::shared_ptr<math_helper::timing_guard_base> local_timing_guard_ptr(math_helper::create_timing_guard(timing_var)); -#define END_TIMING_ZONE() } -#endif - -//#ifdef WINDOWS_PLATFORM_EX - template<uint64_t default_time_window> - class speed - { - public: - - speed() - { - m_time_window = default_time_window; - m_last_speed_value = 0; - } - bool chick() - { -#ifndef DEBUG_STUB - uint64_t ticks = misc_utils::get_tick_count(); - CRITICAL_REGION_BEGIN(m_lock); - m_chicks.push_back(ticks); - CRITICAL_REGION_END(); - //flush(ticks); -#endif - return true; - } - - bool chick(size_t count) - { - for(size_t s = 0; s != count; s++) - chick(); - - return true; - } - - - size_t get_speed() - { - flush(misc_utils::get_tick_count()); - return m_last_speed_value = m_chicks.size(); - } - private: - - bool flush(uint64_t ticks) - { - CRITICAL_REGION_BEGIN(m_lock); - std::list<uint64_t>::iterator it = m_chicks.begin(); - while(it != m_chicks.end()) - { - if(*it + m_time_window < ticks) - m_chicks.erase(it++); - else - break; - } - CRITICAL_REGION_END(); - return true; - } - - std::list<uint64_t> m_chicks; - uint64_t m_time_window; - size_t m_last_speed_value; - critical_section m_lock; - }; -//#endif - - template<class tlist> - void randomize_list(tlist& t_list) - { - for(typename tlist::iterator it = t_list.begin();it!=t_list.end();it++) - { - size_t offset = rand()%t_list.size(); - typename tlist::iterator it_2 = t_list.begin(); - for(size_t local_offset = 0;local_offset!=offset;local_offset++) - it_2++; - if(it_2 == it) - continue; - std::swap(*it_2, *it); - } - - } template<typename get_interval, bool start_immediate = true> class once_a_time { diff --git a/contrib/epee/include/serialization/wire/write.h b/contrib/epee/include/serialization/wire/write.h index c18f7dbcc..c2359918c 100644 --- a/contrib/epee/include/serialization/wire/write.h +++ b/contrib/epee/include/serialization/wire/write.h @@ -30,6 +30,7 @@ #include <boost/utility/string_ref.hpp> #include <boost/range/size.hpp> #include <cstdint> +#include <iterator> #include <system_error> #include <type_traits> @@ -188,7 +189,13 @@ namespace wire_write template<typename T> inline std::size_t array_size(std::true_type, const T& source) - { return boost::size(source); } + { + static_assert( + !std::is_same<typename std::iterator_traits<typename T::const_iterator>::iterator_category, std::input_iterator_tag>{}, + "Input iterators must use json (or similar) derived classes directly" + ); + return boost::size(source); + } template<typename T> inline constexpr std::size_t array_size(std::false_type, const T&) noexcept diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index dbbe1906e..7fcd02726 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -127,7 +127,6 @@ namespace string_tools return s; } - bool validate_hex(uint64_t length, const std::string& str); std::string get_extension(const std::string& str); std::string cut_off_extension(const std::string& str); diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index 5071d2685..d6de6a0e1 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -217,15 +217,13 @@ namespace //// Digest Authentication template<typename Digest> - typename std::result_of<Digest()>::type generate_a1( - Digest digest, const http::login& creds, const boost::string_ref realm) + auto generate_a1(Digest digest, const http::login& creds, const boost::string_ref realm) { return digest(creds.username, u8":", realm, u8":", creds.password); } template<typename Digest> - typename std::result_of<Digest()>::type generate_a1( - Digest digest, const http::http_client_auth::session& user) + auto generate_a1(Digest digest, const http::http_client_auth::session& user) { return generate_a1(std::move(digest), user.credentials, user.server.realm); } diff --git a/contrib/epee/src/string_tools.cpp b/contrib/epee/src/string_tools.cpp index 984a151b5..3abb83c74 100644 --- a/contrib/epee/src/string_tools.cpp +++ b/contrib/epee/src/string_tools.cpp @@ -82,16 +82,6 @@ namespace string_tools return true; } //---------------------------------------------------------------------------- - bool validate_hex(uint64_t length, const std::string& str) - { - if (str.size() != length) - return false; - for (char c: str) - if (!isxdigit(c)) - return false; - return true; - } - //---------------------------------------------------------------------------- bool parse_peer_from_string(uint32_t& ip, uint16_t& port, const std::string& addres) { //parse ip and address diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 63d2bc5d2..41915deb9 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -21,6 +21,7 @@ packages: - "g++-7-arm-linux-gnueabihf" - "gcc-arm-linux-gnueabihf" - "g++-arm-linux-gnueabihf" +- "g++-riscv64-linux-gnu" - "g++-7-multilib" - "gcc-7-multilib" - "binutils-arm-linux-gnueabihf" @@ -43,7 +44,7 @@ files: [] script: | WRAP_DIR=$HOME/wrapped - HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu" + HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu riscv64-linux-gnu" FAKETIME_HOST_PROGS="" FAKETIME_PROGS="date" HOST_CFLAGS="-O2 -g" @@ -159,7 +160,13 @@ script: | fi export C_INCLUDE_PATH="$EXTRA_INCLUDES" export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES" - cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON -DCMAKE_SKIP_RPATH=ON + # glibc only added riscv support in 2.27, disable backwards compatibility + if [ "$i" == "riscv64-linux-gnu" ]; then + BACKCOMPAT_OPTION=OFF + else + BACKCOMPAT_OPTION=ON + fi + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=${BACKCOMPAT_OPTION} -DCMAKE_SKIP_RPATH=ON make ${MAKEOPTS} chmod 755 bin/* cp ../LICENSE ../README.md ../docs/ANONYMITY_NETWORKS.md bin diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fed042ce2..93643af24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,7 @@ add_subdirectory(hardforks) add_subdirectory(blockchain_db) add_subdirectory(mnemonics) add_subdirectory(rpc) +add_subdirectory(seraphis_crypto) if(NOT IOS) add_subdirectory(serialization) endif() diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index f65054fc5..f0a8e5adc 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -33,6 +33,7 @@ #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/cryptonote_core.h" #include "blockchain_db/blockchain_db.h" +#include "time_helper.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/src/crypto/jh.c b/src/crypto/jh.c index 12d536375..738c681f8 100644 --- a/src/crypto/jh.c +++ b/src/crypto/jh.c @@ -34,7 +34,7 @@ typedef struct { unsigned long long databitlen; /*the message size in bits*/ unsigned long long datasize_in_buffer; /*the size of the message remained in buffer; assumed to be multiple of 8bits except for the last partial block at the end of the message*/ DATA_ALIGN16(uint64 x[8][2]); /*the 1024-bit state, ( x[i][0] || x[i][1] ) is the ith row of the state in the pseudocode*/ - unsigned char buffer[64]; /*the 512-bit message block to be hashed;*/ + DATA_ALIGN16(unsigned char buffer[64]); /*the 512-bit message block to be hashed;*/ } hashState; @@ -213,16 +213,24 @@ static void E8(hashState *state) /*The compression function F8 */ static void F8(hashState *state) { - uint64 i; + uint64_t* x = (uint64_t*)state->x; /*xor the 512-bit message with the fist half of the 1024-bit hash state*/ - for (i = 0; i < 8; i++) state->x[i >> 1][i & 1] ^= ((uint64*)state->buffer)[i]; + for (int i = 0; i < 8; ++i) { + uint64 b; + memcpy(&b, &state->buffer[i << 3], sizeof(b)); + x[i] ^= b; + } /*the bijective function E8 */ E8(state); /*xor the 512-bit message with the second half of the 1024-bit hash state*/ - for (i = 0; i < 8; i++) state->x[(8+i) >> 1][(8+i) & 1] ^= ((uint64*)state->buffer)[i]; + for (int i = 0; i < 8; ++i) { + uint64 b; + memcpy(&b, &state->buffer[i << 3], sizeof(b)); + x[i + 8] ^= b; + } } /*before hashing a message, initialize the hash state as H0 */ @@ -240,6 +248,7 @@ static HashReturn Init(hashState *state, int hashbitlen) case 224: memcpy(state->x,JH224_H0,128); break; case 256: memcpy(state->x,JH256_H0,128); break; case 384: memcpy(state->x,JH384_H0,128); break; + default: case 512: memcpy(state->x,JH512_H0,128); break; } diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index ae112c31f..0531439d6 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -77,7 +77,7 @@ namespace cryptonote // outputs <= HF_VERSION_VIEW_TAGS struct txout_to_key { - txout_to_key() { } + txout_to_key(): key() { } txout_to_key(const crypto::public_key &_key) : key(_key) { } crypto::public_key key; }; @@ -85,7 +85,7 @@ namespace cryptonote // outputs >= HF_VERSION_VIEW_TAGS struct txout_to_tagged_key { - txout_to_tagged_key() { } + txout_to_tagged_key(): key(), view_tag() { } txout_to_tagged_key(const crypto::public_key &_key, const crypto::view_tag &_view_tag) : key(_key), view_tag(_view_tag) { } crypto::public_key key; crypto::view_tag view_tag; // optimization to reduce scanning time diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 9aeee3d55..c86e7d848 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -739,22 +739,28 @@ 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) + bool add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, uint64_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); + static const size_t max_varint_size = 16; + tx_extra.resize(tx_extra.size() + 2 + 32 + max_varint_size); //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) + const off_t len_bytes = start_pos; + // one byte placeholder for length since we'll only know the size later after writing a varint + tx_extra[start_pos] = 0; + //write depth varint ++start_pos; - tx_extra[start_pos] = mm_merkle_tree_depth; + uint8_t *ptr = &tx_extra[start_pos], *start = ptr; + tools::write_varint(ptr, mm_merkle_tree_depth); //write data - ++start_pos; + const size_t varint_size = ptr - start; + start_pos += varint_size; memcpy(&tx_extra[start_pos], &mm_merkle_root, 32); + tx_extra.resize(tx_extra.size() - (max_varint_size - varint_size)); + tx_extra[len_bytes] = 32 + varint_size; return true; } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 33fec238e..bd4dcd83d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -83,7 +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 add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, uint64_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 index 0e649e095..3e26c00e3 100644 --- a/src/cryptonote_basic/merge_mining.cpp +++ b/src/cryptonote_basic/merge_mining.cpp @@ -71,21 +71,21 @@ uint32_t get_path_from_aux_slot(uint32_t slot, uint32_t n_aux_chains) return path; } //--------------------------------------------------------------- -uint32_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce) +uint64_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"); + CHECK_AND_ASSERT_THROW_MES(n_aux_chains <= 256, "n_aux_chains is too large"); // 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) + while ((1u << n_bits) < n_aux_chains) ++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)); + const uint64_t depth = (n_bits - 1) | ((n_aux_chains - 1) << 3) | (((uint64_t)nonce) << (3 + n_bits)); return depth; } //--------------------------------------------------------------- -bool decode_mm_depth(uint32_t depth, uint32_t &n_aux_chains, uint32_t &nonce) +bool decode_mm_depth(uint64_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)); diff --git a/src/cryptonote_basic/merge_mining.h b/src/cryptonote_basic/merge_mining.h index acb70ebf0..ef4c92d67 100644 --- a/src/cryptonote_basic/merge_mining.h +++ b/src/cryptonote_basic/merge_mining.h @@ -36,6 +36,6 @@ 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); + uint64_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce); + bool decode_mm_depth(uint64_t depth, uint32_t &n_aux_chains, uint32_t &nonce); } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 91ee86d60..4b0e43213 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -42,6 +42,7 @@ #include "string_coding.h" #include "string_tools.h" #include "storages/portable_storage_template_helper.h" +#include "time_helper.h" #include "boost/logic/tribool.hpp" #include <boost/filesystem.hpp> diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index a29f126ee..cbf942d4c 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -124,7 +124,7 @@ namespace cryptonote END_SERIALIZE() }; - size_t depth; + uint64_t depth; crypto::hash merkle_root; // load diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 45da75b66..fec905928 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -30,6 +30,7 @@ #pragma once +#include <cstdint> #include <stdexcept> #include <string> #include <boost/uuid/uuid.hpp> diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3ad051fc3..be2848d09 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1609,6 +1609,6 @@ namespace cryptonote */ void send_miner_notifications(uint64_t height, const crypto::hash &seed_hash, const crypto::hash &prev_id, uint64_t already_generated_coins); - friend class BlockchainAndPool; + friend struct BlockchainAndPool; }; } // namespace cryptonote diff --git a/src/cryptonote_core/blockchain_and_pool.h b/src/cryptonote_core/blockchain_and_pool.h index c0f607f64..497342aea 100644 --- a/src/cryptonote_core/blockchain_and_pool.h +++ b/src/cryptonote_core/blockchain_and_pool.h @@ -37,6 +37,7 @@ #include "blockchain.h" #include "tx_pool.h" +#include "warnings.h" namespace cryptonote { @@ -52,7 +53,10 @@ struct BlockchainAndPool { Blockchain blockchain; tx_memory_pool tx_pool; - + +PUSH_WARNINGS +DISABLE_GCC_WARNING(uninitialized) BlockchainAndPool(): blockchain(tx_pool), tx_pool(blockchain) {} +POP_WARNINGS }; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 47268efb6..3bb96d3a8 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -676,7 +676,7 @@ private: //! Next timestamp that a DB check for relayable txes is allowed std::atomic<time_t> m_next_check; - friend class BlockchainAndPool; + friend struct BlockchainAndPool; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index a29ee8287..80582fad2 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -39,6 +39,7 @@ #include "byte_slice.h" #include "math_helper.h" +#include "syncobj.h" #include "storages/levin_abstract_invoke2.h" #include "warnings.h" #include "cryptonote_protocol_defs.h" diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 27d080a1c..009ee16ca 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -34,9 +34,6 @@ #include "cryptonote_basic/subaddress_index.h" #include "cryptonote_core/cryptonote_tx_utils.h" -#include <boost/thread/locks.hpp> -#include <boost/thread/lock_guard.hpp> - namespace hw { namespace ledger { @@ -322,10 +319,10 @@ namespace hw { //automatic lock one more level on device ensuring the current thread is allowed to use it #define AUTO_LOCK_CMD() \ /* lock both mutexes without deadlock*/ \ - boost::lock(device_locker, command_locker); \ + std::lock(device_locker, command_locker); \ /* make sure both already-locked mutexes are unlocked at the end of scope */ \ - boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \ - boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock) + std::lock_guard<std::recursive_mutex> lock1(device_locker, std::adopt_lock); \ + std::lock_guard<std::mutex> lock2(command_locker, std::adopt_lock) //lock the device for a long sequence void device_ledger::lock(void) { diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 071274160..b65943f0c 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -35,8 +35,7 @@ #include "device.hpp" #include "log.hpp" #include "device_io_hid.hpp" -#include <boost/thread/mutex.hpp> -#include <boost/thread/recursive_mutex.hpp> +#include <mutex> namespace hw { @@ -140,8 +139,8 @@ namespace hw { class device_ledger : public hw::device { private: // Locker for concurrent access - mutable boost::recursive_mutex device_locker; - mutable boost::mutex command_locker; + mutable std::recursive_mutex device_locker; + mutable std::mutex command_locker; //IO hw::io::device_io_hid hw_device; diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index df6e42b1f..e0fc2aafb 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -32,13 +32,12 @@ #include <cstddef> +#include <mutex> #include <string> #include "device/device.hpp" #include "device/device_default.hpp" #include "device/device_cold.hpp" #include <boost/scope_exit.hpp> -#include <boost/thread/mutex.hpp> -#include <boost/thread/recursive_mutex.hpp> #include "cryptonote_config.h" #include "trezor.hpp" @@ -49,12 +48,12 @@ //automatic lock one more level on device ensuring the current thread is allowed to use it #define TREZOR_AUTO_LOCK_CMD() \ /* lock both mutexes without deadlock*/ \ - boost::lock(device_locker, command_locker); \ + std::lock(device_locker, command_locker); \ /* make sure both already-locked mutexes are unlocked at the end of scope */ \ - boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \ - boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock) + std::lock_guard<std::recursive_mutex> lock1(device_locker, std::adopt_lock); \ + std::lock_guard<std::mutex> lock2(command_locker, std::adopt_lock) -#define TREZOR_AUTO_LOCK_DEVICE() boost::lock_guard<boost::recursive_mutex> lock1_device(device_locker) +#define TREZOR_AUTO_LOCK_DEVICE() std::lock_guard<std::recursive_mutex> lock1_device(device_locker) namespace hw { namespace trezor { @@ -86,8 +85,8 @@ namespace trezor { protected: // Locker for concurrent access - mutable boost::recursive_mutex device_locker; - mutable boost::mutex command_locker; + mutable std::recursive_mutex device_locker; + mutable std::mutex command_locker; std::shared_ptr<Transport> m_transport; i_device_callback * m_callback; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 4f77ce834..815c1b354 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -51,7 +51,6 @@ #include "common/dns_utils.h" #include "common/pruning.h" #include "net/error.h" -#include "math_helper.h" #include "misc_log_ex.h" #include "p2p_protocol_defs.h" #include "crypto/crypto.h" diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 380ca2f53..585d5fb49 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -218,7 +218,7 @@ namespace rct { rct::key a, b, t; Bulletproof(): - A({}), S({}), T1({}), T2({}), taux({}), mu({}), a({}), b({}), t({}) {} + A({}), S({}), T1({}), T2({}), taux({}), mu({}), a({}), b({}), t({}), V({}), L({}), R({}) {} Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): @@ -253,7 +253,7 @@ namespace rct { rct::key r1, s1, d1; rct::keyV L, R; - BulletproofPlus() {} + BulletproofPlus(): V(), A(), A1(), B(), r1(), s1(), d1(), L(), R() {} BulletproofPlus(const rct::key &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): V({V}), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {} BulletproofPlus(const rct::keyV &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): @@ -362,11 +362,17 @@ namespace rct { { if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus) { + // Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the + // first 8 bytes of ecdhInfo[i].amount ar.begin_object(); - if (!typename Archive<W>::is_saving()) + crypto::hash8 trunc_amount; // placeholder variable needed to maintain "strict aliasing" + if (!typename Archive<W>::is_saving()) // loading memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes)); - crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount; - FIELD(amount); + else // saving + memcpy(trunc_amount.data, ecdhInfo[i].amount.bytes, sizeof(trunc_amount)); + FIELD(trunc_amount); + if (!typename Archive<W>::is_saving()) // loading + memcpy(ecdhInfo[i].amount.bytes, trunc_amount.data, sizeof(trunc_amount)); ar.end_object(); } else diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index a6162c3f9..9a0b02f70 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2082,11 +2082,12 @@ namespace cryptonote } 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; + std::vector<crypto::hash> aux_pow_id_raw; aux_pow.reserve(req.aux_pow.size()); - aux_pow_raw.reserve(req.aux_pow.size()); + aux_pow_raw.resize(req.aux_pow.size()); + aux_pow_id_raw.resize(req.aux_pow.size()); for (const auto &s: req.aux_pow) { aux_pow.push_back({}); @@ -2102,7 +2103,6 @@ namespace cryptonote error_resp.message = "Invalid aux pow hash"; return false; } - aux_pow_raw.push_back(aux_pow.back().second); } size_t path_domain = 1; @@ -2111,11 +2111,14 @@ namespace cryptonote uint32_t nonce; const uint32_t max_nonce = 65535; bool collision = true; + std::vector<uint32_t> slots(aux_pow.size()); for (nonce = 0; nonce <= max_nonce; ++nonce) { - std::vector<bool> slots(aux_pow.size(), false); + std::vector<bool> slot_seen(aux_pow.size(), false); collision = false; for (size_t idx = 0; idx < aux_pow.size(); ++idx) + slots[idx] = 0xffffffff; + 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()) @@ -2124,12 +2127,13 @@ namespace cryptonote error_resp.message = "Computed slot is out of range"; return false; } - if (slots[slot]) + if (slot_seen[slot]) { collision = true; break; } - slots[slot] = true; + slot_seen[slot] = true; + slots[idx] = slot; } if (!collision) break; @@ -2141,6 +2145,19 @@ namespace cryptonote return false; } + // set the order determined above + for (size_t i = 0; i < aux_pow.size(); ++i) + { + if (slots[i] >= aux_pow.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Slot value out of range"; + return false; + } + aux_pow_raw[slots[i]] = aux_pow[i].second; + aux_pow_id_raw[slots[i]] = aux_pow[i].first; + } + 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); @@ -2167,7 +2184,7 @@ namespace cryptonote 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)) + if (!add_mm_merkle_root_to_tx_extra(b.miner_tx.extra, merkle_root, res.merkle_tree_depth)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Error adding merkle root"; @@ -2181,7 +2198,8 @@ namespace cryptonote 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; + for (size_t i = 0; i < aux_pow_raw.size(); ++i) + res.aux_pow.push_back({epee::string_tools::pod_to_hex(aux_pow_id_raw[i]), epee::string_tools::pod_to_hex(aux_pow_raw[i])}); res.status = CORE_RPC_STATUS_OK; return true; } @@ -2358,6 +2376,7 @@ namespace cryptonote bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r) { res.untrusted = false; + r = false; boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_bootstrap_daemon_mutex); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index c0330eed9..2a0a6201d 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1094,7 +1094,7 @@ namespace cryptonote blobdata blocktemplate_blob; blobdata blockhashing_blob; std::string merkle_root; - uint32_t merkle_tree_depth; + uint64_t merkle_tree_depth; std::vector<aux_pow_t> aux_pow; BEGIN_KV_SERIALIZE_MAP() diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index cb8a8bea4..d73ea3bc9 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -158,13 +158,22 @@ void ZmqServer::serve() if (!pub || sockets[2].revents) { - std::string message = MONERO_UNWRAP(net::zmq::receive(rep.get(), read_flags)); - MDEBUG("Received RPC request: \"" << message << "\""); - epee::byte_slice response = handler.handle(std::move(message)); - - const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; - MDEBUG("Sending RPC reply: \"" << response_view << "\""); - MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + expect<std::string> message = net::zmq::receive(rep.get(), read_flags); + if (!message) + { + // EAGAIN can occur when using `zmq_poll`, which doesn't inspect for message validity + if (message != net::zmq::make_error_code(EAGAIN)) + MONERO_THROW(message.error(), "Read failure on ZMQ-RPC"); + } + else // no errors + { + MDEBUG("Received RPC request: \"" << *message << "\""); + epee::byte_slice response = handler.handle(std::move(*message)); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Sending RPC reply: \"" << response_view << "\""); + MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + } } } } diff --git a/src/seraphis_crypto/CMakeLists.txt b/src/seraphis_crypto/CMakeLists.txt new file mode 100644 index 000000000..aeae61ad1 --- /dev/null +++ b/src/seraphis_crypto/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (c) 2021, 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. + +set(seraphis_crypto_sources + dummy.cpp) + +monero_find_all_headers(seraphis_crypto_headers, "${CMAKE_CURRENT_SOURCE_DIR}") + +monero_add_library(seraphis_crypto + ${seraphis_crypto_sources} + ${seraphis_crypto_headers}) + +target_link_libraries(seraphis_crypto + PUBLIC + cncrypto + common + epee + ringct + PRIVATE + ${EXTRA_LIBRARIES}) + +target_include_directories(seraphis_crypto + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + PRIVATE + ${Boost_INCLUDE_DIRS}) diff --git a/src/seraphis_crypto/dummy.cpp b/src/seraphis_crypto/dummy.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/seraphis_crypto/dummy.cpp diff --git a/src/seraphis_crypto/sp_transcript.h b/src/seraphis_crypto/sp_transcript.h new file mode 100644 index 000000000..c2fbd88c2 --- /dev/null +++ b/src/seraphis_crypto/sp_transcript.h @@ -0,0 +1,397 @@ +// Copyright (c) 2022, 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. + +// Transcript class for assembling data that needs to be hashed. + +#pragma once + +//local headers +#include "crypto/crypto.h" +#include "cryptonote_config.h" +#include "ringct/rctTypes.h" +#include "wipeable_string.h" + +//third party headers +#include <boost/utility/string_ref.hpp> + +//standard headers +#include <list> +#include <string> +#include <type_traits> +#include <vector> + +//forward declarations + + +namespace sp +{ + +//// +// SpTranscriptBuilder +// - build a transcript +// - the user must provide a label when trying to append something to the transcript; labels are prepended to objects in +// the transcript +// - data types +// - unsigned int: uint_flag || varint(uint_variable) +// - signed int: int_flag || uchar{int_variable < 0 ? 1 : 0} || varint(abs(int_variable)) +// - byte buffer (assumed little-endian): buffer_flag || buffer_length || buffer +// - all labels are treated as byte buffers +// - named container: container_flag || container_name || data_member1 || ... || container_terminator_flag +// - list-type container (same-type elements only): list_flag || list_length || element1 || element2 || ... +// - the transcript can be used like a string via the .data() and .size() member functions +// - simple mode: exclude all labels, flags, and lengths +/// +class SpTranscriptBuilder final +{ +public: +//public member types + /// transcript builder mode + enum class Mode + { + FULL, + SIMPLE + }; + +//constructors + /// normal constructor + SpTranscriptBuilder(const std::size_t estimated_data_size, const Mode mode) : + m_mode{mode} + { + m_transcript.reserve(2 * estimated_data_size + 20); + } + +//overloaded operators + /// disable copy/move (this is a scoped manager [of the 'transcript' concept]) + SpTranscriptBuilder& operator=(SpTranscriptBuilder&&) = delete; + +//member functions + /// append a value to the transcript + template <typename T> + void append(const boost::string_ref label, const T &value) + { + this->append_impl(label, value); + } + + /// access the transcript data + const void* data() const { return m_transcript.data(); } + std::size_t size() const { return m_transcript.size(); } + +private: +//member variables + /// in simple mode, exclude labels, flags, and lengths + Mode m_mode; + /// the transcript buffer (wipeable in case it contains sensitive data) + epee::wipeable_string m_transcript; + +//private member types + /// flags for separating items added to the transcript + enum SpTranscriptBuilderFlag : unsigned char + { + UNSIGNED_INTEGER = 0, + SIGNED_INTEGER = 1, + BYTE_BUFFER = 2, + NAMED_CONTAINER = 3, + NAMED_CONTAINER_TERMINATOR = 4, + LIST_TYPE_CONTAINER = 5 + }; + +//transcript builders + void append_character(const unsigned char character) + { + m_transcript += static_cast<char>(character); + } + void append_uint(const std::uint64_t unsigned_integer) + { + unsigned char v_variable[(sizeof(std::uint64_t) * 8 + 6) / 7]; + unsigned char *v_variable_end = v_variable; + + // append uint to string as a varint + tools::write_varint(v_variable_end, unsigned_integer); + assert(v_variable_end <= v_variable + sizeof(v_variable)); + m_transcript.append(reinterpret_cast<const char*>(v_variable), v_variable_end - v_variable); + } + void append_flag(const SpTranscriptBuilderFlag flag) + { + if (m_mode == Mode::SIMPLE) + return; + + static_assert(sizeof(SpTranscriptBuilderFlag) == sizeof(unsigned char), ""); + this->append_character(static_cast<unsigned char>(flag)); + } + void append_length(const std::size_t length) + { + if (m_mode == Mode::SIMPLE) + return; + + static_assert(sizeof(std::size_t) <= sizeof(std::uint64_t), ""); + this->append_uint(static_cast<std::uint64_t>(length)); + } + void append_buffer(const void *data, const std::size_t length) + { + this->append_flag(SpTranscriptBuilderFlag::BYTE_BUFFER); + this->append_length(length); + m_transcript.append(reinterpret_cast<const char*>(data), length); + } + void append_label(const boost::string_ref label) + { + if (m_mode == Mode::SIMPLE || + label.size() == 0) + return; + + this->append_buffer(label.data(), label.size()); + } + void append_labeled_buffer(const boost::string_ref label, const void *data, const std::size_t length) + { + this->append_label(label); + this->append_buffer(data, length); + } + void begin_named_container(const boost::string_ref container_name) + { + this->append_flag(SpTranscriptBuilderFlag::NAMED_CONTAINER); + this->append_label(container_name); + } + void end_named_container() + { + this->append_flag(SpTranscriptBuilderFlag::NAMED_CONTAINER_TERMINATOR); + } + void begin_list_type_container(const std::size_t list_length) + { + this->append_flag(SpTranscriptBuilderFlag::LIST_TYPE_CONTAINER); + this->append_length(list_length); + } + +//append overloads + void append_impl(const boost::string_ref label, const rct::key &key_buffer) + { + this->append_labeled_buffer(label, key_buffer.bytes, sizeof(key_buffer)); + } + void append_impl(const boost::string_ref label, const rct::ctkey &ctkey) + { + this->append_label(label); + this->append_impl("ctmask", ctkey.mask); + this->append_impl("ctdest", ctkey.dest); + } + void append_impl(const boost::string_ref label, const crypto::secret_key &point_buffer) + { + this->append_labeled_buffer(label, point_buffer.data, sizeof(point_buffer)); + } + void append_impl(const boost::string_ref label, const crypto::public_key &scalar_buffer) + { + this->append_labeled_buffer(label, scalar_buffer.data, sizeof(scalar_buffer)); + } + void append_impl(const boost::string_ref label, const crypto::key_derivation &derivation_buffer) + { + this->append_labeled_buffer(label, derivation_buffer.data, sizeof(derivation_buffer)); + } + void append_impl(const boost::string_ref label, const crypto::key_image &key_image_buffer) + { + this->append_labeled_buffer(label, key_image_buffer.data, sizeof(key_image_buffer)); + } + void append_impl(const boost::string_ref label, const std::string &string_buffer) + { + this->append_labeled_buffer(label, string_buffer.data(), string_buffer.size()); + } + void append_impl(const boost::string_ref label, const epee::wipeable_string &string_buffer) + { + this->append_labeled_buffer(label, string_buffer.data(), string_buffer.size()); + } + void append_impl(const boost::string_ref label, const boost::string_ref string_buffer) + { + this->append_labeled_buffer(label, string_buffer.data(), string_buffer.size()); + } + template<std::size_t Sz> + void append_impl(const boost::string_ref label, const unsigned char(&uchar_buffer)[Sz]) + { + this->append_labeled_buffer(label, uchar_buffer, Sz); + } + template<std::size_t Sz> + void append_impl(const boost::string_ref label, const char(&char_buffer)[Sz]) + { + this->append_labeled_buffer(label, char_buffer, Sz); + } + void append_impl(const boost::string_ref label, const std::vector<unsigned char> &vector_buffer) + { + this->append_labeled_buffer(label, vector_buffer.data(), vector_buffer.size()); + } + void append_impl(const boost::string_ref label, const std::vector<char> &vector_buffer) + { + this->append_labeled_buffer(label, vector_buffer.data(), vector_buffer.size()); + } + void append_impl(const boost::string_ref label, const char single_character) + { + this->append_label(label); + this->append_character(static_cast<unsigned char>(single_character)); + } + void append_impl(const boost::string_ref label, const unsigned char single_character) + { + this->append_label(label); + this->append_character(single_character); + } + template<typename T, + std::enable_if_t<std::is_unsigned<T>::value, bool> = true> + void append_impl(const boost::string_ref label, const T unsigned_integer) + { + static_assert(sizeof(T) <= sizeof(std::uint64_t), "SpTranscriptBuilder: unsupported unsigned integer type."); + this->append_label(label); + this->append_flag(SpTranscriptBuilderFlag::UNSIGNED_INTEGER); + this->append_uint(unsigned_integer); + } + template<typename T, + std::enable_if_t<std::is_integral<T>::value, bool> = true, + std::enable_if_t<!std::is_unsigned<T>::value, bool> = true> + void append_impl(const boost::string_ref label, const T signed_integer) + { + using unsigned_type = std::make_unsigned<T>::type; + static_assert(sizeof(unsigned_type) <= sizeof(std::uint64_t), + "SpTranscriptBuilder: unsupported signed integer type."); + this->append_label(label); + this->append_flag(SpTranscriptBuilderFlag::SIGNED_INTEGER); + this->append_uint(static_cast<std::uint64_t>(static_cast<unsigned_type>(signed_integer))); + } + template<typename T, + std::enable_if_t<!std::is_integral<T>::value, bool> = true> + void append_impl(const boost::string_ref label, const T &named_container) + { + // named containers must satisfy two concepts: + // const boost::string_ref container_name(const T &container); + // void append_to_transcript(const T &container, SpTranscriptBuilder &transcript_inout); + //todo: container_name() and append_to_transcript() must be defined in the sp namespace, but that is not generic + this->append_label(label); + this->begin_named_container(container_name(named_container)); + append_to_transcript(named_container, *this); //non-member function assumed to be implemented elsewhere + this->end_named_container(); + } + template<typename T> + void append_impl(const boost::string_ref label, const std::vector<T> &list_container) + { + this->append_label(label); + this->begin_list_type_container(list_container.size()); + for (const T &element : list_container) + this->append_impl("", element); + } + template<typename T> + void append_impl(const boost::string_ref label, const std::list<T> &list_container) + { + this->append_label(label); + this->begin_list_type_container(list_container.size()); + for (const T &element : list_container) + this->append_impl("", element); + } +}; + +//// +// SpFSTranscript +// - build a Fiat-Shamir transcript +// - main format: prefix || domain_separator || object1_label || object1 || object2_label || object2 || ... +// note: prefix defaults to "monero" +/// +class SpFSTranscript final +{ +public: +//constructors + /// normal constructor: start building a transcript with the domain separator + SpFSTranscript(const boost::string_ref domain_separator, + const std::size_t estimated_data_size, + const boost::string_ref prefix = config::TRANSCRIPT_PREFIX) : + m_transcript_builder{15 + domain_separator.size() + estimated_data_size + prefix.size(), + SpTranscriptBuilder::Mode::FULL} + { + // transcript = prefix || domain_separator + m_transcript_builder.append("FSp", prefix); + m_transcript_builder.append("ds", domain_separator); + } + +//overloaded operators + /// disable copy/move (this is a scoped manager [of the 'transcript' concept]) + SpFSTranscript& operator=(SpFSTranscript&&) = delete; + +//member functions + /// build the transcript + template<typename T> + void append(const boost::string_ref label, const T &value) + { + m_transcript_builder.append(label, value); + } + + /// access the transcript data + const void* data() const { return m_transcript_builder.data(); } + std::size_t size() const { return m_transcript_builder.size(); } + +//member variables +private: + /// underlying transcript builder + SpTranscriptBuilder m_transcript_builder; +}; + +//// +// SpKDFTranscript +// - build a data string for a key-derivation function +// - mainly intended for short transcripts (~128 bytes or less) with fixed-length and statically ordered components +// - main format: prefix || domain_separator || object1 || object2 || ... +// - simple transcript mode: no labels, flags, or lengths +// note: prefix defaults to "monero" +/// +class SpKDFTranscript final +{ +public: +//constructors + /// normal constructor: start building a transcript with the domain separator + SpKDFTranscript(const boost::string_ref domain_separator, + const std::size_t estimated_data_size, + const boost::string_ref prefix = config::TRANSCRIPT_PREFIX) : + m_transcript_builder{10 + domain_separator.size() + estimated_data_size + prefix.size(), + SpTranscriptBuilder::Mode::SIMPLE} + { + // transcript = prefix || domain_separator + m_transcript_builder.append("", prefix); + m_transcript_builder.append("", domain_separator); + } + +//overloaded operators + /// disable copy/move (this is a scoped manager [of the 'transcript' concept]) + SpKDFTranscript& operator=(SpKDFTranscript&&) = delete; + +//member functions + /// build the transcript + template<typename T> + void append(const boost::string_ref, const T &value) + { + m_transcript_builder.append("", value); + } + + /// access the transcript data + const void* data() const { return m_transcript_builder.data(); } + std::size_t size() const { return m_transcript_builder.size(); } + +//member variables +private: + /// underlying transcript builder + SpTranscriptBuilder m_transcript_builder; +}; + +} //namespace sp diff --git a/src/wallet/message_transporter.cpp b/src/wallet/message_transporter.cpp index b4f6ce7bd..e6c26cba0 100644 --- a/src/wallet/message_transporter.cpp +++ b/src/wallet/message_transporter.cpp @@ -273,16 +273,29 @@ bool message_transporter::post_request(const std::string &request, std::string & { if ((string_value.find("API Error 0021") == 0) && (request.find("joinChan") != std::string::npos)) { + // "API Error 0021: Unexpected API Failure" // Error that occurs if one tries to join an already joined chan, which can happen // if several auto-config participants share one PyBitmessage instance: As a little // hack simply ignore the error. (A clean solution would be to check for the chan // with 'listAddresses2', but parsing the returned array is much more complicated.) } + else if ((string_value.find("API Error 0024") == 0) && (request.find("joinChan") != std::string::npos)) + { + // "API Error 0024: Chan address is already present." + // Maybe a result of creating the chan in a slightly different way i.e. not with + // 'createChan'; everything works by just ignoring this error + } else if ((string_value.find("API Error 0013") == 0) && (request.find("leaveChan") != std::string::npos)) { + // "API Error 0013: Could not find your fromAddress in the keys.dat file." // Error that occurs if one tries to leave an already left / deleted chan, which can happen // if several auto-config participants share one PyBitmessage instance: Also ignore. } + else if ((string_value.find("API Error 0025") == 0) && (request.find("leaveChan") != std::string::npos)) + { + // "API Error 0025: Specified address is not a chan address. Use deleteAddress API call instead." + // Error does not really make sense, but everything works by just ignoring + } else { THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index dedb0343f..336f4e159 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6163,6 +6163,20 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); } + // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. + // Here we erase these multisig keys if they're zero'd out to free up space. + for (auto &td : m_transfers) + { + auto mk_it = td.m_multisig_k.begin(); + while (mk_it != td.m_multisig_k.end()) + { + if (*mk_it == rct::zero()) + mk_it = td.m_multisig_k.erase(mk_it); + else + ++mk_it; + } + } + cryptonote::block genesis; generate_genesis(genesis); crypto::hash genesis_hash = get_block_hash(genesis); @@ -7036,7 +7050,10 @@ void wallet2::commit_tx(pending_tx& ptx) // tx generated, get rid of used k values for (size_t idx: ptx.selected_transfers) + { memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); + m_transfers[idx].m_multisig_k.clear(); + } //fee includes dust if dust policy specified it. LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL @@ -7287,9 +7304,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin crypto::key_derivation derivation; std::vector<crypto::key_derivation> additional_derivations; - // compute public keys from out secret keys - crypto::public_key tx_pub_key; - crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); std::vector<crypto::public_key> additional_tx_pub_keys; for (const crypto::secret_key &skey: txs[n].additional_tx_keys) { @@ -7540,7 +7555,10 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) // txes generated, get rid of used k values for (size_t n = 0; n < txs.m_ptx.size(); ++n) for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers) + { memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); + m_transfers[idx].m_multisig_k.clear(); + } // zero out some data we don't want to share for (auto &ptx: txs.m_ptx) @@ -7864,7 +7882,10 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto // inputs in the transactions worked on here) for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers) + { memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); + m_transfers[idx].m_multisig_k.clear(); + } exported_txs.m_signers.insert(get_multisig_signer_public_key()); @@ -8662,6 +8683,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + // The secret picking order contains outputs in the order that we selected them. + // + // We will later sort the output request entries in a pre-determined order so that the daemon + // that we're requesting information from doesn't learn any information about the true spend + // for each ring. However, internally, we want to prefer to construct our rings using the + // outputs that we picked first versus outputs picked later. + // + // The reason why is because each consecutive output pick within a ring becomes increasing less + // statistically independent from other picks, since we pick outputs from a finite set + // *without replacement*, due to the protocol not allowing duplicate ring members. This effect + // is exacerbated by the fact that we pick 1.5x + 75 as many outputs as we need per RPC + // request to account for unusable outputs. This effect is small, but non-neglibile and gets + // worse with larger ring sizes. + std::vector<get_outputs_out> secret_picking_order; + + // Convenience/safety lambda to make sure that both output lists req.outputs and secret_picking_order are updated together + // Each ring section of req.outputs gets sorted later after selecting all outputs for that ring + const auto add_output_to_lists = [&req, &secret_picking_order](const get_outputs_out &goo) + { req.outputs.push_back(goo); secret_picking_order.push_back(goo); }; + std::unique_ptr<gamma_picker> gamma; if (has_rct) gamma.reset(new gamma_picker(rct_offsets)); @@ -8796,7 +8837,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (out < num_outs) { MINFO("Using it"); - req.outputs.push_back({amount, out}); + add_output_to_lists({amount, out}); ++num_found; seen_indices.emplace(out); if (out == td.m_global_output_index) @@ -8818,12 +8859,12 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (num_outs <= requested_outputs_count) { for (uint64_t i = 0; i < num_outs; i++) - req.outputs.push_back({amount, i}); + add_output_to_lists({amount, i}); // duplicate to make up shortfall: this will be caught after the RPC call, // so we can also output the amounts for which we can't reach the required // mixin after checking the actual unlockedness for (uint64_t i = num_outs; i < requested_outputs_count; ++i) - req.outputs.push_back({amount, num_outs - 1}); + add_output_to_lists({amount, num_outs - 1}); } else { @@ -8832,7 +8873,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> { num_found = 1; seen_indices.emplace(td.m_global_output_index); - req.outputs.push_back({amount, td.m_global_output_index}); + add_output_to_lists({amount, td.m_global_output_index}); LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount)); } @@ -8940,7 +8981,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> seen_indices.emplace(i); picks[type].insert(i); - req.outputs.push_back({amount, i}); + add_output_to_lists({amount, i}); ++num_found; MDEBUG("picked " << i << ", " << num_found << " now picked"); } @@ -8954,7 +8995,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // we'll error out later while (num_found < requested_outputs_count) { - req.outputs.push_back({amount, 0}); + add_output_to_lists({amount, 0}); ++num_found; } } @@ -8964,6 +9005,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); } + THROW_WALLET_EXCEPTION_IF(req.outputs.size() != secret_picking_order.size(), error::wallet_internal_error, + "bug: we did not update req.outputs/secret_picking_order in tandem"); + + // List all requested outputs to debug log if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY)) { std::map<uint64_t, std::set<uint64_t>> outs; @@ -9081,18 +9126,21 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } } - // then pick others in random order till we reach the required number - // since we use an equiprobable pick here, we don't upset the triangular distribution - std::vector<size_t> order; - order.resize(requested_outputs_count); - for (size_t n = 0; n < order.size(); ++n) - order[n] = n; - std::shuffle(order.begin(), order.end(), crypto::random_device{}); - + // While we are still lacking outputs in this result ring, in our secret pick order... LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_money(td.is_rct() ? 0 : td.amount())); - for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) + for (size_t ring_pick_idx = base; ring_pick_idx < base + requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++ring_pick_idx) { - size_t i = base + order[o]; + const get_outputs_out attempted_output = secret_picking_order[ring_pick_idx]; + + // Find the index i of our pick in the request/response arrays + size_t i; + for (i = base; i < base + requested_outputs_count; ++i) + if (req.outputs[i].index == attempted_output.index) + break; + THROW_WALLET_EXCEPTION_IF(i == base + requested_outputs_count, error::wallet_internal_error, + "Could not find index of picked output in requested outputs"); + + // Try adding this output's information to result ring if output isn't invalid LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key); tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache); } @@ -13511,7 +13559,10 @@ cryptonote::blobdata wallet2::export_multisig() transfer_details &td = m_transfers[n]; crypto::key_image ki; if (td.m_multisig_k.size()) + { memwipe(td.m_multisig_k.data(), td.m_multisig_k.size() * sizeof(td.m_multisig_k[0])); + td.m_multisig_k.clear(); + } info[n].m_LR.clear(); info[n].m_partial_key_images.clear(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 282035052..97fc0a278 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -35,7 +35,6 @@ #include <string> #include "common/util.h" #include "net/http_server_impl_base.h" -#include "math_helper.h" #include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 00896818c..736872b04 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,7 +32,7 @@ set(MONERO_WALLET_CRYPTO_BENCH "auto" CACHE STRING "Select wallet crypto librari # The docs say this only affects grouping in IDEs set(folder "tests") -set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data") +set(TEST_DATA_DIR "${CMAKE_CURRENT_BINARY_DIR}/data") if (WIN32 AND STATIC) add_definitions(-DSTATICLIB) diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp index 5378b0254..ee29fdad1 100644 --- a/tests/core_tests/wallet_tools.cpp +++ b/tests/core_tests/wallet_tools.cpp @@ -4,7 +4,6 @@ #include "wallet_tools.h" #include <random> -#include <boost/assign.hpp> using namespace std; using namespace epee; @@ -39,7 +38,8 @@ void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64 void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail) { - process_transactions(boost::assign::list_of(wallet), events, blk_head, bt, blk_tail); + std::vector<tools::wallet2*> wallet_vector = { wallet }; + process_transactions(wallet_vector, events, blk_head, bt, blk_tail); } void wallet_tools::process_transactions(const std::vector<tools::wallet2*>& wallets, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail) @@ -56,7 +56,8 @@ void wallet_tools::process_transactions(const std::vector<tools::wallet2*>& wall } void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt){ - process_transactions(boost::assign::list_of(wallet), blockchain, mtx, bt); + std::vector<tools::wallet2*> wallet_vector = { wallet }; + process_transactions(wallet_vector, blockchain, mtx, bt); } void wallet_tools::process_transactions(const std::vector<tools::wallet2*>& wallets, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 7462f7119..5dd278c6f 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -67,6 +67,7 @@ target_link_libraries(performance_tests common cncrypto epee + seraphis_crypto ${Boost_CHRONO_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 40f37c4ed..212f834d2 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -123,6 +123,7 @@ target_link_libraries(unit_tests rpc net serialization + seraphis_crypto wallet p2p version diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index e41f3955f..88ecb5853 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -307,17 +307,21 @@ TEST(Crypto, tree_branch) 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 n_chains = 1; n_chains <= 256; ++n_chains) { - for (uint32_t nonce = 0; nonce < 1024; ++nonce) + for (uint32_t nonce = 0xffffffff - 512; nonce != 1025; ++nonce) { - const uint32_t depth = cryptonote::encode_mm_depth(n_chains, nonce); + const uint64_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); } } + + // 257 chains is too much + try { cryptonote::encode_mm_depth(257, 0); ASSERT_TRUE(false); } + catch (...) {} } TEST(Crypto, generator_consistency) diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 586cb7c60..4094d79b7 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -63,7 +63,7 @@ int main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); // the default test data directory is ../data (relative to the executable's directory) - const auto default_test_data_dir = boost::filesystem::path(argv[0]).parent_path().parent_path() / "data"; + const auto default_test_data_dir = boost::filesystem::canonical(argv[0]).parent_path().parent_path() / "data"; po::options_description desc_options("Command line options"); const command_line::arg_descriptor<std::string> arg_data_dir = { "data-dir", "Data files directory", default_test_data_dir.string() }; diff --git a/tests/unit_tests/wallet_storage.cpp b/tests/unit_tests/wallet_storage.cpp index dacaff960..ff01e452c 100644 --- a/tests/unit_tests/wallet_storage.cpp +++ b/tests/unit_tests/wallet_storage.cpp @@ -29,6 +29,8 @@ #include "unit_tests_utils.h" #include "gtest/gtest.h" +#include <cctype> + #include "file_io_utils.h" #include "wallet/wallet2.h" @@ -38,6 +40,9 @@ using namespace epee::file_io_utils; static constexpr const char WALLET_00fd416a_PRIMARY_ADDRESS[] = "45p2SngJAPSJbqSiUvYfS3BfhEdxZmv8pDt25oW1LzxrZv9Uq6ARagiFViMGUE3gJk5VPWingCXVf1p2tyAy6SUeSHPhbve"; +// https://github.com/monero-project/monero/blob/67d190ce7c33602b6a3b804f633ee1ddb7fbb4a1/src/wallet/wallet2.cpp#L156 +static constexpr const char WALLET2_ASCII_OUTPUT_MAGIC[] = "MoneroAsciiDataV1"; + TEST(wallet_storage, store_to_file2file) { const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a"; @@ -264,3 +269,115 @@ TEST(wallet_storage, change_password_mem2file) EXPECT_EQ(primary_address_1, primary_address_2); } + +TEST(wallet_storage, gen_ascii_format) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_gen_ascii_format"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + const epee::wipeable_string password("https://safecurves.cr.yp.to/rigid.html"); + + std::string primary_address_1, primary_address_2; + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.generate(target_wallet_file.string(), password); + primary_address_1 = w.get_address_as_str(); + } + + ASSERT_TRUE(is_file_exist(target_wallet_file.string())); + ASSERT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + // Assert that we store keys in ascii format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_NE(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + for (const char c : key_file_contents) + ASSERT_TRUE(std::isprint(c) || c == '\n' || c == '\r'); + } + + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.load(target_wallet_file.string(), password); + primary_address_2 = w.get_address_as_str(); + } + + EXPECT_EQ(primary_address_1, primary_address_2); +} + +TEST(wallet_storage, change_export_format) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_change_export_format"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + const epee::wipeable_string password("https://safecurves.cr.yp.to/rigid.html"); + + std::string primary_address_1, primary_address_2; + { + tools::wallet2 w; + ASSERT_EQ(tools::wallet2::Binary, w.export_format()); + w.generate(target_wallet_file.string(), password); + primary_address_1 = w.get_address_as_str(); + w.store(); + + // Assert that we initially store keys in binary format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_EQ(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + bool only_printable = true; + for (const char c : key_file_contents) + { + if (!std::isprint(c) && c != '\n' && c != '\r') + { + only_printable = false; + break; + } + } + EXPECT_FALSE(only_printable); + } + + // switch formats and store + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.store_to("", password, /*force_rewrite_keys=*/ true); + } + + ASSERT_TRUE(is_file_exist(target_wallet_file.string())); + ASSERT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + // Assert that we store keys in ascii format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_NE(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + for (const char c : key_file_contents) + ASSERT_TRUE(std::isprint(c) || c == '\n' || c == '\r'); + } + + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.load(target_wallet_file.string(), password); + primary_address_2 = w.get_address_as_str(); + } + + EXPECT_EQ(primary_address_1, primary_address_2); +} diff --git a/utils/gpg_keys/tobtoht.asc b/utils/gpg_keys/tobtoht.asc new file mode 100644 index 000000000..16311b2d8 --- /dev/null +++ b/utils/gpg_keys/tobtoht.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGLhQcoBEADiEB9S7FRpHpnI8TpFhBoYgeugroHJgupRmSvMt+0myD9AkKbL +6FG3F98HRyFA3ljq3UWd5kUxZIeljdwWn/sHd3eu+BXJ2gXU7jo8MzOFDQrW+bcE +/4PFEdbLhsvmEvXkURFFZTyVA6CgAfsd86l2okG0X4dbDLwGRcVx30bYThH5a/lK +buTKOANbP9Rw6hRkZAIZof2DDZgU4G5UEFvxf80lhbXQgPtQoCAad9b/cRGUnau8 +STtA6qKiVch1IS0Y2+EE91aN2MLmGxjTr5N6kQm/oYeIKqLiZuVBkGmNVVP5CyDw +k4YjmrCyckewAWwsjDNZ6X/eAV06inblOxgX9g2VJKVHck4WtN9c+5SmTkWz5YZ0 +hXxj7h0ASukvud0uf9UwLOAIXRnXRUyxAAw0LhTmHIlsDHw1XJoqIjKnJNpCGIqE +Hn/AQT5n8W5HYF7GJQAav1Qso7yclkUdXP1mfSpbC1Q+R92WAlgLrIwEeUG8iEeO +6KZazSSzn8VwG7Z5VnE32LpIdGDDJOpqABd7CD7b9dtKP/QH8wbdZxVzcMsdD2Zk +VARUSMsMRNKXpQuURN02zPPyu//jYwqrMXHbUE2Mt8quhJQya4xVRbjBW0LHwZQO +/vpUm6reOO5vIJdkgqH0YP5WMyj4/BnhkkOGPjDgffzxMihue34fO+V05wARAQAB +tB90b2J0b2h0IDx0b2JAZmVhdGhlcndhbGxldC5vcmc+iQJOBBMBCAA4FiEE6HvZ +Ic3YhcnXijjF5FsQ3QJ9JHIFAmLhQcoCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC +F4AACgkQ5FsQ3QJ9JHLw/hAA28cJgy2KOAFNaX3lSeOGC6ZdQUv4dtTkg1wZjmDq +AKE8I1ANCaVHtB0Df7CJTnBnCvkLAhf8gA3ldNBD3PKySBmP1rnjRYjYmNOZ+9io +EHwlgL7WyknXUtbitrpe/wM7xWN/cDdEmlRvOb9pf4GTPw2MaZLM+E43KnKoEWJ/ +Ms8vOLi2UIZNmpYhWzjS178EtC9AfvsCwWexVwYhIxlmqF++W2QYMVUEqDaVMKsJ +BohhPrOb/SQJVd5D6vrcIGs/aZZOEuthvBp1ycFdCEGajwBNzmrmhLfl9CuP0NqM +4KkYYHrvJio8QbsMxU27KJfDaGloNfSkk3V+MhZGvMqFUkTuabeyz6Uh/CREV6s/ +JApTPHvAmMeD9p6PNfxZvSq/jcbwbxPdqvWyy+dqEaaPIGbR1sOSJOOzxcJ84/1F +VwA2FQENmlob6VRroqHWTZzKPmwOi2qFAjEleEHZrjfaaHlXrGbi1ucFOqn+2/Qh +F4oi1gzFG0jQYajXxRC1CrM3ykKWgGt6cTeMDXQxEdQOkWdgP7pkVbpc9j4OW0aF +WeUlr1i+BU6UwY6eibAwT57pBiU4A9TtJjHBBPUG+XGIB9JPAWD43mfXAONfmll3 +zyV0Y6Tq8NiQUs0yuWNWqo6fDn/EW5ldNsDz+PwDGGusEMJYrGc8emWqi+CsSg83 +imu5Ag0EYuFBygEQAMXwWjBPxiWN1wDrHNOwoVf4aX37mYjsgtQ+223x5uFD0ULl +6pH2hUCg/FSVqsm+xvVqX6DV+WmzkGhhC1B2bnpEkzxQr79dXEq0V+hsKypa7t66 +8/e+4pNxeSz+4UMJApEc/lIGtEcWPgx2xNChagB2V/074hN3eZ6jpq4KJS4GG7ly +ts+t2jNs8EArrWKWKzIp2vc+Upad5LD4SSWR5qQQUgXtVbJO3EoVfeK3+g0iiVOv +o29yEStCUBrkbuK1Kwe1wjw6ePIDWKXkvo0vSRKxolCGALFhToLM0QNLjp1WNXJG +TuZtcUYIY58b7WAcEoqM7uRc3YcFMjl1tW/e3UF8Ii6CBr69bxzeygfXEdXjM+XC +H9WvxtHFFgelnloA2OHRqe1ehtsLtmlVHoJ1RArKEFYrMiApAiGSf9ztm/eQ3yX4 +6E28k3OOZSRUyyZHC++NxmNDKjQpcVQ2k5yCGje5xB4844n5fTMafWNGYnnveFTx +ZlXwz+ZlwvGHKwwI6u+n+ZA5eV+R24mo9JaPQVlQ9MLpzbWhpUvGy8ugn4rhiaz2 +4gw6hdd1BaTzkMgrCh1uT4GxVSQxanX4ubHK06QiH5yiopAIcrLXQCtIaNwmQPD2 +1K9WvZ0xagrXDOInnsGAUM8V7ce5VhwWxwIjjP7QCh886P2HHg5KM7twSG6rABEB +AAGJAjYEGAEIACAWIQToe9khzdiFydeKOMXkWxDdAn0kcgUCYuFBygIbDAAKCRDk +WxDdAn0kcuc8EACCJWI0o5AYxDQ1tkbPcnfwzFQQ50vH6pfEmk3/YQXbaJxtNLVS +ba13zC7rw5pBfVLXPDu7oOcf0x8/3HoeKfAFP/ChlU/PA2yojigZJV9zCpqCC0dm +vV7N+Mgd/GD6FGwnSM3kHnkYPgbu1/uMDbm7OTq3fbNK5BTKikkmW8ZxDyNwGYlR +5Smt2dymlxcEIYZSw27R5yyJhw6ri5trwJwJYay7LgKnWb+ePNDPUR0+ph0bXYl8 +DKa1c3vkNfmN17AUtzl0DByMZ+C0mJiu1cjWbbgg5PuQQXiNkV2xcDcMrLjVE686 +YkYI9KKLJQVLO8d57OEv0irE1bD7BlyILwemrdjuyHsPRBwHSnw8s1+Sy4qWg099 +rCGcK7wu+L9r/+fgTxONSyqb74pcO4JQDXZKKjsDfR1R2qhcvCbU4VpOvIdoiXh9 +wUBY4pBrMGDbtah3DOqvCEb7Jol754KWOaTkjDNbGrBZ5UBGdVa5flKOzIN+YOVQ +ce9Nz8XeSVy8PzrUpf5P/DFBe4Gr/E4PZkuLtJGfzNHbzhBMVMv/vfbL24uACQM0 +DKQ3vplo+++Zcyey8Nz1MNJDYqcCD/o1SInMPWbtlytNNWLj+5i75K37TIOl3vkq +Aa6OeR9y7rn8bJ88tKgnXiEalMy7HUyBDZbMCeE6IVGZvbUSdLz5VGpKmQ== +=KI4D +-----END PGP PUBLIC KEY BLOCK----- |