aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--contrib/depends/packages/openssl.mk15
-rw-r--r--contrib/depends/packages/unbound.mk1
-rw-r--r--contrib/depends/toolchain.cmake.in4
-rw-r--r--contrib/epee/include/net/http_client.h42
-rw-r--r--contrib/epee/include/storages/portable_storage_from_bin.h3
-rw-r--r--contrib/epee/include/storages/portable_storage_from_json.h3
-rw-r--r--contrib/epee/src/mlog.cpp13
-rw-r--r--contrib/epee/src/readline_buffer.cpp4
-rw-r--r--contrib/gitian/DOCKRUN.md2
-rw-r--r--contrib/gitian/README.md2
-rw-r--r--contrib/gitian/gitian-linux.yml11
-rw-r--r--external/easylogging++/easylogging++.cc5
m---------external/randomx0
-rw-r--r--src/blocks/checkpoints.datbin356356 -> 373124 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp2
-rw-r--r--src/crypto/jh.c17
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/blockchain.cpp37
-rw-r--r--src/multisig/multisig_kex_msg.cpp9
-rw-r--r--src/ringct/rctTypes.h12
-rw-r--r--src/rpc/zmq_server.cpp23
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/wallet2.cpp142
-rw-r--r--src/wallet/wallet2.h24
-rw-r--r--src/wallet/wallet_rpc_server.cpp14
-rw-r--r--tests/CMakeLists.txt10
-rw-r--r--tests/data/wallet_00fd416abin0 -> 2329810 bytes
-rw-r--r--tests/data/wallet_00fd416a.keysbin0 -> 1679 bytes
-rwxr-xr-xtests/functional_tests/multisig.py139
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/long_term_block_weight.cpp35
-rw-r--r--tests/unit_tests/wallet_storage.cpp266
33 files changed, 620 insertions, 229 deletions
diff --git a/README.md b/README.md
index 9acea573a..bf8bbae75 100644
--- a/README.md
+++ b/README.md
@@ -138,8 +138,8 @@ Dates are provided in the format YYYY-MM-DD.
| 1978433 | 2019-11-30 | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs
| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.3.2 | New CLSAG transaction format
| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.3.2 | forbid old MLSAG transaction format
-| 2688888 | 2022-08-13 | v15 | v0.18.0.0 | v0.18.2.2 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
-| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.2.2 | forbid old v14 transaction format
+| 2688888 | 2022-08-13 | v15 | v0.18.0.0 | v0.18.3.1 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
+| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.3.1 | forbid old v14 transaction format
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
@@ -344,7 +344,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/monero-project/monero.git
cd monero
- git checkout v0.18.2.2
+ git checkout v0.18.3.1
```
* Build:
@@ -463,10 +463,10 @@ application.
cd monero
```
-* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.18.2.2'. If you don't care about the version and just want binaries from master, skip this step:
+* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.18.3.1'. If you don't care about the version and just want binaries from master, skip this step:
```bash
- git checkout v0.18.2.2
+ git checkout v0.18.3.1
```
* If you are on a 64-bit system, run:
diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk
index 100e0d33b..b35cf5e97 100644
--- a/contrib/depends/packages/openssl.mk
+++ b/contrib/depends/packages/openssl.mk
@@ -1,20 +1,19 @@
package=openssl
-$(package)_version=1.1.1u
+$(package)_version=3.0.11
$(package)_download_path=https://www.openssl.org/source
$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=e2f8d84b523eecd06c7be7626830370300fbcc15386bf5142d72758f6963ebc6
+$(package)_sha256_hash=b3425d3bb4a2218d0697eb41f7fc0cdede016ed19ca49d168b78e8d947887f55
define $(package)_set_vars
$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
-$(package)_config_env_android=ANDROID_NDK_HOME="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib
-$(package)_build_env_android=ANDROID_NDK_HOME="$(host_prefix)/native"
-$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl
+$(package)_config_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib
+$(package)_build_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native"
+$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl --libdir=$(host_prefix)/lib
$(package)_config_opts+=no-capieng
$(package)_config_opts+=no-dso
$(package)_config_opts+=no-dtls1
$(package)_config_opts+=no-ec_nistp_64_gcc_128
$(package)_config_opts+=no-gost
-$(package)_config_opts+=no-heartbeats
$(package)_config_opts+=no-md2
$(package)_config_opts+=no-rc5
$(package)_config_opts+=no-rdrand
@@ -22,8 +21,8 @@ $(package)_config_opts+=no-rfc3779
$(package)_config_opts+=no-sctp
$(package)_config_opts+=no-shared
$(package)_config_opts+=no-ssl-trace
-$(package)_config_opts+=no-ssl2
$(package)_config_opts+=no-ssl3
+$(package)_config_opts+=no-tests
$(package)_config_opts+=no-unit-test
$(package)_config_opts+=no-weak-ssl-ciphers
$(package)_config_opts+=no-zlib
@@ -49,7 +48,7 @@ $(package)_config_opts_x86_64_freebsd=BSD-x86_64
endef
define $(package)_preprocess_cmds
- sed -i.old 's|"engines", "apps", "test", "util", "tools", "fuzz"|"engines", "tools"|' Configure
+ sed -i.old 's|crypto ssl apps util tools fuzz providers doc|crypto ssl util tools providers|' build.info
endef
define $(package)_config_cmds
diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk
index 421c51f7f..166cc3f79 100644
--- a/contrib/depends/packages/unbound.mk
+++ b/contrib/depends/packages/unbound.mk
@@ -11,6 +11,7 @@ define $(package)_set_vars
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) --with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
$(package)_config_opts_linux=--with-pic
$(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix)
+ $(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes
$(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread"
endef
diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in
index 570065560..c86012a6a 100644
--- a/contrib/depends/toolchain.cmake.in
+++ b/contrib/depends/toolchain.cmake.in
@@ -144,8 +144,8 @@ elseif(ARCHITECTURE STREQUAL "aarch64")
endif()
if(ARCHITECTURE STREQUAL "riscv64")
- set(NO_AES ON)
- set(ARCH "rv64imafdc")
+ set(ARCH_ID "riscv64")
+ set(ARCH "rv64gc")
endif()
if(ARCHITECTURE STREQUAL "i686")
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
index 9ce30b620..af1aee9f6 100644
--- a/contrib/epee/include/net/http_client.h
+++ b/contrib/epee/include/net/http_client.h
@@ -245,8 +245,18 @@ namespace net_utils
}
}
+ // This magic var determines the maximum length for when copying the body message in
+ // memory is faster/more preferable than the round-trip time for one packet
+ constexpr size_t BODY_NO_COPY_CUTOFF = 128 * 1024; // ~262 KB or ~175 packets
+
+ // Maximum expected total headers bytes
+ constexpr size_t HEADER_RESERVE_SIZE = 2048;
+
+ const bool do_copy_body = body.size() <= BODY_NO_COPY_CUTOFF;
+ const size_t req_buff_cap = HEADER_RESERVE_SIZE + (do_copy_body ? body.size() : 0);
+
std::string req_buff{};
- req_buff.reserve(2048);
+ req_buff.reserve(req_buff_cap);
req_buff.append(method.data(), method.size()).append(" ").append(uri.data(), uri.size()).append(" HTTP/1.1\r\n");
add_field(req_buff, "Host", m_host_buff);
add_field(req_buff, "Content-Length", std::to_string(body.size()));
@@ -255,9 +265,7 @@ namespace net_utils
for(const auto& field : additional_params)
add_field(req_buff, field);
- for (unsigned sends = 0; sends < 2; ++sends)
{
- const std::size_t initial_size = req_buff.size();
const auto auth = m_auth.get_auth_field(method, uri);
if (auth)
add_field(req_buff, *auth);
@@ -265,11 +273,21 @@ namespace net_utils
req_buff += "\r\n";
//--
- bool res = m_net_client.send(req_buff, timeout);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
- if(body.size())
+ if (do_copy_body) // small body
+ {
+ // Copy headers + body together and potentially send one fewer packet
+ req_buff.append(body.data(), body.size());
+ const bool res = m_net_client.send(req_buff, timeout);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ }
+ else // large body
+ {
+ // Send headers and body seperately to avoid copying heavy body message
+ bool res = m_net_client.send(req_buff, timeout);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
res = m_net_client.send(body, timeout);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ }
m_response_info.clear();
m_state = reciev_machine_state_header;
@@ -282,19 +300,11 @@ namespace net_utils
return true;
}
- switch (m_auth.handle_401(m_response_info))
+ if (m_auth.handle_401(m_response_info) == http_client_auth::kParseFailure)
{
- case http_client_auth::kSuccess:
- break;
- case http_client_auth::kBadPassword:
- sends = 2;
- break;
- default:
- case http_client_auth::kParseFailure:
LOG_ERROR("Bad server response for authentication");
return false;
}
- req_buff.resize(initial_size); // rollback for new auth generation
}
LOG_ERROR("Client has incorrect username/password for server requiring authentication");
return false;
diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h
index d8a8a4a49..b0af022f5 100644
--- a/contrib/epee/include/storages/portable_storage_from_bin.h
+++ b/contrib/epee/include/storages/portable_storage_from_bin.h
@@ -33,6 +33,9 @@
#include "portable_storage_base.h"
#include "portable_storage_bin_utils.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
+
#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
#else
diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h
index 69192ca6b..f21a89a92 100644
--- a/contrib/epee/include/storages/portable_storage_from_json.h
+++ b/contrib/epee/include/storages/portable_storage_from_json.h
@@ -31,6 +31,9 @@
#include "parserse_base_utils.h"
#include "file_io_utils.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
+
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100
namespace epee
diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp
index 092d41777..4ca1a3632 100644
--- a/contrib/epee/src/mlog.cpp
+++ b/contrib/epee/src/mlog.cpp
@@ -338,11 +338,21 @@ bool is_stdout_a_tty()
return is_a_tty.load(std::memory_order_relaxed);
}
+static bool is_nocolor()
+{
+ static const char *no_color_var = getenv("NO_COLOR");
+ static const bool no_color = no_color_var && *no_color_var; // apparently, NO_COLOR=0 means no color too (as per no-color.org)
+ return no_color;
+}
+
void set_console_color(int color, bool bright)
{
if (!is_stdout_a_tty())
return;
+ if (is_nocolor())
+ return;
+
switch(color)
{
case console_color_default:
@@ -461,6 +471,9 @@ void reset_console_color() {
if (!is_stdout_a_tty())
return;
+ if (is_nocolor())
+ return;
+
#ifdef WIN32
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp
index 1047d1696..ac68d1fdb 100644
--- a/contrib/epee/src/readline_buffer.cpp
+++ b/contrib/epee/src/readline_buffer.cpp
@@ -238,6 +238,10 @@ static char** attempted_completion(const char* text, int start, int end)
static void install_line_handler()
{
+#if RL_READLINE_VERSION >= 0x0801
+ rl_variable_bind("enable-bracketed-paste", "off");
+#endif
+
rl_attempted_completion_function = attempted_completion;
rl_callback_handler_install("", handle_line);
stifle_history(500);
diff --git a/contrib/gitian/DOCKRUN.md b/contrib/gitian/DOCKRUN.md
index d8cf8222e..5e5092507 100644
--- a/contrib/gitian/DOCKRUN.md
+++ b/contrib/gitian/DOCKRUN.md
@@ -57,7 +57,7 @@ The dockrun.sh script will do everything to build the binaries. Just specify the
version to build as its only argument, e.g.
```bash
-VERSION=v0.18.2.2
+VERSION=v0.18.3.1
./dockrun.sh $VERSION
```
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
index 153af7541..d5f6ba0e0 100644
--- a/contrib/gitian/README.md
+++ b/contrib/gitian/README.md
@@ -133,7 +133,7 @@ Common setup part:
su - gitianuser
GH_USER=YOUR_GITHUB_USER_NAME
-VERSION=v0.18.2.2
+VERSION=v0.18.3.1
```
Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.
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/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc
index a765ee8cc..891936b6b 100644
--- a/external/easylogging++/easylogging++.cc
+++ b/external/easylogging++/easylogging++.cc
@@ -149,6 +149,11 @@ static el::Color colorFromLevel(el::Level level)
static void setConsoleColor(el::Color color, bool bright)
{
+ static const char *no_color_var = getenv("NO_COLOR");
+ static const bool no_color = no_color_var && *no_color_var; // apparently, NO_COLOR=0 means no color too (as per no-color.org)
+ if (no_color)
+ return;
+
#if ELPP_OS_WINDOWS
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
switch (color)
diff --git a/external/randomx b/external/randomx
-Subproject 261d58c77fc5547c0aa7fdfeb58421ba7e0e6e1
+Subproject 102f8acf90a7649ada410de5499a7ec62e49e1d
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index eb3a40fa2..bb6f9b421 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 507e807e2..fe1508e21 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -246,6 +246,8 @@ namespace cryptonote
ADD_CHECKPOINT2(2817000, "39726d19ccaac01d150bec827b877ffae710b516bd633503662036ef4422e577", "0x3900669561954c1");
ADD_CHECKPOINT2(2844000, "28fc7b446dfef5b469f5778eb72ddf32a307a5f5a9823d1c394e772349e05d40", "0x3af384ec0e97d12");
ADD_CHECKPOINT2(2851000, "5bf0e47fc782263191a33f63a67db6c711781dc2a3c442e17ed901ec401be5c9", "0x3b6cd8a8ed610e8");
+ ADD_CHECKPOINT2(2971000, "3d4cac5ac515eeabd18769ab943af85f36db51d28720def0d0e6effc2c8f5ce3", "0x436e532738b8b5b");
+ ADD_CHECKPOINT2(2985000, "08f5e6b7301c1b6ed88268a28f8677a06e8ff943b3f9e48d3080f71f9c134bfb", "0x444b7b42a633c96");
return true;
}
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_config.h b/src/cryptonote_config.h
index bac49aa94..61146a114 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.cpp b/src/cryptonote_core/blockchain.cpp
index 2620010c4..9d8f0f4de 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -4616,40 +4616,9 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
}
else
{
- const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
+ const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
+ const uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks);
- uint64_t long_term_median;
- if (db_height == 1)
- {
- long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5;
- }
- else
- {
- uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
- if (nblocks == db_height)
- --nblocks;
- long_term_median = get_long_term_block_weight_median(db_height - nblocks - 1, nblocks);
- }
-
- m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
-
- uint64_t short_term_constraint = m_long_term_effective_median_block_weight;
- if (hf_version >= HF_VERSION_2021_SCALING)
- short_term_constraint += m_long_term_effective_median_block_weight * 7 / 10;
- else
- short_term_constraint += m_long_term_effective_median_block_weight * 2 / 5;
- uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
-
- if (db_height == 1)
- {
- long_term_median = long_term_block_weight;
- }
- else
- {
- m_long_term_block_weights_cache_tip_hash = m_db->get_block_hash_from_height(db_height - 1);
- m_long_term_block_weights_cache_rolling_median.insert(long_term_block_weight);
- long_term_median = m_long_term_block_weights_cache_rolling_median.median();
- }
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
std::vector<uint64_t> weights;
@@ -5582,7 +5551,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "2c95b5af1f3ee41893ae0c585fd59207a40f28ed4addbaad64a46a39b82955e7";
+static const char expected_block_hashes_hash[] = "bc9c91329af96137390d9c709fa3cecc924f1b25dadb7589f0d751cd93f3cc39";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
diff --git a/src/multisig/multisig_kex_msg.cpp b/src/multisig/multisig_kex_msg.cpp
index c717e23ad..024d71df7 100644
--- a/src/multisig/multisig_kex_msg.cpp
+++ b/src/multisig/multisig_kex_msg.cpp
@@ -206,8 +206,13 @@ namespace multisig
//----------------------------------------------------------------------------------------------------------------------
void multisig_kex_msg::parse_and_validate_msg()
{
+ CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() == MULTISIG_KEX_MSG_V2_MAGIC_N.size(),
+ "Multisig kex msg magic inconsistency.");
+ CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() >= MULTISIG_KEX_V1_MAGIC.size(),
+ "Multisig kex msg magic inconsistency.");
+
// check message type
- CHECK_AND_ASSERT_THROW_MES(m_msg.size() > 0, "Kex message unexpectedly empty.");
+ CHECK_AND_ASSERT_THROW_MES(m_msg.size() >= MULTISIG_KEX_MSG_V2_MAGIC_1.size(), "Kex message unexpectedly small.");
CHECK_AND_ASSERT_THROW_MES(m_msg.substr(0, MULTISIG_KEX_V1_MAGIC.size()) != MULTISIG_KEX_V1_MAGIC,
"V1 multisig kex messages are deprecated (unsafe).");
CHECK_AND_ASSERT_THROW_MES(m_msg.substr(0, MULTISIG_KEX_MSG_V1_MAGIC.size()) != MULTISIG_KEX_MSG_V1_MAGIC,
@@ -215,8 +220,6 @@ namespace multisig
// deserialize the message
std::string msg_no_magic;
- CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() == MULTISIG_KEX_MSG_V2_MAGIC_N.size(),
- "Multisig kex msg magic inconsistency.");
CHECK_AND_ASSERT_THROW_MES(tools::base58::decode(m_msg.substr(MULTISIG_KEX_MSG_V2_MAGIC_1.size()), msg_no_magic),
"Multisig kex msg decoding error.");
binary_archive<false> b_archive{epee::strspan<std::uint8_t>(msg_no_magic)};
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index ab1a26b26..32cd8dc6f 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -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/zmq_server.cpp b/src/rpc/zmq_server.cpp
index 398a0499a..7ebb6c49f 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/version.cpp.in b/src/version.cpp.in
index 76cae29eb..c93430003 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.18.2.2"
+#define DEF_MONERO_VERSION "0.18.3.1"
#define DEF_MONERO_RELEASE_NAME "Fluorine Fermi"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 42e2f1d41..0218c8292 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1003,6 +1003,24 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total)
return n_multisig_keys;
}
+/**
+ * @brief Derives the chacha key to encrypt wallet cache files given the chacha key to encrypt the wallet keys files
+ *
+ * @param keys_data_key the chacha key that encrypts wallet keys files
+ * @return crypto::chacha_key the chacha key that encrypts the wallet cache files
+ */
+crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key)
+{
+ static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
+
+ crypto::chacha_key cache_key;
+ epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
+ memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE);
+ cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
+ cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key);
+
+ return cache_key;
+}
//-----------------------------------------------------------------
} //namespace
@@ -3901,7 +3919,7 @@ std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create
return cache;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool, bool try_incremental)
+void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool, bool try_incremental, uint64_t max_blocks)
{
if (m_offline)
{
@@ -3996,7 +4014,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// infer when we get an incoming output
bool first = true, last = false;
- while(m_run.load(std::memory_order_relaxed))
+ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks)
{
uint64_t next_blocks_start_height;
std::vector<cryptonote::block_complete_entry> next_blocks;
@@ -4406,6 +4424,10 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
crypto::chacha_key key;
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password
+ const crypto::chacha_key cache_key = derive_cache_key(key);
+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
+
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
{
account.encrypt_viewkey(key);
@@ -4438,7 +4460,7 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
value2.SetInt(m_key_device_type);
json.AddMember("key_on_device", value2, json.GetAllocator());
- value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
+ value2.SetInt((watch_only || m_watch_only) ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ??
json.AddMember("watch_only", value2, json.GetAllocator());
value2.SetInt(m_multisig ? 1 :0);
@@ -4630,11 +4652,8 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
m_account.decrypt_viewkey(key);
}
- static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
- epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
- memcpy(cache_key_data.data(), &key, HASH_SIZE);
- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
- cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
+ m_cache_key = derive_cache_key(key);
+
get_ringdb_key();
}
//----------------------------------------------------------------------------------------------------
@@ -4643,9 +4662,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(original_password);
setup_keys(new_password);
- rewrite(filename, new_password);
if (!filename.empty())
- store();
+ store_to(filename, new_password, true); // force rewrite keys file to possible new location
}
//----------------------------------------------------------------------------------------------------
/*!
@@ -5151,6 +5169,10 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key)
void wallet2::decrypt_keys(const crypto::chacha_key &key)
{
+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password
+ const crypto::chacha_key cache_key = derive_cache_key(key);
+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
+
m_account.encrypt_viewkey(key);
m_account.decrypt_keys(key);
}
@@ -6222,6 +6244,20 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (!m_persistent_rpc_client_id)
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
+ // 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);
@@ -6311,22 +6347,32 @@ void wallet2::store()
store_to("", epee::wipeable_string());
}
//----------------------------------------------------------------------------------------------------
-void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
+void wallet2::store_to(const std::string &path, const epee::wipeable_string &password, bool force_rewrite_keys)
{
trim_hashchain();
+ const bool had_old_wallet_files = !m_wallet_file.empty();
+ THROW_WALLET_EXCEPTION_IF(!had_old_wallet_files && path.empty(), error::wallet_internal_error,
+ "Cannot resave wallet to current file since wallet was not loaded from file to begin with");
+
// if file is the same, we do:
- // 1. save wallet to the *.new file
- // 2. remove old wallet file
- // 3. rename *.new to wallet_name
+ // 1. overwrite the keys file iff force_rewrite_keys is specified
+ // 2. save cache to the *.new file
+ // 3. rename *.new to wallet_name, replacing old cache file
+ // else we do:
+ // 1. prepare new file names with "path" variable
+ // 2. store new keys files
+ // 3. remove old keys file
+ // 4. store new cache file
+ // 5. remove old cache file
// handle if we want just store wallet state to current files (ex store() replacement);
- bool same_file = true;
- if (!path.empty())
+ bool same_file = had_old_wallet_files && path.empty();
+ if (had_old_wallet_files && !path.empty())
{
- std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
- size_t pos = canonical_path.find(path);
- same_file = pos != std::string::npos;
+ const std::string canonical_old_path = boost::filesystem::canonical(m_wallet_file).string();
+ const std::string canonical_new_path = boost::filesystem::weakly_canonical(path).string();
+ same_file = canonical_old_path == canonical_new_path;
}
@@ -6347,7 +6393,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
}
// get wallet cache data
- boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data(password);
+ boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data();
THROW_WALLET_EXCEPTION_IF(cache_file_data == boost::none, error::wallet_internal_error, "failed to generate wallet cache data");
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
@@ -6356,12 +6402,20 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_address_file = m_wallet_file + ".address.txt";
const std::string old_mms_file = m_mms_file;
- // save keys to the new file
- // if we here, main wallet file is saved and we only need to save keys and address files
- if (!same_file) {
+ if (!same_file)
+ {
prepare_file_names(path);
- bool r = store_keys(m_keys_file, password, false);
+ }
+
+ if (!same_file || force_rewrite_keys)
+ {
+ bool r = store_keys(m_keys_file, password, m_watch_only);
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ }
+
+ if (!same_file && had_old_wallet_files)
+ {
+ bool r = false;
if (boost::filesystem::exists(old_address_file))
{
// save address to the new file
@@ -6374,11 +6428,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
LOG_ERROR("error removing file: " << old_address_file);
}
}
- // remove old wallet file
- r = boost::filesystem::remove(old_file);
- if (!r) {
- LOG_ERROR("error removing file: " << old_file);
- }
// remove old keys file
r = boost::filesystem::remove(old_keys_file);
if (!r) {
@@ -6392,8 +6441,9 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
LOG_ERROR("error removing file: " << old_mms_file);
}
}
- } else {
- // save to new file
+ }
+
+ // Save cache to new file. If storing to the same file, the temp path has the ".new" extension
#ifdef WIN32
// On Windows avoid using std::ofstream which does not work with UTF-8 filenames
// The price to pay is temporary higher memory consumption for string stream + binary archive
@@ -6413,10 +6463,20 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
#endif
+ if (same_file)
+ {
// here we have "*.new" file, we need to rename it to be without ".new"
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
}
+ else if (!same_file && had_old_wallet_files)
+ {
+ // remove old wallet file
+ bool r = boost::filesystem::remove(old_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_file);
+ }
+ }
if (m_message_store.get_active())
{
@@ -6426,7 +6486,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
}
}
//----------------------------------------------------------------------------------------------------
-boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data(const epee::wipeable_string &passwords)
+boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data()
{
trim_hashchain();
try
@@ -7077,7 +7137,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
@@ -7581,7 +7644,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)
@@ -7905,7 +7971,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());
@@ -10928,7 +10997,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
else
{
LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
- while (needed_fee > test_ptx.fee) {
+ do {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
@@ -10939,7 +11008,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
- }
+ } while (needed_fee > test_ptx.fee);
LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
@@ -11335,7 +11404,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
do {
- LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
+ LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
// distribute total transferred amount between outputs
uint64_t amount_transferred = available_for_fee - needed_fee;
uint64_t dt_amount = amount_transferred / outputs;
@@ -14220,7 +14289,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/wallet2.h b/src/wallet/wallet2.h
index baeffe096..877e5afd0 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -940,22 +940,32 @@ private:
/*!
* \brief store_to Stores wallet to another file(s), deleting old ones
* \param path Path to the wallet file (keys and address filenames will be generated based on this filename)
- * \param password Password to protect new wallet (TODO: probably better save the password in the wallet object?)
+ * \param password Password that currently locks the wallet
+ * \param force_rewrite_keys if true, always rewrite keys file
+ *
+ * Leave both "path" and "password" blank to restore the cache file to the current position in the disk
+ * (which is the same as calling `store()`). If you want to store the wallet with a new password,
+ * use the method `change_password()`.
+ *
+ * Normally the keys file is not overwritten when storing, except when force_rewrite_keys is true
+ * or when `path` is a new wallet file.
+ *
+ * \throw error::invalid_password If storing keys file and old password is incorrect
*/
- void store_to(const std::string &path, const epee::wipeable_string &password);
+ void store_to(const std::string &path, const epee::wipeable_string &password, bool force_rewrite_keys = false);
/*!
* \brief get_keys_file_data Get wallet keys data which can be stored to a wallet file.
- * \param password Password of the encrypted wallet buffer (TODO: probably better save the password in the wallet object?)
+ * \param password Password that currently locks the wallet
* \param watch_only true to include only view key, false to include both spend and view keys
* \return Encrypted wallet keys data which can be stored to a wallet file
+ * \throw error::invalid_password if password does not match current wallet
*/
boost::optional<wallet2::keys_file_data> get_keys_file_data(const epee::wipeable_string& password, bool watch_only);
/*!
* \brief get_cache_file_data Get wallet cache data which can be stored to a wallet file.
- * \param password Password to protect the wallet cache data (TODO: probably better save the password in the wallet object?)
- * \return Encrypted wallet cache data which can be stored to a wallet file
+ * \return Encrypted wallet cache data which can be stored to a wallet file (using current password)
*/
- boost::optional<wallet2::cache_file_data> get_cache_file_data(const epee::wipeable_string& password);
+ boost::optional<wallet2::cache_file_data> get_cache_file_data();
std::string path() const;
@@ -1048,7 +1058,7 @@ private:
bool is_deprecated() const;
void refresh(bool trusted_daemon);
void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched);
- void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool = true, bool try_incremental = true);
+ void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool = true, bool try_incremental = true, uint64_t max_blocks = std::numeric_limits<uint64_t>::max());
bool refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok);
void set_refresh_type(RefreshType refresh_type) { m_refresh_type = refresh_type; }
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 8119d9d09..cecd79368 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -60,6 +60,7 @@ using namespace epee;
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
+#define REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE 256 // just to split refresh in separate calls to play nicer with other threads
#define CHECK_MULTISIG_ENABLED() \
do \
@@ -79,6 +80,7 @@ namespace
const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
+ const command_line::arg_descriptor<bool> arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false};
constexpr const char default_rpc_username[] = "monero";
@@ -152,11 +154,14 @@ namespace tools
uint64_t blocks_fetched = 0;
try {
bool received_money = false;
- if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, blocks_fetched, received_money, true, true);
+ if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, blocks_fetched, received_money, true, true, REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE);
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
- m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
+ // if we got the max amount of blocks, do not set the last refresh time, we did only part of the refresh and will
+ // continue asap, and only set the last refresh time once the refresh is actually finished
+ if (blocks_fetched < REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE)
+ m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
return true;
}, 1000);
m_net_server.add_idle_handler([this](){
@@ -4554,6 +4559,7 @@ public:
const auto password_file = command_line::get_arg(vm, arg_password_file);
const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
+ const auto no_initial_sync = command_line::get_arg(vm, arg_no_initial_sync);
if(!wallet_file.empty() && !from_json.empty())
{
@@ -4622,7 +4628,8 @@ public:
try
{
- wal->refresh(wal->is_trusted_daemon());
+ if (!no_initial_sync)
+ wal->refresh(wal->is_trusted_daemon());
}
catch (const std::exception& e)
{
@@ -4733,6 +4740,7 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
command_line::add_arg(desc_params, arg_rpc_client_secret_key);
+ command_line::add_arg(desc_params, arg_no_initial_sync);
daemonizer::init_options(hidden_options, desc_params);
desc_params.add(hidden_options);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2cabb1ba5..e074ceed6 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -72,14 +72,8 @@ else ()
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/gtest/include")
endif (GTest_FOUND)
-file(COPY
- data/wallet_9svHk1.keys
- data/wallet_9svHk1
- data/outputs
- data/unsigned_monero_tx
- data/signed_monero_tx
- data/sha256sum
- DESTINATION data)
+message(STATUS "Copying test data directory...")
+file(COPY data DESTINATION .) # Copy data directory from source root to build root
if (CMAKE_BUILD_TYPE STREQUAL "fuzz" OR OSSFUZZ)
add_subdirectory(fuzz)
diff --git a/tests/data/wallet_00fd416a b/tests/data/wallet_00fd416a
new file mode 100644
index 000000000..a1b7898e6
--- /dev/null
+++ b/tests/data/wallet_00fd416a
Binary files differ
diff --git a/tests/data/wallet_00fd416a.keys b/tests/data/wallet_00fd416a.keys
new file mode 100644
index 000000000..6908cce1b
--- /dev/null
+++ b/tests/data/wallet_00fd416a.keys
Binary files differ
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 980adc2df..3da983d69 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -37,100 +37,61 @@ import random
from framework.daemon import Daemon
from framework.wallet import Wallet
-MULTISIG_PUB_ADDRS = [
- '45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG', # 2/2
- '44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i', # 2/3
- '41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', # 3/3
- '44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', # 3/4
- '47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', # 2/4
- '4A8RnBQixry4VXkqeWhmg8L7vWJVDJj4FN9PV4E7Mgad5ZZ6LKQdn8dYJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ4S8RSB' # 1/2
+TEST_CASES = \
+[
+# M N Primary Address
+ [2, 2, '45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG'],
+ [2, 3, '44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i'],
+ [3, 3, '41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP'],
+ [3, 4, '44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff'],
+ [2, 4, '47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U'],
+ [1, 2, '4A8RnBQixry4VXkqeWhmg8L7vWJVDJj4FN9PV4E7Mgad5ZZ6LKQdn8dYJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ4S8RSB']
]
+PUB_ADDRS = [case[2] for case in TEST_CASES]
+
class MultisigTest():
def run_test(self):
self.reset()
- for pub_addr in MULTISIG_PUB_ADDRS:
- self.mine(pub_addr, 4)
+ for pub_addr in PUB_ADDRS:
+ self.mine(pub_addr, 4)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80)
self.test_states()
- self.fund_addrs_with_normal_wallet(MULTISIG_PUB_ADDRS)
-
- self.create_multisig_wallets(2, 2, '45J58b7PmKJFSiNPFFrTdtfMcFGnruP7V4CMuRpX7NsH4j3jGHKAjo3YJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ3gr7sG')
- self.import_multisig_info([1, 0], 5)
- txid = self.transfer([1, 0])
- self.import_multisig_info([0, 1], 6)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(2)
- self.import_multisig_info([0, 1], 6) # six outputs, same as before
- txid = self.transfer([0, 1])
- self.import_multisig_info([0, 1], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
-
- self.create_multisig_wallets(2, 3, '44G2TQNfsiURKkvxp7gbgaJY8WynZvANnhmyMAwv6WeEbAvyAWMfKXRhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5duN94i')
- self.import_multisig_info([0, 2], 5)
- txid = self.transfer([0, 2])
- self.import_multisig_info([0, 1, 2], 6)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(2)
- self.import_multisig_info([0, 2], 6) # six outputs, same as before
- txid = self.transfer([0, 2])
- self.import_multisig_info([0, 1, 2], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
-
- self.create_multisig_wallets(3, 3, '41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP')
- self.import_multisig_info([2, 0, 1], 5)
- txid = self.transfer([2, 1, 0])
- self.import_multisig_info([0, 2, 1], 6)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(3)
- self.import_multisig_info([2, 0, 1], 6) # six outputs, same as before
- txid = self.transfer([2, 1, 0])
- self.import_multisig_info([0, 2, 1], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
-
- self.create_multisig_wallets(3, 4, '44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff')
- self.import_multisig_info([0, 2, 3], 5)
- txid = self.transfer([0, 2, 3])
- self.import_multisig_info([0, 1, 2, 3], 6)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(3)
- self.import_multisig_info([0, 2, 3], 6) # six outputs, same as before
- txid = self.transfer([0, 2, 3])
- self.import_multisig_info([0, 1, 2, 3], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
-
- self.create_multisig_wallets(2, 4, '47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U')
- self.import_multisig_info([1, 2], 5)
- txid = self.transfer([1, 2])
- self.import_multisig_info([0, 1, 2, 3], 6)
- self.check_transaction(txid)
- txid = self.try_transfer_frozen([2, 3])
- self.import_multisig_info([0, 1, 2, 3], 7)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(2)
- self.import_multisig_info([0, 1, 2, 3], 6) # six outputs, same as before
- txid = self.transfer([2, 3])
- self.import_multisig_info([0, 1, 2, 3], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
-
- self.create_multisig_wallets(1, 2, '4A8RnBQixry4VXkqeWhmg8L7vWJVDJj4FN9PV4E7Mgad5ZZ6LKQdn8dYJP2RePX6HMaSkbvTbrWUFhDNcNcHgtNmQ4S8RSB')
- self.import_multisig_info([0, 1], 5)
- txid = self.transfer([0])
- self.import_multisig_info([0, 1], 6)
- self.check_transaction(txid)
-
- self.remake_some_multisig_wallets_by_multsig_seed(1)
- self.import_multisig_info([0, 1], 6) # six outputs, same as before
- txid = self.transfer([1])
- self.import_multisig_info([0, 1], 7) # seven outputs b/c we're dest plus change
- self.check_transaction(txid)
+ self.fund_addrs_with_normal_wallet(PUB_ADDRS)
+
+ for M, N, pub_addr in TEST_CASES:
+ assert M <= N
+ shuffled_participants = list(range(N))
+ random.shuffle(shuffled_participants)
+ shuffled_signers = shuffled_participants[:M]
+
+ expected_outputs = 5 # each wallet owns four mined outputs & one transferred output
+
+ # Create multisig wallet and test transferring
+ self.create_multisig_wallets(M, N, pub_addr)
+ self.import_multisig_info(shuffled_signers if M != 1 else shuffled_participants, expected_outputs)
+ txid = self.transfer(shuffled_signers)
+ expected_outputs += 1
+ self.import_multisig_info(shuffled_participants, expected_outputs)
+ self.check_transaction(txid)
+
+ # If more than 1 signer, try to freeze key image of one signer, make tx using that key
+ # image on another signer, then have first signer sign multisg_txset. Should fail
+ if M != 1:
+ txid = self.try_transfer_frozen(shuffled_signers)
+ expected_outputs += 1
+ self.import_multisig_info(shuffled_participants, expected_outputs)
+ self.check_transaction(txid)
+
+ # Recreate wallet from multisig seed and test transferring
+ self.remake_some_multisig_wallets_by_multsig_seed(M)
+ self.import_multisig_info(shuffled_signers if M != 1 else shuffled_participants, expected_outputs)
+ txid = self.transfer(shuffled_signers)
+ expected_outputs += 1
+ self.import_multisig_info(shuffled_participants, expected_outputs)
+ self.check_transaction(txid)
def reset(self):
print('Resetting blockchain')
@@ -262,10 +223,10 @@ class MultisigTest():
def remake_some_multisig_wallets_by_multsig_seed(self, threshold):
N = len(self.wallet)
- signers_to_remake = set()
num_signers_to_remake = random.randint(1, N) # Do at least one
- while len(signers_to_remake) < num_signers_to_remake:
- signers_to_remake.add(random.randint(0, N - 1))
+ signers_to_remake = list(range(N))
+ random.shuffle(signers_to_remake)
+ signers_to_remake = signers_to_remake[:num_signers_to_remake]
for i in signers_to_remake:
print("Remaking {}/{} multsig wallet from multisig seed: #{}".format(threshold, N, i+1))
@@ -501,7 +462,7 @@ class MultisigTest():
print("Attemping to sign with frozen key image. This should fail")
try:
res = self.wallet[signers[1]].sign_multisig(multisig_txset)
- raise ValueError('sign_multisig should not have succeeded w/ fronzen enotes')
+ raise ValueError('sign_multisig should not have succeeded w/ frozen enotes')
except AssertionError:
pass
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 147b38dd4..fec36803e 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -97,6 +97,7 @@ set(unit_tests_sources
output_selection.cpp
vercmp.cpp
ringdb.cpp
+ wallet_storage.cpp
wipeable_string.cpp
is_hdd.cpp
aligned.cpp
diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp
index 973055097..57740cf9d 100644
--- a/tests/unit_tests/long_term_block_weight.cpp
+++ b/tests/unit_tests/long_term_block_weight.cpp
@@ -407,3 +407,38 @@ TEST(long_term_block_weight, long_growth_spike_and_drop)
ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07);
ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09);
}
+
+TEST(long_term_block_weight, cache_matches_true_value)
+{
+ PREFIX(16);
+
+ // Add big blocks to increase the block weight limit
+ for (uint64_t h = 0; h <= 2000; ++h)
+ {
+ size_t w = bc->get_current_cumulative_block_weight_limit();
+ uint64_t ltw = bc->get_next_long_term_block_weight(w);
+ bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
+ bc->update_next_cumulative_weight_limit();
+ }
+
+ ASSERT_GT(bc->get_current_cumulative_block_weight_limit() * 10/17 , 300000);
+
+ // Add small blocks to the top of the chain
+ for (uint64_t h = 2000; h <= 5001; ++h)
+ {
+ size_t w = (bc->get_current_cumulative_block_weight_median() * 10/17) - 1000;
+ uint64_t ltw = bc->get_next_long_term_block_weight(w);
+ bc->get_db().add_block(std::make_pair(cryptonote::block(), ""), w, ltw, h, h, {});
+ bc->update_next_cumulative_weight_limit();
+ }
+
+ // get the weight limit
+ uint64_t weight_limit = bc->get_current_cumulative_block_weight_limit();
+ // refresh the cache
+ bc->m_long_term_block_weights_cache_rolling_median.clear();
+ bc->get_long_term_block_weight_median(bc->get_db().height() - TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW);
+ bc->update_next_cumulative_weight_limit();
+
+ // make sure the weight limit is the same
+ ASSERT_EQ(weight_limit, bc->get_current_cumulative_block_weight_limit());
+}
diff --git a/tests/unit_tests/wallet_storage.cpp b/tests/unit_tests/wallet_storage.cpp
new file mode 100644
index 000000000..dacaff960
--- /dev/null
+++ b/tests/unit_tests/wallet_storage.cpp
@@ -0,0 +1,266 @@
+// Copyright (c) 2023, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "unit_tests_utils.h"
+#include "gtest/gtest.h"
+
+#include "file_io_utils.h"
+#include "wallet/wallet2.h"
+
+using namespace boost::filesystem;
+using namespace epee::file_io_utils;
+
+static constexpr const char WALLET_00fd416a_PRIMARY_ADDRESS[] =
+ "45p2SngJAPSJbqSiUvYfS3BfhEdxZmv8pDt25oW1LzxrZv9Uq6ARagiFViMGUE3gJk5VPWingCXVf1p2tyAy6SUeSHPhbve";
+
+TEST(wallet_storage, store_to_file2file)
+{
+ const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a";
+ const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_file2file";
+ const path target_wallet_file = unit_test::data_dir / "wallet_00fd416a_new_file2file";
+
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys"));
+
+ copy_file(source_wallet_file, interm_wallet_file, copy_option::overwrite_if_exists);
+ copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys", copy_option::overwrite_if_exists);
+
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys"));
+
+ 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"));
+
+ epee::wipeable_string password("beepbeep");
+
+ const auto files_are_expected = [&]()
+ {
+ EXPECT_FALSE(is_file_exist(interm_wallet_file.string()));
+ EXPECT_FALSE(is_file_exist(interm_wallet_file.string() + ".keys"));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+ };
+
+ {
+ tools::wallet2 w;
+ w.load(interm_wallet_file.string(), password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ w.store_to(target_wallet_file.string(), password);
+ files_are_expected();
+ }
+
+ files_are_expected();
+
+ {
+ tools::wallet2 w;
+ w.load(target_wallet_file.string(), password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ w.store_to("", "");
+ files_are_expected();
+ }
+
+ files_are_expected();
+}
+
+TEST(wallet_storage, store_to_mem2file)
+{
+ const path target_wallet_file = unit_test::data_dir / "wallet_mem2file";
+
+ 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"));
+
+ epee::wipeable_string password("beepbeep2");
+
+ {
+ tools::wallet2 w;
+ w.generate("", password);
+ w.store_to(target_wallet_file.string(), password);
+
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+ }
+
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+
+ {
+ tools::wallet2 w;
+ w.load(target_wallet_file.string(), password);
+
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+ }
+
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+}
+
+TEST(wallet_storage, change_password_same_file)
+{
+ const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a";
+ const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_change_password_same";
+
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys"));
+
+ copy_file(source_wallet_file, interm_wallet_file, copy_option::overwrite_if_exists);
+ copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys", copy_option::overwrite_if_exists);
+
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys"));
+
+ epee::wipeable_string old_password("beepbeep");
+ epee::wipeable_string new_password("meepmeep");
+
+ {
+ tools::wallet2 w;
+ w.load(interm_wallet_file.string(), old_password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ w.change_password(w.get_wallet_file(), old_password, new_password);
+ }
+
+ {
+ tools::wallet2 w;
+ w.load(interm_wallet_file.string(), new_password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ }
+
+ {
+ tools::wallet2 w;
+ EXPECT_THROW(w.load(interm_wallet_file.string(), old_password), tools::error::invalid_password);
+ }
+}
+
+TEST(wallet_storage, change_password_different_file)
+{
+ const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a";
+ const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_change_password_diff";
+ const path target_wallet_file = unit_test::data_dir / "wallet_00fd416a_new_change_password_diff";
+
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys"));
+
+ copy_file(source_wallet_file, interm_wallet_file, copy_option::overwrite_if_exists);
+ copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys", copy_option::overwrite_if_exists);
+
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string()));
+ ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys"));
+
+ 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"));
+
+ epee::wipeable_string old_password("beepbeep");
+ epee::wipeable_string new_password("meepmeep");
+
+ {
+ tools::wallet2 w;
+ w.load(interm_wallet_file.string(), old_password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ w.change_password(target_wallet_file.string(), old_password, new_password);
+ }
+
+ EXPECT_FALSE(is_file_exist(interm_wallet_file.string()));
+ EXPECT_FALSE(is_file_exist(interm_wallet_file.string() + ".keys"));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+
+ {
+ tools::wallet2 w;
+ w.load(target_wallet_file.string(), new_password);
+ const std::string primary_address = w.get_address_as_str();
+ EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address);
+ }
+}
+
+TEST(wallet_storage, change_password_in_memory)
+{
+ const epee::wipeable_string password1("monero");
+ const epee::wipeable_string password2("means money");
+ const epee::wipeable_string password_wrong("is traceable");
+
+ tools::wallet2 w;
+ w.generate("", password1);
+ const std::string primary_address_1 = w.get_address_as_str();
+ w.change_password("", password1, password2);
+ const std::string primary_address_2 = w.get_address_as_str();
+ EXPECT_EQ(primary_address_1, primary_address_2);
+
+ EXPECT_THROW(w.change_password("", password_wrong, password1), tools::error::invalid_password);
+}
+
+TEST(wallet_storage, change_password_mem2file)
+{
+ const path target_wallet_file = unit_test::data_dir / "wallet_change_password_mem2file";
+
+ 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 password1("https://safecurves.cr.yp.to/rigid.html");
+ const epee::wipeable_string password2(
+ "https://csrc.nist.gov/csrc/media/projects/crypto-standards-development-process/documents/dualec_in_x982_and_sp800-90.pdf");
+
+ std::string primary_address_1, primary_address_2;
+ {
+ tools::wallet2 w;
+ w.generate("", password1);
+ primary_address_1 = w.get_address_as_str();
+ w.change_password(target_wallet_file.string(), password1, password2);
+ }
+
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string()));
+ EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys"));
+
+ {
+ tools::wallet2 w;
+ w.load(target_wallet_file.string(), password2);
+ primary_address_2 = w.get_address_as_str();
+ }
+
+ EXPECT_EQ(primary_address_1, primary_address_2);
+}