diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blockchain_db/blockchain_db.cpp | 9 | ||||
-rw-r--r-- | src/crypto/keccak.c | 95 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_format_utils.cpp | 2 | ||||
-rw-r--r-- | src/multisig/multisig_account.cpp | 47 | ||||
-rw-r--r-- | src/multisig/multisig_account.h | 24 | ||||
-rw-r--r-- | src/multisig/multisig_account_kex_impl.cpp | 331 | ||||
-rw-r--r-- | src/net/zmq.cpp | 2 | ||||
-rw-r--r-- | src/ringct/bulletproofs_plus.cc | 6 | ||||
-rw-r--r-- | src/ringct/rctSigs.cpp | 19 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 10 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 13 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 3 |
12 files changed, 348 insertions, 213 deletions
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 44be08003..ab73e255c 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -241,15 +241,8 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair } else { - rct::key commitment; - if (tx.version > 1) - { - commitment = tx.rct_signatures.outPk[i].mask; - if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type)) - commitment = rct::scalarmult8(commitment); - } amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time, - tx.version > 1 ? &commitment : NULL); + tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL); } } add_tx_amount_output_indices(tx_id, amount_output_indices); 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_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 432617a4f..f101f10c5 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -190,7 +190,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V"); rv.p.bulletproofs_plus[0].V.resize(n_amounts); for (size_t i = 0; i < n_amounts; ++i) - rv.p.bulletproofs_plus[0].V[i] = rv.outPk[i].mask; + rv.p.bulletproofs_plus[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); } else if (bulletproof) { diff --git a/src/multisig/multisig_account.cpp b/src/multisig/multisig_account.cpp index 3fbb717f2..9bdcf2dbc 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 689001e92..7b372bbff 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 bb272fed1..be9ed9cb2 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/net/zmq.cpp b/src/net/zmq.cpp index bd7855f21..2b3ca8376 100644 --- a/src/net/zmq.cpp +++ b/src/net/zmq.cpp @@ -134,7 +134,7 @@ namespace zmq { /* ZMQ documentation states that message parts are atomic - either all are received or none are. Looking through ZMQ code and - Github discussions indicates that after part 1 is returned, + GitHub discussions indicates that after part 1 is returned, `EAGAIN` cannot be returned to meet these guarantees. Unit tests verify (for the `inproc://` case) that this is the behavior. Therefore, read errors after the first part are treated as a diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc index 4472bbd84..77b800064 100644 --- a/src/ringct/bulletproofs_plus.cc +++ b/src/ringct/bulletproofs_plus.cc @@ -644,8 +644,7 @@ try_again: { sc_mul(temp.bytes, temp.bytes, z_squared.bytes); sc_mul(temp2.bytes, y_powers[MN+1].bytes, temp.bytes); - sc_mul(temp2.bytes, temp2.bytes, gamma[j].bytes); - sc_add(alpha1.bytes, alpha1.bytes, temp2.bytes); + sc_muladd(alpha1.bytes, temp2.bytes, gamma[j].bytes, alpha1.bytes); } // These are used in the inner product rounds @@ -706,7 +705,8 @@ try_again: rct::key challenge_squared; sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes); - rct::key challenge_squared_inv = invert(challenge_squared); + rct::key challenge_squared_inv; + sc_mul(challenge_squared_inv.bytes, challenge_inv.bytes, challenge_inv.bytes); sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.bytes, alpha1.bytes); sc_muladd(alpha1.bytes, dR.bytes, challenge_squared_inv.bytes, alpha1.bytes); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index d7883baac..3e85f60ce 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1235,10 +1235,7 @@ namespace rct { } for (i = 0; i < outamounts.size(); ++i) { - if (plus) - rv.outPk[i].mask = C[i]; - else - rv.outPk[i].mask = rct::scalarmult8(C[i]); + rv.outPk[i].mask = rct::scalarmult8(C[i]); outSk[i].mask = masks[i]; } } @@ -1276,10 +1273,7 @@ namespace rct { } for (i = 0; i < batch_size; ++i) { - if (plus) - rv.outPk[i + amounts_proved].mask = C[i]; - else - rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); + rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); outSk[i + amounts_proved].mask = masks[i]; } amounts_proved += batch_size; @@ -1486,10 +1480,7 @@ namespace rct { rct::keyV masks(rv.outPk.size()); for (size_t i = 0; i < rv.outPk.size(); i++) { - if (bulletproof_plus) - masks[i] = rct::scalarmult8(rv.outPk[i].mask); - else - masks[i] = rv.outPk[i].mask; + masks[i] = rv.outPk[i].mask; } key sumOutpks = addKeys(masks); DP(sumOutpks); @@ -1649,8 +1640,6 @@ namespace rct { mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; - if (is_rct_bulletproof_plus(rv.type)) - C = scalarmult8(C); DP("C"); DP(C); key Ctmp; @@ -1682,8 +1671,6 @@ namespace rct { mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; - if (is_rct_bulletproof_plus(rv.type)) - C = scalarmult8(C); DP("C"); DP(C); key Ctmp; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f30225e1d..d3e40ab74 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1087,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; @@ -1148,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; @@ -1157,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; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f0b29b9a2..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; } //---------------------------------------------------------------------------------------------------- @@ -11356,9 +11359,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt crypto::derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus); - rct::key C = tx.rct_signatures.outPk[n].mask; - if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type)) - C = rct::scalarmult8(C); + const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount"); 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()); } |