diff options
30 files changed, 463 insertions, 571 deletions
diff --git a/.gitignore b/.gitignore index cf7da3a04..a39168ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,8 @@ cmake-build-debug/ # KDE directory preferences .directory +### VSCode ### +.vscode/ ### Eclipse ### *.pydevproject diff --git a/CMakeLists.txt b/CMakeLists.txt index a0f11608c..3abd0722a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -472,9 +472,11 @@ endif() option(STATIC "Link libraries statically" ${DEFAULT_STATIC}) # This is a CMake built-in switch that concerns internal libraries -if (NOT DEFINED BUILD_SHARED_LIBS AND NOT STATIC AND CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") - set(BUILD_SHARED_LIBS ON) +set(BUILD_SHARED_LIBS_DEFAULT OFF) +if (NOT STATIC AND CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") + set(BUILD_SHARED_LIBS_DEFAULT ON) endif() +option(BUILD_SHARED_LIBS "Build internal libraries as shared" ${BUILD_SHARED_LIBS_DEFAULT}) if (BUILD_SHARED_LIBS) message(STATUS "Building internal libraries with position independent code") diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index f68b2bc99..258b07e2c 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -55,6 +55,7 @@ namespace net_utils std::string m_folder; std::vector<std::string> m_access_control_origins; boost::optional<login> m_user; + size_t m_max_content_length{std::numeric_limits<size_t>::max()}; critical_section m_lock; }; @@ -141,6 +142,7 @@ namespace net_utils config_type& m_config; bool m_want_close; size_t m_newlines; + size_t m_bytes_read; protected: i_service_endpoint* m_psnd_hndlr; t_connection_context& m_conn_context; diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index df0afc5cf..f7d2074b2 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -206,6 +206,7 @@ namespace net_utils m_config(config), m_want_close(false), m_newlines(0), + m_bytes_read(0), m_psnd_hndlr(psnd_hndlr), m_conn_context(conn_context) { @@ -221,6 +222,7 @@ namespace net_utils m_query_info.clear(); m_len_summary = 0; m_newlines = 0; + m_bytes_read = 0; return true; } //-------------------------------------------------------------------------------------------- @@ -243,6 +245,14 @@ namespace net_utils size_t ndel; + m_bytes_read += buf.size(); + if (m_bytes_read > m_config.m_max_content_length) + { + LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too much data: got " << m_bytes_read); + m_state = http_state_error; + return false; + } + if(m_cache.size()) m_cache += buf; else diff --git a/contrib/epee/include/serialization/enableable.h b/contrib/epee/include/serialization/enableable.h deleted file mode 100644 index e1be2a774..000000000 --- a/contrib/epee/include/serialization/enableable.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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. -// - -#pragma once - -namespace epee -{ - - template<class t_obj> - struct enableable - { - t_obj v; - bool enabled; - - enableable() - : v(t_obj()), enabled(true) - { // construct from defaults - } - - enableable(const t_obj& _v) - : v(_v), enabled(true) - { // construct from specified values - } - - enableable(const enableable<t_obj>& _v) - : v(_v.v), enabled(_v.enabled) - { // construct from specified values - } - }; -} diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index 2e4a0faad..06d74329f 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -30,7 +30,6 @@ #include <boost/utility/value_init.hpp> #include <boost/foreach.hpp> #include "misc_log_ex.h" -#include "enableable.h" #include "keyvalue_serialization_overloads.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 1f9d6b6d7..b637df5b0 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -81,24 +81,6 @@ namespace epee return obj._load(stg, hchild_section); } //------------------------------------------------------------------------------------------------------------------- - template<class serializible_type, class t_storage> - static bool serialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - if(!obj.enabled) - return true; - return serialize_t_obj(obj.v, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template<class serializible_type, class t_storage> - static bool unserialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - obj.enabled = false; - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false); - if(!hchild_section) return false; - obj.enabled = true; - return obj.v._load(stg, hchild_section); - } - //------------------------------------------------------------------------------------------------------------------- template<class stl_container, class t_storage> static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h index ae0be6a34..c15c9b826 100644 --- a/contrib/epee/include/storages/portable_storage_base.h +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -57,7 +57,7 @@ #define SERIALIZE_TYPE_UINT32 6 #define SERIALIZE_TYPE_UINT16 7 #define SERIALIZE_TYPE_UINT8 8 -#define SERIALIZE_TYPE_DUOBLE 9 +#define SERIALIZE_TYPE_DOUBLE 9 #define SERIALIZE_TYPE_STRING 10 #define SERIALIZE_TYPE_BOOL 11 #define SERIALIZE_TYPE_OBJECT 12 diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 6f081dbc7..d8a8a4a49 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -220,7 +220,7 @@ namespace epee case SERIALIZE_TYPE_UINT32: return read_ae<uint32_t>(); case SERIALIZE_TYPE_UINT16: return read_ae<uint16_t>(); case SERIALIZE_TYPE_UINT8: return read_ae<uint8_t>(); - case SERIALIZE_TYPE_DUOBLE: return read_ae<double>(); + case SERIALIZE_TYPE_DOUBLE: return read_ae<double>(); case SERIALIZE_TYPE_BOOL: return read_ae<bool>(); case SERIALIZE_TYPE_STRING: return read_ae<std::string>(); case SERIALIZE_TYPE_OBJECT: return read_ae<section>(); @@ -311,7 +311,7 @@ namespace epee case SERIALIZE_TYPE_UINT32: return read_se<uint32_t>(); case SERIALIZE_TYPE_UINT16: return read_se<uint16_t>(); case SERIALIZE_TYPE_UINT8: return read_se<uint8_t>(); - case SERIALIZE_TYPE_DUOBLE: return read_se<double>(); + case SERIALIZE_TYPE_DOUBLE: return read_se<double>(); case SERIALIZE_TYPE_BOOL: return read_se<bool>(); case SERIALIZE_TYPE_STRING: return read_se<std::string>(); case SERIALIZE_TYPE_OBJECT: return read_se<section>(); diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h index be4033dd8..70757607e 100644 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -107,7 +107,7 @@ namespace epee bool operator()(const array_entry_t<int32_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);} bool operator()(const array_entry_t<int16_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);} bool operator()(const array_entry_t<int8_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);} - bool operator()(const array_entry_t<double>& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const array_entry_t<double>& v) { return pack_pod_array_type(SERIALIZE_TYPE_DOUBLE, v);} bool operator()(const array_entry_t<bool>& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);} bool operator()(const array_entry_t<std::string>& arr_str) { @@ -160,7 +160,7 @@ namespace epee bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);} bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);} bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);} - bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DOUBLE, v);} bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);} bool operator()(const std::string& v) { diff --git a/include/INode.h b/include/INode.h deleted file mode 100644 index 8ce5c0242..000000000 --- a/include/INode.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2014-2022, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#pragma once - -#include <cstdint> -#include <system_error> - -namespace CryptoNote { - -class INodeObserver { -public: - virtual void initCompleted(std::error_code result) {} - - virtual void peerCountUpdated(size_t count) {} - virtual void lastLocalBlockHeightUpdated(uint64_t height) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) {} - - virtual void blockchainReorganized(uint64_t height) {} -}; - -class INode { -public: - virtual ~INode() = 0; - virtual void addObserver(INodeObserver* observer) = 0; - virtual void removeObserver(INodeObserver* observer) = 0; - - virtual void init() = 0; - virtual void shutdown() = 0; - - virtual size_t getPeerCount() = 0; - virtual uint64_t getLastLocalBlockHeight() = 0; - virtual uint64_t getLastKnownBlockHeight() = 0; -}; - -} diff --git a/include/IWallet.h b/include/IWallet.h deleted file mode 100644 index 2577159a2..000000000 --- a/include/IWallet.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2014-2022, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#pragma once - -#include <array> -#include <cstdint> -#include <istream> -#include <limits> -#include <ostream> -#include <string> -#include <system_error> -#include <vector> - -namespace CryptoNote { - -typedef size_t TransactionId; -typedef size_t TransferId; -typedef std::array<uint8_t, 32> TransacitonHash; - -struct Transfer { - std::string address; - int64_t amount; -}; - -const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits<TransactionId>::max(); -const TransferId INVALID_TRANSFER_ID = std::numeric_limits<TransferId>::max(); -const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits<uint64_t>::max(); - -struct Transaction { - TransferId firstTransferId; - size_t transferCount; - int64_t totalAmount; - uint64_t fee; - TransacitonHash hash; - bool isCoinbase; - uint64_t blockHeight; - uint64_t timestamp; - std::string extra; -}; - -class IWalletObserver { -public: - virtual void initCompleted(std::error_code result) {} - virtual void saveCompleted(std::error_code result) {} - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} - virtual void actualBalanceUpdated(uint64_t actualBalance) {} - virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} - virtual void externalTransactionCreated(TransactionId transactionId) {} - virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} - virtual void transactionUpdated(TransactionId transactionId) {} -}; - -class IWallet { -public: - virtual ~IWallet() = 0; - virtual void addObserver(IWalletObserver* observer) = 0; - virtual void removeObserver(IWalletObserver* observer) = 0; - - virtual void initAndGenerate(const std::string& password) = 0; - virtual void initAndLoad(std::istream& source, const std::string& password) = 0; - virtual void shutdown() = 0; - - virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; - - virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; - - virtual std::string getAddress() = 0; - - virtual uint64_t actualBalance() = 0; - virtual uint64_t pendingBalance() = 0; - - virtual size_t getTransactionCount() = 0; - virtual size_t getTransferCount() = 0; - - virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; - - virtual bool getTransaction(TransactionId transactionId, Transaction& transaction) = 0; - virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; - - virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual TransactionId sendTransaction(const std::vector<Transfer>& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual std::error_code cancelTransaction(size_t transferId) = 0; -}; - -} diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 72d472d8a..f098cbdf0 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -31,54 +31,83 @@ const uint64_t keccakf_rndc[24] = 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 }; -const int keccakf_rotc[24] = -{ - 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 -}; - -const int keccakf_piln[24] = -{ - 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 -}; - // update the state with given number of rounds void keccakf(uint64_t st[25], int rounds) { - int i, j, round; + int round; uint64_t t, bc[5]; - for (round = 0; round < rounds; round++) { - + for (round = 0; round < rounds; ++round) { // Theta - for (i = 0; i < 5; i++) - bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; - - for (i = 0; i < 5; i++) { - t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); - for (j = 0; j < 25; j += 5) - st[j + i] ^= t; + bc[0] = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20]; + bc[1] = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21]; + bc[2] = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22]; + bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23]; + bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24]; + +#define THETA(i) { \ + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \ + st[i ] ^= t; \ + st[i + 5] ^= t; \ + st[i + 10] ^= t; \ + st[i + 15] ^= t; \ + st[i + 20] ^= t; \ } + THETA(0); + THETA(1); + THETA(2); + THETA(3); + THETA(4); + // Rho Pi t = st[1]; - for (i = 0; i < 24; i++) { - j = keccakf_piln[i]; - bc[0] = st[j]; - st[j] = ROTL64(t, keccakf_rotc[i]); - t = bc[0]; - } + st[ 1] = ROTL64(st[ 6], 44); + st[ 6] = ROTL64(st[ 9], 20); + st[ 9] = ROTL64(st[22], 61); + st[22] = ROTL64(st[14], 39); + st[14] = ROTL64(st[20], 18); + st[20] = ROTL64(st[ 2], 62); + st[ 2] = ROTL64(st[12], 43); + st[12] = ROTL64(st[13], 25); + st[13] = ROTL64(st[19], 8); + st[19] = ROTL64(st[23], 56); + st[23] = ROTL64(st[15], 41); + st[15] = ROTL64(st[ 4], 27); + st[ 4] = ROTL64(st[24], 14); + st[24] = ROTL64(st[21], 2); + st[21] = ROTL64(st[ 8], 55); + st[ 8] = ROTL64(st[16], 45); + st[16] = ROTL64(st[ 5], 36); + st[ 5] = ROTL64(st[ 3], 28); + st[ 3] = ROTL64(st[18], 21); + st[18] = ROTL64(st[17], 15); + st[17] = ROTL64(st[11], 10); + st[11] = ROTL64(st[ 7], 6); + st[ 7] = ROTL64(st[10], 3); + st[10] = ROTL64(t, 1); // Chi - for (j = 0; j < 25; j += 5) { - for (i = 0; i < 5; i++) - bc[i] = st[j + i]; - for (i = 0; i < 5; i++) - st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; +#define CHI(j) { \ + const uint64_t st0 = st[j ]; \ + const uint64_t st1 = st[j + 1]; \ + const uint64_t st2 = st[j + 2]; \ + const uint64_t st3 = st[j + 3]; \ + const uint64_t st4 = st[j + 4]; \ + st[j ] ^= ~st1 & st2; \ + st[j + 1] ^= ~st2 & st3; \ + st[j + 2] ^= ~st3 & st4; \ + st[j + 3] ^= ~st4 & st0; \ + st[j + 4] ^= ~st0 & st1; \ } + CHI( 0); + CHI( 5); + CHI(10); + CHI(15); + CHI(20); + // Iota st[0] ^= keccakf_rndc[round]; } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 88316fd6a..f2a8e9b79 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -126,6 +126,7 @@ #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000 #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000 +#define MAX_RPC_CONTENT_LENGTH 1048576 // 1 MB #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 #define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 @@ -169,6 +170,7 @@ #define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_MIN_MIXIN_10 8 +#define HF_VERSION_MIN_MIXIN_15 15 #define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_PER_BYTE_FEE 8 #define HF_VERSION_SMALLER_BP 10 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0b6a3abf3..5b7b4353d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3321,7 +3321,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, size_t n_unmixable = 0, n_mixable = 0; size_t min_actual_mixin = std::numeric_limits<size_t>::max(); size_t max_actual_mixin = 0; - const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_15 ? 15 : hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -3364,14 +3364,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10)) - { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be 11"); - tvc.m_low_mixin = true; - return false; - } - - if (min_actual_mixin < min_mixin) + // The only circumstance where ring sizes less than expected are + // allowed is when spending unmixable non-RCT outputs in the chain. + // Caveat: at HF_VERSION_MIN_MIXIN_15, temporarily allow ring sizes + // of 11 to allow a grace period in the transition to larger ring size. + if (min_actual_mixin < min_mixin && !(hf_version == HF_VERSION_MIN_MIXIN_15 && min_actual_mixin == 10)) { if (n_unmixable == 0) { @@ -3385,6 +3382,15 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tvc.m_low_mixin = true; return false; } + } else if ((hf_version > HF_VERSION_MIN_MIXIN_15 && min_actual_mixin > 15) + || (hf_version == HF_VERSION_MIN_MIXIN_15 && min_actual_mixin != 15 && min_actual_mixin != 10) // grace period to allow either 15 or 10 + || (hf_version < HF_VERSION_MIN_MIXIN_15 && hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10) + || ((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) + ) + { + MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be " << (min_mixin + 1)); + tvc.m_low_mixin = true; + return false; } // min/max tx version based on HF, and we accept v1 txes if having a non mixable diff --git a/src/multisig/multisig_account.cpp b/src/multisig/multisig_account.cpp index b7298c4b6..8bd97cf21 100644 --- a/src/multisig/multisig_account.cpp +++ b/src/multisig/multisig_account.cpp @@ -73,7 +73,7 @@ namespace multisig const crypto::public_key &multisig_pubkey, const crypto::public_key &common_pubkey, const std::uint32_t kex_rounds_complete, - kex_origins_map_t kex_origins_map, + multisig_keyset_map_memsafe_t kex_origins_map, std::string next_round_kex_message) : m_base_privkey{base_privkey}, m_base_common_privkey{base_common_privkey}, @@ -89,6 +89,20 @@ namespace multisig CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_base_privkey, m_base_pubkey), "Failed to derive public key"); set_multisig_config(threshold, std::move(signers)); + + // kex rounds should not exceed post-kex verification round + const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(m_signers.size(), m_threshold)}; + CHECK_AND_ASSERT_THROW_MES(m_kex_rounds_complete <= kex_rounds_required + 1, + "multisig account: tried to reconstruct account, but kex rounds complete counter is invalid."); + + // once an account is done with kex, the 'next kex msg' is always the post-kex verification message + // i.e. the multisig account pubkey signed by the signer's privkey AND the common pubkey + if (main_kex_rounds_done()) + { + m_next_round_kex_message = multisig_kex_msg{kex_rounds_required + 1, + m_base_privkey, + std::vector<crypto::public_key>{m_multisig_pubkey, m_common_pubkey}}.get_msg(); + } } //---------------------------------------------------------------------------------------------------------------------- // multisig_account: EXTERNAL @@ -100,14 +114,24 @@ namespace multisig //---------------------------------------------------------------------------------------------------------------------- // multisig_account: EXTERNAL //---------------------------------------------------------------------------------------------------------------------- - bool multisig_account::multisig_is_ready() const + bool multisig_account::main_kex_rounds_done() const { if (account_is_active()) - return multisig_kex_rounds_required(m_signers.size(), m_threshold) == m_kex_rounds_complete; + return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold); + else + return false; + } + //---------------------------------------------------------------------------------------------------------------------- + // multisig_account: EXTERNAL + //---------------------------------------------------------------------------------------------------------------------- + bool multisig_account::multisig_is_ready() const + { + if (main_kex_rounds_done()) + return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold) + 1; else return false; } - //---------------------------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------------------------- // multisig_account: INTERNAL //---------------------------------------------------------------------------------------------------------------------- void multisig_account::set_multisig_config(const std::size_t threshold, std::vector<crypto::public_key> signers) @@ -119,10 +143,6 @@ namespace multisig for (auto signer_it = signers.begin(); signer_it != signers.end(); ++signer_it) { - // signers should all be unique - CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signer_it, *signer_it) == signer_it, - "multisig account: tried to set signers, but found a duplicate signer unexpectedly."); - // signer pubkeys must be in main subgroup, and not identity CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::pk2rct(*signer_it)) && !(*signer_it == rct::rct2pk(rct::identity())), "multisig account: tried to set signers, but a signer pubkey is invalid."); @@ -133,12 +153,11 @@ namespace multisig "multisig account: tried to set signers, but did not find the account's base pubkey in signer list."); // sort signers - std::sort(signers.begin(), signers.end(), - [](const crypto::public_key &key1, const crypto::public_key &key2) -> bool - { - return memcmp(&key1, &key2, sizeof(crypto::public_key)) < 0; - } - ); + std::sort(signers.begin(), signers.end()); + + // signers should all be unique + CHECK_AND_ASSERT_THROW_MES(std::adjacent_find(signers.begin(), signers.end()) == signers.end(), + "multisig account: tried to set signers, but there are duplicate signers unexpectedly."); // set m_threshold = threshold; diff --git a/src/multisig/multisig_account.h b/src/multisig/multisig_account.h index b01ae6c88..bb853246a 100644 --- a/src/multisig/multisig_account.h +++ b/src/multisig/multisig_account.h @@ -75,12 +75,12 @@ namespace multisig * - ZtM2: https://web.getmonero.org/library/Zero-to-Monero-2-0-0.pdf Ch. 9, especially Section 9.6.3 * - FROST: https://eprint.iacr.org/2018/417 */ + using multisig_keyset_map_memsafe_t = + std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>>; + class multisig_account final { public: - //member types - using kex_origins_map_t = std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>>; - //constructors // default constructor multisig_account() = default; @@ -105,7 +105,7 @@ namespace multisig const crypto::public_key &multisig_pubkey, const crypto::public_key &common_pubkey, const std::uint32_t kex_rounds_complete, - kex_origins_map_t kex_origins_map, + multisig_keyset_map_memsafe_t kex_origins_map, std::string next_round_kex_message); // copy constructor: default @@ -137,13 +137,15 @@ namespace multisig // get kex rounds complete std::uint32_t get_kex_rounds_complete() const { return m_kex_rounds_complete; } // get kex keys to origins map - const kex_origins_map_t& get_kex_keys_to_origins_map() const { return m_kex_keys_to_origins_map; } + const multisig_keyset_map_memsafe_t& get_kex_keys_to_origins_map() const { return m_kex_keys_to_origins_map; } // get the kex msg for the next round const std::string& get_next_kex_round_msg() const { return m_next_round_kex_message; } //account status functions // account has been intialized, and the account holder can use the 'common' key bool account_is_active() const; + // account has gone through main kex rounds, only remaining step is to verify all other participants are ready + bool main_kex_rounds_done() const; // account is ready to make multisig signatures bool multisig_is_ready() const; @@ -178,21 +180,21 @@ namespace multisig * - Collect the local signer's shared keys to ignore in incoming messages, build the aggregate ancillary key * if appropriate. * param: expanded_msgs - set of multisig kex messages to process - * param: rounds_required - number of rounds required for kex + * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round) * outparam: exclude_pubkeys_out - keys held by the local account corresponding to round 'current_round' * - If 'current_round' is the final round, these are the local account's shares of the final aggregate key. */ void initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs, - const std::uint32_t rounds_required, + const std::uint32_t kex_rounds_required, std::vector<crypto::public_key> &exclude_pubkeys_out); /** * brief: finalize_kex_update - Helper for kex_update_impl() - * param: rounds_required - number of rounds required for kex + * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round) * param: result_keys_to_origins_map - map between keys for the next round and the other participants they correspond to * inoutparam: temp_account_inout - account to perform last update steps on */ - void finalize_kex_update(const std::uint32_t rounds_required, - kex_origins_map_t result_keys_to_origins_map); + void finalize_kex_update(const std::uint32_t kex_rounds_required, + multisig_keyset_map_memsafe_t result_keys_to_origins_map); //member variables private: @@ -226,7 +228,7 @@ namespace multisig std::uint32_t m_kex_rounds_complete{0}; // this account's pubkeys for the in-progress key exchange round // - either DH derivations (intermediate rounds), H(derivation)*G (final round), empty (when kex is done) - kex_origins_map_t m_kex_keys_to_origins_map; + multisig_keyset_map_memsafe_t m_kex_keys_to_origins_map; // the account's message for the in-progress key exchange round std::string m_next_round_kex_message; }; diff --git a/src/multisig/multisig_account_kex_impl.cpp b/src/multisig/multisig_account_kex_impl.cpp index 0a0ca7bdc..2127ee04a 100644 --- a/src/multisig/multisig_account_kex_impl.cpp +++ b/src/multisig/multisig_account_kex_impl.cpp @@ -57,6 +57,30 @@ namespace multisig /** * INTERNAL * + * brief: check_multisig_config - validate multisig configuration details + * param: round - the round of the message that should be produced + * param: threshold - threshold for multisig (M in M-of-N) + * param: num_signers - number of participants in multisig (N) + */ + //---------------------------------------------------------------------------------------------------------------------- + static void check_multisig_config(const std::uint32_t round, + const std::uint32_t threshold, + const std::uint32_t num_signers) + { + CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer."); + CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS, + "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange)."); + CHECK_AND_ASSERT_THROW_MES(num_signers >= threshold, + "Multisig threshold may not be larger than number of signers."); + CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0."); + CHECK_AND_ASSERT_THROW_MES(round > 0, "Multisig kex round must be > 0."); + CHECK_AND_ASSERT_THROW_MES(round <= multisig_kex_rounds_required(num_signers, threshold) + 1, + "Trying to process multisig kex for an invalid round."); + } + //---------------------------------------------------------------------------------------------------------------------- + /** + * INTERNAL + * * brief: calculate_multisig_keypair_from_derivation - wrapper on calculate_multisig_keypair() for an input public key * Converts an input public key into a crypto private key (type cast, does not change serialization), * then passes it to get_multisig_blinded_secret_key(). @@ -224,50 +248,23 @@ namespace multisig /** * INTERNAL * - * brief: multisig_kex_make_next_msg - Construct a kex msg for any round > 1 of multisig key construction. + * brief: multisig_kex_make_round_keys - Makes a kex round's keys. * - Involves DH exchanges with pubkeys provided by other participants. * - Conserves mapping [pubkey -> DH derivation] : [origin keys of participants that share this secret with you]. * param: base_privkey - account's base private key, for performing DH exchanges and signing messages - * param: round - the round of the message that should be produced - * param: threshold - threshold for multisig (M in M-of-N) - * param: num_signers - number of participants in multisig (N) * param: pubkey_origins_map - map between pubkeys to produce DH derivations with and identity keys of * participants who will share each derivation with you * outparam: derivation_origins_map_out - map between DH derivations (shared secrets) and identity keys - * - If msg is not for the last round, then these derivations are also stored in the output message - * so they can be sent to other participants, who will make more DH derivations for the next kex round. - * - If msg is for the last round, then these derivations won't be sent to other participants. - * Instead, they are converted to share secrets (i.e. s = H(derivation)) and multiplied by G. - * The keys s*G are sent to other participants in the message, so they can be used to produce the final - * multisig key via generate_multisig_spend_public_key(). - * - The values s are the local account's shares of the final multisig key's private key. The caller can - * compute those values with calculate_multisig_keypair_from_derivation() (or compute them directly). - * return: multisig kex message for the specified round */ //---------------------------------------------------------------------------------------------------------------------- - static multisig_kex_msg multisig_kex_make_next_msg(const crypto::secret_key &base_privkey, - const std::uint32_t round, - const std::uint32_t threshold, - const std::uint32_t num_signers, - const std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &pubkey_origins_map, - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &derivation_origins_map_out) + static void multisig_kex_make_round_keys(const crypto::secret_key &base_privkey, + multisig_keyset_map_memsafe_t pubkey_origins_map, + multisig_keyset_map_memsafe_t &derivation_origins_map_out) { - CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer."); - CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS, - "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange)."); - CHECK_AND_ASSERT_THROW_MES(num_signers >= threshold, - "Multisig threshold may not be larger than number of signers."); - CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0."); - CHECK_AND_ASSERT_THROW_MES(round > 1, "Round for next msg must be > 1."); - CHECK_AND_ASSERT_THROW_MES(round <= multisig_kex_rounds_required(num_signers, threshold), - "Trying to make key exchange message for an invalid round."); - // make shared secrets with input pubkeys - std::vector<crypto::public_key> msg_pubkeys; - msg_pubkeys.reserve(pubkey_origins_map.size()); derivation_origins_map_out.clear(); - for (const auto &pubkey_and_origins : pubkey_origins_map) + for (auto &pubkey_and_origins : pubkey_origins_map) { // D = 8 * k_base * K_pubkey // note: must be mul8 (cofactor), otherwise it is possible to leak to a malicious participant if the local @@ -281,27 +278,29 @@ namespace multisig rct::scalarmultKey(derivation_rct, rct::pk2rct(pubkey_and_origins.first), rct::sk2rct(base_privkey)); rct::scalarmultKey(derivation_rct, derivation_rct, rct::EIGHT); - crypto::public_key_memsafe derivation{rct::rct2pk(derivation_rct)}; - // retain mapping between pubkey's origins and the DH derivation - // note: if msg for last round, then caller must know how to handle these derivations properly - derivation_origins_map_out[derivation] = pubkey_and_origins.second; - - // if the last round, convert derivations to public keys for the output message - if (round == multisig_kex_rounds_required(num_signers, threshold)) - { - // derived_pubkey = H(derivation)*G - crypto::public_key derived_pubkey; - calculate_multisig_keypair_from_derivation(derivation, derived_pubkey); - msg_pubkeys.push_back(derived_pubkey); - } - // otherwise, put derivations in message directly, so other signers can in turn create derivations (shared secrets) - // with them for the next round - else - msg_pubkeys.push_back(derivation); + // note: if working on last kex round, then caller must know how to handle these derivations properly + derivation_origins_map_out[rct::rct2pk(derivation_rct)] = std::move(pubkey_and_origins.second); } + } + //---------------------------------------------------------------------------------------------------------------------- + /** + * INTERNAL + * + * brief: check_messages_round - Check that a set of messages have an expected round number. + * param: expanded_msgs - set of multisig kex messages to process + * param: expected_round - round number the kex messages should have + */ + //---------------------------------------------------------------------------------------------------------------------- + static void check_messages_round(const std::vector<multisig_kex_msg> &expanded_msgs, + const std::uint32_t expected_round) + { + CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input message expected."); + const std::uint32_t round{expanded_msgs[0].get_round()}; + CHECK_AND_ASSERT_THROW_MES(round == expected_round, "Messages don't have the expected kex round number."); - return multisig_kex_msg{round, base_privkey, std::move(msg_pubkeys)}; + for (const auto &expanded_msg : expanded_msgs) + CHECK_AND_ASSERT_THROW_MES(expanded_msg.get_round() == round, "All messages must have the same kex round number."); } //---------------------------------------------------------------------------------------------------------------------- /** @@ -327,19 +326,19 @@ namespace multisig static std::uint32_t multisig_kex_msgs_sanitize_pubkeys(const crypto::public_key &own_pubkey, const std::vector<multisig_kex_msg> &expanded_msgs, const std::vector<crypto::public_key> &exclude_pubkeys, - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &sanitized_pubkeys_out) + multisig_keyset_map_memsafe_t &sanitized_pubkeys_out) { + // all messages should have the same round (redundant sanity check) CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input message expected."); + const std::uint32_t round{expanded_msgs[0].get_round()}; + check_messages_round(expanded_msgs, round); - std::uint32_t round = expanded_msgs[0].get_round(); sanitized_pubkeys_out.clear(); // get all pubkeys from input messages, add them to pubkey:origins map // - origins = all the signing pubkeys that recommended a given msg pubkey for (const auto &expanded_msg : expanded_msgs) { - CHECK_AND_ASSERT_THROW_MES(expanded_msg.get_round() == round, "All messages must have the same kex round number."); - // ignore messages from self if (expanded_msg.get_signing_pubkey() == own_pubkey) continue; @@ -378,7 +377,7 @@ namespace multisig * * brief: evaluate_multisig_kex_round_msgs - Evaluate pubkeys from a kex round in order to prepare for the next round. * - Sanitizes input msgs. - * - Require uniqueness in: 'signers', 'exclude_pubkeys'. + * - Require uniqueness in: 'exclude_pubkeys'. * - Requires each input pubkey be recommended by 'num_recommendations = expected_round' msg signers. * - For a final multisig key to be truly 'M-of-N', each of the the private key's components must be * shared by (N - M + 1) signers. @@ -388,39 +387,21 @@ namespace multisig * with the local account. * - Requires that 'exclude_pubkeys' has [num_signers - 1 CHOOSE (expected_round - 1)] pubkeys. * - These should be derivations the local account has corresponding to round 'expected_round'. - * param: base_privkey - multisig account's base private key + * param: base_pubkey - multisig account's base public key * param: expected_round - expected kex round of input messages - * param: threshold - threshold for multisig (M in M-of-N) * param: signers - expected participants in multisig kex * param: expanded_msgs - set of multisig kex messages to process * param: exclude_pubkeys - derivations held by the local account corresponding to round 'expected_round' * return: fully sanitized and validated pubkey:origins map for building the account's next kex round message */ //---------------------------------------------------------------------------------------------------------------------- - static std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> evaluate_multisig_kex_round_msgs( + static multisig_keyset_map_memsafe_t evaluate_multisig_kex_round_msgs( const crypto::public_key &base_pubkey, const std::uint32_t expected_round, - const std::uint32_t threshold, const std::vector<crypto::public_key> &signers, const std::vector<multisig_kex_msg> &expanded_msgs, const std::vector<crypto::public_key> &exclude_pubkeys) { - CHECK_AND_ASSERT_THROW_MES(signers.size() > 1, "Must be at least one other multisig signer."); - CHECK_AND_ASSERT_THROW_MES(signers.size() <= config::MULTISIG_MAX_SIGNERS, - "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange)."); - CHECK_AND_ASSERT_THROW_MES(signers.size() >= threshold, "Multisig threshold may not be larger than number of signers."); - CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0."); - CHECK_AND_ASSERT_THROW_MES(expected_round > 0, "Expected round must be > 0."); - CHECK_AND_ASSERT_THROW_MES(expected_round <= multisig_kex_rounds_required(signers.size(), threshold), - "Expecting key exchange messages for an invalid round."); - - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> pubkey_origins_map; - - // leave early in the last round of 1-of-N, where all signers share a key so the local signer doesn't care about - // recommendations from other signers - if (threshold == 1 && expected_round == multisig_kex_rounds_required(signers.size(), threshold)) - return pubkey_origins_map; - // exclude_pubkeys should all be unique for (auto it = exclude_pubkeys.begin(); it != exclude_pubkeys.end(); ++it) { @@ -429,7 +410,8 @@ namespace multisig } // sanitize input messages - std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, exclude_pubkeys, pubkey_origins_map); + multisig_keyset_map_memsafe_t pubkey_origins_map; + const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, exclude_pubkeys, pubkey_origins_map); CHECK_AND_ASSERT_THROW_MES(round == expected_round, "Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]"); @@ -486,10 +468,10 @@ namespace multisig // - Each origin should have a shared key with each group of size 'round - 1'. // Note: Keys shared with local are ignored to facilitate kex round boosting, where one or more signers may // have boosted the local signer (implying they didn't have access to the local signer's previous round msg). - std::uint32_t expected_recommendations_others = n_choose_k_f(signers.size() - 2, round - 1); + const std::uint32_t expected_recommendations_others = n_choose_k_f(signers.size() - 2, round - 1); // local: (N - 1) choose (msg_round_num - 1) - std::uint32_t expected_recommendations_self = n_choose_k_f(signers.size() - 1, round - 1); + const std::uint32_t expected_recommendations_self = n_choose_k_f(signers.size() - 1, round - 1); // note: expected_recommendations_others would be 0 in the last round of 1-of-N, but we return early for that case CHECK_AND_ASSERT_THROW_MES(expected_recommendations_self > 0 && expected_recommendations_others > 0, @@ -517,7 +499,60 @@ namespace multisig /** * INTERNAL * - * brief: multisig_kex_process_round - Process kex messages for the active kex round. + * brief: evaluate_multisig_post_kex_round_msgs - Evaluate messages for the post-kex verification round. + * - Sanitizes input msgs. + * - Requires that only one pubkey is recommended. + * - Requires that all signers (other than self) recommend that one pubkey. + * param: base_pubkey - multisig account's base public key + * param: expected_round - expected kex round of input messages + * param: signers - expected participants in multisig kex + * param: expanded_msgs - set of multisig kex messages to process + * return: sanitized and validated pubkey:origins map + */ + //---------------------------------------------------------------------------------------------------------------------- + static multisig_keyset_map_memsafe_t evaluate_multisig_post_kex_round_msgs( + const crypto::public_key &base_pubkey, + const std::uint32_t expected_round, + const std::vector<crypto::public_key> &signers, + const std::vector<multisig_kex_msg> &expanded_msgs) + { + // sanitize input messages + const std::vector<crypto::public_key> dummy; + multisig_keyset_map_memsafe_t pubkey_origins_map; + const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, dummy, pubkey_origins_map); + CHECK_AND_ASSERT_THROW_MES(round == expected_round, + "Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]"); + + // evaluate pubkeys collected + + // 1) there should only be two pubkeys + CHECK_AND_ASSERT_THROW_MES(pubkey_origins_map.size() == 2, + "Multisig post-kex round messages from other signers did not all contain two pubkeys."); + + // 2) both keys should be recommended by the same set of signers + CHECK_AND_ASSERT_THROW_MES(pubkey_origins_map.begin()->second == (++(pubkey_origins_map.begin()))->second, + "Multisig post-kex round messages from other signers did not all recommend the same pubkey pair."); + + // 3) all signers should be present in the recommendation list + auto origins = pubkey_origins_map.begin()->second; + origins.insert(base_pubkey); //add self + + CHECK_AND_ASSERT_THROW_MES(origins.size() == signers.size(), + "Multisig post-kex round message origins don't line up with multisig signer set."); + + for (const crypto::public_key &signer : signers) + { + CHECK_AND_ASSERT_THROW_MES(origins.find(signer) != origins.end(), + "Could not find an expected signer in multisig post-kex round messages (all signers expected)."); + } + + return pubkey_origins_map; + } + //---------------------------------------------------------------------------------------------------------------------- + /** + * INTERNAL + * + * brief: multisig_kex_process_round_msgs - Process kex messages for the active kex round. * - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_next_msg(). * - In other words, evaluate the input messages and try to make a message for the next round. * - Note: Must be called on the final round's msgs to evaluate the final key components @@ -536,43 +571,62 @@ namespace multisig * return: multisig kex message for next round, or empty message if 'current_round' is the final round */ //---------------------------------------------------------------------------------------------------------------------- - static multisig_kex_msg multisig_kex_process_round(const crypto::secret_key &base_privkey, + static void multisig_kex_process_round_msgs(const crypto::secret_key &base_privkey, const crypto::public_key &base_pubkey, const std::uint32_t current_round, const std::uint32_t threshold, const std::vector<crypto::public_key> &signers, const std::vector<multisig_kex_msg> &expanded_msgs, const std::vector<crypto::public_key> &exclude_pubkeys, - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &keys_to_origins_map_out) + multisig_keyset_map_memsafe_t &keys_to_origins_map_out) { - // evaluate messages - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> evaluated_pubkeys = - evaluate_multisig_kex_round_msgs(base_pubkey, current_round, threshold, signers, expanded_msgs, exclude_pubkeys); + check_multisig_config(current_round, threshold, signers.size()); + const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(signers.size(), threshold)}; - // produce message for next round (if there is one) - if (current_round < multisig_kex_rounds_required(signers.size(), threshold)) + // process messages into a [pubkey : {origins}] map + multisig_keyset_map_memsafe_t evaluated_pubkeys; + + if (threshold == 1 && current_round == kex_rounds_required) { - return multisig_kex_make_next_msg(base_privkey, - current_round + 1, - threshold, - signers.size(), - evaluated_pubkeys, - keys_to_origins_map_out); + // in the last main kex round of 1-of-N, all signers share a key so the local signer doesn't care about evaluating + // recommendations from other signers } - else + else if (current_round <= kex_rounds_required) { - // no more rounds, so collect the key shares recommended by other signers for the final aggregate key - keys_to_origins_map_out.clear(); - keys_to_origins_map_out = std::move(evaluated_pubkeys); + // for normal kex rounds, fully evaluate kex round messages + evaluated_pubkeys = evaluate_multisig_kex_round_msgs(base_pubkey, + current_round, + signers, + expanded_msgs, + exclude_pubkeys); + } + else //(current_round == kex_rounds_required + 1) + { + // for the post-kex verification round, validate the last kex round's messages + evaluated_pubkeys = evaluate_multisig_post_kex_round_msgs(base_pubkey, + current_round, + signers, + expanded_msgs); + } - return multisig_kex_msg{}; + // prepare keys-to-origins map for updating the multisig account + if (current_round < kex_rounds_required) + { + // normal kex round: make new keys + multisig_kex_make_round_keys(base_privkey, std::move(evaluated_pubkeys), keys_to_origins_map_out); + } + else if (current_round >= kex_rounds_required) + { + // last kex round: collect the key shares recommended by other signers for the final aggregate key + // post-kex verification round: save the keys found in input messages + keys_to_origins_map_out = std::move(evaluated_pubkeys); } } //---------------------------------------------------------------------------------------------------------------------- // multisig_account: INTERNAL //---------------------------------------------------------------------------------------------------------------------- void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs, - const std::uint32_t rounds_required, + const std::uint32_t kex_rounds_required, std::vector<crypto::public_key> &exclude_pubkeys_out) { if (m_kex_rounds_complete == 0) @@ -605,7 +659,7 @@ namespace multisig "Failed to derive public key"); // if N-of-N, then the base privkey will be used directly to make the account's share of the final key - if (rounds_required == 1) + if (kex_rounds_required == 1) { m_multisig_privkeys.clear(); m_multisig_privkeys.emplace_back(m_base_privkey); @@ -629,13 +683,29 @@ namespace multisig //---------------------------------------------------------------------------------------------------------------------- // multisig_account: INTERNAL //---------------------------------------------------------------------------------------------------------------------- - void multisig_account::finalize_kex_update(const std::uint32_t rounds_required, - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> result_keys_to_origins_map) + void multisig_account::finalize_kex_update(const std::uint32_t kex_rounds_required, + multisig_keyset_map_memsafe_t result_keys_to_origins_map) { + std::vector<crypto::public_key> next_msg_keys; + // prepare for next round (or complete the multisig account fully) - if (rounds_required == m_kex_rounds_complete + 1) + if (m_kex_rounds_complete == kex_rounds_required) + { + // post-kex verification round: check that the multisig pubkey and common pubkey were recommended by other signers + CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_multisig_pubkey) > 0, + "Multisig post-kex round: expected multisig pubkey wasn't found in other signers' messages."); + CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_common_pubkey) > 0, + "Multisig post-kex round: expected common pubkey wasn't found in other signers' messages."); + + // save keys that should be recommended to other signers + // - for convenience, re-recommend the post-kex verification message once an account is complete + next_msg_keys.reserve(2); + next_msg_keys.push_back(m_multisig_pubkey); + next_msg_keys.push_back(m_common_pubkey); + } + else if (m_kex_rounds_complete + 1 == kex_rounds_required) { - // finished (have set of msgs to complete address) + // finished with main kex rounds (have set of msgs to complete address) // when 'completing the final round', result keys are other signers' shares of the final key std::vector<crypto::public_key> result_keys; @@ -652,8 +722,14 @@ namespace multisig // no longer need the account's pubkeys saved for this round (they were only used to build exclude_pubkeys) // TODO: record [pre-aggregation pubkeys : origins] map for aggregation-style signing m_kex_keys_to_origins_map.clear(); + + // save keys that should be recommended to other signers + // - for post-kex verification, recommend the multisig pubkeys to notify other signers that the local signer is done + next_msg_keys.reserve(2); + next_msg_keys.push_back(m_multisig_pubkey); + next_msg_keys.push_back(m_common_pubkey); } - else if (rounds_required == m_kex_rounds_complete + 2) + else if (m_kex_rounds_complete + 2 == kex_rounds_required) { // one more round (must send/receive one more set of kex msgs) // - at this point, have local signer's pre-aggregation private key shares of the final address @@ -668,6 +744,7 @@ namespace multisig m_multisig_privkeys.reserve(result_keys_to_origins_map.size()); m_kex_keys_to_origins_map.clear(); + next_msg_keys.reserve(result_keys_to_origins_map.size()); for (const auto &derivation_and_origins : result_keys_to_origins_map) { @@ -679,37 +756,59 @@ namespace multisig // save the account's kex key mappings for this round [derived pubkey : other signers who will have the same key] m_kex_keys_to_origins_map[derived_pubkey] = std::move(derivation_and_origins.second); + + // save keys that should be recommended to other signers + // - The keys multisig_key*G are sent to other participants in the message, so they can be used to produce the final + // multisig key via generate_multisig_spend_public_key(). + next_msg_keys.push_back(derived_pubkey); } } - else + else //(m_kex_rounds_complete + 3 <= kex_rounds_required) { // next round is an 'intermediate' key exchange round, so there is nothing special to do here - // save the account's kex keys for this round [DH derivation : other signers who will have the same derivation] + // save keys that should be recommended to other signers + // - Send this round's DH derivations to other participants, who will make more DH derivations for the following round. + next_msg_keys.reserve(result_keys_to_origins_map.size()); + + for (const auto &derivation_and_origins : result_keys_to_origins_map) + next_msg_keys.push_back(derivation_and_origins.first); + + // save the account's kex keys for this round [DH derivation : other signers who should have the same derivation] m_kex_keys_to_origins_map = std::move(result_keys_to_origins_map); } // a full set of msgs has been collected and processed, so the 'round is complete' ++m_kex_rounds_complete; + + // make next round's message (or reproduce the post-kex verification round if kex is complete) + m_next_round_kex_message = multisig_kex_msg{ + (m_kex_rounds_complete > kex_rounds_required ? kex_rounds_required : m_kex_rounds_complete) + 1, + m_base_privkey, + std::move(next_msg_keys)}.get_msg(); } //---------------------------------------------------------------------------------------------------------------------- // multisig_account: INTERNAL //---------------------------------------------------------------------------------------------------------------------- void multisig_account::kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs) { - CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "No key exchange messages passed in."); + // check messages are for the expected kex round + check_messages_round(expanded_msgs, m_kex_rounds_complete + 1); + + // check kex round count + const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(m_signers.size(), m_threshold)}; - const std::uint32_t rounds_required = multisig_kex_rounds_required(m_signers.size(), m_threshold); - CHECK_AND_ASSERT_THROW_MES(rounds_required > 0, "Multisig kex rounds required unexpectedly 0."); + CHECK_AND_ASSERT_THROW_MES(kex_rounds_required > 0, "Multisig kex rounds required unexpectedly 0."); + CHECK_AND_ASSERT_THROW_MES(m_kex_rounds_complete < kex_rounds_required + 1, + "Multisig kex has already completed all required rounds (including post-kex verification)."); // initialize account update std::vector<crypto::public_key> exclude_pubkeys; - initialize_kex_update(expanded_msgs, rounds_required, exclude_pubkeys); - - // evaluate messages and get this account's kex msg for the next round - std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> result_keys_to_origins_map; + initialize_kex_update(expanded_msgs, kex_rounds_required, exclude_pubkeys); - m_next_round_kex_message = multisig_kex_process_round( + // process messages into a [pubkey : {origins}] map + multisig_keyset_map_memsafe_t result_keys_to_origins_map; + multisig_kex_process_round_msgs( m_base_privkey, m_base_pubkey, m_kex_rounds_complete + 1, @@ -717,10 +816,10 @@ namespace multisig m_signers, expanded_msgs, exclude_pubkeys, - result_keys_to_origins_map).get_msg(); + result_keys_to_origins_map); // finish account update - finalize_kex_update(rounds_required, std::move(result_keys_to_origins_map)); + finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map)); } //---------------------------------------------------------------------------------------------------------------------- } //namespace multisig diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index bbcfa6168..869040657 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -365,6 +365,8 @@ namespace cryptonote std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options) ); + m_net_server.get_config_object().m_max_content_length = MAX_RPC_CONTENT_LENGTH; + if (store_ssl_key && inited) { // new keys were generated, store for next run diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index be6bf6388..d3e40ab74 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -99,10 +99,6 @@ typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" -#define DEFAULT_MIX 10 - -#define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol - #define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days #define LOCK_IDLE_SCOPE() \ @@ -1091,7 +1087,9 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo auto local_args = args; local_args.erase(local_args.begin()); std::string multisig_extra_info = m_wallet->make_multisig(orig_pwd_container->password(), local_args, threshold); - if (!multisig_extra_info.empty()) + bool ready; + m_wallet->multisig(&ready); + if (!ready) { success_msg_writer() << tr("Another step is needed"); success_msg_writer() << multisig_extra_info; @@ -1152,7 +1150,7 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & return false; } - if (args.size() < 2) + if (args.size() < 1) { PRINT_USAGE(USAGE_EXCHANGE_MULTISIG_KEYS); return false; @@ -1161,7 +1159,9 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> & try { std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args); - if (!multisig_extra_info.empty()) + bool ready; + m_wallet->multisig(&ready); + if (!ready) { message_writer() << tr("Another step is needed"); message_writer() << multisig_extra_info; @@ -2472,59 +2472,6 @@ bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = s return true; } -bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/* = std::vector<std::string>()*/) -{ - if (m_wallet->watch_only()) - { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); - return true; - } - try - { - if (strchr(args[1].c_str(), '-')) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - uint32_t ring_size = boost::lexical_cast<uint32_t>(args[1]); - if (ring_size < MIN_RING_SIZE && ring_size != 0) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - - if (ring_size != 0 && ring_size != DEFAULT_MIX+1) - { - if (m_wallet->use_fork_rules(8, 0)) - { - message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored."); - } - else - { - message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); - } - } - - const auto pwd_container = get_and_verify_password(); - if (pwd_container) - { - m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); - } - return true; - } - catch(const boost::bad_lexical_cast &) - { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; - return true; - } - catch(...) - { - fail_msg_writer() << tr("could not change default ring size"); - return true; - } -} - bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { uint32_t priority = 0; @@ -3386,8 +3333,6 @@ simple_wallet::simple_wallet() " Whether to print detailed information about ring members during confirmation.\n " "store-tx-info <1|0>\n " " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n " - "default-ring-size <n>\n " - " Set the default ring size (obsolete).\n " "auto-refresh <1|0>\n " " Whether to automatically synchronize new blocks from the daemon.\n " "refresh-type <full|optimize-coinbase|no-coinbase|default>\n " @@ -3896,7 +3841,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1")); - CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE); CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); @@ -6539,7 +6483,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri priority = m_wallet->adjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + const size_t min_ring_size = m_wallet->get_min_ring_size(); + size_t fake_outs_count = min_ring_size - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -6862,7 +6807,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (vin.type() == typeid(txin_to_key)) { const txin_to_key& in_to_key = boost::get<txin_to_key>(vin); - if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1) + if (in_to_key.key_offsets.size() != min_ring_size) default_ring_size = false; } } @@ -7140,7 +7085,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co priority = m_wallet->adjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + size_t fake_outs_count = m_wallet->get_min_ring_size() - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) @@ -7417,7 +7362,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) priority = m_wallet->adjust_priority(priority); - size_t fake_outs_count = DEFAULT_MIX; + size_t fake_outs_count = m_wallet->get_min_ring_size() - 1; if(local_args.size() > 0) { size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 473120eac..4c005c53a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -125,7 +125,6 @@ namespace cryptonote bool set_always_confirm_transfers(const std::vector<std::string> &args = std::vector<std::string>()); bool set_print_ring_members(const std::vector<std::string> &args = std::vector<std::string>()); bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>()); - bool set_default_ring_size(const std::vector<std::string> &args = std::vector<std::string>()); bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>()); bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>()); bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>()); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 0701e1a0e..c6f81f0e4 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -423,7 +423,6 @@ struct WalletListener /** * @brief Interface for wallet operations. - * TODO: check if /include/IWallet.h is still actual */ struct Wallet { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 25df372c5..f4a5a5855 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5125,7 +5125,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor // reconstruct multisig account crypto::public_key dummy; - multisig::multisig_account::kex_origins_map_t kex_origins_map; + multisig::multisig_keyset_map_memsafe_t kex_origins_map; for (const auto &derivation : m_multisig_derivations) kex_origins_map[derivation]; @@ -5138,7 +5138,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor get_account().get_keys().m_multisig_keys, get_account().get_keys().m_view_secret_key, m_account_public_address.m_spend_public_key, - dummy, //common pubkey: not used + m_account_public_address.m_view_public_key, m_multisig_rounds_passed, std::move(kex_origins_map), "" @@ -5225,7 +5225,10 @@ bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const if (total) *total = m_multisig_signers.size(); if (ready) - *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())); + { + *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) && + (m_multisig_rounds_passed == multisig::multisig_kex_rounds_required(m_multisig_signers.size(), m_multisig_threshold) + 1); + } return true; } //---------------------------------------------------------------------------------------------------- @@ -7406,6 +7409,8 @@ int wallet2::get_fee_algorithm() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_min_ring_size() { + if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0)) + return 16; if (use_fork_rules(8, 10)) return 11; if (use_fork_rules(7, 10)) @@ -7419,6 +7424,8 @@ uint64_t wallet2::get_min_ring_size() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_max_ring_size() { + if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0)) + return 16; if (use_fork_rules(8, 10)) return 11; return 0; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 543caac1b..57baf428f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -4134,7 +4134,8 @@ namespace tools try { res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info); - if (res.multisig_info.empty()) + m_wallet->multisig(&ready); + if (ready) { res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 72d0a652e..3db3d4059 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -81,9 +81,7 @@ static bool make_multisig_accounts(std::vector<cryptonote::account_base> &accoun for (std::size_t account_index{0}; account_index < accounts.size(); ++account_index) { multisig_accounts[account_index].initialize_kex(threshold, signers, round_msgs); - - if (!multisig_accounts[account_index].multisig_is_ready()) - temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg(); + temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg(); } // perform key exchange rounds @@ -94,9 +92,7 @@ static bool make_multisig_accounts(std::vector<cryptonote::account_base> &accoun for (std::size_t account_index{0}; account_index < multisig_accounts.size(); ++account_index) { multisig_accounts[account_index].kex_update(round_msgs); - - if (!multisig_accounts[account_index].multisig_is_ready()) - temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg(); + temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg(); } } diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index 2233f19e7..31d5780bb 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -101,7 +101,7 @@ class ColdSigningTest(): res = self.cold_wallet.export_key_images(True) self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset) - res = self.hot_wallet.transfer([dst], ring_size = 11, get_tx_key = False) + res = self.hot_wallet.transfer([dst], ring_size = 16, get_tx_key = False) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 0 @@ -121,7 +121,7 @@ class ColdSigningTest(): desc = res.desc[0] assert desc.amount_in >= amount + fee assert desc.amount_out == desc.amount_in - fee - assert desc.ring_size == 11 + assert desc.ring_size == 16 assert desc.unlock_time == 0 assert desc.payment_id in ['', '0000000000000000'] assert desc.change_amount == desc.amount_in - 1000000000000 - fee diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index d3b7a9592..41c55e4d4 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -51,7 +51,7 @@ namespace const command_line::arg_descriptor<std::string> arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"}; const command_line::arg_descriptor<uint64_t> arg_transfer_amount = {"transfer_amount", "", 60000000000000}; - const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 10}; + const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 15}; const command_line::arg_descriptor<size_t> arg_tx_count = {"tx-count", "", 100}; const command_line::arg_descriptor<size_t> arg_tx_per_second = {"tx-per-second", "", 20}; const command_line::arg_descriptor<size_t> arg_test_repeat_count = {"test_repeat_count", "", 1}; diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index 17b94494a..89cb2fdc7 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -44,7 +44,7 @@ class MultisigTest(): self.mine('41mro238grj56GnrWkakAKTkBy2yDcXYsUZ2iXCM9pe5Ueajd2RRc6Fhh3uBXT2UAKhAsUJ7Fg5zjjF2U1iGciFk5ief4ZP', 5) self.mine('44vZSprQKJQRFe6t1VHgU4ESvq2dv7TjBLVGE7QscKxMdFSiyyPCEV64NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6dakeff', 5) self.mine('47puypSwsV1gvUDratmX4y58fSwikXVehEiBhVLxJA1gRCxHyrRgTDr4NnKUQssFPyWxc2meyt7j63F2S2qtCTRL6aRPj5U', 5) - self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60) + self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) self.test_states() @@ -125,17 +125,18 @@ class MultisigTest(): for i in range(N_total): res = self.wallet[i].is_multisig() assert res.multisig == True - assert res.ready == (M_threshold == N_total) + assert not res.ready assert res.threshold == M_threshold assert res.total == N_total while True: - n_empty = 0 - for i in range(len(next_stage)): - if len(next_stage[i]) == 0: - n_empty += 1 - assert n_empty == 0 or n_empty == len(next_stage) - if n_empty == len(next_stage): + n_ready = 0 + for i in range(N_total): + res = self.wallet[i].is_multisig() + if res.ready == True: + n_ready += 1 + assert n_ready == 0 or n_ready == N_total + if n_ready == N_total: break info = next_stage next_stage = [] @@ -162,54 +163,72 @@ class MultisigTest(): 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', ] - info = [] - wallet = [None, None, None] - for i in range(3): - wallet[i] = Wallet(idx = i) - try: wallet[i].close_wallet() + info2of2 = [] + wallet2of2 = [None, None] + for i in range(2): + wallet2of2[i] = Wallet(idx = i) + try: wallet2of2[i].close_wallet() except: pass - res = wallet[i].restore_deterministic_wallet(seed = seeds[i]) - res = wallet[i].is_multisig() + res = wallet2of2[i].restore_deterministic_wallet(seed = seeds[i]) + res = wallet2of2[i].is_multisig() assert not res.multisig - res = wallet[i].prepare_multisig() + res = wallet2of2[i].prepare_multisig() assert len(res.multisig_info) > 0 - info.append(res.multisig_info) - - for i in range(3): - ok = False - try: res = wallet[i].exchange_multisig_keys(info) - except: ok = True - assert ok - res = wallet[i].is_multisig() - assert not res.multisig - - res = wallet[0].make_multisig(info[0:2], 2) - res = wallet[0].is_multisig() + info2of2.append(res.multisig_info) + + kex_info = [] + res = wallet2of2[0].make_multisig(info2of2, 2) + kex_info.append(res.multisig_info) + res = wallet2of2[1].make_multisig(info2of2, 2) + kex_info.append(res.multisig_info) + res = wallet2of2[0].exchange_multisig_keys(kex_info) + res = wallet2of2[0].is_multisig() assert res.multisig assert res.ready ok = False - try: res = wallet[0].prepare_multisig() + try: res = wallet2of2[0].prepare_multisig() except: ok = True assert ok ok = False - try: res = wallet[0].make_multisig(info[0:2], 2) + try: res = wallet2of2[0].make_multisig(info2of2, 2) except: ok = True assert ok - res = wallet[1].make_multisig(info, 2) - res = wallet[1].is_multisig() + info2of3 = [] + wallet2of3 = [None, None, None] + for i in range(3): + wallet2of3[i] = Wallet(idx = i) + try: wallet2of3[i].close_wallet() + except: pass + res = wallet2of3[i].restore_deterministic_wallet(seed = seeds[i]) + res = wallet2of3[i].is_multisig() + assert not res.multisig + res = wallet2of3[i].prepare_multisig() + assert len(res.multisig_info) > 0 + info2of3.append(res.multisig_info) + + for i in range(3): + ok = False + try: res = wallet2of3[i].exchange_multisig_keys(info) + except: ok = True + assert ok + res = wallet2of3[i].is_multisig() + assert not res.multisig + + res = wallet2of3[1].make_multisig(info2of3, 2) + res = wallet2of3[1].is_multisig() assert res.multisig assert not res.ready ok = False - try: res = wallet[1].prepare_multisig() + try: res = wallet2of3[1].prepare_multisig() except: ok = True assert ok ok = False - try: res = wallet[1].make_multisig(info[0:2], 2) + try: res = wallet2of3[1].make_multisig(info2of3[0:2], 2) except: ok = True assert ok @@ -261,7 +280,7 @@ class MultisigTest(): desc = res.desc[0] assert desc.amount_in >= amount + fee assert desc.amount_out == desc.amount_in - fee - assert desc.ring_size == 11 + assert desc.ring_size == 16 assert desc.unlock_time == 0 assert not 'payment_id' in desc or desc.payment_id in ['', '0000000000000000'] assert desc.change_amount == desc.amount_in - 1000000000000 - fee diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 5314b045d..dd15369d3 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -82,11 +82,11 @@ class TransferTest(): res = daemon.get_info() height = res.height - daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80) + daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 100) for i in range(len(self.wallet)): self.wallet[i].refresh() res = self.wallet[i].get_height() - assert res.height == height + 80 + assert res.height == height + 100 def transfer(self): daemon = Daemon() @@ -110,23 +110,23 @@ class TransferTest(): print ('Checking short payment IDs cannot be used when not in an integrated address') ok = False - try: self.wallet[0].transfer([dst], ring_size = 11, payment_id = '1234567812345678', get_tx_key = False) + try: self.wallet[0].transfer([dst], ring_size = 16, payment_id = '1234567812345678', get_tx_key = False) except: ok = True assert ok print ('Checking long payment IDs are rejected') ok = False - try: self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False, get_tx_hex = True) + try: self.wallet[0].transfer([dst], ring_size = 16, payment_id = payment_id, get_tx_key = False, get_tx_hex = True) except: ok = True assert ok print ('Checking empty destination is rejected') ok = False - try: self.wallet[0].transfer([], ring_size = 11, get_tx_key = False) + try: self.wallet[0].transfer([], ring_size = 16, get_tx_key = False) except: ok = True assert ok - res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = False, get_tx_hex = True) + res = self.wallet[0].transfer([dst], ring_size = 16, get_tx_key = False, get_tx_hex = True) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 0 @@ -231,7 +231,7 @@ class TransferTest(): print("Creating transfer to another, manual relay") dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1000000000000} - res = self.wallet[0].transfer([dst], ring_size = 11, get_tx_key = True, do_not_relay = True, get_tx_hex = True) + res = self.wallet[0].transfer([dst], ring_size = 16, get_tx_key = True, do_not_relay = True, get_tx_hex = True) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 32*2 @@ -321,7 +321,7 @@ class TransferTest(): dst0 = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000} dst1 = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1100000000000} dst2 = {'address': '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 'amount': 1200000000000} - res = self.wallet[0].transfer([dst0, dst1, dst2], ring_size = 11, get_tx_key = True) + res = self.wallet[0].transfer([dst0, dst1, dst2], ring_size = 16, get_tx_key = True) assert len(res.tx_hash) == 32*2 txid = res.tx_hash assert len(res.tx_key) == 32*2 diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index deee10aed..5ddd78955 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -120,7 +120,7 @@ static void check_results(const std::vector<std::string> &intermediate_infos, for (size_t i = 0; i < wallets.size(); ++i) { - EXPECT_TRUE(intermediate_infos[i].empty()); + EXPECT_TRUE(!intermediate_infos[i].empty()); bool ready; uint32_t threshold, total; EXPECT_TRUE(wallets[i].multisig(&ready, &threshold, &total)); @@ -171,7 +171,7 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M) { ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT); ASSERT_TRUE(M <= wallets.size()); - std::uint32_t rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M); + std::uint32_t total_rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M) + 1; std::uint32_t rounds_complete{0}; // initialize wallets, get first round multisig kex msgs @@ -203,18 +203,17 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M) ++rounds_complete; // perform kex rounds until kex is complete - while (!intermediate_infos[0].empty()) + bool ready; + wallets[0].multisig(&ready); + while (!ready) { - bool ready{false}; - wallets[0].multisig(&ready); - EXPECT_FALSE(ready); - intermediate_infos = exchange_round(wallets, intermediate_infos); + wallets[0].multisig(&ready); ++rounds_complete; } - EXPECT_EQ(rounds_required, rounds_complete); + EXPECT_EQ(total_rounds_required, rounds_complete); check_results(intermediate_infos, wallets, M); } |