diff options
Diffstat (limited to 'src')
43 files changed, 2167 insertions, 214 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 50887e35c..7ad08ea83 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -35,6 +35,7 @@ set(common_sources download.cpp util.cpp i18n.cpp + memwipe.c password.cpp perf_timer.cpp threadpool.cpp @@ -63,6 +64,7 @@ set(common_private_headers util.h varint.h i18n.h + memwipe.h password.h perf_timer.h stack_trace.h @@ -90,5 +92,9 @@ target_link_libraries(common ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) +if(HAVE_C11) +SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11) +endif() + #monero_install_headers(common # ${common_headers}) diff --git a/src/common/memwipe.c b/src/common/memwipe.c new file mode 100644 index 000000000..da7e9f346 --- /dev/null +++ b/src/common/memwipe.c @@ -0,0 +1,106 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file Copyright (c) 2009-2015 The Bitcoin Core developers + +#define __STDC_WANT_LIB_EXT1__ 1 +#include <string.h> +#include <stdlib.h> +#ifdef HAVE_EXPLICIT_BZERO +#include <strings.h> +#endif +#include "memwipe.h" + +#if defined(_MSC_VER) +#define SCARECROW \ + __asm; +#else +#define SCARECROW \ + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif + +#ifdef HAVE_MEMSET_S + +void *memwipe(void *ptr, size_t n) +{ + if (memset_s(ptr, n, 0, n)) + { + abort(); + } + SCARECROW // might as well... + return ptr; +} + +#elif defined HAVE_EXPLICIT_BZERO + +void *memwipe(void *ptr, size_t n) +{ + explicit_bzero(ptr, n); + SCARECROW + return ptr; +} + +#else + +/* The memory_cleanse implementation is taken from Bitcoin */ + +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley <agl@google.com> + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ +static void memory_cleanse(void *ptr, size_t len) +{ + memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ + SCARECROW +} + +void *memwipe(void *ptr, size_t n) +{ + memory_cleanse(ptr, n); + SCARECROW + return ptr; +} + +#endif diff --git a/src/common/memwipe.h b/src/common/memwipe.h new file mode 100644 index 000000000..e9a3fba7b --- /dev/null +++ b/src/common/memwipe.h @@ -0,0 +1,41 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void *memwipe(void *src, size_t n); + +#ifdef __cplusplus +} +#endif diff --git a/src/common/password.cpp b/src/common/password.cpp index 5d56464a5..dc0856160 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -46,6 +46,8 @@ #include "readline_buffer.h" #endif +#include "common/memwipe.h" + namespace { #if defined(_WIN32) @@ -54,7 +56,7 @@ namespace return 0 != _isatty(_fileno(stdin)); } - bool read_from_tty(std::string& pass) + bool read_from_tty(epee::wipeable_string& pass) { static constexpr const char BACKSPACE = 8; @@ -86,8 +88,7 @@ namespace { if (!pass.empty()) { - pass.back() = '\0'; - pass.resize(pass.size() - 1); + pass.pop_back(); } } else @@ -125,7 +126,7 @@ namespace return ch; } - bool read_from_tty(std::string& aPass) + bool read_from_tty(epee::wipeable_string& aPass) { static constexpr const char BACKSPACE = 127; @@ -146,8 +147,7 @@ namespace { if (!aPass.empty()) { - aPass.back() = '\0'; - aPass.resize(aPass.size() - 1); + aPass.pop_back(); } } else @@ -161,14 +161,7 @@ namespace #endif // end !WIN32 - void clear(std::string& pass) noexcept - { - //! TODO Call a memory wipe function that hopefully is not optimized out - pass.replace(0, pass.capacity(), pass.capacity(), '\0'); - pass.clear(); - } - - bool read_from_tty(const bool verify, const char *message, std::string& pass1, std::string& pass2) + bool read_from_tty(const bool verify, const char *message, epee::wipeable_string& pass1, epee::wipeable_string& pass2) { while (true) { @@ -178,14 +171,14 @@ namespace return false; if (verify) { - std::cout << "Confirm Password: "; + std::cout << "Confirm password: "; if (!read_from_tty(pass2)) return false; if(pass1!=pass2) { std::cout << "Passwords do not match! Please try again." << std::endl; - clear(pass1); - clear(pass2); + pass1.clear(); + pass2.clear(); } else //new password matches return true; @@ -198,7 +191,7 @@ namespace return false; } - bool read_from_file(std::string& pass) + bool read_from_file(epee::wipeable_string& pass) { pass.reserve(tools::password_container::max_password_size); for (size_t i = 0; i < tools::password_container::max_password_size; ++i) @@ -233,7 +226,7 @@ namespace tools password_container::~password_container() noexcept { - clear(m_password); + m_password.clear(); } boost::optional<password_container> password_container::prompt(const bool verify, const char *message) @@ -249,9 +242,8 @@ namespace tools boost::optional<login> login::parse(std::string&& userpass, bool verify, const std::function<boost::optional<password_container>(bool)> &prompt) { login out{}; - password_container wipe{std::move(userpass)}; - const auto loc = wipe.password().find(':'); + const auto loc = userpass.find(':'); if (loc == std::string::npos) { auto result = prompt(verify); @@ -262,10 +254,11 @@ namespace tools } else { - out.password = password_container{wipe.password().substr(loc + 1)}; + out.password = password_container{userpass.substr(loc + 1)}; } - out.username = wipe.password().substr(0, loc); + out.username = userpass.substr(0, loc); + password_container wipe{std::move(userpass)}; return {std::move(out)}; } } diff --git a/src/common/password.h b/src/common/password.h index ba1c30a28..01c6bf05a 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -32,6 +32,7 @@ #include <string> #include <boost/optional/optional.hpp> +#include "wipeable_string.h" namespace tools { @@ -58,11 +59,10 @@ namespace tools password_container& operator=(const password_container&) = delete; password_container& operator=(password_container&&) = default; - const std::string& password() const noexcept { return m_password; } + const epee::wipeable_string &password() const noexcept { return m_password; } private: - //! TODO Custom allocator that locks to RAM? - std::string m_password; + epee::wipeable_string m_password; }; struct login diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index bc8e05800..4d7d99afb 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -94,5 +94,8 @@ void set_performance_timer_log_level(el::Level level); #define PERF_TIMER_UNIT_L(name, unit, l) tools::PerformanceTimer pt_##name(#name, unit, l) #define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000) #define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l) +#define PERF_TIMER_START_UNIT(name, unit) tools::PerformanceTimer *pt_##name = new tools::PerformanceTimer(#name, unit, el::Level::Info) +#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000) +#define PERF_TIMER_STOP(name) do { delete pt_##name; pt_##name = NULL; } while(0) } diff --git a/src/common/util.cpp b/src/common/util.cpp index e8ac61815..de19fec81 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -36,9 +36,11 @@ #include "include_base_utils.h" #include "file_io_utils.h" +#include "wipeable_string.h" using namespace epee; #include "util.h" +#include "memwipe.h" #include "cryptonote_config.h" #include "net/http_client.h" // epee::net_utils::... @@ -542,6 +544,10 @@ std::string get_nix_version_display_string() } bool on_startup() { + wipeable_string::set_wipe(&memwipe); + + mlog_configure("", true); + sanitize_locale(); #ifdef __GLIBC__ diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 80557e9f5..1bf695731 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -39,6 +39,7 @@ #if defined(__cplusplus) #include <memory.h> +#include "common/memwipe.h" #include "hash.h" namespace crypto { @@ -54,7 +55,7 @@ namespace crypto { ~chacha8_key() { - memset(data, 0, sizeof(data)); + memwipe(data, sizeof(data)); } }; @@ -75,7 +76,7 @@ namespace crypto { char pwd_hash[HASH_SIZE]; crypto::cn_slow_hash(data, size, pwd_hash); memcpy(&key, pwd_hash, sizeof(key)); - memset(pwd_hash, 0, sizeof(pwd_hash)); + memwipe(pwd_hash, sizeof(pwd_hash)); } inline void generate_chacha8_key(std::string password, chacha8_key& key) { diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 4edfee0ce..b5c62bce4 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -2000,17 +2000,15 @@ void ge_scalarmult(ge_p2 *r, const unsigned char *a, const ge_p3 *A) { } } -void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) { +void ge_double_scalarmult_precomp_vartime2(ge_p2 *r, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) { signed char aslide[256]; signed char bslide[256]; - ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ ge_p1p1 t; ge_p3 u; int i; slide(aslide, a); slide(bslide, b); - ge_dsm_precomp(Ai, A); ge_p2_0(r); @@ -2041,6 +2039,13 @@ void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, cons } } +void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) { + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + + ge_dsm_precomp(Ai, A); + ge_double_scalarmult_precomp_vartime2(r, a, Ai, b, Bi); +} + void ge_mul8(ge_p1p1 *r, const ge_p2 *t) { ge_p2 u; ge_p2_dbl(r, t); @@ -2898,6 +2903,658 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s[31] = s11 >> 17; } +//copied from above and modified +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ +void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = a0*b0; + s1 = (a0*b1 + a1*b0); + s2 = (a0*b2 + a1*b1 + a2*b0); + s3 = (a0*b3 + a1*b2 + a2*b1 + a3*b0); + s4 = (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0); + s5 = (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0); + s6 = (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0); + s7 = (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0); + s8 = (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0); + s9 = (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0); + s10 = (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0); + s11 = (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0); + s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1); + s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2); + s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3); + s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4); + s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5); + s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6); + s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7); + s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8); + s20 = (a9*b11 + a10*b10 + a11*b9); + s21 = (a10*b11 + a11*b10); + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + +//copied from above and modified +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (c+ab) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0*b0; + s1 = c1 + (a0*b1 + a1*b0); + s2 = c2 + (a0*b2 + a1*b1 + a2*b0); + s3 = c3 + (a0*b3 + a1*b2 + a2*b1 + a3*b0); + s4 = c4 + (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0); + s5 = c5 + (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0); + s6 = c6 + (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0); + s7 = c7 + (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0); + s8 = c8 + (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0); + s9 = c9 + (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0); + s10 = c10 + (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0); + s11 = c11 + (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0); + s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1); + s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2); + s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3); + s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4); + s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5); + s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6); + s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7); + s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8); + s20 = (a9*b11 + a10*b10 + a11*b9); + s21 = (a10*b11 + a11*b10); + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + /* Assumes that a != INT64_MIN */ static int64_t signum(int64_t a) { return (a >> 63) - ((-a) >> 63); diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 37edf5b6d..c76455551 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -128,6 +128,7 @@ void sc_reduce(unsigned char *); void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *); void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp); +void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); extern const fe fe_ma2; extern const fe fe_ma; @@ -141,6 +142,8 @@ void sc_reduce32(unsigned char *); void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); +void sc_mul(unsigned char *, const unsigned char *, const unsigned char *); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); int sc_check(const unsigned char *); int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a67fa0ae7..760edf9b9 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -212,6 +212,23 @@ namespace boost } template <class Archive> + inline void serialize(Archive &a, rct::Bulletproof &x, const boost::serialization::version_type ver) + { + a & x.V; + a & x.A; + a & x.S; + a & x.T1; + a & x.T2; + a & x.taux; + a & x.mu; + a & x.L; + a & x.R; + a & x.a; + a & x.b; + a & x.t; + } + + template <class Archive> inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver) { a & x.s0; @@ -263,11 +280,11 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple) + if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -278,6 +295,8 @@ namespace boost inline void serialize(Archive &a, rct::rctSigPrunable &x, const boost::serialization::version_type ver) { a & x.rangeSigs; + if (x.rangeSigs.empty()) + a & x.bulletproofs; a & x.MGs; } @@ -287,17 +306,19 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple) + if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); a & x.txnFee; //-------------- a & x.p.rangeSigs; + if (x.p.rangeSigs.empty()) + a & x.p.bulletproofs; a & x.p.MGs; } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 3c760493f..6759f1826 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -32,6 +32,7 @@ using namespace epee; #include <atomic> +#include "wipeable_string.h" #include "cryptonote_format_utils.h" #include "cryptonote_config.h" #include "crypto/crypto.h" @@ -994,7 +995,7 @@ namespace cryptonote block_hashes_cached = block_hashes_cached_count; } //--------------------------------------------------------------- - crypto::secret_key encrypt_key(crypto::secret_key key, const std::string &passphrase) + crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) { crypto::hash hash; crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); @@ -1002,7 +1003,7 @@ namespace cryptonote return key; } //--------------------------------------------------------------- - crypto::secret_key decrypt_key(crypto::secret_key key, const std::string &passphrase) + crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) { crypto::hash hash; crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index aebeaa6f4..714d80195 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -38,6 +38,11 @@ #include "crypto/hash.h" #include <unordered_map> +namespace epee +{ + class wipeable_string; +} + namespace cryptonote { //--------------------------------------------------------------- @@ -226,8 +231,8 @@ namespace cryptonote bool is_valid_decomposed_amount(uint64_t amount); void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached); - crypto::secret_key encrypt_key(crypto::secret_key key, const std::string &passphrase); - crypto::secret_key decrypt_key(crypto::secret_key key, const std::string &passphrase); + crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase); + crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get<specific_type>(variant_var); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3d586a704..123bd194b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -127,6 +127,7 @@ static const struct { { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 { 6, 971400, 0, 1501709789 }, + { 7, 1057028, 0, 1512211236 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -304,7 +305,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options) +bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const cryptonote::test_options *test_options) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -326,6 +327,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te m_db = db; m_testnet = testnet; + m_offline = offline; if (m_hardfork == nullptr) { if (fakechain) @@ -413,11 +415,11 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te return true; } //------------------------------------------------------------------ -bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet) +bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet, bool offline) { if (hf != nullptr) m_hardfork = hf; - bool res = init(db, testnet, NULL); + bool res = init(db, testnet, offline, NULL); if (hf == nullptr) hf = m_hardfork; return res; @@ -2387,8 +2389,10 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + const uint8_t hf_version = m_hardfork->get_current_version(); + // from hard fork 2, we forbid dust and compound outputs - if (m_hardfork->get_current_version() >= 2) { + if (hf_version >= 2) { for (auto &o: tx.vout) { if (tx.version == 1) { @@ -2401,7 +2405,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // in a v2 tx, all outputs must have 0 amount - if (m_hardfork->get_current_version() >= 3) { + if (hf_version >= 3) { if (tx.version >= 2) { for (auto &o: tx.vout) { if (o.amount != 0) { @@ -2413,7 +2417,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // from v4, forbid invalid pubkeys - if (m_hardfork->get_current_version() >= 4) { + if (hf_version >= 4) { for (const auto &o: tx.vout) { if (o.target.type() == typeid(txout_to_key)) { const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target); @@ -2425,6 +2429,16 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } + // from v7, allow bulletproofs + if (hf_version < 7 || !m_testnet) { + if (!tx.rct_signatures.p.bulletproofs.empty()) + { + MERROR("Bulletproofs are not allowed before v7 or on mainnet"); + tvc.m_invalid_output = true; + return false; + } + } + return true; } //------------------------------------------------------------------ @@ -2450,7 +2464,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr rv.message = rct::hash2rct(tx_prefix_hash); // mixRing - full and simple store it in opposite ways - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { rv.mixRing.resize(pubkeys[0].size()); for (size_t m = 0; m < pubkeys[0].size(); ++m) @@ -2464,7 +2478,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) { rv.mixRing.resize(pubkeys.size()); for (size_t n = 0; n < pubkeys.size(); ++n) @@ -2482,14 +2496,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } // II - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { rv.p.MGs.resize(1); rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -2753,7 +2767,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, MERROR_VER("Null rct signature on non-coinbase tx"); return false; } - case rct::RCTTypeSimple: { + case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: + { // check all this, either recontructed (so should really pass), or not { if (pubkeys.size() != rv.mixRing.size()) @@ -2809,7 +2825,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } break; } - case rct::RCTTypeFull: { + case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: + { // check all this, either recontructed (so should really pass), or not { bool size_matches = true; @@ -3624,14 +3642,14 @@ bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns // if we're checking both dns and json, load checkpoints from dns. // if we're not hard-enforcing dns checkpoints, handle accordingly - if (m_enforce_dns_checkpoints && check_dns) + if (m_enforce_dns_checkpoints && check_dns && !m_offline) { if (!m_checkpoints.load_checkpoints_from_dns()) { return false; } } - else if (check_dns) + else if (check_dns && !m_offline) { checkpoints dns_points; dns_points.load_checkpoints_from_dns(); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index e0936da8f..b76d0555f 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -112,11 +112,12 @@ namespace cryptonote * * @param db a pointer to the backing store to use for the blockchain * @param testnet true if on testnet, else false + * @param offline true if running offline, else false * @param test_options test parameters * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const bool testnet = false, const cryptonote::test_options *test_options = NULL); + bool init(BlockchainDB* db, const bool testnet = false, bool offline = false, const cryptonote::test_options *test_options = NULL); /** * @brief Initialize the Blockchain state @@ -124,10 +125,11 @@ namespace cryptonote * @param db a pointer to the backing store to use for the blockchain * @param hf a structure containing hardfork information * @param testnet true if on testnet, else false + * @param offline true if running offline, else false * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false); + bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false, bool offline = false); /** * @brief Uninitializes the blockchain state @@ -1027,6 +1029,7 @@ namespace cryptonote HardFork *m_hardfork; bool m_testnet; + bool m_offline; std::atomic<bool> m_cancel; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index edc2dfdaa..04f1e46a7 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -75,6 +75,10 @@ namespace cryptonote , "Run on testnet. The wallet must be launched with --testnet flag." , false }; + const command_line::arg_descriptor<bool> arg_offline = { + "offline" + , "Do not listen for peers, nor connect to any" + }; static const command_line::arg_descriptor<bool> arg_test_drop_download = { "test-drop-download" @@ -227,6 +231,7 @@ namespace cryptonote command_line::add_arg(desc, arg_check_updates); command_line::add_arg(desc, arg_fluffy_blocks); command_line::add_arg(desc, arg_test_dbg_lock_sleep); + command_line::add_arg(desc, arg_offline); // we now also need some of net_node's options (p2p bind arg, for separate data dir) command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); @@ -264,6 +269,7 @@ namespace cryptonote set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks); + m_offline = get_arg(vm, arg_offline); if (command_line::get_arg(vm, arg_test_drop_download) == true) test_drop_download(); @@ -625,6 +631,22 @@ namespace cryptonote } for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key); + + const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof; + if (bulletproof) + { + if (rv.p.bulletproofs.size() != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + for (size_t n = 0; n < rv.outPk.size(); ++n) + { + rv.p.bulletproofs[n].V.resize(1); + rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; + } + } } if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area()) @@ -828,6 +850,7 @@ namespace cryptonote MERROR_VER("Unexpected Null rctSig type"); return false; case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: if (!rct::verRctSimple(rv, true)) { MERROR_VER("rct signature semantics check failed"); @@ -835,6 +858,7 @@ namespace cryptonote } break; case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: if (!rct::verRct(rv, true)) { MERROR_VER("rct signature semantics check failed"); @@ -1322,11 +1346,16 @@ namespace cryptonote { if(!m_starter_message_showed) { + std::string main_message; + if (m_offline) + main_message = "The daemon is running offline and will not attempt to sync to the Monero network."; + else + main_message = "The daemon will start synchronizing with the network. This may take a long time to complete."; MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL - << "The daemon will start synchronizing with the network. This may take a long time to complete." << ENDL + << main_message << ENDL << ENDL - << "You can set the level of process detailization* through \"set_log <level|categories>\" command*," << ENDL - << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL + << "You can set the level of process detailization through \"set_log <level|categories>\" command," << ENDL + << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL << "Use the \"help\" command to see the list of available commands." << ENDL << "Use \"help <command>\" to see a command's documentation." << ENDL @@ -1386,6 +1415,9 @@ namespace cryptonote static const char subdir[] = "source"; // because it can never be simple #endif + if (m_offline) + return true; + if (check_updates_level == UPDATES_DISABLED) return true; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 905e67f6d..6cb4fc938 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -62,6 +62,7 @@ namespace cryptonote extern const command_line::arg_descriptor<std::string> arg_data_dir; extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir; extern const command_line::arg_descriptor<bool, false> arg_testnet_on; + extern const command_line::arg_descriptor<bool> arg_offline; /************************************************************************/ /* */ @@ -773,6 +774,13 @@ namespace cryptonote */ uint64_t get_free_space() const; + /** + * @brief get whether the core is running offline + * + * @return whether the core is running offline + */ + bool offline() const { return m_offline; } + private: /** @@ -1000,6 +1008,7 @@ namespace cryptonote boost::mutex m_update_mutex; bool m_fluffy_blocks_enabled; + bool m_offline; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index feefc1592..4afa669fd 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -160,7 +160,7 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - 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, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, 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) + 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, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, 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, bool bulletproof) { std::vector<rct::key> amount_keys; tx.set_null(); @@ -281,7 +281,7 @@ namespace cryptonote std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) { const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]); const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]); - return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) < 0; + return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0; }); tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) { std::swap(tx.vin[i0], tx.vin[i1]); @@ -552,9 +552,9 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, bulletproof); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 8d9a1e332..d72f5d13b 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -88,7 +88,7 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); 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, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - 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, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, 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); + 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, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, 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, bool bulletproof = false); bool generate_genesis_block( block& bl diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 3bda50c22..094e4fc95 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -140,7 +140,7 @@ void cryptonote_protocol_handler_base::handler_response_blocks_now(size_t packet { CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); - delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); // decission from global + delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 9ae24551c..48ab1eadf 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1414,6 +1414,10 @@ skip: // take out blocks we already have while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front())) { + // if we're popping the last hash, record it so we can ask again from that hash, + // this prevents never being able to progress on peers we get old hash lists from + if (context.m_needed_objects.size() == 1) + context.m_last_known_hash = context.m_needed_objects.front(); context.m_needed_objects.pop_front(); } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp index 8edd75b3e..06baa7893 100644 --- a/src/p2p/connection_basic.cpp +++ b/src/p2p/connection_basic.cpp @@ -173,14 +173,9 @@ connection_basic::~connection_basic() noexcept(false) { } void connection_basic::set_rate_up_limit(uint64_t limit) { - - // TODO remove __SCALING_FACTOR... - const double SCALING_FACTOR = 2.1; // to acheve the best performance - limit *= SCALING_FACTOR; { CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); network_throttle_manager::get_global_throttle_out().set_target_speed(limit); - network_throttle_manager::get_global_throttle_out().set_real_target_speed(limit / SCALING_FACTOR); } save_limit_to_file(limit); } @@ -238,7 +233,7 @@ void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q { CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); - delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); // decission from global + delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); } delay *= 0.50; @@ -252,7 +247,7 @@ void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q // XXX LATER XXX { CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); - network_throttle_manager::get_global_throttle_out().handle_trafic_exact( packet_size * 700); // increase counter - global + network_throttle_manager::get_global_throttle_out().handle_trafic_exact( packet_size ); // increase counter - global } } @@ -262,13 +257,13 @@ void connection_basic::set_start_time() { } void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) { - sleep_before_packet(cb,1,-1); + // No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write MTRACE("handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)"); set_start_time(); } void connection_basic::do_send_handler_write_from_queue( const boost::system::error_code& e, size_t cb, int q_len ) { - sleep_before_packet(cb,2,q_len); + // No sleeping here; sleeping is done once and for all in connection<t_protocol_handler>::handle_write MTRACE("handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)"); set_start_time(); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f64b29c1f..c6792f138 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -72,8 +72,8 @@ namespace nodetool { namespace { - const int64_t default_limit_up = 2048; - const int64_t default_limit_down = 8192; + const int64_t default_limit_up = 2048; // kB/s + const int64_t default_limit_down = 8192; // kB/s const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; const command_line::arg_descriptor<std::string> arg_p2p_bind_port = { "p2p-bind-port" @@ -95,7 +95,6 @@ namespace nodetool const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; - const command_line::arg_descriptor<bool> arg_offline = {"offline", "Do not listen for peers, nor connect to any"}; const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1}; const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1}; @@ -120,7 +119,6 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_seed_node); command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_igd); - command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_out_peers); command_line::add_arg(desc, arg_tos_flag); command_line::add_arg(desc, arg_limit_rate_up); @@ -306,7 +304,7 @@ namespace nodetool m_external_port = command_line::get_arg(vm, arg_p2p_external_port); m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); m_no_igd = command_line::get_arg(vm, arg_no_igd); - m_offline = command_line::get_arg(vm, arg_offline); + m_offline = command_line::get_arg(vm, cryptonote::arg_offline); if (command_line::has_arg(vm, arg_p2p_add_peer)) { @@ -1141,7 +1139,7 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::connect_to_seed() { - if (m_seed_nodes.empty()) + if (m_seed_nodes.empty() || m_offline) return true; size_t try_count = 0; @@ -1844,9 +1842,8 @@ namespace nodetool this->islimitup=false; } - limit *= 1024; epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit( limit ); - MINFO("Set limit-up to " << limit/1024 << " kB/s"); + MINFO("Set limit-up to " << limit << " kB/s"); return true; } @@ -1858,9 +1855,8 @@ namespace nodetool limit=default_limit_down; this->islimitdown=false; } - limit *= 1024; epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit( limit ); - MINFO("Set limit-down to " << limit/1024 << " kB/s"); + MINFO("Set limit-down to " << limit << " kB/s"); return true; } @@ -1872,21 +1868,21 @@ namespace nodetool if(limit == -1) { - limit_up = default_limit_up * 1024; - limit_down = default_limit_down * 1024; + limit_up = default_limit_up; + limit_down = default_limit_down; } else { - limit_up = limit * 1024; - limit_down = limit * 1024; + limit_up = limit; + limit_down = limit; } if(!this->islimitup) { epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit(limit_up); - MINFO("Set limit-up to " << limit_up/1024 << " kB/s"); + MINFO("Set limit-up to " << limit_up << " kB/s"); } if(!this->islimitdown) { epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit(limit_down); - MINFO("Set limit-down to " << limit_down/1024 << " kB/s"); + MINFO("Set limit-down to " << limit_down << " kB/s"); } return true; diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp index 1df48ee26..651e01e6b 100644 --- a/src/p2p/network_throttle-detail.cpp +++ b/src/p2p/network_throttle-detail.cpp @@ -160,17 +160,11 @@ void network_throttle::set_target_speed( network_speed_kbps target ) { m_target_speed = target * 1024; MINFO("Setting LIMIT: " << target << " kbps"); - set_real_target_speed(target); -} - -void network_throttle::set_real_target_speed( network_speed_kbps real_target ) -{ - m_real_target_speed = real_target * 1024; } network_speed_kbps network_throttle::get_target_speed() { - return m_real_target_speed / 1024; + return m_target_speed / 1024; } void network_throttle::tick() diff --git a/src/p2p/network_throttle-detail.hpp b/src/p2p/network_throttle-detail.hpp index 27caa85d3..676d4341a 100644 --- a/src/p2p/network_throttle-detail.hpp +++ b/src/p2p/network_throttle-detail.hpp @@ -52,8 +52,7 @@ class network_throttle : public i_network_throttle { }; - network_speed_kbps m_target_speed; - network_speed_kbps m_real_target_speed; + network_speed_bps m_target_speed; size_t m_network_add_cost; // estimated add cost of headers size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to size_t m_network_max_segment; // recommended max size of 1 TCP transmission @@ -76,7 +75,6 @@ class network_throttle : public i_network_throttle { virtual ~network_throttle(); virtual void set_name(const std::string &name); virtual void set_target_speed( network_speed_kbps target ); - virtual void set_real_target_speed( network_speed_kbps real_target ); // only for throttle_out virtual network_speed_kbps get_target_speed(); // add information about events: diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp index 9853df5e1..bf1f93859 100644 --- a/src/p2p/network_throttle.hpp +++ b/src/p2p/network_throttle.hpp @@ -80,7 +80,8 @@ namespace net_utils { // just typedefs to in code define the units used. TODO later it will be enforced that casts to other numericals are only explicit to avoid mistakes? use boost::chrono? -typedef double network_speed_kbps; +typedef double network_speed_kbps; // externally, for parameters and return values, all defined in kilobytes per second +typedef double network_speed_bps; // throttle-internally, bytes per second typedef double network_time_seconds; typedef double network_MB; @@ -137,7 +138,6 @@ class i_network_throttle { public: virtual void set_name(const std::string &name)=0; virtual void set_target_speed( network_speed_kbps target )=0; - virtual void set_real_target_speed(network_speed_kbps real_target)=0; virtual network_speed_kbps get_target_speed()=0; virtual void handle_trafic_exact(size_t packet_size) =0; // count the new traffic/packet; the size is exact considering all network costs diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index f9862ac80..1452e5367 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -30,14 +30,16 @@ set(ringct_sources rctOps.cpp rctSigs.cpp rctTypes.cpp - rctCryptoOps.c) + rctCryptoOps.c + bulletproofs.cc) set(ringct_headers) set(ringct_private_headers rctOps.h rctSigs.h - rctTypes.h) + rctTypes.h + bulletproofs.h) monero_private_headers(ringct ${crypto_private_headers}) @@ -51,4 +53,5 @@ target_link_libraries(ringct cncrypto cryptonote_basic PRIVATE + ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc new file mode 100644 index 000000000..51cf9e3be --- /dev/null +++ b/src/ringct/bulletproofs.cc @@ -0,0 +1,761 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Java code by Sarang Noether + +#include <stdlib.h> +#include <openssl/ssl.h> +#include <boost/thread/mutex.hpp> +#include "misc_log_ex.h" +#include "common/perf_timer.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "rctOps.h" +#include "bulletproofs.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bulletproofs" + +//#define DEBUG_BP + +#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) + +namespace rct +{ + +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b); +static rct::keyV vector_powers(rct::key x, size_t n); +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); + +static constexpr size_t maxN = 64; +static rct::key Hi[maxN], Gi[maxN]; +static ge_dsmp Gprecomp[64], Hprecomp[64]; +static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; +static const rct::keyV oneN = vector_powers(rct::identity(), maxN); +static const rct::keyV twoN = vector_powers(TWO, maxN); +static const rct::key ip12 = inner_product(oneN, twoN); +static boost::mutex init_mutex; + +static rct::key get_exponent(const rct::key &base, size_t idx) +{ + static const std::string salt("bulletproof"); + std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx); + return rct::hashToPoint(rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); +} + +static void init_exponents() +{ + boost::lock_guard<boost::mutex> lock(init_mutex); + + static bool init_done = false; + if (init_done) + return; + for (size_t i = 0; i < maxN; ++i) + { + Hi[i] = get_exponent(rct::H, i * 2); + rct::precomp(Hprecomp[i], Hi[i]); + Gi[i] = get_exponent(rct::H, i * 2 + 1); + rct::precomp(Gprecomp[i], Gi[i]); + } + init_done = true; +} + +/* Given two scalar arrays, construct a vector commitment */ +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN"); + rct::key res = rct::identity(); + for (size_t i = 0; i < a.size(); ++i) + { + rct::key term; + rct::addKeys3(term, a[i], Gprecomp[i], b[i], Hprecomp[i]); + rct::addKeys(res, res, term); + } + return res; +} + +/* Compute a custom vector-scalar commitment */ +static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(A.size() == B.size(), "Incompatible sizes of A and B"); + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN"); + rct::key res = rct::identity(); + for (size_t i = 0; i < a.size(); ++i) + { + rct::key term; +#if 0 + // we happen to know where A and B might fall, so don't bother checking the rest + ge_dsmp *Acache = NULL, *Bcache = NULL; + ge_dsmp Acache_custom[1], Bcache_custom[1]; + if (Gi[i] == A[i]) + Acache = Gprecomp + i; + else if (i<32 && Gi[i+32] == A[i]) + Acache = Gprecomp + i + 32; + else + { + rct::precomp(Acache_custom[0], A[i]); + Acache = Acache_custom; + } + if (i == 0 && B[i] == Hi[0]) + Bcache = Hprecomp; + else + { + rct::precomp(Bcache_custom[0], B[i]); + Bcache = Bcache_custom; + } + rct::addKeys3(term, a[i], *Acache, b[i], *Bcache); +#else + ge_dsmp Acache, Bcache; + rct::precomp(Bcache, B[i]); + rct::addKeys3(term, a[i], A[i], b[i], Bcache); +#endif + rct::addKeys(res, res, term); + } + return res; +} + +/* Given a scalar, construct a vector of powers */ +static rct::keyV vector_powers(rct::key x, size_t n) +{ + rct::keyV res(n); + if (n == 0) + return res; + res[0] = rct::identity(); + if (n == 1) + return res; + res[1] = x; + for (size_t i = 2; i < n; ++i) + { + sc_mul(res[i].bytes, res[i-1].bytes, x.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the inner product */ +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_muladd(res.bytes, a[i].bytes, b[i].bytes, res.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the Hadamard product */ +static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Given two curvepoint arrays, construct the Hadamard product */ +static rct::keyV hadamard2(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::addKeys(res[i], a[i], b[i]); + } + return res; +} + +/* Add two vectors */ +static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Subtract two vectors */ +static rct::keyV vector_subtract(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_sub(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Multiply a scalar and a vector */ +static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, x.bytes); + } + return res; +} + +/* Exponentiate a curve vector by a scalar */ +static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::scalarmultKey(res[i], a[i], x); + } + return res; +} + +static rct::key switch_endianness(rct::key k) +{ + std::reverse(k.bytes, k.bytes + sizeof(k)); + return k; +} + +/* Compute the inverse of a scalar, the stupid way */ +static rct::key invert(const rct::key &x) +{ + rct::key inv; + + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *X = BN_new(); + BIGNUM *L = BN_new(); + BIGNUM *I = BN_new(); + + BN_bin2bn(switch_endianness(x).bytes, sizeof(rct::key), X); + BN_bin2bn(switch_endianness(rct::curveOrder()).bytes, sizeof(rct::key), L); + + CHECK_AND_ASSERT_THROW_MES(BN_mod_inverse(I, X, L, ctx), "Failed to invert"); + + const int len = BN_num_bytes(I); + CHECK_AND_ASSERT_THROW_MES((size_t)len <= sizeof(rct::key), "Invalid number length"); + inv = rct::zero(); + BN_bn2bin(I, inv.bytes); + std::reverse(inv.bytes, inv.bytes + len); + + BN_free(I); + BN_free(L); + BN_free(X); + BN_CTX_free(ctx); + +#ifdef DEBUG_BP + rct::key tmp; + sc_mul(tmp.bytes, inv.bytes, x.bytes); + CHECK_AND_ASSERT_THROW_MES(tmp == rct::identity(), "invert failed"); +#endif + return inv; +} + +/* Compute the slice of a vector */ +static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) +{ + CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); + CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); + CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); + rct::keyV res(stop - start); + for (size_t i = start; i < stop; ++i) + { + res[i - start] = a[i]; + } + return res; +} + +/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ +Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) +{ + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1<<logN; + + rct::key V; + rct::keyV aL(N), aR(N); + + PERF_TIMER_START_BP(PROVE_v); + rct::addKeys2(V, gamma, sv, rct::H); + PERF_TIMER_STOP(PROVE_v); + + PERF_TIMER_START_BP(PROVE_aLaR); + for (size_t i = N; i-- > 0; ) + { + if (sv[i/8] & (((uint64_t)1)<<(i%8))) + { + aL[i] = rct::identity(); + } + else + { + aL[i] = rct::zero(); + } + sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes); + } + PERF_TIMER_STOP(PROVE_aLaR); + + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[i] == rct::identity()) + test_aL += ((uint64_t)1)<<i; + if (aR[i] == rct::zero()) + test_aR += ((uint64_t)1)<<i; + } + uint64_t v_test = 0; + for (int n = 0; n < 8; ++n) v_test |= (((uint64_t)sv[n]) << (8*n)); + CHECK_AND_ASSERT_THROW_MES(test_aL == v_test, "test_aL failed"); + CHECK_AND_ASSERT_THROW_MES(test_aR == v_test, "test_aR failed"); +#endif + + PERF_TIMER_START_BP(PROVE_step1); + // PAPER LINES 38-39 + rct::key alpha = rct::skGen(); + rct::key ve = vector_exponent(aL, aR); + rct::key A; + rct::addKeys(A, ve, rct::scalarmultBase(alpha)); + + // PAPER LINES 40-42 + rct::keyV sL = rct::skvGen(N), sR = rct::skvGen(N); + rct::key rho = rct::skGen(); + ve = vector_exponent(sL, sR); + rct::key S; + rct::addKeys(S, ve, rct::scalarmultBase(rho)); + + // PAPER LINES 43-45 + rct::keyV hashed; + hashed.push_back(A); + hashed.push_back(S); + rct::key y = rct::hash_to_scalar(hashed); + rct::key z = rct::hash_to_scalar(y); + + // Polynomial construction before PAPER LINE 46 + rct::key t0 = rct::zero(); + rct::key t1 = rct::zero(); + rct::key t2 = rct::zero(); + + const auto yN = vector_powers(y, N); + + rct::key ip1y = inner_product(oneN, yN); + rct::key tmp; + sc_muladd(t0.bytes, z.bytes, ip1y.bytes, t0.bytes); + + rct::key zsq; + sc_mul(zsq.bytes, z.bytes, z.bytes); + sc_muladd(t0.bytes, zsq.bytes, sv.bytes, t0.bytes); + + rct::key k = rct::zero(); + sc_mulsub(k.bytes, zsq.bytes, ip1y.bytes, k.bytes); + + rct::key zcu; + sc_mul(zcu.bytes, zsq.bytes, z.bytes); + sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes); + sc_add(t0.bytes, t0.bytes, k.bytes); + + // DEBUG: Test the value of t0 has the correct form +#ifdef DEBUG_BP + rct::key test_t0 = rct::zero(); + rct::key iph = inner_product(aL, hadamard(aR, yN)); + sc_add(test_t0.bytes, test_t0.bytes, iph.bytes); + rct::key ips = inner_product(vector_subtract(aL, aR), yN); + sc_muladd(test_t0.bytes, z.bytes, ips.bytes, test_t0.bytes); + rct::key ipt = inner_product(twoN, aL); + sc_muladd(test_t0.bytes, zsq.bytes, ipt.bytes, test_t0.bytes); + sc_add(test_t0.bytes, test_t0.bytes, k.bytes); + CHECK_AND_ASSERT_THROW_MES(t0 == test_t0, "t0 check failed"); +#endif + PERF_TIMER_STOP(PROVE_step1); + + PERF_TIMER_START_BP(PROVE_step2); + const auto HyNsR = hadamard(yN, sR); + const auto vpIz = vector_scalar(oneN, z); + const auto vp2zsq = vector_scalar(twoN, zsq); + const auto aL_vpIz = vector_subtract(aL, vpIz); + const auto aR_vpIz = vector_add(aR, vpIz); + + rct::key ip1 = inner_product(aL_vpIz, HyNsR); + sc_add(t1.bytes, t1.bytes, ip1.bytes); + + rct::key ip2 = inner_product(sL, vector_add(hadamard(yN, aR_vpIz), vp2zsq)); + sc_add(t1.bytes, t1.bytes, ip2.bytes); + + rct::key ip3 = inner_product(sL, HyNsR); + sc_add(t2.bytes, t2.bytes, ip3.bytes); + + // PAPER LINES 47-48 + rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); + + rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + + // PAPER LINES 49-51 + hashed.clear(); + hashed.push_back(z); + hashed.push_back(T1); + hashed.push_back(T2); + rct::key x = rct::hash_to_scalar(hashed); + + // PAPER LINES 52-53 + rct::key taux = rct::zero(); + sc_mul(taux.bytes, tau1.bytes, x.bytes); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes); + sc_muladd(taux.bytes, gamma.bytes, zsq.bytes, taux.bytes); + rct::key mu; + sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); + + // PAPER LINES 54-57 + rct::keyV l = vector_add(aL_vpIz, vector_scalar(sL, x)); + rct::keyV r = vector_add(hadamard(yN, vector_add(aR_vpIz, vector_scalar(sR, x))), vp2zsq); + PERF_TIMER_STOP(PROVE_step2); + + PERF_TIMER_START_BP(PROVE_step3); + rct::key t = inner_product(l, r); + + // DEBUG: Test if the l and r vectors match the polynomial forms +#ifdef DEBUG_BP + rct::key test_t; + sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes); + sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes); + CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); +#endif + + // PAPER LINES 32-33 + hashed.clear(); + hashed.push_back(x); + hashed.push_back(taux); + hashed.push_back(mu); + hashed.push_back(t); + rct::key x_ip = rct::hash_to_scalar(hashed); + + // These are used in the inner product rounds + size_t nprime = N; + rct::keyV Gprime(N); + rct::keyV Hprime(N); + rct::keyV aprime(N); + rct::keyV bprime(N); + const rct::key yinv = invert(y); + rct::key yinvpow = rct::identity(); + for (size_t i = 0; i < N; ++i) + { + Gprime[i] = Gi[i]; + Hprime[i] = scalarmultKey(Hi[i], yinvpow); + sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + aprime[i] = l[i]; + bprime[i] = r[i]; + } + rct::keyV L(logN); + rct::keyV R(logN); + int round = 0; + rct::keyV w(logN); // this is the challenge x in the inner product protocol + PERF_TIMER_STOP(PROVE_step3); + + PERF_TIMER_START_BP(PROVE_step4); + // PAPER LINE 13 + while (nprime > 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + hashed.clear(); + if (round == 0) + { + hashed.push_back(L[0]); + hashed.push_back(R[0]); + w[0] = rct::hash_to_scalar(hashed); + } + else + { + hashed.push_back(w[round - 1]); + hashed.push_back(L[round]); + hashed.push_back(R[round]); + w[round] = rct::hash_to_scalar(hashed); + } + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma) +{ + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::key sv = rct::zero(); + sv.bytes[0] = v & 255; + sv.bytes[1] = (v >> 8) & 255; + sv.bytes[2] = (v >> 16) & 255; + sv.bytes[3] = (v >> 24) & 255; + sv.bytes[4] = (v >> 32) & 255; + sv.bytes[5] = (v >> 40) & 255; + sv.bytes[6] = (v >> 48) & 255; + sv.bytes[7] = (v >> 56) & 255; + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE(sv, gamma); +} + +/* Given a range proof, determine if it is valid */ +bool bulletproof_VERIFY(const Bulletproof &proof) +{ + init_exponents(); + + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); + CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); + CHECK_AND_ASSERT_MES(proof.L.size() == 6, false, "Proof is not for 64 bits"); + + const size_t logN = proof.L.size(); + const size_t N = 1 << logN; + + // Reconstruct the challenges + PERF_TIMER_START_BP(VERIFY); + PERF_TIMER_START_BP(VERIFY_start); + rct::keyV hashed; + hashed.push_back(proof.A); + hashed.push_back(proof.S); + rct::key y = rct::hash_to_scalar(hashed); + rct::key z = rct::hash_to_scalar(y); + hashed.clear(); + hashed.push_back(z); + hashed.push_back(proof.T1); + hashed.push_back(proof.T2); + rct::key x = rct::hash_to_scalar(hashed); + PERF_TIMER_STOP(VERIFY_start); + + PERF_TIMER_START_BP(VERIFY_line_60); + // Reconstruct the challenges + hashed.clear(); + hashed.push_back(x); + hashed.push_back(proof.taux); + hashed.push_back(proof.mu); + hashed.push_back(proof.t); + rct::key x_ip = hash_to_scalar(hashed); + PERF_TIMER_STOP(VERIFY_line_60); + + PERF_TIMER_START_BP(VERIFY_line_61); + // PAPER LINE 61 + rct::key L61Left = rct::addKeys(rct::scalarmultBase(proof.taux), rct::scalarmultKey(rct::H, proof.t)); + + rct::key k = rct::zero(); + const auto yN = vector_powers(y, N); + rct::key ip1y = inner_product(oneN, yN); + rct::key zsq; + sc_mul(zsq.bytes, z.bytes, z.bytes); + rct::key tmp, tmp2; + sc_mulsub(k.bytes, zsq.bytes, ip1y.bytes, k.bytes); + rct::key zcu; + sc_mul(zcu.bytes, zsq.bytes, z.bytes); + sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes); + PERF_TIMER_STOP(VERIFY_line_61); + + PERF_TIMER_START_BP(VERIFY_line_61rl); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + rct::key L61Right = rct::scalarmultKey(rct::H, tmp); + + CHECK_AND_ASSERT_MES(proof.V.size() == 1, false, "proof.V does not have exactly one element"); + tmp = rct::scalarmultKey(proof.V[0], zsq); + rct::addKeys(L61Right, L61Right, tmp); + + tmp = rct::scalarmultKey(proof.T1, x); + rct::addKeys(L61Right, L61Right, tmp); + + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + tmp = rct::scalarmultKey(proof.T2, xsq); + rct::addKeys(L61Right, L61Right, tmp); + PERF_TIMER_STOP(VERIFY_line_61rl); + + if (!(L61Right == L61Left)) + { + MERROR("Verification failure at step 1"); + return false; + } + + PERF_TIMER_START_BP(VERIFY_line_62); + // PAPER LINE 62 + rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)); + PERF_TIMER_STOP(VERIFY_line_62); + + // Compute the number of rounds for the inner product + const size_t rounds = proof.L.size(); + CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); + + PERF_TIMER_START_BP(VERIFY_line_21_22); + // PAPER LINES 21-22 + // The inner product challenges are computed per round + rct::keyV w(rounds); + hashed.clear(); + hashed.push_back(proof.L[0]); + hashed.push_back(proof.R[0]); + w[0] = rct::hash_to_scalar(hashed); + for (size_t i = 1; i < rounds; ++i) + { + hashed.clear(); + hashed.push_back(w[i-1]); + hashed.push_back(proof.L[i]); + hashed.push_back(proof.R[i]); + w[i] = rct::hash_to_scalar(hashed); + } + PERF_TIMER_STOP(VERIFY_line_21_22); + + PERF_TIMER_START_BP(VERIFY_line_24_25); + // Basically PAPER LINES 24-25 + // Compute the curvepoints from G[i] and H[i] + rct::key inner_prod = rct::identity(); + rct::key yinvpow = rct::identity(); + rct::key ypow = rct::identity(); + + PERF_TIMER_START_BP(VERIFY_line_24_25_invert); + const rct::key yinv = invert(y); + rct::keyV winv(rounds); + for (size_t i = 0; i < rounds; ++i) + winv[i] = invert(w[i]); + PERF_TIMER_STOP(VERIFY_line_24_25_invert); + + for (size_t i = 0; i < N; ++i) + { + // Convert the index to binary IN REVERSE and construct the scalar exponent + rct::key g_scalar = proof.a; + rct::key h_scalar; + sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); + + for (size_t j = rounds; j-- > 0; ) + { + size_t J = w.size() - j - 1; + + if ((i & (((size_t)1)<<j)) == 0) + { + sc_mul(g_scalar.bytes, g_scalar.bytes, winv[J].bytes); + sc_mul(h_scalar.bytes, h_scalar.bytes, w[J].bytes); + } + else + { + sc_mul(g_scalar.bytes, g_scalar.bytes, w[J].bytes); + sc_mul(h_scalar.bytes, h_scalar.bytes, winv[J].bytes); + } + } + + // Adjust the scalars using the exponents from PAPER LINE 62 + sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes); + sc_mul(tmp.bytes, zsq.bytes, twoN[i].bytes); + sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes); + sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); + + // Now compute the basepoint's scalar multiplication + // Each of these could be written as a multiexp operation instead + rct::addKeys3(tmp, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]); + rct::addKeys(inner_prod, inner_prod, tmp); + + if (i != N-1) + { + sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + sc_mul(ypow.bytes, ypow.bytes, y.bytes); + } + } + PERF_TIMER_STOP(VERIFY_line_24_25); + + PERF_TIMER_START_BP(VERIFY_line_26); + // PAPER LINE 26 + rct::key pprime; + sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); + rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); + + for (size_t i = 0; i < rounds; ++i) + { + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); +#if 1 + ge_dsmp cacheL, cacheR; + rct::precomp(cacheL, proof.L[i]); + rct::precomp(cacheR, proof.R[i]); + rct::addKeys3(tmp, tmp, cacheL, tmp2, cacheR); + rct::addKeys(pprime, pprime, tmp); +#else + rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp)); + rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2)); +#endif + } + sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); + rct::addKeys(pprime, pprime, rct::scalarmultKey(rct::H, tmp)); + PERF_TIMER_STOP(VERIFY_line_26); + + PERF_TIMER_START_BP(VERIFY_step2_check); + sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes); + sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); + tmp = rct::scalarmultKey(rct::H, tmp); + rct::addKeys(tmp, tmp, inner_prod); + PERF_TIMER_STOP(VERIFY_step2_check); + if (!(pprime == tmp)) + { + MERROR("Verification failure at step 2"); + return false; + } + + PERF_TIMER_STOP(VERIFY); + return true; +} + +} diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h new file mode 100644 index 000000000..aca470f47 --- /dev/null +++ b/src/ringct/bulletproofs.h @@ -0,0 +1,47 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Java code by Sarang Noether + +#pragma once + +#ifndef BULLETPROOFS_H +#define BULLETPROOFS_H + +#include "rctTypes.h" + +namespace rct +{ + +Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma); +Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); +bool bulletproof_VERIFY(const Bulletproof &proof); + +} + +#endif diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index d0e0964b6..8e94b52b3 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -220,6 +220,11 @@ namespace rct { ge_p3_tobytes(AB.bytes, &A2); } + rct::key addKeys(const key &A, const key &B) { + key k; + addKeys(k, A, B); + return k; + } //addKeys1 //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point @@ -257,6 +262,15 @@ namespace rct { ge_tobytes(aAbB.bytes, &rv); } + //addKeys3 + //aAbB = a*A + b*B where a, b are scalars, A, B are curve points + //A and B must be input after applying "precomp" + void addKeys3(key &aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) { + ge_p2 rv; + ge_double_scalarmult_precomp_vartime2(&rv, a.bytes, A, b.bytes, B); + ge_tobytes(aAbB.bytes, &rv); + } + //subtract Keys (subtracts curve points) //AB = A - B where A, B are curve points diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 412450c18..3f8f6955c 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -123,6 +123,7 @@ namespace rct { //for curve points: AB = A + B void addKeys(key &AB, const key &A, const key &B); + rct::key addKeys(const key &A, const key &B); //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point void addKeys1(key &aGB, const key &a, const key & B); //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point @@ -133,6 +134,7 @@ namespace rct { //aAbB = a*A + b*B where a, b are scalars, A, B are curve points //B must be input after applying "precomp" void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B); + void addKeys3(key &aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B); //AB = A - B where A, B are curve points void subKeys(key &AB, const key &A, const key &B); //checks if A, B are equal as curve points diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 946325367..38b213e8b 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -33,6 +33,7 @@ #include "common/threadpool.h" #include "common/util.h" #include "rctSigs.h" +#include "bulletproofs.h" #include "cryptonote_basic/cryptonote_format_utils.h" using namespace crypto; @@ -42,6 +43,15 @@ using namespace std; #define MONERO_DEFAULT_LOG_CATEGORY "ringct" namespace rct { + Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) + { + mask = rct::skGen(); + Bulletproof proof = bulletproof_PROVE(amount, mask); + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); + C = proof.V[0]; + return proof; + } + //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; @@ -335,16 +345,41 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; - kv.reserve((64*3+1) * rv.p.rangeSigs.size()); - for (auto r: rv.p.rangeSigs) + if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof) + { + kv.reserve((6*2+9) * rv.p.bulletproofs.size()); + for (const auto &p: rv.p.bulletproofs) + { + // V are not hashed as they're expanded from outPk.mask + // (and thus hashed as part of rctSigBase above) + kv.push_back(p.A); + kv.push_back(p.S); + kv.push_back(p.T1); + kv.push_back(p.T2); + kv.push_back(p.taux); + kv.push_back(p.mu); + for (size_t n = 0; n < p.L.size(); ++n) + kv.push_back(p.L[n]); + for (size_t n = 0; n < p.R.size(); ++n) + kv.push_back(p.R[n]); + kv.push_back(p.a); + kv.push_back(p.b); + kv.push_back(p.t); + } + } + else { - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.s0[n]); - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.s1[n]); - kv.push_back(r.asig.ee); - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.Ci[n]); + kv.reserve((64*3+1) * rv.p.rangeSigs.size()); + for (const auto &r: rv.p.rangeSigs) + { + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s0[n]); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s1[n]); + kv.push_back(r.asig.ee); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.Ci[n]); + } } hashes.push_back(cn_fast_hash(kv)); return cn_fast_hash(hashes); @@ -563,7 +598,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -572,10 +607,13 @@ namespace rct { } rctSig rv; - rv.type = RCTTypeFull; + rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - rv.p.rangeSigs.resize(destinations.size()); + if (bulletproof) + rv.p.bulletproofs.resize(destinations.size()); + else + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -585,8 +623,14 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + if (bulletproof) + rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + else + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG + if (bulletproof) + CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof"); + else CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif @@ -618,12 +662,12 @@ namespace rct { ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk, false); } //RCT simple //for post-rct only - rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk) { + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof) { CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -635,10 +679,13 @@ namespace rct { } rctSig rv; - rv.type = RCTTypeSimple; + rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple; rv.message = message; rv.outPk.resize(destinations.size()); - rv.p.rangeSigs.resize(destinations.size()); + if (bulletproof) + rv.p.bulletproofs.resize(destinations.size()); + else + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i; @@ -650,10 +697,16 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); - #ifdef DBG - verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - #endif + if (bulletproof) + rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + else + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + if (bulletproof) + CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof"); + else + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); + #endif sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); @@ -699,7 +752,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, false); } //RingCT protocol @@ -714,10 +767,13 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); if (semantics) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + if (rv.type == RCTTypeFullBulletproof) + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + else + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); } @@ -736,7 +792,10 @@ namespace rct { DP("range proofs verified?"); for (size_t i = 0; i < rv.outPk.size(); i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + if (rv.p.rangeSigs.empty()) + results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); + else + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); } waiter.wait(); @@ -776,10 +835,13 @@ namespace rct { { PERF_TIMER(verRctSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig"); if (semantics) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + if (rv.type == RCTTypeSimpleBulletproof) + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + else + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); } @@ -820,7 +882,10 @@ namespace rct { results.resize(rv.outPk.size()); for (size_t i = 0; i < rv.outPk.size(); i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + if (rv.p.rangeSigs.empty()) + results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); + else + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); } waiter.wait(); @@ -869,9 +934,9 @@ namespace rct { // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; @@ -897,9 +962,9 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index d158f06f0..46c9cb2df 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -118,10 +118,10 @@ namespace rct { //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSimple(const rctSig & rv, bool semantics); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 8147cb602..50dfdb432 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -161,6 +161,39 @@ namespace rct { FIELD(Ci) END_SERIALIZE() }; + + struct Bulletproof + { + rct::keyV V; + rct::key A, S, T1, T2; + rct::key taux, mu; + rct::keyV L, R; + rct::key a, b, t; + + Bulletproof() {} + Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): + V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} + + BEGIN_SERIALIZE_OBJECT() + // Commitments aren't saved, they're restored via outPk + // FIELD(V) + FIELD(A) + FIELD(S) + FIELD(T1) + FIELD(T2) + FIELD(taux) + FIELD(mu) + FIELD(L) + FIELD(R) + FIELD(a) + FIELD(b) + FIELD(t) + + if (L.empty() || L.size() != R.size()) + return false; + END_SERIALIZE() + }; + //A container to hold all signatures necessary for RingCT // rangeSigs holds all the rangeproof data of a transaction // MG holds the MLSAG signature of a transaction @@ -172,6 +205,8 @@ namespace rct { RCTTypeNull = 0, RCTTypeFull = 1, RCTTypeSimple = 2, + RCTTypeFullBulletproof = 3, + RCTTypeSimpleBulletproof = 4, }; struct rctSigBase { uint8_t type; @@ -189,13 +224,13 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple) + if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) return false; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help // FIELD(message) - not serialized, it can be reconstructed // FIELD(mixRing) - not serialized, it can be reconstructed - if (type == RCTTypeSimple) + if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -241,6 +276,7 @@ namespace rct { }; struct rctSigPrunable { std::vector<rangeSig> rangeSigs; + std::vector<Bulletproof> bulletproofs; std::vector<mgSig> MGs; // simple rct has N, full has 1 template<bool W, template <bool> class Archive> @@ -248,26 +284,44 @@ namespace rct { { if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple) + if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) return false; - ar.tag("rangeSigs"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs); - if (rangeSigs.size() != outputs) - return false; - for (size_t i = 0; i < outputs; ++i) + if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof) { - FIELDS(rangeSigs[i]) - if (outputs - i > 1) - ar.delimit_array(); + ar.tag("bp"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); + if (bulletproofs.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + FIELDS(bulletproofs[i]) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } + else + { + ar.tag("rangeSigs"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs); + if (rangeSigs.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + FIELDS(rangeSigs[i]) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); } - ar.end_array(); ar.tag("MGs"); ar.begin_array(); // we keep a byte for size of MGs, because we don't know whether this is // a simple or full rct signature, and it's starting to annoy the hell out of me - size_t mg_elements = type == RCTTypeSimple ? inputs : 1; + size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); if (MGs.size() != mg_elements) return false; @@ -285,7 +339,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { ar.begin_array(); - size_t mg_ss2_elements = (type == RCTTypeSimple ? 1 : inputs) + 1; + size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; @@ -464,6 +518,7 @@ VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig"); VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig"); VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig"); VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig"); +VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof"); VARIANT_TAG(binary_archive, rct::key, 0x90); VARIANT_TAG(binary_archive, rct::key64, 0x91); @@ -477,6 +532,7 @@ VARIANT_TAG(binary_archive, rct::mgSig, 0x98); VARIANT_TAG(binary_archive, rct::rangeSig, 0x99); VARIANT_TAG(binary_archive, rct::boroSig, 0x9a); VARIANT_TAG(binary_archive, rct::rctSig, 0x9b); +VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c); VARIANT_TAG(json_archive, rct::key, "rct_key"); VARIANT_TAG(json_archive, rct::key64, "rct_key64"); @@ -490,5 +546,6 @@ VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig"); VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig"); VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig"); VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig"); +VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof"); #endif /* RCTTYPES_H */ diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c9c668e8f..c2206c89a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -49,7 +49,7 @@ using namespace epee; #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" #define MAX_RESTRICTED_FAKE_OUTS_COUNT 40 -#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500 +#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 5000 namespace { @@ -152,6 +152,7 @@ namespace cryptonote res.status = CORE_RPC_STATUS_OK; res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); + res.offline = m_core.offline(); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1332,6 +1333,7 @@ namespace cryptonote res.status = CORE_RPC_STATUS_OK; res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); + res.offline = m_core.offline(); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1549,7 +1551,7 @@ namespace cryptonote res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM; return false; } - epee::net_utils::connection_basic::set_rate_down_limit(nodetool::default_limit_down * 1024); + epee::net_utils::connection_basic::set_rate_down_limit(nodetool::default_limit_down); } if (req.limit_up > 0) @@ -1563,7 +1565,7 @@ namespace cryptonote res.status = CORE_RPC_ERROR_CODE_WRONG_PARAM; return false; } - epee::net_utils::connection_basic::set_rate_up_limit(nodetool::default_limit_up * 1024); + epee::net_utils::connection_basic::set_rate_up_limit(nodetool::default_limit_up); } res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit(); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 58a6ce9e1..ad0bff077 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,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 1 -#define CORE_RPC_VERSION_MINOR 16 +#define CORE_RPC_VERSION_MINOR 17 #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) @@ -928,6 +928,7 @@ namespace cryptonote uint64_t block_size_limit; uint64_t start_time; uint64_t free_space; + bool offline; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -949,6 +950,7 @@ namespace cryptonote KV_SERIALIZE(block_size_limit) KV_SERIALIZE(start_time) KV_SERIALIZE(free_space) + KV_SERIALIZE(offline) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 6e6e51528..2c86d4054 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1007,6 +1007,7 @@ void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapid val.SetObject(); INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs); + INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs); INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs); } @@ -1018,6 +1019,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig) } GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs); + GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs); GET_FROM_JSON_OBJECT(val, sig.MGs, MGs); } @@ -1052,6 +1054,45 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) } } +void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, V, p.V); + INSERT_INTO_JSON_OBJECT(val, doc, A, p.A); + INSERT_INTO_JSON_OBJECT(val, doc, S, p.S); + INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1); + INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2); + INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux); + INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu); + INSERT_INTO_JSON_OBJECT(val, doc, L, p.L); + INSERT_INTO_JSON_OBJECT(val, doc, R, p.R); + INSERT_INTO_JSON_OBJECT(val, doc, a, p.a); + INSERT_INTO_JSON_OBJECT(val, doc, b, p.b); + INSERT_INTO_JSON_OBJECT(val, doc, t, p.t); +} + +void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, p.V, V); + GET_FROM_JSON_OBJECT(val, p.A, A); + GET_FROM_JSON_OBJECT(val, p.S, S); + GET_FROM_JSON_OBJECT(val, p.T1, T1); + GET_FROM_JSON_OBJECT(val, p.T2, T2); + GET_FROM_JSON_OBJECT(val, p.taux, taux); + GET_FROM_JSON_OBJECT(val, p.mu, mu); + GET_FROM_JSON_OBJECT(val, p.L, L); + GET_FROM_JSON_OBJECT(val, p.R, R); + GET_FROM_JSON_OBJECT(val, p.a, a); + GET_FROM_JSON_OBJECT(val, p.b, b); + GET_FROM_JSON_OBJECT(val, p.t, t); +} + void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val) { val.SetObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 7b9519c48..5dca7b249 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -274,6 +274,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig); void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); +void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p); + void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a307f9d3d..bbf794c05 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -42,6 +42,7 @@ #include <boost/program_options.hpp> #include <boost/algorithm/string.hpp> #include <boost/format.hpp> +#include <boost/regex.hpp> #include "include_base_utils.h" #include "common/i18n.h" #include "common/command_line.h" @@ -432,7 +433,7 @@ bool simple_wallet::print_seed(bool encrypted) m_wallet->set_seed_language(mnemonic_language); } - std::string seed_pass; + epee::wipeable_string seed_pass; if (encrypted) { auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed")); @@ -933,6 +934,10 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), tr("Stop mining in the daemon.")); + m_cmd_binder.set_handler("set_daemon", + boost::bind(&simple_wallet::set_daemon, this, _1), + tr("set_daemon <host>[:<port>]"), + tr("Set another daemon to connect to.")); m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), tr("Save the current blockchain data.")); @@ -1344,8 +1349,8 @@ bool simple_wallet::ask_wallet_create_if_needed() */ void simple_wallet::print_seed(std::string seed) { - success_msg_writer(true) << "\n" << tr("PLEASE NOTE: the following 25 words can be used to recover access to your wallet. " - "Please write them down and store them somewhere safe and secure. Please do not store them in " + success_msg_writer(true) << "\n" << tr("NOTE: the following 25 words 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"); boost::replace_nth(seed, " ", 15, "\n"); boost::replace_nth(seed, " ", 7, "\n"); @@ -1421,7 +1426,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none")); if (std::cin.eof() || !pwd_container) return false; - std::string seed_pass = pwd_container->password(); + epee::wipeable_string seed_pass = pwd_container->password(); if (!seed_pass.empty()) m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass); } @@ -1890,7 +1895,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version) if (!silent) fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " << tr("Daemon either is not started or wrong port was passed. " - "Please make sure daemon is running or restart the wallet with the correct daemon address."); + "Please make sure daemon is running or change the daemon address using the 'set_daemon' command."); return false; } if (!m_allow_mismatched_daemon_version && ((*version >> 16) != CORE_RPC_VERSION_MAJOR)) @@ -2082,12 +2087,12 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("wallet file path not valid: ") << m_wallet_file; return false; } - std::string password; + epee::wipeable_string password; try { auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter); m_wallet = std::move(rc.first); - password = std::move(rc.second).password(); + password = std::move(std::move(rc.second).password()); if (!m_wallet) { return false; @@ -2295,6 +2300,42 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::set_daemon(const std::vector<std::string>& args) +{ + std::string daemon_url; + + if (args.size() < 1) + { + fail_msg_writer() << tr("missing daemon URL argument"); + return true; + } + + boost::regex rgx("^(.*://)?([A-Za-z0-9\\-\\.]+)(:[0-9]+)?"); + boost::cmatch match; + // If user input matches URL regex + if (boost::regex_match(args[0].c_str(), match, rgx)) + { + if (match.length() < 4) + { + fail_msg_writer() << tr("Unexpected array length - Exited simple_wallet::set_daemon()"); + return true; + } + // If no port has been provided, use the default from config + if (!match[3].length()) + { + int daemon_port = m_wallet->testnet() ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; + daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port); + } else { + daemon_url = args[0]; + } + LOCK_IDLE_SCOPE(); + m_wallet->init(daemon_url); + } else { + fail_msg_writer() << tr("This does not seem to be a valid daemon URL."); + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::save_bc(const std::vector<std::string>& args) { if (!try_connect_to_daemon()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index f6405426b..ad174a636 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -128,7 +128,8 @@ namespace cryptonote bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); - bool save_bc(const std::vector<std::string>& args); + bool set_daemon(const std::vector<std::string> &args); + bool save_bc(const std::vector<std::string> &args); bool refresh(const std::vector<std::string> &args); bool show_balance_unlocked(bool detailed = false); bool show_balance(const std::vector<std::string> &args = std::vector<std::string>()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 181bb1852..59e759bfc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -456,7 +456,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_ } } -size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size) +size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) { size_t size = 0; @@ -480,7 +480,10 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra size += 1; // rangeSigs - size += (2*64*32+32+64*32) * n_outputs; + if (bulletproof) + size += ((2*6 + 4 + 5)*32 + 3) * n_outputs; + else + size += (2*64*32+32+64*32) * n_outputs; // MGs size += n_inputs * (64 * (mixin+1) + 32); @@ -501,14 +504,22 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra return size; } -size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size) +size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) { if (use_rct) - return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size); + return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof); else return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; } +uint8_t get_bulletproof_fork(bool testnet) +{ + if (testnet) + return 7; + else + return 255; // TODO +} + } //namespace namespace tools @@ -602,7 +613,7 @@ bool wallet2::is_deterministic() const return keys_deterministic; } //---------------------------------------------------------------------------------------------------- -bool wallet2::get_seed(std::string& electrum_words, const std::string &passphrase) const +bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase) const { bool keys_deterministic = is_deterministic(); if (!keys_deterministic) @@ -812,8 +823,10 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & switch (rv.type) { case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask); case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask); default: LOG_ERROR("Unsupported rct type: " << rv.type); @@ -2137,7 +2150,7 @@ bool wallet2::clear() * \param watch_only true to save only view key, false to save both spend and view keys * \return Whether it was successful. */ -bool wallet2::store_keys(const std::string& keys_file_name, const std::string& password, bool watch_only) +bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only) { std::string account_data; cryptonote::account_base account = m_account; @@ -2223,7 +2236,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p // Encrypt the entire JSON object. crypto::chacha8_key key; - crypto::generate_chacha8_key(password, key); + crypto::generate_chacha8_key(password.data(), password.size(), key); std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand<crypto::chacha8_iv>(); @@ -2253,7 +2266,7 @@ namespace * \param keys_file_name Name of wallet file * \param password Password of wallet file */ -bool wallet2::load_keys(const std::string& keys_file_name, const std::string& password) +bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password) { wallet2::keys_file_data keys_file_data; std::string buf; @@ -2264,7 +2277,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); crypto::chacha8_key key; - crypto::generate_chacha8_key(password, key); + crypto::generate_chacha8_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -2289,7 +2302,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_confirm_backlog = true; m_confirm_backlog_threshold = 0; } - else + else if(json.IsObject()) { if (!json.HasMember("key_data")) { @@ -2368,6 +2381,11 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa // Wallet is being opened without testnet flag but is saved as a testnet wallet. THROW_WALLET_EXCEPTION_IF(!m_testnet && field_testnet, error::wallet_internal_error, "Testnet wallet can not be opened as mainnet wallet"); } + else + { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password"); + return false; + } const cryptonote::account_keys& keys = m_account.get_keys(); r = epee::serialization::load_t_from_binary(m_account, account_data); @@ -2388,7 +2406,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password * */ -bool wallet2::verify_password(const std::string& password) const +bool wallet2::verify_password(const epee::wipeable_string& password) const { return verify_password(m_keys_file, password, m_watch_only); } @@ -2405,7 +2423,7 @@ bool wallet2::verify_password(const std::string& password) const * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password * */ -bool wallet2::verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only) +bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only) { wallet2::keys_file_data keys_file_data; std::string buf; @@ -2416,7 +2434,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const std::stri r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); crypto::chacha8_key key; - crypto::generate_chacha8_key(password, key); + crypto::generate_chacha8_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -2453,7 +2471,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const std::stri * \param two_random Whether it is a non-deterministic wallet * \return The secret key of the generated wallet */ -crypto::secret_key wallet2::generate(const std::string& wallet_, const std::string& password, +crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, const crypto::secret_key& recovery_param, bool recover, bool two_random) { clear(); @@ -2533,7 +2551,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const std::stri * \param password Password of wallet file * \param viewkey view secret key */ -void wallet2::generate(const std::string& wallet_, const std::string& password, +void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& viewkey) { @@ -2569,7 +2587,7 @@ void wallet2::generate(const std::string& wallet_, const std::string& password, * \param spendkey spend secret key * \param viewkey view secret key */ -void wallet2::generate(const std::string& wallet_, const std::string& password, +void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey) { @@ -2603,7 +2621,7 @@ void wallet2::generate(const std::string& wallet_, const std::string& password, * \param wallet_name Name of wallet file (should exist) * \param password Password for wallet file */ -void wallet2::rewrite(const std::string& wallet_name, const std::string& password) +void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_string& password) { prepare_file_names(wallet_name); boost::system::error_code ignored_ec; @@ -2616,7 +2634,7 @@ void wallet2::rewrite(const std::string& wallet_name, const std::string& passwor * \param wallet_name Base name of wallet file * \param password Password for wallet file */ -void wallet2::write_watch_only_wallet(const std::string& wallet_name, const std::string& password) +void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password) { prepare_file_names(wallet_name); boost::system::error_code ignored_ec; @@ -2752,7 +2770,7 @@ bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) co return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::load(const std::string& wallet_, const std::string& password) +void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password) { clear(); prepare_file_names(wallet_); @@ -2903,10 +2921,10 @@ std::string wallet2::path() const //---------------------------------------------------------------------------------------------------- void wallet2::store() { - store_to("", ""); + store_to("", epee::wipeable_string()); } //---------------------------------------------------------------------------------------------------- -void wallet2::store_to(const std::string &path, const std::string &password) +void wallet2::store_to(const std::string &path, const epee::wipeable_string &password) { trim_hashchain(); @@ -3763,9 +3781,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f 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(); + bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty(); 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); + 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, bulletproof); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet); // 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, @@ -4066,7 +4085,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto pending_tx ptx; // loop until fee is met without increasing tx size to next KB boundary. - const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size()); + const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false); uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); do { @@ -4648,7 +4667,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, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -4764,7 +4783,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry 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, true); + 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, bulletproof); LOG_PRINT_L2("constructed tx, r="<<r); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); @@ -5432,6 +5451,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); const bool use_rct = use_fork_rules(4, 0); + const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -5567,7 +5587,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp { // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which // will get us a known fee. - uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size()), fee_multiplier); + uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier); preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices); if (!preferred_inputs.empty()) { @@ -5670,7 +5690,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) + while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can fully pay that destination LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << @@ -5682,7 +5702,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp ++original_output_index; } - if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); @@ -5706,7 +5726,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); } } @@ -5715,14 +5735,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp cryptonote::transaction test_tx; pending_tx test_ptx; - const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " << tx.selected_transfers.size() << " inputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -5765,7 +5785,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp while (needed_fee > test_ptx.fee) { if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -5917,6 +5937,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton std::vector<std::vector<get_outs_entry>> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); + const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -5955,14 +5976,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_size_limit); - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size()); + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); if (try_tx) { cryptonote::transaction test_tx; pending_tx test_ptx; - const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); @@ -5971,7 +5992,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton tx.selected_transfers.size() << " outputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -5988,7 +6009,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eeec7c338..de1bed90c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -168,7 +168,7 @@ namespace tools //! Just parses variables. static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); - static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only); + static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only); wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), m_light_wallet(false), m_light_wallet_scanned_block_height(0), m_light_wallet_blockchain_height(0), m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0) {} @@ -398,7 +398,7 @@ namespace tools * \param two_random Whether it is a non-deterministic wallet * \return The secret key of the generated wallet */ - crypto::secret_key generate(const std::string& wallet, const std::string& password, + crypto::secret_key generate(const std::string& wallet, const epee::wipeable_string& password, const crypto::secret_key& recovery_param = crypto::secret_key(), bool recover = false, bool two_random = false); /*! @@ -408,7 +408,7 @@ namespace tools * \param viewkey view secret key * \param spendkey spend secret key */ - void generate(const std::string& wallet, const std::string& password, + void generate(const std::string& wallet, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); /*! @@ -417,7 +417,7 @@ namespace tools * \param password Password of wallet file * \param viewkey view secret key */ - void generate(const std::string& wallet, const std::string& password, + void generate(const std::string& wallet, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, const crypto::secret_key& viewkey = crypto::secret_key()); /*! @@ -425,23 +425,23 @@ namespace tools * \param wallet_name Name of wallet file (should exist) * \param password Password for wallet file */ - void rewrite(const std::string& wallet_name, const std::string& password); - void write_watch_only_wallet(const std::string& wallet_name, const std::string& password); - void load(const std::string& wallet, const std::string& password); + void rewrite(const std::string& wallet_name, const epee::wipeable_string& password); + void write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password); + void load(const std::string& wallet, const epee::wipeable_string& password); void store(); /*! * \brief store_to - stores wallet to another file(s), deleting old ones * \param path - path to the wallet file (keys and address filenames will be generated based on this filename) * \param password - password to protect new wallet (TODO: probably better save the password in the wallet object?) */ - void store_to(const std::string &path, const std::string &password); + void store_to(const std::string &path, const epee::wipeable_string &password); std::string path() const; /*! * \brief verifies given password is correct for default wallet keys file */ - bool verify_password(const std::string& password) const; + bool verify_password(const epee::wipeable_string& password) const; cryptonote::account_base& get_account(){return m_account;} const cryptonote::account_base& get_account()const{return m_account;} @@ -466,7 +466,7 @@ namespace tools * \brief Checks if deterministic wallet */ bool is_deterministic() const; - bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const; + bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; /*! * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. @@ -536,7 +536,7 @@ namespace tools 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); 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, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx); + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof); void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector<pending_tx>& ptx_vector); @@ -839,13 +839,13 @@ namespace tools * \param watch_only true to save only view key, false to save both spend and view keys * \return Whether it was successful. */ - bool store_keys(const std::string& keys_file_name, const std::string& password, bool watch_only = false); + bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); /*! * \brief Load wallet information from wallet file. * \param keys_file_name Name of wallet file * \param password Password of wallet file */ - bool load_keys(const std::string& keys_file_name, const std::string& password); + bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index c315684de..e790b9954 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -206,7 +206,8 @@ namespace tools } std::fputs(http_login->username.c_str(), rpc_login_file.handle()); std::fputc(':', rpc_login_file.handle()); - std::fputs(http_login->password.c_str(), rpc_login_file.handle()); + const epee::wipeable_string password = http_login->password; + std::fwrite(password.data(), 1, password.size(), rpc_login_file.handle()); std::fflush(rpc_login_file.handle()); if (std::ferror(rpc_login_file.handle())) { |