mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-26 09:24:47 +00:00
multisig address generation RPC
This commit is contained in:
parent
fff871a455
commit
cd64c7990c
6 changed files with 400 additions and 6 deletions
|
@ -4234,7 +4234,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::save_multisig_tx(multisig_tx_set txs, const std::string &filename)
|
||||
std::string wallet2::save_multisig_tx(multisig_tx_set txs)
|
||||
{
|
||||
LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions");
|
||||
|
||||
|
@ -4265,21 +4265,37 @@ bool wallet2::save_multisig_tx(multisig_tx_set txs, const std::string &filename)
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
return std::string();
|
||||
}
|
||||
LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str());
|
||||
std::string ciphertext = encrypt_with_view_secret_key(oss.str());
|
||||
return epee::file_io_utils::save_string_to_file(filename, std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext);
|
||||
return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
|
||||
bool wallet2::save_multisig_tx(const multisig_tx_set &txs, const std::string &filename)
|
||||
{
|
||||
std::string ciphertext = save_multisig_tx(txs);
|
||||
if (ciphertext.empty())
|
||||
return false;
|
||||
return epee::file_io_utils::save_string_to_file(filename, ciphertext);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector)
|
||||
{
|
||||
multisig_tx_set txs;
|
||||
txs.m_ptx = ptx_vector;
|
||||
crypto::hash hash;
|
||||
cn_fast_hash(&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (char*)&hash);
|
||||
txs.m_signers.insert(hash);
|
||||
return save_multisig_tx(txs, filename);
|
||||
return save_multisig_tx(txs);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
|
||||
{
|
||||
std::string ciphertext = save_multisig_tx(ptx_vector);
|
||||
if (ciphertext.empty())
|
||||
return false;
|
||||
return epee::file_io_utils::save_string_to_file(filename, ciphertext);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
|
||||
|
|
|
@ -592,7 +592,9 @@ namespace tools
|
|||
void commit_tx(pending_tx& ptx_vector);
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
|
||||
bool save_multisig_tx(multisig_tx_set txs, const std::string &filename);
|
||||
std::string save_multisig_tx(multisig_tx_set txs);
|
||||
bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename);
|
||||
std::string save_multisig_tx(const std::vector<pending_tx>& ptx_vector);
|
||||
bool save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
|
||||
// load unsigned tx from file and sign it. Takes confirmation callback as argument. Used by the cli wallet
|
||||
bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, std::function<bool(const unsigned_tx_set&)> accept_func = NULL, bool export_raw = false);
|
||||
|
|
|
@ -2339,6 +2339,240 @@ namespace tools
|
|||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
res.multisig = m_wallet->multisig(&res.threshold, &res.total);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_wallet->restricted())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
|
||||
er.message = "This wallet is already multisig";
|
||||
return false;
|
||||
}
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "wallet is watch-only and cannot be made multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
res.multisig_info = m_wallet->get_multisig_info();
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_wallet->restricted())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
|
||||
er.message = "This wallet is already multisig";
|
||||
return false;
|
||||
}
|
||||
if (m_wallet->watch_only())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
|
||||
er.message = "wallet is watch-only and cannot be made multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse all multisig info
|
||||
std::vector<crypto::secret_key> secret_keys(req.multisig_info.size());
|
||||
std::vector<crypto::public_key> public_keys(req.multisig_info.size());
|
||||
for (size_t i = 0; i < req.multisig_info.size(); ++i)
|
||||
{
|
||||
if (!m_wallet->verify_multisig_info(req.multisig_info[i], secret_keys[i], public_keys[i]))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_INFO;
|
||||
er.message = "Bad multisig info: " + req.multisig_info[i];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplicates
|
||||
for (size_t i = 1; i < secret_keys.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < secret_keys.size(); ++j)
|
||||
{
|
||||
if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[j]))
|
||||
{
|
||||
secret_keys[j] = secret_keys.back();
|
||||
public_keys[j] = public_keys.back();
|
||||
secret_keys.pop_back();
|
||||
public_keys.pop_back();
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// people may include their own, weed it out
|
||||
crypto::hash hash;
|
||||
crypto::cn_fast_hash(&m_wallet->get_account().get_keys().m_view_secret_key, sizeof(crypto::secret_key), hash);
|
||||
for (size_t i = 0; i < secret_keys.size(); ++i)
|
||||
{
|
||||
if (rct::sk2rct(secret_keys[i]) == rct::hash2rct(hash))
|
||||
{
|
||||
secret_keys[i] = secret_keys.back();
|
||||
public_keys[i] = public_keys.back();
|
||||
secret_keys.pop_back();
|
||||
public_keys.pop_back();
|
||||
--i;
|
||||
}
|
||||
else if (public_keys[i] == m_wallet->get_account().get_keys().m_account_address.m_spend_public_key)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_INFO;
|
||||
er.message = "Found local spend public key, but not local view secret key - something very weird";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->make_multisig(req.password, secret_keys, public_keys, req.threshold);
|
||||
res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_wallet->restricted())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
if (!m_wallet->multisig())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is not multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<tools::wallet2::multisig_info> info;
|
||||
try
|
||||
{
|
||||
info = m_wallet->export_multisig();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
res.info.resize(info.size());
|
||||
for (size_t n = 0; n < info.size(); ++n)
|
||||
{
|
||||
res.info[n].partial_key_image = epee::string_tools::pod_to_hex(info[n].m_partial_key_image);
|
||||
res.info[n].L = epee::string_tools::pod_to_hex(info[n].m_L);
|
||||
res.info[n].R = epee::string_tools::pod_to_hex(info[n].m_R);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_wallet->restricted())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
uint32_t threshold, total;
|
||||
if (!m_wallet->multisig(&threshold, &total))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is not multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.info.size() < threshold - 1)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
|
||||
er.message = "Needs multisig export info from more participants";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::vector<tools::wallet2::multisig_info>> 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::hex_to_pod(req.info[n].info[i].partial_key_image, info[n][i].m_partial_key_image))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
|
||||
er.message = "Failed to parse partial key image from multisig info";
|
||||
return false;
|
||||
}
|
||||
if (!epee::string_tools::hex_to_pod(req.info[n].info[i].L, info[n][i].m_L) || !epee::string_tools::hex_to_pod(req.info[n].info[i].R, info[n][i].m_R))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_LR;
|
||||
er.message = "Failed to parse L/R info from hex";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
res.n_outputs = m_wallet->import_multisig(info);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Error calling import_multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_trusted_daemon)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_wallet->rescan_spent();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.message = std::string("Success, but failed to update spent status after import multisig info: ") + e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
er.message = "Success, but cannot update spent status after import multisig info as dameon is untrusted";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
|
@ -117,6 +117,11 @@ namespace tools
|
|||
MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES)
|
||||
MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET)
|
||||
MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
|
||||
MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG)
|
||||
MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG)
|
||||
MAP_JON_RPC_WE("make_multisig", on_make_multisig, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG)
|
||||
MAP_JON_RPC_WE("export_multisig_info", on_export_multisig, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG)
|
||||
MAP_JON_RPC_WE("import_multisig_info", on_import_multisig, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG)
|
||||
END_JSON_RPC_MAP()
|
||||
END_URI_MAP2()
|
||||
|
||||
|
@ -171,6 +176,11 @@ namespace tools
|
|||
bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er);
|
||||
bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er);
|
||||
bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er);
|
||||
bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
|
||||
//json rpc v2
|
||||
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
|
||||
|
|
|
@ -1487,5 +1487,131 @@ namespace wallet_rpc
|
|||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_IS_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
bool multisig;
|
||||
uint32_t threshold;
|
||||
uint32_t total;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(multisig)
|
||||
KV_SERIALIZE(threshold)
|
||||
KV_SERIALIZE(total)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_PREPARE_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string multisig_info;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(multisig_info)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_MAKE_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::vector<std::string> multisig_info;
|
||||
uint32_t threshold;
|
||||
std::string password;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(multisig_info)
|
||||
KV_SERIALIZE(threshold)
|
||||
KV_SERIALIZE(password)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string address;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct multisig_info_entry
|
||||
{
|
||||
std::string partial_key_image;
|
||||
std::string L;
|
||||
std::string R;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(partial_key_image)
|
||||
KV_SERIALIZE(L)
|
||||
KV_SERIALIZE(R)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_EXPORT_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::vector<multisig_info_entry> info;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(info)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
std::vector<participant_entry> info;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(info)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
uint64_t n_outputs;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(n_outputs)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,3 +58,9 @@
|
|||
#define WALLET_RPC_ERROR_CODE_WRONG_KEY -25
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_HEX -26
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA -27
|
||||
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG -28
|
||||
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY -29
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_INFO -30
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG -31
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_LR -32
|
||||
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED -33
|
||||
|
|
Loading…
Reference in a new issue