diff options
Diffstat (limited to 'src')
42 files changed, 1270 insertions, 626 deletions
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex 2ed1d630f..2dc9ce3b3 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 70ae58cf0..63ec6ede9 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -240,6 +240,16 @@ namespace cryptonote ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb"); ADD_CHECKPOINT2(2182500, "0d22b5f81982eff21d094af9e821dc2007e6342069e3b1a37b15d97646353124", "0xead4a874083492"); ADD_CHECKPOINT2(2661600, "41c9060e8426012238e8a26da26fcb90797436896cc70886a894c2c560bcccf2", "0x2e0d87526ff161f"); + ADD_CHECKPOINT2(2677000, "1b9fee6246eeb176bd17d637bf252e9af54a4218675f01b4449cc0901867f9eb", "0x2f165bc1a5163ba"); + ADD_CHECKPOINT2(2706000, "d8eb144c5e1fe6b329ecc900ec95e7792fccff84175fb23a25ed59d7299a511c", "0x310f7d89372f705"); + ADD_CHECKPOINT2(2720000, "b19fb41dff15bd1016afbee9f8469f05aab715c9e5d1b974466a11fd58ecbb86", "0x3216b5851ddbb61"); + 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"); + ADD_CHECKPOINT2(3088000, "bddf8ca09110d33d6d497f13a113630c2b6af1c84d4f3a6f35cb1446f2604ade", "0x4aed3615c2f8c3e"); + ADD_CHECKPOINT2(3102800, "083f4a34f9490403b564286e7f13fd1ed45c52c86fa47195f151594e5bc87504", "0x4bbed52d4da5dfb"); return true; } diff --git a/src/common/timings.cc b/src/common/timings.cc index 612ac2cc6..aa719fa5e 100644 --- a/src/common/timings.cc +++ b/src/common/timings.cc @@ -12,10 +12,11 @@ TimingsDatabase::TimingsDatabase() { } -TimingsDatabase::TimingsDatabase(const std::string &filename): +TimingsDatabase::TimingsDatabase(const std::string &filename, const bool load_previous /*=false*/): filename(filename) { - load(); + if (load_previous) + load(); } TimingsDatabase::~TimingsDatabase() @@ -73,53 +74,96 @@ bool TimingsDatabase::load() { i.deciles.push_back(atoi(fields[idx++].c_str())); } - instances.insert(std::make_pair(name, i)); + instances.emplace_back(name, i); } fclose(f); return true; } -bool TimingsDatabase::save() +bool TimingsDatabase::save(const bool save_current_time /*=true*/) { - if (filename.empty()) + if (filename.empty() || instances.empty()) return true; - FILE *f = fopen(filename.c_str(), "w"); + FILE *f = fopen(filename.c_str(), "a"); // append if (!f) { MERROR("Failed to write to file " << filename << ": " << strerror(errno)); return false; } - for (const auto &i: instances) + + if (save_current_time) { - fprintf(f, "%s", i.first.c_str()); - fprintf(f, "\t%lu", (unsigned long)i.second.t); - fprintf(f, " %zu", i.second.npoints); - fprintf(f, " %f", i.second.min); - fprintf(f, " %f", i.second.max); - fprintf(f, " %f", i.second.mean); - fprintf(f, " %f", i.second.median); - fprintf(f, " %f", i.second.stddev); - fprintf(f, " %f", i.second.npskew); - for (uint64_t v: i.second.deciles) - fprintf(f, " %lu", (unsigned long)v); + // save current time in readable format (UTC) + std::time_t sys_time{std::time(nullptr)}; + std::tm *utc_time = std::gmtime(&sys_time); //GMT is equivalent to UTC + + // format: year-month-day : hour:minute:second + std::string current_time{}; + if (utc_time && sys_time != (std::time_t)(-1)) + { + char timeString[22]; //length = std::size("yyyy-mm-dd : hh:mm:ss") (constexpr std::size is C++17) + std::strftime(timeString, 22, "%F : %T", utc_time); + current_time += timeString; + } + else + { + current_time += "TIME_ERROR_"; + } + fputc('\n', f); // add an extra line before each 'print time' + fprintf(f, "%s", current_time.c_str()); fputc('\n', f); } + + for (const auto &i: instances) + { + fprintf(f, "%s,", i.first.c_str()); + + if (i.second.npoints > 0) + { + fprintf(f, "%lu,", (unsigned long)i.second.t); + fprintf(f, "%zu,", i.second.npoints); + fprintf(f, "%f,", i.second.min); + fprintf(f, "%f,", i.second.max); + fprintf(f, "%f,", i.second.mean); + fprintf(f, "%f,", i.second.median); + fprintf(f, "%f,", i.second.stddev); + fprintf(f, "%f,", i.second.npskew); + for (uint64_t v: i.second.deciles) + fprintf(f, "%lu,", (unsigned long)v); + + // note: only add a new line if there are points; assume that 'no points' means i.first is a message meant to be + // prepended to the next save operation + fputc('\n', f); + } + } fclose(f); + + // after saving, clear so next save does not append the same stuff over again + instances.clear(); + return true; } -std::vector<TimingsDatabase::instance> TimingsDatabase::get(const char *name) const +const TimingsDatabase::instance* TimingsDatabase::get_most_recent(const char *name) const { - std::vector<instance> ret; - auto range = instances.equal_range(name); - for (auto i = range.first; i != range.second; ++i) - ret.push_back(i->second); - std::sort(ret.begin(), ret.end(), [](const instance &e0, const instance &e1){ return e0.t < e1.t; }); - return ret; + time_t latest_time = 0; + const TimingsDatabase::instance *instance_ptr = nullptr; + + for (const auto &i: instances) + { + if (i.first != name) + continue; + if (i.second.t < latest_time) + continue; + + latest_time = i.second.t; + instance_ptr = &i.second; + } + return instance_ptr; } void TimingsDatabase::add(const char *name, const instance &i) { - instances.insert(std::make_pair(name, i)); + instances.emplace_back(name, i); } diff --git a/src/common/timings.h b/src/common/timings.h index fb905611f..6e7ff5659 100644 --- a/src/common/timings.h +++ b/src/common/timings.h @@ -2,8 +2,8 @@ #include <stdint.h> #include <string> +#include <utility> #include <vector> -#include <map> class TimingsDatabase { @@ -18,17 +18,17 @@ public: public: TimingsDatabase(); - TimingsDatabase(const std::string &filename); + TimingsDatabase(const std::string &filename, const bool load_previous = false); ~TimingsDatabase(); - std::vector<instance> get(const char *name) const; + const instance* get_most_recent(const char *name) const; void add(const char *name, const instance &data); + bool save(const bool print_current_time = true); private: bool load(); - bool save(); private: std::string filename; - std::multimap<std::string, instance> instances; + std::vector<std::pair<std::string, instance>> instances; }; diff --git a/src/common/util.cpp b/src/common/util.cpp index b4f3360ef..05ddfc359 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -114,6 +114,24 @@ static int flock_exnb(int fd) namespace tools { + + void copy_file(const std::string& from, const std::string& to) + { + using boost::filesystem::path; + #if BOOST_VERSION < 107400 + // Remove this preprocessor if/else when we are bumping the boost version. + boost::filesystem::copy_file( + path(from), + path(to), + boost::filesystem::copy_option::overwrite_if_exists); + #else + boost::filesystem::copy_file( + path(from), + path(to), + boost::filesystem::copy_options::overwrite_existing); + #endif + } + std::function<void(int)> signal_handler::m_handler; private_file::private_file() noexcept : m_handle(), m_filename() {} @@ -121,7 +139,7 @@ namespace tools private_file::private_file(std::FILE* handle, std::string&& filename) noexcept : m_handle(handle), m_filename(std::move(filename)) {} - private_file private_file::create(std::string name) + private_file private_file::create(std::string name, uint32_t extra_flags) { #ifdef WIN32 struct close_handle @@ -174,7 +192,7 @@ namespace tools name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, std::addressof(attributes), - CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE), + CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | extra_flags), nullptr ) }; @@ -193,7 +211,7 @@ namespace tools } } #else - const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR); + const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT | extra_flags), S_IRUSR); if (0 <= fdr) { struct stat rstats = {}; @@ -224,6 +242,23 @@ namespace tools return {}; } + private_file private_file::drop_and_recreate(std::string filename) + { + if (epee::file_io_utils::is_file_exist(filename)) { + boost::system::error_code ec{}; + boost::filesystem::remove(filename, ec); + if (ec) { + MERROR("Failed to remove " << filename << ": " << ec.message()); + return {}; + } + } +#ifdef WIN32 + return create(filename); +#else + return create(filename, O_EXCL); +#endif + } + private_file::~private_file() noexcept { try diff --git a/src/common/util.h b/src/common/util.h index 05748e020..ef591fc12 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -67,6 +67,8 @@ namespace tools } }; + void copy_file(const std::string& from, const std::string& to); + //! A file restricted to process owner AND process. Deletes file on destruction. class private_file { std::unique_ptr<std::FILE, close_file> m_handle; @@ -80,7 +82,11 @@ namespace tools /*! \return File only readable by owner and only used by this process OR `private_file{}` on error. */ - static private_file create(std::string filename); + static private_file create(std::string filename, uint32_t extra_flags = 0); + + /*! \return Drop and create file only readable by owner and only used + by this process OR `private_file{}` on error. */ + static private_file drop_and_recreate(std::string filename); private_file(private_file&&) = default; private_file& operator=(private_file&&) = default; diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 9ac376780..2559c754e 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -29,6 +29,7 @@ set(crypto_sources aesb.c blake256.c + blake2b.c chacha.c crypto-ops-data.c crypto-ops.c diff --git a/src/crypto/blake2b.c b/src/crypto/blake2b.c new file mode 100644 index 000000000..7e8be7890 --- /dev/null +++ b/src/crypto/blake2b.c @@ -0,0 +1,563 @@ +/* +Copyright (c) 2018-2019, tevador <tevador@gmail.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * 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. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#include "blake2b.h" + +#include "memwipe.h" + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +/// BEGIN: endian.h + +#if defined(_MSC_VER) +#define FORCE_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define FORCE_INLINE __inline__ +#else +#define FORCE_INLINE +#endif + + /* Argon2 Team - Begin Code */ + /* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. + */ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif + /* Argon2 Team - End Code */ + +static FORCE_INLINE uint32_t load32(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static FORCE_INLINE uint64_t load64_native(const void *src) { + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +} + +static FORCE_INLINE uint64_t load64(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + return load64_native(src); +#else + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + w |= (uint64_t)(*p++) << 48; + w |= (uint64_t)(*p++) << 56; + return w; +#endif +} + +static FORCE_INLINE void store32(void *dst, uint32_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static FORCE_INLINE void store64_native(void *dst, uint64_t w) { + memcpy(dst, &w, sizeof w); +} + +static FORCE_INLINE void store64(void *dst, uint64_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + store64_native(dst, w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +/// END: endian.h + +/// BEGIN: blake2-impl.h + +static FORCE_INLINE uint64_t load48(const void *src) { + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static FORCE_INLINE void store48(void *dst, uint64_t w) { + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +} + +static FORCE_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static FORCE_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +/// END: blake2-impl.h + +void clear_internal_memory(void *mem, const size_t length) { + memwipe(mem, length); +} + +/// BEGIN: blake2b.c + +static const uint64_t blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179) }; + +static const unsigned int blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static FORCE_INLINE void blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (uint64_t)-1; +} + +static FORCE_INLINE void blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (uint64_t)-1; +} + +static FORCE_INLINE void blake2b_increment_counter(blake2b_state *S, + uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static FORCE_INLINE void blake2b_invalidate_state(blake2b_state *S) { + clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static FORCE_INLINE void blake2b_init0(blake2b_state *S) { + memset(S, 0, sizeof(*S)); + memcpy(S->h, blake2b_IV, sizeof(S->h)); +} + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const unsigned char *p = (const unsigned char *)P; + unsigned int i; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_init(blake2b_state *S, size_t outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + return blake2b_init_param(S, &P); +} + +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = (uint8_t)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static void blake2b_compress(blake2b_state *S, const uint8_t *block) { + uint64_t m[16]; + uint64_t v[16]; + unsigned int i, r; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((void)0, 0) + + for (r = 0; r < 12; ++r) { + ROUND(r); + } + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { + const uint8_t *pin = (const uint8_t *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + memcpy(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* Avoid buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + memcpy(&S->buf[S->buflen], pin, inlen); + S->buflen += (unsigned int)inlen; + return 0; +} + +int blake2b_final(blake2b_state *S, void *out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = { 0 }; + unsigned int i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + memcpy(out, buffer, S->outlen); + clear_internal_memory(buffer, sizeof(buffer)); + clear_internal_memory(S->buf, sizeof(S->buf)); + clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen) { + blake2b_state S; + int ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } + else { + if (blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = blake2b_final(&S, out, outlen); + +fail: + clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = { 0 }; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } + else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ + +/// END: blake2b.c diff --git a/src/crypto/blake2b.h b/src/crypto/blake2b.h new file mode 100644 index 000000000..ea00f381b --- /dev/null +++ b/src/crypto/blake2b.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2018-2019, tevador <tevador@gmail.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * 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. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +/// NOTE: implementation lifted out of randomx package + +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <limits.h> + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + +#pragma pack(push, 1) + typedef struct __blake2b_param { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint64_t node_offset; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + } blake2b_param; +#pragma pack(pop) + + typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + unsigned buflen; + unsigned outlen; + uint8_t last_node; + } blake2b_state; + + /* Ensure param structs have not been wrongly padded */ + /* Poor man's static_assert */ + enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) + }; + + // crypto namespace (fixes naming collisions with libsodium) +#define blake2b_init crypto_blake2b_init +#define blake2b_init_key crypto_blake2b_init_key +#define blake2b_init_param crypto_blake2b_init_param +#define blake2b_update crypto_blake2b_update +#define blake2b_final crypto_blake2b_final +#define blake2b crypto_blake2b +#define blake2b_long crypto_blake2b_long + + /* Streaming API */ + int blake2b_init(blake2b_state *S, size_t outlen); + int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen); + int blake2b_init_param(blake2b_state *S, const blake2b_param *P); + int blake2b_update(blake2b_state *S, const void *in, size_t inlen); + int blake2b_final(blake2b_state *S, void *out, size_t outlen); + + /* Simple API */ + int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + + /* Argon2 Team - Begin Code */ + int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); + /* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index 11d54ae9f..8dcd55187 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -59,6 +59,7 @@ namespace cryptonote bool m_fee_too_low; bool m_too_few_outputs; bool m_tx_extra_too_big; + bool m_nonzero_unlock_time; }; struct block_verification_context diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3e1704048..3a7575119 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3691,18 +3691,16 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const } //------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version) +uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight) { - const uint64_t min_block_weight = get_min_block_weight(version); + constexpr uint64_t min_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; if (median_block_weight < min_block_weight) median_block_weight = min_block_weight; uint64_t hi, lo; - if (version >= HF_VERSION_PER_BYTE_FEE) { lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi); div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL); - if (version >= HF_VERSION_2021_SCALING) { // min_fee_per_byte = round_up( 0.95 * block_reward * ref_weight / (fee_median^2) ) // note: since hardfork HF_VERSION_2021_SCALING, fee_median (a.k.a. median_block_weight) equals effective long term median @@ -3711,29 +3709,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b lo -= lo / 20; return lo == 0 ? 1 : lo; } - else - { - // min_fee_per_byte = 0.2 * block_reward * ref_weight / (min_penalty_free_zone * fee_median) - div128_64(hi, lo, min_block_weight, &hi, &lo, NULL, NULL); - assert(hi == 0); - lo /= 5; - return lo; - } } - - const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; - - uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight); - lo = mul128(unscaled_fee_base, block_reward, &hi); - div128_64(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD, &hi, &lo, NULL, NULL); - assert(hi == 0); - - // quantize fee up to 8 decimals - uint64_t mask = get_fee_quantization_mask(); - uint64_t qlo = (lo + mask - 1) / mask * mask; - MDEBUG("lo " << print_money(lo) << ", qlo " << print_money(qlo) << ", mask " << mask); - - return qlo; } //------------------------------------------------------------------ @@ -3744,7 +3720,6 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const uint64_t median = 0; uint64_t already_generated_coins = 0; uint64_t base_reward = 0; - if (version >= HF_VERSION_DYNAMIC_FEE) { median = m_current_block_cumul_weight_limit / 2; const uint64_t blockchain_height = m_db->height(); @@ -3754,33 +3729,15 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const } uint64_t needed_fee; - if (version >= HF_VERSION_PER_BYTE_FEE) { - const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median, version); + const uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, + std::min<uint64_t>(median, m_long_term_effective_median_block_weight)); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals const uint64_t mask = get_fee_quantization_mask(); needed_fee = (needed_fee + mask - 1) / mask * mask; } - else - { - uint64_t fee_per_kb; - if (version < HF_VERSION_DYNAMIC_FEE) - { - fee_per_kb = FEE_PER_KB; - } - else - { - fee_per_kb = get_dynamic_base_fee(base_reward, median, version); - } - MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee"); - - needed_fee = tx_weight / 1024; - needed_fee += (tx_weight % 1024) ? 1 : 0; - needed_fee *= fee_per_kb; - } if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow { @@ -3791,7 +3748,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const } //------------------------------------------------------------------ -void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, uint64_t base_reward, uint64_t Mnw, uint64_t Mlw, std::vector<uint64_t> &fees) const +void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t base_reward, uint64_t Mnw, + uint64_t Mlw, std::vector<uint64_t> &fees) { // variable names and calculations as per https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf // from (earlier than) this fork, the base fee is per byte @@ -3863,55 +3821,8 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block base_reward = BLOCK_REWARD_OVERESTIMATE; } - get_dynamic_base_fee_estimate_2021_scaling(grace_blocks, base_reward, Mnw, Mlw_penalty_free_zone_for_wallet, fees); -} - -//------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const -{ - const uint8_t version = get_current_hard_fork_version(); - const uint64_t db_height = m_db->height(); - - if (version < HF_VERSION_DYNAMIC_FEE) - return FEE_PER_KB; - - if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) - grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; - - if (version >= HF_VERSION_2021_SCALING) - { - std::vector<uint64_t> fees; - get_dynamic_base_fee_estimate_2021_scaling(grace_blocks, fees); - return fees[0]; - } - - const uint64_t min_block_weight = get_min_block_weight(version); - std::vector<uint64_t> weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); - weights.reserve(grace_blocks); - for (size_t i = 0; i < grace_blocks; ++i) - weights.push_back(min_block_weight); - - uint64_t median = epee::misc_utils::median(weights); - if(median <= min_block_weight) - median = min_block_weight; - - uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0; - uint64_t base_reward; - if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version)) - { - MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound"); - base_reward = BLOCK_REWARD_OVERESTIMATE; - } - - const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - const uint64_t use_median_value = use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median; - const uint64_t fee = get_dynamic_base_fee(base_reward, use_median_value, version); - const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; - MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); - return fee; + get_dynamic_base_fee_estimate_2021_scaling(base_reward, Mnw, Mlw_penalty_free_zone_for_wallet, fees); } - //------------------------------------------------------------------ // This function checks to see if a tx is unlocked. unlock_time is either // a block index or a unix time. @@ -5551,7 +5462,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "e9371004b9f6be59921b27bc81e28b4715845ade1c6d16891d5c455f72e21365"; +static const char expected_block_hashes_hash[] = "0046a0019beb6e697e27d834d6127851425f7ee09bfb8e9f8df7b1420131aca8"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index be2848d09..2249d8cb2 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -638,27 +638,24 @@ namespace cryptonote * * @param block_reward the current block reward * @param median_block_weight the median block weight in the past window - * @param version hard fork version for rules and constants to use * * @return the fee */ - static uint64_t get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version); + static uint64_t get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight); /** - * @brief get dynamic per kB or byte fee estimate for the next few blocks + * @brief get four levels of dynamic per byte fee estimate for the next few blocks * * The dynamic fee is based on the block weight in a past window, and - * the current block reward. It is expressed by kB before v8, and - * per byte from v8. - * This function calculates an estimate for a dynamic fee which will be - * valid for the next grace_blocks - * - * @param grace_blocks number of blocks we want the fee to be valid for + * the current block reward. It is expressed per byte, and is based on + * https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf * - * @return the fee estimate + * @param Mnw min(Msw, 50*Mlw) + * @param Mlw The median over the last 99990 and future 10 blocks of max(min(Mbw, 2*Ml), Zm, Ml/2) + * @param[out] fees fee estimate levels [Fl, Fn, Fm, Fh] */ - uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const; - void get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, uint64_t base_reward, uint64_t Mnw, uint64_t Mlw, std::vector<uint64_t> &fees) const; + static void get_dynamic_base_fee_estimate_2021_scaling(uint64_t base_reward, uint64_t Mnw, + uint64_t Mlw, std::vector<uint64_t> &fees); /** * @brief get four levels of dynamic per byte fee estimate for the next few blocks @@ -670,8 +667,7 @@ namespace cryptonote * valid for the next grace_blocks * * @param grace_blocks number of blocks we want the fee to be valid for - * - * @return the fee estimates (4 of them) + * @param[out] fees fee estimate levels [Fl, Fn, Fm, Fh] */ void get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees) const; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 60b399bb5..d5bb8fee7 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -203,7 +203,7 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool shuffle_outs, bool use_view_tags) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool shuffle_outs, bool use_view_tags) { hw::device &hwdev = sender_account_keys.get_device(); @@ -218,7 +218,7 @@ namespace cryptonote amount_keys.clear(); tx.version = rct ? 2 : 1; - tx.unlock_time = unlock_time; + tx.unlock_time = 0; tx.extra = extra; crypto::public_key txkey_pub; @@ -606,7 +606,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags) { hw::device &hwdev = sender_account_keys.get_device(); hwdev.open_tx(tx_key); @@ -627,7 +627,7 @@ namespace cryptonote } bool shuffle_outs = true; - bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags); + bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags); hwdev.close_tx(); return r; } catch(...) { @@ -636,14 +636,14 @@ namespace cryptonote } } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx) { std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; std::vector<tx_destination_entry> destinations_copy = destinations; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 2ee93fd8f..8f1073766 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -118,9 +118,9 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr); - bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false); + bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false); bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 4f0501c1d..96b229177 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -235,6 +235,15 @@ namespace cryptonote return false; } + if (!kept_by_block && tx.unlock_time) + { + LOG_PRINT_L1("transaction unlock time is not zero: " << tx.unlock_time); + tvc.m_verifivation_failed = true; + tvc.m_nonzero_unlock_time = true; + tvc.m_no_drop_offense = true; + return false; + } + // if the transaction came from a block popped from the chain, // don't check if we have its key images as spent. // TODO: Investigate why not? diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 5b420ec3f..1c3a2901c 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -741,9 +741,14 @@ namespace levin notify::status notify::get_status() const noexcept { if (!zone_) - return {false, false}; - - return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count}; + return {false, false, false}; + + // `connection_count` is only set when `!noise.empty()`. + const std::size_t connection_count = zone_->connection_count; + bool has_outgoing = connection_count; + if (zone_->noise.empty()) + has_outgoing = zone_->p2p->get_out_connections_count(); + return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= connection_count, has_outgoing}; } void notify::new_out_connection() diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 52d36efb0..816170841 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -75,7 +75,8 @@ namespace levin struct status { bool has_noise; - bool connections_filled; + bool connections_filled; //!< True when has zone has `CRYPTONOTE_NOISE_CHANNELS` outgoing noise channels + bool has_outgoing; //!< True when zone has outgoing connections }; //! Construct an instance that cannot notify. diff --git a/src/device_trezor/README.md b/src/device_trezor/README.md index ce08c0009..dede853ae 100644 --- a/src/device_trezor/README.md +++ b/src/device_trezor/README.md @@ -15,19 +15,29 @@ Please, refer to [monero readme](https://github.com/trezor/trezor-firmware/blob/ ## Dependencies -Trezor uses [Protobuf](https://protobuf.dev/) library. As Monero is compiled with C++14, the newest Protobuf library version cannot be compiled because it requires C++17 (through its dependency Abseil library). -This can result in a compilation failure. +Trezor uses [Protobuf](https://protobuf.dev/) library. Monero is now compiled with C++ 17 by default. +Protobuf v21 is tested, older versions are not guaranteed to work. Note that Protobuf v23+ requires C++ 17. -Protobuf v21 is the latest compatible protobuf version. +If you are getting Trezor compilation errors, it may be caused by abseil (protobuf dependency) not being compiled with C++17. +To fix this try installing protobuf from sources: -If you want to compile Monero with Trezor support, please make sure the Protobuf v21 is installed. +```shell +git clone --recursive git@github.com:protocolbuffers/protobuf.git +cd protobuf +cmake -DABSL_PROPAGATE_CXX_STD=TRUE -DCMAKE_CXX_STANDARD=17 -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_BUILD_TESTS=OFF . +cmake --build . +sudo make install +``` + +If Monero is compiled with C++14, Protobuf v21 is the latest compatible protobuf version for C++ 14. +If you want to compile Monero with Trezor support with C++14, please make sure the Protobuf v21 is installed. More about this limitation: [PR #8752](https://github.com/monero-project/monero/pull/8752), [1](https://github.com/monero-project/monero/pull/8752#discussion_r1246174755), [2](https://github.com/monero-project/monero/pull/8752#discussion_r1246480393) ### OSX -To build with installed, but not linked protobuf: +To build with installed, but not linked Protobuf v21: ```bash CMAKE_PREFIX_PATH=$(find /opt/homebrew/Cellar/protobuf@21 -maxdepth 1 -type d -name "21.*" -print -quit) \ @@ -53,7 +63,7 @@ pacman --noconfirm -U mingw-w64-x86_64-protobuf-c-1.4.1-1-any.pkg.tar.zst mingw- ### Other systems -- install protobufv21 +- install Protobuf v21 - point `CMAKE_PREFIX_PATH` environment variable to Protobuf v21 installation. ## Troubleshooting diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 48cf818ef..fd82c66af 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -121,16 +121,15 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str } // exchange keys until the wallets are done - bool ready{false}; - wallets[0]->multisig(&ready); - while (!ready) + multisig::multisig_account_status ms_status{wallets[0]->get_multisig_status()}; + while (!ms_status.is_ready) { for (size_t n = 0; n < total; ++n) { kex_msgs_intermediate[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), kex_msgs_intermediate); } - wallets[0]->multisig(&ready); + ms_status = wallets[0]->get_multisig_status(); } std::string address = wallets[0]->get_account().get_public_address_str(wallets[0]->nettype()); diff --git a/src/multisig/multisig_account.h b/src/multisig/multisig_account.h index 0d832f243..2ea8d0133 100644 --- a/src/multisig/multisig_account.h +++ b/src/multisig/multisig_account.h @@ -40,6 +40,19 @@ namespace multisig { + struct multisig_account_status + { + // is the multisig account active/initialized? + bool multisig_is_active{false}; + // has the multisig account completed the main key exchange rounds? + bool kex_is_done{false}; + // is the multisig account ready to use? + bool is_ready{false}; + // multisig is: M-of-N + std::uint32_t threshold{0}; // M + std::uint32_t total{0}; // N + }; + /** * multisig account: * diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp index 8643a8af4..2653a70dd 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -820,7 +820,6 @@ tx_builder_ringct_t::~tx_builder_ringct_t() bool tx_builder_ringct_t::init( const cryptonote::account_keys& account_keys, const std::vector<std::uint8_t>& extra, - const std::uint64_t unlock_time, const std::uint32_t subaddr_account, const std::set<std::uint32_t>& subaddr_minor_indices, std::vector<cryptonote::tx_source_entry>& sources, @@ -854,7 +853,7 @@ bool tx_builder_ringct_t::init( // misc. fields unsigned_tx.version = 2; //rct = 2 - unsigned_tx.unlock_time = unlock_time; + unsigned_tx.unlock_time = 0; // sort inputs sort_sources(sources); diff --git a/src/multisig/multisig_tx_builder_ringct.h b/src/multisig/multisig_tx_builder_ringct.h index f1bd24e73..a1b72b177 100644 --- a/src/multisig/multisig_tx_builder_ringct.h +++ b/src/multisig/multisig_tx_builder_ringct.h @@ -71,7 +71,6 @@ public: bool init( const cryptonote::account_keys& account_keys, const std::vector<std::uint8_t>& extra, - const std::uint64_t unlock_time, const std::uint32_t subaddr_account, const std::set<std::uint32_t>& subaddr_minor_indices, std::vector<cryptonote::tx_source_entry>& sources, diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 815c1b354..60f6728b3 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -2296,11 +2296,12 @@ namespace nodetool if (enet::zone::tor < network->first) break; // unknown network - if (network->second.m_connect) + const auto status = network->second.m_notifier.get_status(); + if (network->second.m_connect && status.has_outgoing) return send(*network); } - // configuration should not allow this scenario + MWARNING("Unable to send " << txs.size() << " transaction(s): anonymity networks had no outgoing connections"); return enet::zone::invalid; } //----------------------------------------------------------------------------------- diff --git a/src/p2p/net_peerlist.cpp b/src/p2p/net_peerlist.cpp index 11cf235a5..78cf13785 100644 --- a/src/p2p/net_peerlist.cpp +++ b/src/p2p/net_peerlist.cpp @@ -42,6 +42,7 @@ #include <boost/serialization/version.hpp> #include "net_peerlist_boost_serialization.h" +#include "common/util.h" namespace nodetool @@ -200,7 +201,7 @@ namespace nodetool if (!out) { // if failed, try reading in unportable mode - boost::filesystem::copy_file(path, path + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + tools::copy_file(path, path + ".unportable"); src_file.close(); src_file.open( path , std::ios_base::binary | std::ios_base::in); if(src_file.fail()) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 299798e17..dea95ace0 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -771,7 +771,7 @@ namespace std BLOB_SERIALIZER(rct::key); BLOB_SERIALIZER(rct::key64); BLOB_SERIALIZER(rct::ctkey); -BLOB_SERIALIZER(rct::multisig_kLRki); +BLOB_SERIALIZER_FORCED(rct::multisig_kLRki); BLOB_SERIALIZER(rct::boroSig); VARIANT_TAG(debug_archive, rct::key, "rct::key"); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9a0b02f70..16d66e79d 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1378,6 +1378,8 @@ namespace cryptonote add_reason(reason, "too few outputs"); if ((res.tx_extra_too_big = tvc.m_tx_extra_too_big)) add_reason(reason, "tx-extra too big"); + if ((res.nonzero_unlock_time = tvc.m_nonzero_unlock_time)) + add_reason(reason, "tx unlock time is not zero"); const std::string punctuation = reason.empty() ? "" : ": "; if (tvc.m_verifivation_failed) { @@ -3019,16 +3021,10 @@ namespace cryptonote CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE); - const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version(); - if (version >= HF_VERSION_2021_SCALING) { m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees); res.fee = res.fees[0]; } - else - { - res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks); - } res.quantization_mask = Blockchain::get_fee_quantization_mask(); res.status = CORE_RPC_STATUS_OK; return true; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 2a0a6201d..c634b8957 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 13 +#define CORE_RPC_VERSION_MINOR 14 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -640,6 +640,7 @@ namespace cryptonote bool too_few_outputs; bool sanity_check_failed; bool tx_extra_too_big; + bool nonzero_unlock_time; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_access_response_base) @@ -655,6 +656,7 @@ namespace cryptonote KV_SERIALIZE(too_few_outputs) KV_SERIALIZE(sanity_check_failed) KV_SERIALIZE(tx_extra_too_big) + KV_SERIALIZE(nonzero_unlock_time) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 52067bd4d..4712de134 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -422,6 +422,16 @@ namespace rpc if (!res.error_details.empty()) res.error_details += " and "; res.error_details += "too few outputs"; } + if (tvc.m_tx_extra_too_big) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details += "tx_extra too long"; + } + if (tvc.m_nonzero_unlock_time) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details += "non-zero unlock time"; + } if (res.error_details.empty()) { res.error_details = "an unknown issue was found with the transaction"; @@ -831,14 +841,11 @@ namespace rpc void DaemonHandler::handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res) { res.hard_fork_version = m_core.get_blockchain_storage().get_current_hard_fork_version(); - res.estimated_base_fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks); - if (res.hard_fork_version < HF_VERSION_PER_BYTE_FEE) - { - res.size_scale = 1024; // per KiB fee - res.fee_mask = 1; - } - else + std::vector<uint64_t> fees; + m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.num_grace_blocks, fees); + res.estimated_base_fee = fees[0]; + { res.size_scale = 1; // per byte fee res.fee_mask = Blockchain::get_fee_quantization_mask(); diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 896d00583..2da26abe7 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -81,7 +81,7 @@ BLOB_SERIALIZER(crypto::chacha_iv); BLOB_SERIALIZER(crypto::hash); BLOB_SERIALIZER(crypto::hash8); BLOB_SERIALIZER(crypto::public_key); -BLOB_SERIALIZER(crypto::secret_key); +BLOB_SERIALIZER_FORCED(crypto::secret_key); BLOB_SERIALIZER(crypto::key_derivation); BLOB_SERIALIZER(crypto::key_image); BLOB_SERIALIZER(crypto::signature); diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index d1f97e324..33edf3f62 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -50,13 +50,16 @@ #include <boost/type_traits/integral_constant.hpp> #include <boost/mpl/bool.hpp> -/*! \struct is_blob_type +/*! \struct is_blob_type / is_blob_forced * - * \brief a descriptor for dispatching serialize + * \brief descriptors for dispatching serialize: whether to take byte-wise copy/store to type */ template <class T> struct is_blob_type { typedef boost::false_type type; }; +template <class T> +struct is_blob_forced: std::false_type {}; + /*! \fn do_serialize(Archive &ar, T &v) * * \brief main function for dispatching serialization for a given pair of archive and value types @@ -68,6 +71,8 @@ struct is_blob_type { typedef boost::false_type type; }; template <class Archive, class T> inline std::enable_if_t<is_blob_type<T>::type::value, bool> do_serialize(Archive &ar, T &v) { + static_assert(std::is_trivially_copyable<T>() || is_blob_forced<T>(), + "sanity check: types that can't be trivially copied shouldn't be using the blob serializer"); ar.serialize_blob(&v, sizeof(v)); return true; } @@ -94,6 +99,9 @@ inline bool do_serialize(Archive &ar, bool &v) /*! \macro BLOB_SERIALIZER * * \brief makes the type have a blob serializer trait defined + * + * In case your type is not a good candidate to be blob serialized, a static assertion may be thrown + * at compile-time. */ #define BLOB_SERIALIZER(T) \ template<> \ @@ -101,6 +109,20 @@ inline bool do_serialize(Archive &ar, bool &v) typedef boost::true_type type; \ } +/*! \macro BLOB_SERIALIZER_FORCED + * + * \brief makes the type have a blob serializer trait defined, even if it isn't trivially copyable + * + * Caution: do NOT use this macro for your type <T>, unless you are absolutely sure that moving raw + * bytes in/out of this type will not cause undefined behavior. Any types with managed memory + * (e.g. vector, string, etc) will segfault and/or cause memory errors if you use this macro with + * that type. + */ +#define BLOB_SERIALIZER_FORCED(T) \ + BLOB_SERIALIZER(T); \ + template<> \ + struct is_blob_forced<T>: std::true_type {}; + /*! \macro VARIANT_TAG * * \brief Adds the tag \tag to the \a Archive of \a Type diff --git a/src/serialization/tuple.h b/src/serialization/tuple.h index d9592bc96..b1ef94097 100644 --- a/src/serialization/tuple.h +++ b/src/serialization/tuple.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include <memory> +#include <tuple> #include "serialization.h" namespace serialization @@ -51,119 +51,71 @@ namespace serialization } } -template <template <bool> class Archive, class E0, class E1, class E2> -inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2>& p) +template <size_t I, bool BackwardsCompat, bool W, template <bool> class Archive, typename... Ts> +bool do_serialize_tuple_nth(Archive<W>& ar, std::tuple<Ts...>& v) { - size_t cnt; - ar.begin_array(cnt); - if (!ar.good()) - return false; - if (cnt != 3) - return false; + static constexpr const size_t tuple_size = std::tuple_size<std::tuple<Ts...>>(); + static_assert(I <= tuple_size, "bad call"); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; + if constexpr (I == 0) + { + // If BackwardsCompat is true, we serialize the size of 3-tuples and 4-tuples + if constexpr (BackwardsCompat && (tuple_size == 3 || tuple_size == 4)) + { + size_t cnt = tuple_size; + ar.begin_array(cnt); + if (cnt != tuple_size) + return false; + } + else + { + ar.begin_array(); + } + } + else if constexpr (I < tuple_size) + { + ar.delimit_array(); + } - ar.end_array(); - return true; + if constexpr (I == tuple_size) + { + ar.end_array(); + return ar.good(); + } + else + { + if (!::serialization::detail::serialize_tuple_element(ar, std::get<I>(v)) + || !ar.good()) + return false; + + return do_serialize_tuple_nth<I + 1, BackwardsCompat>(ar, v); + } } -template <template <bool> class Archive, class E0, class E1, class E2> -inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2>& p) +template <bool BackwardsCompat, bool W, template <bool> class Archive, typename... Ts> +bool do_serialize_tuple(Archive<W>& ar, std::tuple<Ts...>& v) { - ar.begin_array(3); - if (!ar.good()) - return false; - if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.end_array(); - return true; + return do_serialize_tuple_nth<0, BackwardsCompat>(ar, v); } -template <template <bool> class Archive, class E0, class E1, class E2, class E3> -inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2,E3>& p) +template <bool W, template <bool> class Archive, typename... Ts> +bool do_serialize(Archive<W>& ar, std::tuple<Ts...>& v) { - size_t cnt; - ar.begin_array(cnt); - if (!ar.good()) - return false; - if (cnt != 4) - return false; + return do_serialize_tuple<true>(ar, v); +} - if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) - return false; - if (!ar.good()) - return false; +#define TUPLE_COMPACT_FIELDS(v) \ + do { \ + if (!do_serialize_tuple<false>(ar, v) || !ar.good()) \ + return false; \ + } while (0); - ar.end_array(); - return true; -} +#define TUPLE_COMPACT_FIELD_N(t, v) \ + do { \ + ar.tag(t); \ + TUPLE_COMPACT_FIELDS(v); \ + } while (0); -template <template <bool> class Archive, class E0, class E1, class E2, class E3> -inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2,E3>& p) -{ - ar.begin_array(4); - if (!ar.good()) - return false; - if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) - return false; - if (!ar.good()) - return false; - ar.end_array(); - return true; -} +#define TUPLE_COMPACT_FIELD(f) TUPLE_COMPACT_FIELD_N(#f, f) +#define TUPLE_COMPACT_FIELD_F(f) TUPLE_COMPACT_FIELD_N(#f, v.f) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5a9d790cb..2f08ac025 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -148,11 +148,6 @@ typedef cryptonote::simple_wallet sw; } \ } while(0) -enum TransferType { - Transfer, - TransferLocked, -}; - static std::string get_human_readable_timespan(std::chrono::seconds seconds); static std::string get_human_readable_timespan(uint64_t seconds); @@ -189,8 +184,6 @@ namespace const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]"); const char* USAGE_PAYMENT_ID("payment_id"); const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [subtractfeefrom=<D0>[,<D1>,all,...]] [<payment_id>]"); - const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]"); - const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_ACCOUNT("sweep_account <account> [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]"); @@ -819,7 +812,6 @@ bool simple_wallet::print_seed(bool encrypted) { bool success = false; epee::wipeable_string seed; - bool ready, multisig; if (m_wallet->key_on_device()) { @@ -832,10 +824,10 @@ bool simple_wallet::print_seed(bool encrypted) return true; } - multisig = m_wallet->multisig(&ready); - if (multisig) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (ms_status.multisig_is_active) { - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("wallet is multisig but not yet finalized"); return true; @@ -844,7 +836,7 @@ bool simple_wallet::print_seed(bool encrypted) SCOPED_WALLET_UNLOCK(); - if (!multisig && !m_wallet->is_deterministic()) + if (!ms_status.multisig_is_active && !m_wallet->is_deterministic()) { fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); return true; @@ -859,7 +851,7 @@ bool simple_wallet::print_seed(bool encrypted) seed_pass = pwd_container->password(); } - if (multisig) + if (ms_status.multisig_is_active) success = m_wallet->get_multisig_seed(seed, seed_pass); else if (m_wallet->is_deterministic()) success = m_wallet->get_seed(seed, seed_pass); @@ -898,7 +890,7 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("wallet is multisig and has no seed"); return true; @@ -1044,7 +1036,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args, fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("This wallet is already multisig"); return false; @@ -1091,7 +1083,7 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("This wallet is already multisig"); return false; @@ -1136,9 +1128,7 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo auto local_args = args; local_args.erase(local_args.begin()); std::string multisig_extra_info = m_wallet->make_multisig(orig_pwd_container->password(), local_args, threshold); - bool ready; - m_wallet->multisig(&ready); - if (!ready) + if (!m_wallet->get_multisig_status().is_ready) { success_msg_writer() << tr("Another step is needed"); success_msg_writer() << multisig_extra_info; @@ -1156,13 +1146,13 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo return false; } - uint32_t total; - if (!m_wallet->multisig(NULL, &threshold, &total)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("Error creating multisig: new wallet is not multisig"); return false; } - success_msg_writer() << std::to_string(threshold) << "/" << total << tr(" multisig address: ") + success_msg_writer() << std::to_string(ms_status.threshold) << "/" << ms_status.total << tr(" multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); return true; @@ -1188,18 +1178,18 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & const bool force_update_use_with_caution, const bool called_by_mms) { CHECK_MULTISIG_ENABLED(); - bool ready; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (!m_wallet->multisig(&ready)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This wallet is not multisig"); return false; } - if (ready) + if (ms_status.is_ready) { fail_msg_writer() << tr("This wallet is already finalized"); return false; @@ -1215,9 +1205,7 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & try { std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args, force_update_use_with_caution); - bool ready; - m_wallet->multisig(&ready); - if (!ready) + if (!m_wallet->get_multisig_status().is_ready) { message_writer() << tr("Another step is needed"); message_writer() << multisig_extra_info; @@ -1228,9 +1216,8 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & } return true; } else { - uint32_t threshold, total; - m_wallet->multisig(NULL, &threshold, &total); - success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total; + const multisig::multisig_account_status ms_status_new{m_wallet->get_multisig_status()}; + success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << ms_status_new.threshold << "/" << ms_status_new.total; success_msg_writer() << tr("Multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } } @@ -1253,18 +1240,18 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args) bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, bool called_by_mms) { CHECK_MULTISIG_ENABLED(); - bool ready; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (!m_wallet->multisig(&ready)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This wallet is not multisig"); return false; } - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); return false; @@ -1320,24 +1307,24 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args) bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, bool called_by_mms) { CHECK_MULTISIG_ENABLED(); - bool ready; - uint32_t threshold, total; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (!m_wallet->multisig(&ready, &threshold, &total)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This wallet is not multisig"); return false; } - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); return false; } - if (args.size() < threshold - 1) + if (args.size() + 1 < ms_status.threshold) { PRINT_USAGE(USAGE_IMPORT_MULTISIG_INFO); return false; @@ -1417,18 +1404,19 @@ bool simple_wallet::sign_multisig(const std::vector<std::string> &args) bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, bool called_by_mms) { CHECK_MULTISIG_ENABLED(); - bool ready; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()};\ + if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if(!m_wallet->multisig(&ready)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This is not a multisig wallet"); return false; } - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); return false; @@ -1502,9 +1490,7 @@ bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, boo if (txids.empty()) { - uint32_t threshold; - m_wallet->multisig(NULL, &threshold); - uint32_t signers_needed = threshold - signers - 1; + uint32_t signers_needed = ms_status.threshold - signers - 1; success_msg_writer(true) << tr("Transaction successfully signed to file ") << filename << ", " << signers_needed << " more signer(s) needed"; return true; @@ -1534,19 +1520,19 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args) bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, bool called_by_mms) { CHECK_MULTISIG_ENABLED(); - bool ready; - uint32_t threshold; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return false; } - if (!m_wallet->multisig(&ready, &threshold)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This is not a multisig wallet"); return false; } - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); return false; @@ -1584,10 +1570,10 @@ bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, b return false; } } - if (txs.m_signers.size() < threshold) + if (txs.m_signers.size() < ms_status.threshold) { fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures")) - % txs.m_signers.size() % (threshold - txs.m_signers.size())).str(); + % txs.m_signers.size() % (ms_status.threshold - txs.m_signers.size())).str(); return false; } @@ -1616,19 +1602,19 @@ bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, b bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args) { CHECK_MULTISIG_ENABLED(); - bool ready; - uint32_t threshold; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (!m_wallet->multisig(&ready, &threshold)) + if (!ms_status.multisig_is_active) { fail_msg_writer() << tr("This is not a multisig wallet"); return true; } - if (!ready) + if (!ms_status.is_ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); return true; @@ -1654,10 +1640,10 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args) fail_msg_writer() << tr("Failed to load multisig transaction from file"); return true; } - if (txs.m_signers.size() < threshold) + if (txs.m_signers.size() < ms_status.threshold) { fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures")) - % txs.m_signers.size() % (threshold - txs.m_signers.size())).str(); + % txs.m_signers.size() % (ms_status.threshold - txs.m_signers.size())).str(); return true; } @@ -3126,14 +3112,6 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1), tr(USAGE_TRANSFER), tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer <addr1> 3 <addr2> 0.5 <addr3> 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 XMR including fees, and addr1 & addr3 would receive 2.925 & 0.975 XMR, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations.")); - m_cmd_binder.set_handler("locked_transfer", - boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_transfer,_1), - tr(USAGE_LOCKED_TRANSFER), - tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); - m_cmd_binder.set_handler("locked_sweep_all", - boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1), - tr(USAGE_LOCKED_SWEEP_ALL), - tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); @@ -3838,7 +3816,7 @@ bool simple_wallet::ask_wallet_create_if_needed() bool ok = true; if (!m_restoring) { - message_writer() << tr("Looking for filename: ") << boost::filesystem::complete(wallet_path); + message_writer() << tr("Looking for filename: ") << boost::filesystem::absolute(wallet_path); message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path; confirm_creation = input_line("", true); if(std::cin.eof()) @@ -3870,7 +3848,7 @@ void simple_wallet::print_seed(const epee::wipeable_string &seed) { success_msg_writer(true) << "\n" << boost::format(tr("NOTE: the following %s can be used to recover access to your wallet. " "Write them down and store them somewhere safe and secure. Please do not store them in " - "your email or on file storage services outside of your immediate control.\n")) % (m_wallet->multisig() ? tr("string") : tr("25 words")); + "your email or on file storage services outside of your immediate control.\n")) % (m_wallet->get_multisig_status().multisig_is_active ? tr("string") : tr("25 words")); // don't log int space_index = 0; size_t len = seed.size(); @@ -4774,7 +4752,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr "your current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") ; - success_msg_writer() << tr("Filename: ") << boost::filesystem::complete(m_wallet->get_keys_file()); + success_msg_writer() << tr("Filename: ") << boost::filesystem::absolute(m_wallet->get_keys_file()); if (!two_random) { @@ -4922,14 +4900,14 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr const epee::wipeable_string &msig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true); m_wallet->generate(m_wallet_file, std::move(rc.second).password(), msig_keys, create_address_file); } - bool ready; - uint32_t threshold, total; - if (!m_wallet->multisig(&ready, &threshold, &total) || !ready) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active || !ms_status.is_ready) { fail_msg_writer() << tr("failed to generate new mutlisig wallet"); return {}; } - message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total + message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % ms_status.threshold % ms_status.total << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } catch (const std::exception& e) @@ -4973,12 +4951,11 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p m_wallet->callback(this); m_wallet->load(m_wallet_file, password); std::string prefix; - bool ready; - uint32_t threshold, total; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; if (m_wallet->watch_only()) prefix = tr("Opened watch-only wallet"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); + else if (ms_status.multisig_is_active) + prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % ms_status.threshold % ms_status.total % (ms_status.is_ready ? "" : " (not yet finalized)")).str(); else prefix = tr("Opened wallet"); message_writer(console_color_white, true) << @@ -5096,7 +5073,7 @@ bool simple_wallet::save(const std::vector<std::string> &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::save_watch_only(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("wallet is multisig and cannot save a watch-only version"); return true; @@ -6297,7 +6274,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector<std: return (this->*cmd)(args); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) +bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool called_by_mms) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" if (!try_connect_to_daemon()) @@ -6349,7 +6326,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri return false; } - const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1; + const size_t min_args = 1; if(local_args.size() < min_args) { fail_msg_writer() << tr("wrong number of arguments"); @@ -6374,26 +6351,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } } - uint64_t locked_blocks = 0; - if (transfer_type == TransferLocked) - { - try - { - locked_blocks = boost::lexical_cast<uint64_t>(local_args.back()); - } - catch (const std::exception &e) - { - fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back(); - return false; - } - if (locked_blocks > 1000000) - { - fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); - return false; - } - local_args.pop_back(); - } - // Parse subtractfeefrom destination list tools::wallet2::unique_index_container subtract_fee_from_outputs; bool subtract_fee_from_all = false; @@ -6523,28 +6480,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri try { // figure out what tx will be necessary - std::vector<tools::wallet2::pending_tx> ptx_vector; - uint64_t bc_height, unlock_block = 0; - std::string err; - switch (transfer_type) - { - case TransferLocked: - bc_height = get_daemon_blockchain_height(err); - if (!err.empty()) - { - fail_msg_writer() << tr("failed to get blockchain height: ") << err; - return false; - } - unlock_block = bc_height + locked_blocks; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; - default: - LOG_ERROR("Unknown transfer method, using default"); - /* FALLTHRU */ - case Transfer: - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); - break; - } + auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra, + m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); if (ptx_vector.empty()) { @@ -6654,11 +6591,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee); if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address")) % print_money(dust_not_in_fee); - if (transfer_type == TransferLocked) - { - float days = locked_blocks / 720.0f; - prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days; - } if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) return false; @@ -6676,7 +6608,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } // actually commit the transactions - if (m_wallet->multisig() && called_by_mms) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (ms_status.multisig_is_active && called_by_mms) { std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector); if (!ciphertext.empty()) @@ -6685,7 +6618,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to MMS"); } } - else if (m_wallet->multisig()) + else if (ms_status.multisig_is_active) { bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); if (!r) @@ -6762,29 +6695,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_) PRINT_USAGE(USAGE_TRANSFER); return true; } - transfer_main(Transfer, args_, false); - return true; -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) -{ - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_LOCKED_TRANSFER); - return true; - } - transfer_main(TransferLocked, args_, false); - return true; -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) -{ - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_LOCKED_SWEEP_ALL); - return true; - } - sweep_main(m_current_subaddress_account, 0, true, args_); + transfer_main(args_, false); return true; } //---------------------------------------------------------------------------------------------------- @@ -6839,7 +6750,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) } // actually commit the transactions - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { CHECK_MULTISIG_ENABLED(); bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); @@ -6896,7 +6807,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args_) +bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args_) { auto print_usage = [this, account, below]() { @@ -6976,41 +6887,6 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co return true; } - uint64_t unlock_block = 0; - if (locked) { - uint64_t locked_blocks = 0; - - if (local_args.size() < 2) { - fail_msg_writer() << tr("missing lockedblocks parameter"); - return true; - } - - try - { - locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]); - } - catch (const std::exception &e) - { - fail_msg_writer() << tr("bad locked_blocks parameter"); - return true; - } - if (locked_blocks > 1000000) - { - fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); - return true; - } - std::string err; - uint64_t bc_height = get_daemon_blockchain_height(err); - if (!err.empty()) - { - fail_msg_writer() << tr("failed to get blockchain height: ") << err; - return true; - } - unlock_block = bc_height + locked_blocks; - - local_args.erase(local_args.begin() + 1); - } - size_t outputs = 1; if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=") { @@ -7085,7 +6961,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra, account, subaddr_indices); if (ptx_vector.empty()) { @@ -7144,7 +7020,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co } // actually commit the transactions - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { CHECK_MULTISIG_ENABLED(); bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); @@ -7342,7 +7218,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra); if (ptx_vector.empty()) { @@ -7379,7 +7255,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) } // actually commit the transactions - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { CHECK_MULTISIG_ENABLED(); bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); @@ -7452,7 +7328,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { - sweep_main(m_current_subaddress_account, 0, false, args_); + sweep_main(m_current_subaddress_account, 0, args_); return true; } //---------------------------------------------------------------------------------------------------- @@ -7472,7 +7348,7 @@ bool simple_wallet::sweep_account(const std::vector<std::string> &args_) } local_args.erase(local_args.begin()); - sweep_main(account, 0, false, local_args); + sweep_main(account, 0, local_args); return true; } //---------------------------------------------------------------------------------------------------- @@ -7490,7 +7366,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - sweep_main(m_current_subaddress_account, below, false, std::vector<std::string>(++args_.begin(), args_.end())); + sweep_main(m_current_subaddress_account, below, std::vector<std::string>(++args_.begin(), args_.end())); return true; } //---------------------------------------------------------------------------------------------------- @@ -7727,7 +7603,7 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_) fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if(m_wallet->multisig()) + if(m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("This is a multisig wallet, it can only sign with sign_multisig"); return true; @@ -8260,7 +8136,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) return true; } - if (m_wallet->watch_only() || m_wallet->multisig()) + if (m_wallet->watch_only() || m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("The reserve proof can be generated only by a full wallet"); return true; @@ -9748,8 +9624,8 @@ bool simple_wallet::status(const std::vector<std::string> &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::wallet_info(const std::vector<std::string> &args) { - bool ready; - uint32_t threshold, total; + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + std::string description = m_wallet->get_description(); if (description.empty()) { @@ -9761,8 +9637,8 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args) std::string type; if (m_wallet->watch_only()) type = tr("Watch only"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); + else if (ms_status.multisig_is_active) + type = (boost::format(tr("%u/%u multisig%s")) % ms_status.threshold % ms_status.total % (ms_status.is_ready ? "" : " (not yet finalized)")).str(); else type = tr("Normal"); message_writer() << tr("Type: ") << type; @@ -9789,7 +9665,7 @@ bool simple_wallet::sign(const std::vector<std::string> &args) fail_msg_writer() << tr("wallet is watch-only and cannot sign"); return true; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { fail_msg_writer() << tr("This wallet is multisig and cannot sign"); return true; @@ -11018,7 +10894,7 @@ void simple_wallet::mms_sync(const std::vector<std::string> &args) void simple_wallet::mms_transfer(const std::vector<std::string> &args) { // It's too complicated to check any arguments here, just let 'transfer_main' do the whole job - transfer_main(Transfer, args, true); + transfer_main(args, true); } void simple_wallet::mms_delete(const std::vector<std::string> &args) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2a5d7f2b6..11b2be342 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -167,11 +167,9 @@ namespace cryptonote bool show_incoming_transfers(const std::vector<std::string> &args); bool show_payments(const std::vector<std::string> &args); bool show_blockchain_height(const std::vector<std::string> &args); - bool transfer_main(int transfer_type, const std::vector<std::string> &args, bool called_by_mms); + bool transfer_main(const std::vector<std::string> &args, bool called_by_mms); bool transfer(const std::vector<std::string> &args); - bool locked_transfer(const std::vector<std::string> &args); - bool locked_sweep_all(const std::vector<std::string> &args); - bool sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args); + bool sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); bool sweep_account(const std::vector<std::string> &args); bool sweep_below(const std::vector<std::string> &args); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7f4dbbc79..58cb84947 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -38,6 +38,7 @@ #include "subaddress_account.h" #include "common_defines.h" #include "common/util.h" +#include "multisig/multisig_account.h" #include "mnemonics/electrum-words.h" #include "mnemonics/english.h" @@ -87,12 +88,13 @@ namespace { throw runtime_error("Wallet is not initialized yet"); } - bool ready; - if (!wallet->multisig(&ready)) { + const multisig::multisig_account_status ms_status{wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { throw runtime_error("Wallet is not multisig"); } - if (!ready) { + if (!ms_status.is_ready) { throw runtime_error("Multisig wallet is not finalized yet"); } } @@ -105,12 +107,13 @@ namespace { throw runtime_error("Wallet is not initialized yet"); } - bool ready; - if (!wallet->multisig(&ready)) { + const multisig::multisig_account_status ms_status{wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { throw runtime_error("Wallet is not multisig"); } - if (ready) { + if (ms_status.is_ready) { throw runtime_error("Multisig wallet is already finalized"); } } @@ -1297,7 +1300,13 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex MultisigState WalletImpl::multisig() const { MultisigState state; - state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + state.isMultisig = ms_status.multisig_is_active; + state.kexIsDone = ms_status.kex_is_done; + state.isReady = ms_status.is_ready; + state.threshold = ms_status.threshold; + state.total = ms_status.total; return state; } @@ -1318,7 +1327,7 @@ string WalletImpl::makeMultisig(const vector<string>& info, const uint32_t thres try { clearStatus(); - if (m_wallet->multisig()) { + if (m_wallet->get_multisig_status().multisig_is_active) { throw runtime_error("Wallet is already multisig"); } @@ -1519,11 +1528,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri fake_outs_count = m_wallet->adjust_mixin(mixin_count); if (amount) { - transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, adjusted_priority, extra, subaddr_account, subaddr_indices); } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, adjusted_priority, extra, subaddr_account, subaddr_indices); } @@ -2053,8 +2062,8 @@ std::string WalletImpl::signMultisigParticipant(const std::string &message) cons { clearStatus(); - bool ready = false; - if (!m_wallet->multisig(&ready) || !ready) { + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (!ms_status.multisig_is_active || !ms_status.is_ready) { m_status = Status_Error; m_errorString = tr("The wallet must be in multisig ready state"); return {}; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index df86da847..53210832b 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -322,9 +322,10 @@ struct SubaddressAccount }; struct MultisigState { - MultisigState() : isMultisig(false), isReady(false), threshold(0), total(0) {} + MultisigState() : isMultisig(false), kexIsDone(false), isReady(false), threshold(0), total(0) {} bool isMultisig; + bool kexIsDone; bool isReady; uint32_t threshold; uint32_t total; diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h index 202d77be6..c0afa2afa 100644 --- a/src/wallet/message_store.h +++ b/src/wallet/message_store.h @@ -245,18 +245,23 @@ namespace mms crypto::secret_key view_secret_key; bool multisig; bool multisig_is_ready; + bool multisig_kex_is_done; bool has_multisig_partial_key_images; uint32_t multisig_rounds_passed; size_t num_transfer_details; std::string mms_file; BEGIN_SERIALIZE_OBJECT() - VERSION_FIELD(0) + VERSION_FIELD(1) FIELD(address) VARINT_FIELD(nettype) FIELD(view_secret_key) FIELD(multisig) FIELD(multisig_is_ready) + if (version > 0) + FIELD(multisig_kex_is_done) + else + multisig_kex_is_done = multisig_is_ready; FIELD(has_multisig_partial_key_images) VARINT_FIELD(multisig_rounds_passed) VARINT_FIELD(num_transfer_details) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 30c09886b..621b7ac61 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -30,6 +30,7 @@ #include <algorithm> #include <numeric> +#include <string> #include <tuple> #include <queue> #include <boost/format.hpp> @@ -1360,6 +1361,10 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u { boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); + if(daemon_address.empty()) { + daemon_address.append("http://localhost:" + std::to_string(get_config(m_nettype).RPC_DEFAULT_PORT)); + } + if(m_http_client->is_connected()) m_http_client->disconnect(); CHECK_AND_ASSERT_MES2(m_proxy.empty() || proxy.empty() , "It is not possible to set global proxy (--proxy) and daemon specific proxy together."); @@ -1438,14 +1443,14 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab //---------------------------------------------------------------------------------------------------- bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const { - bool ready; - uint32_t threshold, total; - if (!multisig(&ready, &threshold, &total)) + const multisig::multisig_account_status ms_status{get_multisig_status()}; + + if (!ms_status.multisig_is_active) { std::cout << "This is not a multisig wallet" << std::endl; return false; } - if (!ready) + if (!ms_status.is_ready) { std::cout << "This multisig wallet is not yet finalized" << std::endl; return false; @@ -1459,8 +1464,8 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl THROW_WALLET_EXCEPTION_IF(num_expected_ms_keys != keys.m_multisig_keys.size(), error::wallet_internal_error, "Unexpected number of private multisig keys") epee::wipeable_string data; - data.append((const char*)&threshold, sizeof(uint32_t)); - data.append((const char*)&total, sizeof(uint32_t)); + data.append((const char*)&ms_status.threshold, sizeof(uint32_t)); + data.append((const char*)&ms_status.total, sizeof(uint32_t)); skey = keys.m_spend_secret_key; data.append((const char*)&skey, sizeof(skey)); pkey = keys.m_account_address.m_spend_public_key; @@ -5617,8 +5622,8 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor const std::vector<std::string> &kex_messages, const bool force_update_use_with_caution /*= false*/) { - bool ready{false}; - CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig"); + const multisig::multisig_account_status ms_status{get_multisig_status()}; + CHECK_AND_ASSERT_THROW_MES(ms_status.multisig_is_active, "The wallet is not multisig"); // decrypt account keys epee::misc_utils::auto_scope_leave_caller keys_reencryptor; @@ -5745,20 +5750,30 @@ std::string wallet2::get_multisig_first_kex_msg() const return multisig_account.get_next_kex_round_msg(); } //---------------------------------------------------------------------------------------------------- -bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const +multisig::multisig_account_status wallet2::get_multisig_status() const { - if (!m_multisig) - return false; - if (threshold) - *threshold = m_multisig_threshold; - if (total) - *total = m_multisig_signers.size(); - if (ready) + multisig::multisig_account_status ret; + + if (m_multisig) { - *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) && + ret.multisig_is_active = true; + ret.threshold = m_multisig_threshold; + ret.total = m_multisig_signers.size(); + ret.kex_is_done = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) && + (m_multisig_rounds_passed >= multisig::multisig_kex_rounds_required(m_multisig_signers.size(), m_multisig_threshold)); + ret.is_ready = ret.kex_is_done && (m_multisig_rounds_passed == multisig::multisig_setup_rounds_required(m_multisig_signers.size(), m_multisig_threshold)); } - return true; + else + { + ret.multisig_is_active = false; + ret.threshold = 0; + ret.total = 0; + ret.kex_is_done = false; + ret.is_ready = false; + } + + return ret; } //---------------------------------------------------------------------------------------------------- bool wallet2::has_multisig_partial_key_images() const @@ -6136,7 +6151,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass catch (...) { LOG_PRINT_L0("Failed to open portable binary, trying unportable"); - if (use_fs) boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + if (use_fs) tools::copy_file(m_wallet_file, m_wallet_file + ".unportable"); std::stringstream iss; iss.str(""); iss << cache_data; @@ -6158,7 +6173,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass catch (...) { LOG_PRINT_L0("Failed to open portable binary, trying unportable"); - if (use_fs) boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + if (use_fs) tools::copy_file(m_wallet_file, m_wallet_file + ".unportable"); std::stringstream iss; iss.str(""); iss << cache_file_buf; @@ -7252,14 +7267,15 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin { tools::wallet2::tx_construction_data &sd = exported_txs.txes[n]; THROW_WALLET_EXCEPTION_IF(sd.sources.empty(), error::wallet_internal_error, "Empty sources"); + THROW_WALLET_EXCEPTION_IF(sd.unlock_time, error::nonzero_unlock_time); LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size()); signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); rct::RCTConfig rct_config = sd.rct_config; crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, sd.use_view_tags); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, tx_key, additional_tx_keys, sd.use_rct, rct_config, sd.use_view_tags); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, m_nettype); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's @@ -7787,7 +7803,6 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto not multisig_tx_builder.init( m_account.get_keys(), ptx.construction_data.extra, - ptx.construction_data.unlock_time, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices, ptx.construction_data.sources, @@ -9199,7 +9214,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> template<typename T> void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, + uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, bool use_view_tags) { using namespace cryptonote; @@ -9310,9 +9325,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; LOG_PRINT_L2("constructing tx"); - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, use_view_tags); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, tx_key, additional_tx_keys, false, {}, use_view_tags); LOG_PRINT_L2("constructed tx, r="<<r); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); std::string key_images; @@ -9345,7 +9360,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent ptx.construction_data.splitted_dsts = splitted_dsts; ptx.construction_data.selected_transfers = selected_transfers; ptx.construction_data.extra = tx.extra; - ptx.construction_data.unlock_time = unlock_time; + ptx.construction_data.unlock_time = 0; ptx.construction_data.use_rct = false; ptx.construction_data.rct_config = { rct::RangeProofBorromean, 0 }; ptx.construction_data.use_view_tags = use_view_tags; @@ -9360,7 +9375,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags) + uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -9556,7 +9571,6 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry THROW_WALLET_EXCEPTION_IF( not multisig_tx_builder.init(m_account.get_keys(), extra, - unlock_time, subaddr_account, subaddr_minor_indices, sources, @@ -9576,9 +9590,9 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry } else { // make a normal tx - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, tx_key, additional_tx_keys, true, rct_config, use_view_tags); LOG_PRINT_L2("constructed tx, r="<<r); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, m_nettype); } THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); @@ -9700,7 +9714,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry ptx.construction_data.splitted_dsts = splitted_dsts; ptx.construction_data.selected_transfers = ptx.selected_transfers; ptx.construction_data.extra = tx.extra; - ptx.construction_data.unlock_time = unlock_time; + ptx.construction_data.unlock_time = 0; ptx.construction_data.use_rct = true; ptx.construction_data.rct_config = { rct::RangeProofPaddedBulletproof, @@ -9860,7 +9874,7 @@ static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &tr // This system allows for sending (almost) the entire balance, since it does // not generate spurious change in all txes, thus decreasing the instantaneous // usable balance. -std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs) +std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -10383,10 +10397,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp tx.selected_transfers.size() << " inputs"); auto tx_dsts = tx.get_adjusted_dsts(needed_fee); if (use_rct) - transfer_selected_rct(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected_rct(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10422,21 +10436,21 @@ 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)); - size_t fee_tries; - for (fee_tries = 0; fee_tries < 10 && needed_fee > test_ptx.fee; ++fee_tries) { + size_t fee_tries = 0; + do { tx_dsts = tx.get_adjusted_dsts(needed_fee); if (use_rct) - transfer_selected_rct(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected_rct(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected(tx_dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); txBlob = t_serializable_object_to_blob(test_ptx.tx); 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 && ++fee_tries < 10); THROW_WALLET_EXCEPTION_IF(fee_tries == 10, error::wallet_internal_error, "Too many attempts to raise pending tx fee to level of needed fee"); @@ -10503,7 +10517,6 @@ skip_tx: fake_outs_count, /* CONST size_t fake_outputs_count, */ tx.outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */ valid_public_keys_cache, - unlock_time, /* CONST uint64_t unlock_time, */ tx.needed_fee, /* CONST uint64_t fee, */ extra, /* const std::vector<uint8_t>& extra, */ test_tx, /* OUT cryptonote::transaction& tx, */ @@ -10516,7 +10529,6 @@ skip_tx: fake_outs_count, tx.outs, valid_public_keys_cache, - unlock_time, tx.needed_fee, extra, detail::digit_split_strategy, @@ -10630,7 +10642,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c return true; } -std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) +std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -10700,10 +10712,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below } } - return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, priority, extra); } -std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra) +std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -10721,10 +10733,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt break; } } - return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, priority, extra); } -std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra) +std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -10833,10 +10845,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << tx.selected_transfers.size() << " outputs"); if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10870,10 +10882,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton dt.amount = dt_amount + dt_residue; } if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10909,10 +10921,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton cryptonote::transaction test_tx; pending_tx test_ptx; if (use_rct) { - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, tx.needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); } else { - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, tx.needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); } auto txBlob = t_serializable_object_to_blob(test_ptx.tx); @@ -11214,7 +11226,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions() unmixable_transfer_outputs.push_back(n); } - return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector<uint8_t>()); + return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 1 /*priority */, std::vector<uint8_t>()); } //---------------------------------------------------------------------------------------------------- void wallet2::discard_unmixable_outputs() @@ -14302,9 +14314,13 @@ void wallet2::generate_genesis(cryptonote::block& b) const { //---------------------------------------------------------------------------------------------------- mms::multisig_wallet_state wallet2::get_multisig_wallet_state() const { + const multisig::multisig_account_status ms_status{get_multisig_status()}; + mms::multisig_wallet_state state; state.nettype = m_nettype; - state.multisig = multisig(&state.multisig_is_ready); + state.multisig = ms_status.multisig_is_active; + state.multisig_is_ready = ms_status.is_ready; + state.multisig_kex_is_done = ms_status.kex_is_done; state.has_multisig_partial_key_images = has_multisig_partial_key_images(); state.multisig_rounds_passed = m_multisig_rounds_passed; state.num_transfer_details = m_transfers.size(); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 5f884e374..5f2f0e0c4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -57,6 +57,7 @@ #include "common/util.h" #include "crypto/chacha.h" #include "crypto/hash.h" +#include "multisig/multisig_account.h" #include "ringct/rctTypes.h" #include "ringct/rctOps.h" #include "checkpoints/checkpoints.h" @@ -991,13 +992,13 @@ private: uint64_t max_reorg_depth() const {return m_max_reorg_depth;} bool deinit(); - bool init(std::string daemon_address = "http://localhost:8080", + bool init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login = boost::none, const std::string &proxy = "", uint64_t upper_transaction_weight_limit = 0, bool trusted_daemon = true, epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - bool set_daemon(std::string daemon_address = "http://localhost:8080", + bool set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login = boost::none, bool trusted_daemon = true, epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::string &proxy = ""); @@ -1059,7 +1060,7 @@ private: cryptonote::network_type nettype() const { return m_nettype; } bool watch_only() const { return m_watch_only; } - bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; + multisig::multisig_account_status get_multisig_status() const; bool has_multisig_partial_key_images() const; bool has_unknown_key_images() const; bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; @@ -1079,10 +1080,10 @@ private: template<typename T> void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags); + uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags); void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags); + uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags); void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector<pending_tx>& ptx_vector); @@ -1104,10 +1105,10 @@ private: bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func); - std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose - std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); - std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra); - std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra); + std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose + std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); + std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra); + std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra); bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux); void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 5166f868f..2bbfc3167 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -86,6 +86,7 @@ namespace tools // zero_amount // zero_destination // subtract_fee_from_bad_index + // nonzero_unlock_time // wallet_rpc_error * // daemon_busy // no_connection_to_daemon @@ -592,20 +593,17 @@ namespace tools std::string && loc , sources_t const & sources , destinations_t const & destinations - , uint64_t unlock_time , cryptonote::network_type nettype ) : transfer_error(std::move(loc), "transaction was not constructed") , m_sources(sources) , m_destinations(destinations) - , m_unlock_time(unlock_time) , m_nettype(nettype) { } const sources_t& sources() const { return m_sources; } const destinations_t& destinations() const { return m_destinations; } - uint64_t unlock_time() const { return m_unlock_time; } std::string to_string() const { @@ -637,15 +635,12 @@ namespace tools cryptonote::print_money(dst.amount); } - ss << "\nunlock_time: " << m_unlock_time; - return ss.str(); } private: sources_t m_sources; destinations_t m_destinations; - uint64_t m_unlock_time; cryptonote::network_type m_nettype; }; //---------------------------------------------------------------------------------------------------- @@ -789,6 +784,14 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct nonzero_unlock_time : public transfer_error + { + explicit nonzero_unlock_time(std::string&& loc) + : transfer_error(std::move(loc), "transaction cannot have non-zero unlock time") + { + } + }; + //---------------------------------------------------------------------------------------------------- struct wallet_rpc_error : public wallet_logic_error { const std::string& request() const { return m_request; } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index d7aa80e0a..376c58f89 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -65,7 +65,7 @@ using namespace epee; #define CHECK_MULTISIG_ENABLED() \ do \ { \ - if (m_wallet->multisig() && !m_wallet->is_multisig_enabled()) \ + if (m_wallet->get_multisig_status().multisig_is_active && !m_wallet->is_multisig_enabled()) \ { \ er.code = WALLET_RPC_ERROR_CODE_DISABLED; \ er.message = "This wallet is multisig, and multisig is disabled. Multisig is an experimental feature and may have bugs. Things that could go wrong include: funds sent to a multisig wallet can't be spent at all, can only be spent with the participation of a malicious group member, or can be stolen by a malicious group member. You can enable it by running this once in monero-wallet-cli: set enable-multisig-experimental 1"; \ @@ -245,7 +245,7 @@ namespace tools ); std::string temp = "monero-wallet-rpc." + bind_port + ".login"; - rpc_login_file = tools::private_file::create(temp); + rpc_login_file = tools::private_file::drop_and_recreate(temp); if (!rpc_login_file.handle()) { LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file")); @@ -459,7 +459,7 @@ namespace tools { res.balance = req.all_accounts ? m_wallet->balance_all(req.strict) : m_wallet->balance(req.account_index, req.strict); res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(req.strict, &res.blocks_to_unlock, &res.time_to_unlock) : m_wallet->unlocked_balance(req.account_index, req.strict, &res.blocks_to_unlock, &res.time_to_unlock); - res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images(); + res.multisig_import_needed = m_wallet->get_multisig_status().multisig_is_active && m_wallet->has_multisig_partial_key_images(); std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account; std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>> unlocked_balance_per_subaddress_per_account; if (req.all_accounts) @@ -1024,7 +1024,7 @@ namespace tools fill(spent_key_images, key_image_list); } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector)); if (multisig_txset.empty()) @@ -1079,6 +1079,12 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } + else if (req.unlock_time) + { + er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME; + er.message = "Transaction cannot have non-zero unlock time"; + return false; + } CHECK_MULTISIG_ENABLED(); @@ -1092,7 +1098,7 @@ namespace tools { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); if (ptx_vector.empty()) { @@ -1133,6 +1139,12 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } + else if (req.unlock_time) + { + er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME; + er.message = "Transaction cannot have non-zero unlock time"; + return false; + } CHECK_MULTISIG_ENABLED(); @@ -1147,7 +1159,7 @@ namespace tools uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); if (ptx_vector.empty()) @@ -1569,6 +1581,12 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } + else if (req.unlock_time) + { + er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME; + er.message = "Transaction cannot have non-zero unlock time"; + return false; + } CHECK_MULTISIG_ENABLED(); @@ -1604,7 +1622,7 @@ namespace tools { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, priority, extra, req.account_index, subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er); @@ -1629,6 +1647,12 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } + else if (req.unlock_time) + { + er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME; + er.message = "Transaction cannot have non-zero unlock time"; + return false; + } if (req.outputs < 1) { @@ -1661,7 +1685,7 @@ namespace tools { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, priority, extra); if (ptx_vector.empty()) { @@ -2066,10 +2090,11 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { epee::wipeable_string seed; - bool ready; - if (m_wallet->multisig(&ready)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (ms_status.multisig_is_active) { - if (!ready) + if (!ms_status.is_ready) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is multisig, but not yet finalized"; @@ -3979,7 +4004,14 @@ namespace tools bool wallet_rpc_server::on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx) { if (!m_wallet) return not_open(er); - res.multisig = m_wallet->multisig(&res.ready, &res.threshold, &res.total); + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + res.multisig = ms_status.multisig_is_active; + res.kex_is_done = ms_status.kex_is_done; + res.ready = ms_status.is_ready; + res.threshold = ms_status.threshold; + res.total = ms_status.total; + return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -3992,7 +4024,7 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG; er.message = "This wallet is already multisig"; @@ -4021,7 +4053,7 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - if (m_wallet->multisig()) + if (m_wallet->get_multisig_status().multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG; er.message = "This wallet is already multisig"; @@ -4059,14 +4091,15 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - bool ready; - if (!m_wallet->multisig(&ready)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is not multisig"; return false; } - if (!ready) + if (!ms_status.is_ready) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is multisig, but not yet finalized"; @@ -4100,15 +4133,15 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - bool ready; - uint32_t threshold, total; - if (!m_wallet->multisig(&ready, &threshold, &total)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is not multisig"; return false; } - if (!ready) + if (!ms_status.is_ready) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is multisig, but not yet finalized"; @@ -4116,7 +4149,7 @@ namespace tools } CHECK_MULTISIG_ENABLED(); - if (req.info.size() < threshold - 1) + if (req.info.size() + 1 < ms_status.threshold) { er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED; er.message = "Needs multisig export info from more participants"; @@ -4180,9 +4213,9 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - bool ready; - uint32_t threshold, total; - if (!m_wallet->multisig(&ready, &threshold, &total)) + multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is not multisig"; @@ -4190,7 +4223,7 @@ namespace tools } CHECK_MULTISIG_ENABLED(); - if (req.multisig_info.size() + 1 < total) + if (req.multisig_info.size() + 1 < ms_status.total) { er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED; er.message = "Needs multisig info from more participants"; @@ -4200,8 +4233,8 @@ namespace tools try { res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info, req.force_update_use_with_caution); - m_wallet->multisig(&ready); - if (ready) + ms_status = m_wallet->get_multisig_status(); + if (ms_status.is_ready) { res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } @@ -4224,15 +4257,15 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - bool ready; - uint32_t threshold, total; - if (!m_wallet->multisig(&ready, &threshold, &total)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is not multisig"; return false; } - if (!ready) + if (!ms_status.is_ready) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is multisig, but not yet finalized"; @@ -4294,15 +4327,15 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - bool ready; - uint32_t threshold, total; - if (!m_wallet->multisig(&ready, &threshold, &total)) + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + + if (!ms_status.multisig_is_active) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is not multisig"; return false; } - if (!ready) + if (!ms_status.is_ready) { er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; er.message = "This wallet is multisig, but not yet finalized"; @@ -4327,7 +4360,7 @@ namespace tools return false; } - if (txs.m_signers.size() < threshold) + if (txs.m_signers.size() < ms_status.threshold) { er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED; er.message = "Not enough signers signed this transaction."; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index f9f534097..2173f5b6e 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -2323,12 +2323,14 @@ namespace wallet_rpc struct response_t { bool multisig; + bool kex_is_done; bool ready; uint32_t threshold; uint32_t total; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(multisig) + KV_SERIALIZE(kex_is_done) KV_SERIALIZE(ready) KV_SERIALIZE(threshold) KV_SERIALIZE(total) diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 2988f52ad..ca2de0cc6 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -80,3 +80,4 @@ #define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47 #define WALLET_RPC_ERROR_CODE_DISABLED -48 #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 +#define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 |