diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 99504139..50f96fdc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7961,6 +7961,27 @@ void wallet2::restore_key_images_in_wo_wallet(const std::wstring& filename, cons wo.store(); } //---------------------------------------------------------------------------------------------------- +void wallet2::clear_utxo_cold_sig_reservation(std::vector& affected_transfer_ids) +{ + affected_transfer_ids.clear(); + + for(auto& [tid, td] : m_transfers) + { + if (!td.is_spent() && (td.m_flags & WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION) != 0) + { + affected_transfer_ids.push_back(tid); + crypto::public_key pk{}; + get_out_pub_key_from_tx_out_v(td.output(), pk); + WLT_LOG_L0("clear_utxo_cold_sig_reservation: tid: " << tid << ", pk: " << pk << ", amount: " << td.amount() << + (!td.is_native_coin() ? std::string(", aid: ") + crypto::pod_to_hex(td.get_asset_id()) : std::string()) << + (td.m_key_image != null_ki ? std::string(", ki: ") + crypto::pod_to_hex(td.m_key_image) : std::string())); + } + } + + clear_transfers_from_flag(affected_transfer_ids, WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION, "clear_utxo_cold_sig_reservation"); + m_found_free_amounts.clear(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::prepare_tx_destinations(const assets_selection_context& needed_money_map, detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c0b08519..8e20d0c0 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -619,6 +619,7 @@ namespace tools void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); void restore_key_images_in_wo_wallet(const std::wstring& filename, const std::string& password) const; + void clear_utxo_cold_sig_reservation(std::vector& affected_transfer_ids); void sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx = nullptr, std::string* p_filename_or_unsigned_tx_blob_str = nullptr); diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 3ac0f839..f3908c24 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -2159,5 +2159,35 @@ namespace wallet_public }; }; + struct COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION + { + DOC_COMMAND("Clears cold sig reservation flag for all unspent transaction outputs, that have one. Please, use with CAUTION!"); + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response_item + { + crypto::public_key pk; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(pk) DOC_DSCR("Output's one-time public key") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector affected_outputs; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status of the call") DOC_EXMP("OK") DOC_END + KV_SERIALIZE(affected_outputs) DOC_DSCR("List of affected outputs (for reference).") DOC_EXMP_AUTO(1) DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + } // namespace wallet_rpc } // namespace tools diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 15a4d276..13922a20 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1620,6 +1620,31 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_clear_utxo_cold_sig_reservation(const wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::request& req, wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + + std::vector affected_transfer_ids; + w.get_wallet()->clear_utxo_cold_sig_reservation(affected_transfer_ids); + + for (auto tid : affected_transfer_ids) + { + transfer_details td{}; + if (!w.get_wallet()->get_transfer_info_by_index(tid, td)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "internal error: get_transfer_info_by_index failed for tid " + epee::string_tools::num_to_string_fast(tid); + return false; + } + + res.affected_outputs.emplace_back(); + currency::get_out_pub_key_from_tx_out_v(td.output(), res.affected_outputs.back().pk); + } + + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 6962ed83..276b4f95 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -179,6 +179,7 @@ namespace tools //utility call MAP_JON_RPC_WE("proxy_to_daemon", on_proxy_to_daemon, wallet_public::COMMAND_PROXY_TO_DAEMON) + MAP_JON_RPC_WE("clear_utxo_cold_sig_reservation", on_clear_utxo_cold_sig_reservation, wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -252,6 +253,7 @@ namespace tools bool on_decrypt_data(const wallet_public::COMMAND_DECRYPT_DATA::request& req, wallet_public::COMMAND_DECRYPT_DATA::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_proxy_to_daemon(const wallet_public::COMMAND_PROXY_TO_DAEMON::request& req, wallet_public::COMMAND_PROXY_TO_DAEMON::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_clear_utxo_cold_sig_reservation(const wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::request& req, wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::response& res, epee::json_rpc::error& er, connection_context& cntx); //std::shared_ptr get_wallet(); diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index e8787411..004c1bf6 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -1234,6 +1234,16 @@ bool make_cold_signing_transaction(tools::wallet_rpc_server& rpc_wo, tools::wall r = invoke_text_json_for_rpc(rpc_wo, "transfer", req_transfer, res_transfer); CHECK_AND_ASSERT_MES(r, false, "RPC 'transfer' failed"); + // skip this reservation just for fun + tools::wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::request req_clear_csr{}; + tools::wallet_public::COMMAND_CLEAR_UTXO_COLD_SIG_RESERVATION::response res_clear_csr{}; + invoke_text_json_for_rpc(rpc_wo, "clear_utxo_cold_sig_reservation", req_clear_csr, res_clear_csr); + CHECK_AND_ASSERT_MES(res_clear_csr.affected_outputs.size() > 0, false, "no outputs were affected after clear_utxo_cold_sig_reservation"); + + // watch only creates a transaction once again + res_transfer = tools::wallet_public::COMMAND_RPC_TRANSFER::response{}; + r = invoke_text_json_for_rpc(rpc_wo, "transfer", req_transfer, res_transfer); + CHECK_AND_ASSERT_MES(r, false, "RPC 'transfer' failed"); // secure wallet with full keys set signs the transaction tools::wallet_public::COMMAND_SIGN_TRANSFER::request req_sign{};