mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-26 09:24:47 +00:00
wallet: use raw encrypted data in multisig import/export RPC
This commit is contained in:
parent
2fa707d1a5
commit
31a97e761e
5 changed files with 73 additions and 163 deletions
|
@ -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 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 OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
|
||||||
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
|
|
||||||
|
|
||||||
#define LOCK_IDLE_SCOPE() \
|
#define LOCK_IDLE_SCOPE() \
|
||||||
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
|
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<std::string> &args)
|
||||||
const std::string filename = args[0];
|
const std::string filename = args[0];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<tools::wallet2::multisig_info> outs = m_wallet->export_multisig();
|
cryptonote::blobdata ciphertext = m_wallet->export_multisig();
|
||||||
|
|
||||||
std::stringstream oss;
|
bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext);
|
||||||
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);
|
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to save file ") << filename;
|
fail_msg_writer() << tr("failed to save file ") << filename;
|
||||||
|
@ -970,8 +957,7 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
||||||
if (m_wallet->ask_password() && !get_and_verify_password())
|
if (m_wallet->ask_password() && !get_and_verify_password())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::vector<std::vector<tools::wallet2::multisig_info>> info;
|
std::vector<cryptonote::blobdata> info;
|
||||||
std::unordered_set<crypto::public_key> seen;
|
|
||||||
for (size_t n = 0; n < args.size(); ++n)
|
for (size_t n = 0; n < args.size(); ++n)
|
||||||
{
|
{
|
||||||
const std::string filename = args[n];
|
const std::string filename = args[n];
|
||||||
|
@ -982,65 +968,7 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
||||||
fail_msg_writer() << tr("failed to read file ") << filename;
|
fail_msg_writer() << tr("failed to read file ") << filename;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const size_t magiclen = strlen(MULTISIG_EXPORT_FILE_MAGIC);
|
info.push_back(std::move(data));
|
||||||
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<tools::wallet2::multisig_info> i;
|
|
||||||
boost::archive::portable_binary_iarchive ar(iss);
|
|
||||||
ar >> i;
|
|
||||||
message_writer() << (boost::format(tr("%u outputs found in %s")) % boost::lexical_cast<std::string>(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK_IDLE_SCOPE();
|
LOCK_IDLE_SCOPE();
|
||||||
|
|
|
@ -103,6 +103,8 @@ using namespace cryptonote;
|
||||||
|
|
||||||
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
|
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
|
||||||
|
|
||||||
|
#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// Create on-demand to prevent static initialization order fiasco issues.
|
// 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;
|
return ki;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
std::vector<tools::wallet2::multisig_info> wallet2::export_multisig()
|
cryptonote::blobdata wallet2::export_multisig()
|
||||||
{
|
{
|
||||||
std::vector<tools::wallet2::multisig_info> info;
|
std::vector<tools::wallet2::multisig_info> info;
|
||||||
|
|
||||||
|
@ -8456,7 +8458,19 @@ std::vector<tools::wallet2::multisig_info> wallet2::export_multisig()
|
||||||
info[n].m_signer = signer;
|
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<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n)
|
void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n)
|
||||||
|
@ -8480,9 +8494,50 @@ void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key
|
||||||
m_key_images[td.m_key_image] = n;
|
m_key_images[td.m_key_image] = n;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
size_t wallet2::import_multisig(std::vector<std::vector<tools::wallet2::multisig_info>> info)
|
size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
|
||||||
{
|
{
|
||||||
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
|
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
|
||||||
|
|
||||||
|
std::vector<std::vector<tools::wallet2::multisig_info>> info;
|
||||||
|
std::unordered_set<crypto::public_key> 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<tools::wallet2::multisig_info> i;
|
||||||
|
boost::archive::portable_binary_iarchive ar(iss);
|
||||||
|
ar >> i;
|
||||||
|
MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(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");
|
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<std::vector<rct::key>> k;
|
std::vector<std::vector<rct::key>> k;
|
||||||
|
|
|
@ -499,12 +499,12 @@ namespace tools
|
||||||
* Export multisig info
|
* Export multisig info
|
||||||
* This will generate and remember new k values
|
* This will generate and remember new k values
|
||||||
*/
|
*/
|
||||||
std::vector<tools::wallet2::multisig_info> export_multisig();
|
cryptonote::blobdata export_multisig();
|
||||||
/*!
|
/*!
|
||||||
* Import a set of multisig info from multisig partners
|
* Import a set of multisig info from multisig partners
|
||||||
* \return the number of inputs which were imported
|
* \return the number of inputs which were imported
|
||||||
*/
|
*/
|
||||||
size_t import_multisig(std::vector<std::vector<tools::wallet2::multisig_info>> info);
|
size_t import_multisig(std::vector<cryptonote::blobdata> info);
|
||||||
/*!
|
/*!
|
||||||
* \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there)
|
* \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)
|
* \param wallet_name Name of wallet file (should exist)
|
||||||
|
|
|
@ -2626,7 +2626,7 @@ namespace tools
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<tools::wallet2::multisig_info> info;
|
cryptonote::blobdata info;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
info = m_wallet->export_multisig();
|
info = m_wallet->export_multisig();
|
||||||
|
@ -2638,20 +2638,7 @@ namespace tools
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.info.resize(info.size());
|
res.info = epee::string_tools::buff_to_hex_nodelimer(info);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2687,42 +2674,15 @@ namespace tools
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<tools::wallet2::multisig_info>> info;
|
std::vector<cryptonote::blobdata> info;
|
||||||
info.resize(req.info.size());
|
info.resize(req.info.size());
|
||||||
for (size_t n = 0; n < info.size(); ++n)
|
for (size_t n = 0; n < info.size(); ++n)
|
||||||
{
|
{
|
||||||
info[n].resize(req.info[n].info.size());
|
if (!epee::string_tools::parse_hexstr_to_binbuff(req.info[n], info[n]))
|
||||||
for (size_t i = 0; i < info[n].size(); ++i)
|
|
||||||
{
|
{
|
||||||
const auto &src = req.info[n].info[i];
|
er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
|
||||||
auto &dst = info[n][i];
|
er.message = "Failed to parse hex.";
|
||||||
|
return false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_entry> LR;
|
|
||||||
std::vector<std::string> 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 COMMAND_RPC_EXPORT_MULTISIG
|
||||||
{
|
{
|
||||||
struct request
|
struct request
|
||||||
|
@ -1601,7 +1577,7 @@ namespace wallet_rpc
|
||||||
|
|
||||||
struct response
|
struct response
|
||||||
{
|
{
|
||||||
std::vector<multisig_info_entry> info;
|
std::string info;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(info)
|
KV_SERIALIZE(info)
|
||||||
|
@ -1611,18 +1587,9 @@ namespace wallet_rpc
|
||||||
|
|
||||||
struct COMMAND_RPC_IMPORT_MULTISIG
|
struct COMMAND_RPC_IMPORT_MULTISIG
|
||||||
{
|
{
|
||||||
struct participant_entry
|
|
||||||
{
|
|
||||||
std::vector<multisig_info_entry> info;
|
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
|
||||||
KV_SERIALIZE(info)
|
|
||||||
END_KV_SERIALIZE_MAP()
|
|
||||||
};
|
|
||||||
|
|
||||||
struct request
|
struct request
|
||||||
{
|
{
|
||||||
std::vector<participant_entry> info;
|
std::vector<std::string> info;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(info)
|
KV_SERIALIZE(info)
|
||||||
|
|
Loading…
Reference in a new issue