aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/depends.yml2
-rw-r--r--CMakeLists.txt16
-rw-r--r--README.md2
-rw-r--r--contrib/depends/toolchain.cmake.in2
-rw-r--r--contrib/epee/include/math_helper.h205
-rw-r--r--contrib/epee/include/serialization/wire/write.h9
-rw-r--r--contrib/epee/include/string_tools.h1
-rw-r--r--contrib/epee/src/http_auth.cpp6
-rw-r--r--contrib/epee/src/string_tools.cpp10
-rw-r--r--contrib/gitian/gitian-linux.yml11
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp1
-rw-r--r--src/crypto/jh.c17
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h4
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp20
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/merge_mining.cpp10
-rw-r--r--src/cryptonote_basic/merge_mining.h4
-rw-r--r--src/cryptonote_basic/miner.cpp1
-rw-r--r--src/cryptonote_basic/tx_extra.h2
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/blockchain.h2
-rw-r--r--src/cryptonote_core/blockchain_and_pool.h6
-rw-r--r--src/cryptonote_core/tx_pool.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h1
-rw-r--r--src/device/device_ledger.cpp9
-rw-r--r--src/device/device_ledger.hpp7
-rw-r--r--src/device_trezor/device_trezor_base.hpp15
-rw-r--r--src/p2p/net_node.inl1
-rw-r--r--src/ringct/rctTypes.h16
-rw-r--r--src/rpc/core_rpc_server.cpp35
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h2
-rw-r--r--src/rpc/zmq_server.cpp23
-rw-r--r--src/seraphis_crypto/CMakeLists.txt51
-rw-r--r--src/seraphis_crypto/dummy.cpp0
-rw-r--r--src/seraphis_crypto/sp_transcript.h397
-rw-r--r--src/wallet/message_transporter.cpp13
-rw-r--r--src/wallet/wallet2.cpp89
-rw-r--r--src/wallet/wallet_rpc_server.h1
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/core_tests/wallet_tools.cpp7
-rw-r--r--tests/performance_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/crypto.cpp10
-rw-r--r--tests/unit_tests/main.cpp2
-rw-r--r--tests/unit_tests/wallet_storage.cpp117
-rw-r--r--utils/gpg_keys/tobtoht.asc51
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)
diff --git a/README.md b/README.md
index 062bf1104..f0c0fe7fd 100644
--- a/README.md
+++ b/README.md
@@ -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-----