wallet: add multisig sign/submit RPC
This commit is contained in:
parent
e36f5b6021
commit
2fa707d1a5
|
@ -4477,25 +4477,12 @@ bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const
|
|||
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)
|
||||
bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
|
||||
{
|
||||
std::string s;
|
||||
boost::system::error_code errcode;
|
||||
|
||||
if (!boost::filesystem::exists(filename, errcode))
|
||||
{
|
||||
LOG_PRINT_L0("File " << filename << " does not exist: " << errcode);
|
||||
return false;
|
||||
}
|
||||
if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s))
|
||||
{
|
||||
LOG_PRINT_L0("Failed to load from " << filename);
|
||||
return false;
|
||||
}
|
||||
const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX);
|
||||
if (strncmp(s.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
|
||||
{
|
||||
LOG_PRINT_L0("Bad magic from " << filename);
|
||||
LOG_PRINT_L0("Bad magic from multisig tx data");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
|
@ -4504,8 +4491,8 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t
|
|||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to decrypt " << filename << ": " << e.what());
|
||||
return 0;
|
||||
LOG_PRINT_L0("Failed to decrypt multisig tx data: " << e.what());
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
|
@ -4515,7 +4502,7 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to parse data from " << filename);
|
||||
LOG_PRINT_L0("Failed to parse multisig tx data");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4557,12 +4544,43 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
std::string s;
|
||||
boost::system::error_code errcode;
|
||||
|
||||
if (!boost::filesystem::exists(filename, errcode))
|
||||
{
|
||||
LOG_PRINT_L0("File " << filename << " does not exist: " << errcode);
|
||||
return false;
|
||||
}
|
||||
if (!epee::file_io_utils::load_file_to_string(filename.c_str(), s))
|
||||
{
|
||||
LOG_PRINT_L0("Failed to load from " << filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!load_multisig_tx(s, exported_txs, accept_func))
|
||||
{
|
||||
LOG_PRINT_L0("Failed to parse multisig tx data from " << filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found");
|
||||
|
||||
const crypto::public_key local_signer = get_multisig_signer_public_key();
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.find(local_signer) != exported_txs.m_signers.end(),
|
||||
error::wallet_internal_error, "Transaction already signed by this private key");
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() > m_multisig_threshold,
|
||||
error::wallet_internal_error, "Transaction was signed by too many signers");
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() == m_multisig_threshold,
|
||||
error::wallet_internal_error, "Transaction is already fully signed");
|
||||
|
||||
txids.clear();
|
||||
|
||||
// sign the transactions
|
||||
|
@ -4667,14 +4685,6 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
|
|||
if(!load_multisig_tx_from_file(filename, exported_txs))
|
||||
return false;
|
||||
|
||||
const crypto::public_key signer = get_multisig_signer_public_key();
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.find(signer) != exported_txs.m_signers.end(),
|
||||
error::wallet_internal_error, "Transaction already signed by this private key");
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() > m_multisig_threshold,
|
||||
error::wallet_internal_error, "Transaction was signed by too many signers");
|
||||
THROW_WALLET_EXCEPTION_IF(exported_txs.m_signers.size() == m_multisig_threshold,
|
||||
error::wallet_internal_error, "Transaction is already fully signed");
|
||||
|
||||
if (accept_func && !accept_func(exported_txs))
|
||||
{
|
||||
LOG_PRINT_L1("Transactions rejected by callback");
|
||||
|
|
|
@ -395,6 +395,11 @@ namespace tools
|
|||
{
|
||||
std::vector<pending_tx> m_ptx;
|
||||
std::unordered_set<crypto::public_key> m_signers;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(m_ptx)
|
||||
FIELD(m_signers)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct keys_file_data
|
||||
|
@ -640,6 +645,7 @@ namespace tools
|
|||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
|
||||
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
|
||||
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
|
||||
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);
|
||||
bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids);
|
||||
|
|
|
@ -610,11 +610,6 @@ namespace tools
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hash
|
||||
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
|
||||
if (req.get_tx_key)
|
||||
{
|
||||
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
|
||||
|
@ -623,18 +618,45 @@ namespace tools
|
|||
}
|
||||
res.fee = ptx_vector.back().fee;
|
||||
|
||||
if (req.get_tx_hex)
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx_vector.back().tx, blob);
|
||||
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
|
||||
res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
|
||||
if (res.multisig_txset.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
else
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
::serialization::serialize(ar, ptx_vector.back());
|
||||
res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hash
|
||||
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx_vector.back().tx, blob);
|
||||
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx_vector.back();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -675,17 +697,9 @@ namespace tools
|
|||
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
|
||||
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
|
||||
|
||||
if (!req.do_not_relay)
|
||||
{
|
||||
LOG_PRINT_L2("on_transfer_split calling commit_tx");
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
LOG_PRINT_L2("on_transfer_split called commit_tx");
|
||||
}
|
||||
|
||||
// populate response with tx hashes
|
||||
for (const auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
if (req.get_tx_keys)
|
||||
{
|
||||
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
|
||||
|
@ -699,19 +713,56 @@ namespace tools
|
|||
res.amount_list.push_back(ptx_amount);
|
||||
|
||||
res.fee_list.push_back(ptx.fee);
|
||||
}
|
||||
|
||||
if (req.get_tx_hex)
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
|
||||
if (res.multisig_txset.empty())
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
}
|
||||
|
||||
// populate response with tx hashes
|
||||
for (const auto & ptx : ptx_vector)
|
||||
{
|
||||
if (!req.do_not_relay)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
LOG_PRINT_L2("on_transfer_split calling commit_tx");
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
LOG_PRINT_L2("on_transfer_split called commit_tx");
|
||||
}
|
||||
|
||||
// populate response with tx hashes
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,30 +790,65 @@ namespace tools
|
|||
{
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
|
||||
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
for (const auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
if (req.get_tx_keys)
|
||||
{
|
||||
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
|
||||
}
|
||||
res.fee_list.push_back(ptx.fee);
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
}
|
||||
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
for (tools::wallet2::pending_tx &ptx: ptx_vector)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -804,29 +890,64 @@ namespace tools
|
|||
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
|
||||
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
for (const auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
if (req.get_tx_keys)
|
||||
{
|
||||
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
|
||||
}
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
}
|
||||
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
for (tools::wallet2::pending_tx &ptx: ptx_vector)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
for (auto & ptx : ptx_vector)
|
||||
{
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -888,37 +1009,59 @@ namespace tools
|
|||
er.message = "Multiple transactions are created, which is not supposed to happen";
|
||||
return false;
|
||||
}
|
||||
if (ptx_vector[0].selected_transfers.size() > 1)
|
||||
const wallet2::pending_tx &ptx = ptx_vector[0];
|
||||
if (ptx.selected_transfers.size() > 1)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "The transaction uses multiple inputs, which is not supposed to happen";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
const wallet2::pending_tx &ptx = ptx_vector[0];
|
||||
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
|
||||
if (req.get_tx_key)
|
||||
{
|
||||
res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key);
|
||||
}
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
|
||||
res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
|
||||
}
|
||||
|
||||
if (m_wallet->multisig())
|
||||
{
|
||||
res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
|
||||
if (res.multisig_txset.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!req.do_not_relay)
|
||||
m_wallet->commit_tx(ptx_vector);
|
||||
|
||||
// populate response with tx hashes
|
||||
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
|
||||
if (req.get_tx_hex)
|
||||
{
|
||||
cryptonote::blobdata blob;
|
||||
tx_to_blob(ptx.tx, blob);
|
||||
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
|
||||
}
|
||||
if (req.get_tx_metadata)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::portable_binary_oarchive ar(oss);
|
||||
try
|
||||
{
|
||||
ar << ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to save multisig tx set after creation";
|
||||
return false;
|
||||
}
|
||||
res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const tools::error::daemon_busy& e)
|
||||
|
@ -954,13 +1097,14 @@ namespace tools
|
|||
return false;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << blob;
|
||||
binary_archive<false> ba(ss);
|
||||
|
||||
tools::wallet2::pending_tx ptx;
|
||||
bool r = ::serialization::serialize(ba, ptx);
|
||||
if (!r)
|
||||
try
|
||||
{
|
||||
std::istringstream iss(blob);
|
||||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
ar >> ptx;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_TX_METADATA;
|
||||
er.message = "Failed to parse tx metadata.";
|
||||
|
@ -2663,6 +2807,141 @@ namespace tools
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_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;
|
||||
}
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
if (!m_wallet->multisig(&ready, &threshold, &total))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is not multisig";
|
||||
return false;
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is multisig, but not yet finalized";
|
||||
return false;
|
||||
}
|
||||
|
||||
cryptonote::blobdata blob;
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
|
||||
er.message = "Failed to parse hex.";
|
||||
return false;
|
||||
}
|
||||
|
||||
tools::wallet2::multisig_tx_set txs;
|
||||
bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
|
||||
if (!r)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA;
|
||||
er.message = "Failed to parse multisig tx data.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<crypto::hash> txids;
|
||||
try
|
||||
{
|
||||
bool r = m_wallet->sign_multisig_tx(txs, txids);
|
||||
if (!r)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE;
|
||||
er.message = "Failed to sign multisig tx";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE;
|
||||
er.message = std::string("Failed to sign multisig tx: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
res.tx_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(txs));
|
||||
if (!txids.empty())
|
||||
{
|
||||
for (const crypto::hash &txid: txids)
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(txid));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_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;
|
||||
}
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
if (!m_wallet->multisig(&ready, &threshold, &total))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is not multisig";
|
||||
return false;
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
er.message = "This wallet is multisig, but not yet finalized";
|
||||
return false;
|
||||
}
|
||||
|
||||
cryptonote::blobdata blob;
|
||||
if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
|
||||
er.message = "Failed to parse hex.";
|
||||
return false;
|
||||
}
|
||||
|
||||
tools::wallet2::multisig_tx_set txs;
|
||||
bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
|
||||
if (!r)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA;
|
||||
er.message = "Failed to parse multisig tx data.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (txs.m_signers.size() < threshold)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
|
||||
er.message = "Not enough signers signed this transaction.";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (auto &ptx: txs.m_ptx)
|
||||
{
|
||||
m_wallet->commit_tx(ptx);
|
||||
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION;
|
||||
er.message = std::string("Failed to submit multisig tx: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
|
@ -122,6 +122,9 @@ namespace tools
|
|||
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)
|
||||
MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
|
||||
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
|
||||
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
|
||||
END_JSON_RPC_MAP()
|
||||
END_URI_MAP2()
|
||||
|
||||
|
@ -182,6 +185,8 @@ namespace tools
|
|||
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);
|
||||
bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_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);
|
||||
|
|
|
@ -318,6 +318,7 @@ namespace wallet_rpc
|
|||
uint64_t fee;
|
||||
std::string tx_blob;
|
||||
std::string tx_metadata;
|
||||
std::string multisig_txset;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash)
|
||||
|
@ -326,6 +327,7 @@ namespace wallet_rpc
|
|||
KV_SERIALIZE(fee)
|
||||
KV_SERIALIZE(tx_blob)
|
||||
KV_SERIALIZE(tx_metadata)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -378,6 +380,7 @@ namespace wallet_rpc
|
|||
std::list<uint64_t> fee_list;
|
||||
std::list<std::string> tx_blob_list;
|
||||
std::list<std::string> tx_metadata_list;
|
||||
std::string multisig_txset;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
|
@ -386,6 +389,7 @@ namespace wallet_rpc
|
|||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -423,6 +427,7 @@ namespace wallet_rpc
|
|||
std::list<uint64_t> fee_list;
|
||||
std::list<std::string> tx_blob_list;
|
||||
std::list<std::string> tx_metadata_list;
|
||||
std::list<std::string> multisig_txset;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
|
@ -430,6 +435,7 @@ namespace wallet_rpc
|
|||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -483,6 +489,7 @@ namespace wallet_rpc
|
|||
std::list<uint64_t> fee_list;
|
||||
std::list<std::string> tx_blob_list;
|
||||
std::list<std::string> tx_metadata_list;
|
||||
std::list<std::string> multisig_txset;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
|
@ -490,6 +497,7 @@ namespace wallet_rpc
|
|||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -530,6 +538,7 @@ namespace wallet_rpc
|
|||
uint64_t fee;
|
||||
std::string tx_blob;
|
||||
std::string tx_metadata;
|
||||
std::string multisig_txset;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash)
|
||||
|
@ -537,6 +546,7 @@ namespace wallet_rpc
|
|||
KV_SERIALIZE(fee)
|
||||
KV_SERIALIZE(tx_blob)
|
||||
KV_SERIALIZE(tx_metadata)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
@ -1652,5 +1662,49 @@ namespace wallet_rpc
|
|||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_SIGN_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::string tx_data_hex;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_data_hex)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string tx_data_hex;
|
||||
std::list<std::string> tx_hash_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_data_hex)
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_SUBMIT_MULTISIG
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::string tx_data_hex;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_data_hex)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,3 +64,6 @@
|
|||
#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
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA -34
|
||||
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE -35
|
||||
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION -36
|
||||
|
|
Loading…
Reference in New Issue