diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 145d90d91..5c94f1704 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -85,7 +85,6 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 5 // Used to inform user about min ring size -- does not track actual protocol #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" -#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ @@ -916,21 +915,9 @@ bool simple_wallet::export_multisig(const std::vector &args) const std::string filename = args[0]; try { - std::vector outs = m_wallet->export_multisig(); + cryptonote::blobdata ciphertext = m_wallet->export_multisig(); - std::stringstream oss; - boost::archive::portable_binary_oarchive ar(oss); - ar << outs; - - std::string magic(MULTISIG_EXPORT_FILE_MAGIC, strlen(MULTISIG_EXPORT_FILE_MAGIC)); - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - std::string header; - header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); - header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); - crypto::public_key signer = m_wallet->get_multisig_signer_public_key(); - header += std::string((const char *)&signer, sizeof(crypto::public_key)); - std::string ciphertext = m_wallet->encrypt_with_view_secret_key(header + oss.str()); - bool r = epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); + bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); if (!r) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -970,8 +957,7 @@ bool simple_wallet::import_multisig(const std::vector &args) if (m_wallet->ask_password() && !get_and_verify_password()) return true; - std::vector> info; - std::unordered_set seen; + std::vector info; for (size_t n = 0; n < args.size(); ++n) { const std::string filename = args[n]; @@ -982,65 +968,7 @@ bool simple_wallet::import_multisig(const std::vector &args) fail_msg_writer() << tr("failed to read file ") << filename; return true; } - const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC); - if (data.size() < magiclen || memcmp(data.data(), MULTISIG_EXPORT_FILE_MAGIC, magiclen)) - { - fail_msg_writer() << tr("Bad multisig info file magic in ") << filename; - return true; - } - - try - { - data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen)); - } - catch (const std::exception &e) - { - fail_msg_writer() << tr("Failed to decrypt ") << filename << ": " << e.what(); - return true; - } - - const size_t headerlen = 3 * sizeof(crypto::public_key); - if (data.size() < headerlen) - { - fail_msg_writer() << tr("Bad data size from file ") << filename; - return true; - } - const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; - const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; - const crypto::public_key &signer = *(const crypto::public_key*)&data[2*sizeof(crypto::public_key)]; - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) - { - fail_msg_writer() << (boost::format(tr("Multisig info from %s is for a different account")) % filename).str(); - return true; - } - if (m_wallet->get_multisig_signer_public_key() == signer) - { - message_writer() << (boost::format(tr("Multisig info from %s is from this wallet, ignored")) % filename).str(); - continue; - } - if (seen.find(signer) != seen.end()) - { - message_writer() << (boost::format(tr("Multisig info from %s already seen, ignored")) % filename).str(); - continue; - } - seen.insert(signer); - - try - { - std::string body(data, headerlen); - std::istringstream iss(body); - std::vector i; - boost::archive::portable_binary_iarchive ar(iss); - ar >> i; - message_writer() << (boost::format(tr("%u outputs found in %s")) % boost::lexical_cast(i.size()) % filename).str(); - info.push_back(std::move(i)); - } - catch (const std::exception &e) - { - fail_msg_writer() << tr("Failed to import multisig info: ") << e.what(); - return true; - } + info.push_back(std::move(data)); } LOCK_IDLE_SCOPE(); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 07f986e02..635264d70 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -103,6 +103,8 @@ using namespace cryptonote; #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" +#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" + namespace { // Create on-demand to prevent static initialization order fiasco issues. @@ -8421,7 +8423,7 @@ crypto::key_image wallet2::get_multisig_composite_key_image(size_t n) const return ki; } //---------------------------------------------------------------------------------------------------- -std::vector wallet2::export_multisig() +cryptonote::blobdata wallet2::export_multisig() { std::vector info; @@ -8456,7 +8458,19 @@ std::vector wallet2::export_multisig() info[n].m_signer = signer; } - return info; + std::stringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + ar << info; + + std::string magic(MULTISIG_EXPORT_FILE_MAGIC, strlen(MULTISIG_EXPORT_FILE_MAGIC)); + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + std::string header; + header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); + header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + header += std::string((const char *)&signer, sizeof(crypto::public_key)); + std::string ciphertext = encrypt_with_view_secret_key(header + oss.str()); + + return MULTISIG_EXPORT_FILE_MAGIC + ciphertext; } //---------------------------------------------------------------------------------------------------- void wallet2::update_multisig_rescan_info(const std::vector> &multisig_k, const std::vector> &info, size_t n) @@ -8480,9 +8494,50 @@ void wallet2::update_multisig_rescan_info(const std::vector> info) +size_t wallet2::import_multisig(std::vector blobs) { CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig"); + + std::vector> info; + std::unordered_set seen; + for (cryptonote::blobdata &data: blobs) + { + const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC); + THROW_WALLET_EXCEPTION_IF(data.size() < magiclen || memcmp(data.data(), MULTISIG_EXPORT_FILE_MAGIC, magiclen), + error::wallet_internal_error, "Bad multisig info file magic in "); + + data = decrypt_with_view_secret_key(std::string(data, magiclen)); + + const size_t headerlen = 3 * sizeof(crypto::public_key); + THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, "Bad data size"); + + const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; + const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; + const crypto::public_key &signer = *(const crypto::public_key*)&data[2*sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + THROW_WALLET_EXCEPTION_IF(public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key, + error::wallet_internal_error, "Multisig info is for a different account"); + if (get_multisig_signer_public_key() == signer) + { + MINFO("Multisig info from this wallet ignored"); + continue; + } + if (seen.find(signer) != seen.end()) + { + MINFO("Duplicate multisig info ignored"); + continue; + } + seen.insert(signer); + + std::string body(data, headerlen); + std::istringstream iss(body); + std::vector i; + boost::archive::portable_binary_iarchive ar(iss); + ar >> i; + MINFO(boost::format("%u outputs found") % boost::lexical_cast(i.size())); + info.push_back(std::move(i)); + } + CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); std::vector> k; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7a8becb3c..cb9d7e980 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -499,12 +499,12 @@ namespace tools * Export multisig info * This will generate and remember new k values */ - std::vector export_multisig(); + cryptonote::blobdata export_multisig(); /*! * Import a set of multisig info from multisig partners * \return the number of inputs which were imported */ - size_t import_multisig(std::vector> info); + size_t import_multisig(std::vector info); /*! * \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there) * \param wallet_name Name of wallet file (should exist) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 0157a6f71..4c14433f4 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2626,7 +2626,7 @@ namespace tools return false; } - std::vector info; + cryptonote::blobdata info; try { info = m_wallet->export_multisig(); @@ -2638,20 +2638,7 @@ namespace tools return false; } - res.info.resize(info.size()); - for (size_t n = 0; n < info.size(); ++n) - { - res.info[n].signer = epee::string_tools::pod_to_hex(info[n].m_signer); - res.info[n].LR.resize(info[n].m_LR.size()); - for (size_t l = 0; l < info[n].m_LR.size(); ++l) - { - res.info[n].LR[l].L = epee::string_tools::pod_to_hex(info[n].m_LR[l].m_L); - res.info[n].LR[l].R = epee::string_tools::pod_to_hex(info[n].m_LR[l].m_R); - } - res.info[n].partial_key_images.resize(info[n].m_partial_key_images.size()); - for (size_t l = 0; l < info[n].m_partial_key_images.size(); ++l) - res.info[n].partial_key_images[l] = epee::string_tools::pod_to_hex(info[n].m_partial_key_images[l]); - } + res.info = epee::string_tools::buff_to_hex_nodelimer(info); return true; } @@ -2687,42 +2674,15 @@ namespace tools return false; } - std::vector> info; + std::vector info; info.resize(req.info.size()); for (size_t n = 0; n < info.size(); ++n) { - info[n].resize(req.info[n].info.size()); - for (size_t i = 0; i < info[n].size(); ++i) + if (!epee::string_tools::parse_hexstr_to_binbuff(req.info[n], info[n])) { - const auto &src = req.info[n].info[i]; - auto &dst = info[n][i]; - - if (!epee::string_tools::hex_to_pod(src.signer, dst.m_signer)) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; - er.message = "Failed to parse signer from multisig info"; - return false; - } - dst.m_LR.resize(src.LR.size()); - for (size_t l = 0; l < src.LR.size(); ++l) - { - if (!epee::string_tools::hex_to_pod(src.LR[l].L, dst.m_LR[l].m_L) || !epee::string_tools::hex_to_pod(src.LR[l].R, dst.m_LR[l].m_R)) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_LR; - er.message = "Failed to parse L/R from multisig info"; - return false; - } - } - dst.m_partial_key_images.resize(src.partial_key_images.size()); - for (size_t l = 0; l < src.partial_key_images.size(); ++l) - { - if (!epee::string_tools::hex_to_pod(src.partial_key_images[l], dst.m_partial_key_images[l])) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; - er.message = "Failed to parse partial key image from multisig info"; - return false; - } - } + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; } } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 07917a683..57cc01e27 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1567,30 +1567,6 @@ namespace wallet_rpc }; }; - struct LR_entry - { - std::string L; - std::string R; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(L) - KV_SERIALIZE(R) - END_KV_SERIALIZE_MAP() - }; - - struct multisig_info_entry - { - std::string signer; - std::vector LR; - std::vector partial_key_images; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(signer) - KV_SERIALIZE(LR) - KV_SERIALIZE(partial_key_images) - END_KV_SERIALIZE_MAP() - }; - struct COMMAND_RPC_EXPORT_MULTISIG { struct request @@ -1601,7 +1577,7 @@ namespace wallet_rpc struct response { - std::vector info; + std::string info; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(info) @@ -1611,18 +1587,9 @@ namespace wallet_rpc struct COMMAND_RPC_IMPORT_MULTISIG { - struct participant_entry - { - std::vector info; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(info) - END_KV_SERIALIZE_MAP() - }; - struct request { - std::vector info; + std::vector info; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(info)