diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index a04a7c63..bc20ffda 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1630,9 +1630,9 @@ bool blockchain_storage::purge_altblock_keyimages_from_big_heap(const block& b, transaction& tx = *tx_ptr; for (size_t n = 0; n < tx.vin.size(); ++n) { - if (tx.vin[n].type() == typeid(txin_to_key)) + if (tx.vin[n].type() == typeid(txin_to_key) || tx.vin[n].type() == typeid(txin_htlc)) { - purge_keyimage_from_big_heap(boost::get(tx.vin[n]).k_image, id); + purge_keyimage_from_big_heap(get_to_key_input_from_txin_v(tx.vin[n]).k_image, id); } } } @@ -4185,13 +4185,12 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const // check all tx's inputs for being already spent for (const txin_v& in : tx.vin) { - if (in.type() == typeid(txin_to_key)) + if (in.type() == typeid(txin_to_key) || in.type() == typeid(txin_htlc)) { - if (have_tx_keyimg_as_spent(boost::get(in).k_image)) + if (have_tx_keyimg_as_spent(get_to_key_input_from_txin_v(in).k_image)) { return true; - } - + } } else if (in.type() == typeid(txin_multisig)) { @@ -4264,6 +4263,14 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha else if (txin.type() == typeid(txin_htlc)) { const txin_htlc& in_htlc = boost::get(txin); + CHECK_AND_ASSERT_MES(in_htlc.key_offsets.size(), false, "Empty in_to_key.key_offsets for input #" << sig_index << " tx: " << tx_prefix_hash); + TIME_MEASURE_START_PD(tx_check_inputs_loop_kimage_check); + if (have_tx_keyimg_as_spent(in_htlc.k_image)) + { + LOG_ERROR("Key image was already spent in blockchain: " << string_tools::pod_to_hex(in_htlc.k_image) << " for input #" << sig_index << " tx: " << tx_prefix_hash); + return false; + } + TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_kimage_check); if (!check_tx_input(tx, sig_index, in_htlc, tx_prefix_hash, *psig, max_used_block_height)) { LOG_ERROR("Failed to validate multisig input #" << sig_index << " (ms out id: " << obj_to_json_str(in_htlc) << ") in tx: " << tx_prefix_hash); @@ -4763,9 +4770,9 @@ std::shared_ptr blockchain_storage::find_key_imag } for (auto& in : tx_chain_entry->tx.vin) { - if (in.type() == typeid(txin_to_key)) + if (in.type() == typeid(txin_to_key) || in.type() == typeid(txin_htlc)) { - if (boost::get(in).k_image == ki) + if (get_to_key_input_from_txin_v(in).k_image == ki) { id_result = get_transaction_hash(tx_chain_entry->tx); return tx_chain_entry; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 2a01891f..1a780ce7 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1476,8 +1476,8 @@ namespace currency sigs.resize(src_entr.outputs.size()); if (!watch_only_mode) - crypto::generate_ring_signature(tx_hash_for_signature, boost::get(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data()); - + crypto::generate_ring_signature(tx_hash_for_signature, get_to_key_input_from_txin_v(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data()); + ss_ring_s << "signatures:" << ENDL; std::for_each(sigs.begin(), sigs.end(), [&ss_ring_s](const crypto::signature& s) { ss_ring_s << s << ENDL; }); ss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[in_context_index].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; @@ -1657,8 +1657,8 @@ namespace currency { for(const auto& in : tx.vin) { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_multisig), false, "wrong variant type: " - << in.type().name() << ", expected " << typeid(txin_to_key).name() + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_multisig) || in.type() == typeid(txin_htlc), false, "wrong variant type: " + << in.type().name() << ", in transaction id=" << get_transaction_hash(tx)); } @@ -1775,8 +1775,13 @@ namespace currency } else if (in.type() == typeid(txin_multisig)) { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_multisig, tokey_in, false); - this_amount = tokey_in.amount; + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_multisig, ms_in, false); + this_amount = ms_in.amount; + } + else if (in.type() == typeid(txin_htlc)) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_htlc, htlc_in, false); + this_amount = htlc_in.amount; } else { @@ -2370,7 +2375,7 @@ namespace currency { for (const auto& e : tx.vin) { - if (e.type() != typeid(txin_to_key)) + if (e.type() != typeid(txin_to_key) || e.type() != typeid(txin_multisig) || e.type() != typeid(txin_htlc)) return false; if (boost::get(e).key_offsets.size() < 2) return false; @@ -2668,9 +2673,10 @@ namespace currency { tei.ins.back().amount = 0; } - else if (in.type() == typeid(txin_to_key)) + else if (in.type() == typeid(txin_to_key) || in.type() == typeid(txin_htlc)) { - txin_to_key& tk = boost::get(in); + //TODO: add htlc info + const txin_to_key& tk = get_to_key_input_from_txin_v(in); tei.ins.back().amount = tk.amount; tei.ins.back().kimage_or_ms_id = epee::string_tools::pod_to_hex(tk.k_image); std::vector absolute_offsets = relative_output_offsets_to_absolute(tk.key_offsets); @@ -2687,6 +2693,10 @@ namespace currency tei.ins.back().global_indexes.back() = std::numeric_limits::max(); } } + if (in.type() == typeid(txin_htlc)) + { + tei.ins.back().htlc_origin = epee::string_tools::buff_to_hex_nodelimer(boost::get(in).hltc_origin); + } //tk.etc_details -> visualize it may be later } else if (in.type() == typeid(txin_multisig)) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 872df0fa..42ba6942 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -371,6 +371,8 @@ namespace currency if (in.type() == typeid(txin_to_key)) return boost::get(in).etc_details; + else if (in.type() == typeid(txin_htlc)) + return boost::get(in).etc_details; else if (in.type() == typeid(txin_multisig)) return boost::get(in).etc_details; else @@ -502,11 +504,11 @@ namespace currency } //--------------------------------------------------------------- template - extra_t& get_or_add_field_to_variant_vector(container_type& container) + add_type_t& get_or_add_field_to_variant_vector(container_type& container) { for (auto& ev : container) { - if (ev.type() == typeid(exadd_type_ttra_t)) + if (ev.type() == typeid(add_type_t)) return boost::get(ev); } container.push_back(add_type_t()); @@ -660,6 +662,8 @@ namespace currency { if (in.type().hash_code() == typeid(txin_to_key).hash_code()) return &boost::get(in).etc_details; + if (in.type().hash_code() == typeid(txin_htlc).hash_code()) + return &boost::get(in).etc_details; if (in.type().hash_code() == typeid(txin_multisig).hash_code()) return &boost::get(in).etc_details; return nullptr; @@ -669,6 +673,8 @@ namespace currency { if (in.type().hash_code() == typeid(txin_to_key).hash_code()) return &boost::get(in).etc_details; + if (in.type().hash_code() == typeid(txin_htlc).hash_code()) + return &boost::get(in).etc_details; if (in.type().hash_code() == typeid(txin_multisig).hash_code()) return &boost::get(in).etc_details; return nullptr; diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 6e84961a..264f604e 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -262,10 +262,10 @@ namespace currency std::unordered_set ki; BOOST_FOREACH(const auto& in, tx.vin) { - if (in.type() == typeid(txin_to_key)) + if (in.type() == typeid(txin_to_key) || in.type() == typeid(txin_htlc)) { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - if (!ki.insert(tokey_in.k_image).second) + + if (!ki.insert(get_to_key_input_from_txin_v(in).k_image).second) return false; } } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index a362a15c..2ef811ac 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1198,10 +1198,12 @@ namespace currency { uint64_t amount; uint64_t multisig_count; + std::string htlc_origin; std::string kimage_or_ms_id; std::vector global_indexes; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) + KV_SERIALIZE(htlc_origin) KV_SERIALIZE(kimage_or_ms_id) KV_SERIALIZE(global_indexes) KV_SERIALIZE(multisig_count) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5cf45e0e..f89a2f7a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -398,7 +398,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].target.type() == typeid(txout_htlc), "Internal error: wrong index in m_transfers"); //input spend active htlc m_transfers[it->second].m_spent_height = height; - transfer_details_extra_option_htlc_info& tdeohi = get_or_add_field_to_variant_vector(); + transfer_details_extra_option_htlc_info& tdeohi = get_or_add_field_to_variant_vector(td.varian_options); tdeohi.origin = in_htlc.hltc_origin; } } @@ -4029,7 +4029,7 @@ void wallet2::create_htlc_proposal(uint64_t amount, const currency::account_publ origin = ft.htlc_origin; } //---------------------------------------------------------------------------------------------------- -void wallet2::get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs) +void wallet2::get_list_of_active_htlc(std::list& htlcs, bool only_redeem_txs) { for (auto htlc_entry : m_active_htlcs_txid) { @@ -4059,7 +4059,12 @@ void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin) ctp.htlc_origin = origin; ctp.dsts.resize(1); ctp.dsts.back().addr.push_back(m_account.get_keys().account_address); - ctp.dsts.back().amount = 0; + + auto it = m_active_htlcs_txid.find(htlc_tx_id); + WLT_THROW_IF_FALSE_WITH_CODE(it != m_active_htlcs_txid.end(), + "htlc not found with tx_id = " << htlc_tx_id, API_RETURN_CODE_NOT_FOUND); + + ctp.dsts.back().amount = m_transfers[it->second].amount() - ctp.fee; currency::transaction result_tx = AUTO_VAL_INIT(result_tx); this->transfer(ctp, result_tx, true, nullptr); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 92313c20..3d815d69 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -400,7 +400,7 @@ namespace tools struct transfer_details_extra_option_htlc_info { std::string origin; //this field filled only if htlc had been redeemed - } + }; typedef boost::variant transfer_details_extra_options_v; @@ -408,7 +408,7 @@ namespace tools { uint64_t m_global_output_index; crypto::key_image m_key_image; //TODO: key_image stored twice :( - transfer_details_extra_options_v varian_options; + std::vector varian_options; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_global_output_index) @@ -856,9 +856,8 @@ namespace tools we use deterministic origin, if given some particular htlc_hash, then we use this hash, and this means that opener-hash will be given by other side */ - void create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, - currency::transaction &tx, std::string &origin); - void get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs); + void create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, currency::transaction &tx, const crypto::hash& htlc_hash, std::string &origin); + void get_list_of_active_htlc(std::list& htlcs, bool only_redeem_txs); void redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin); bool check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& origin); @@ -1077,6 +1076,14 @@ namespace boost a & x.m_spent_height; } + + template + inline void serialize(Archive &a, tools::wallet2::transfer_details_extra_option_htlc_info &x, const boost::serialization::version_type ver) + { + a & x.origin; + } + + template inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver) { diff --git a/tests/core_tests/atomic_tests.cpp b/tests/core_tests/atomic_tests.cpp index c9eb5395..4ecfc044 100644 --- a/tests/core_tests/atomic_tests.cpp +++ b/tests/core_tests/atomic_tests.cpp @@ -124,7 +124,7 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto std::string alice_origin; //will be deterministically generated by Alice's A wallet currency::transaction res_tx = AUTO_VAL_INIT(res_tx); - alice_a_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, bob_a_wlt_instance->get_account().get_public_address(), 20, res_tx, currency::null_hash, alice_origin); + alice_a_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, bob_a_wlt_instance->get_account().get_public_address(), 100, res_tx, currency::null_hash, alice_origin); r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); alice_a_wlt_instance->refresh(); @@ -132,19 +132,19 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto bob_a_wlt_instance->refresh(); bob_b_wlt_instance->refresh(); - std::list htlcs_alice_a; + std::list htlcs_alice_a; alice_a_wlt_instance->get_list_of_active_htlc(htlcs_alice_a, false); CHECK_AND_ASSERT_MES(htlcs_alice_a.size() == 1, false, "htlcs_alice.size() == 1 failed"); - std::list htlcs_bob_a; + std::list htlcs_bob_a; bob_a_wlt_instance->get_list_of_active_htlc(htlcs_bob_a, false); CHECK_AND_ASSERT_MES(htlcs_bob_a.size() == 1, false, "htlcs_bob.size() == 1 failed"); - const wallet_public::htlc_entry_info& hei_bob = *htlcs_bob_a.begin(); + const tools::wallet_public::htlc_entry_info& hei_bob = *htlcs_bob_a.begin(); CHECK_AND_ASSERT_MES(hei_bob.is_redeem == true, false, "hei_bob.is_redeem == true failed"); - const wallet_public::htlc_entry_info& hei_alice = *htlcs_alice_a.begin(); + const tools::wallet_public::htlc_entry_info& hei_alice = *htlcs_alice_a.begin(); CHECK_AND_ASSERT_MES(hei_alice.is_redeem == false, false, "hei_alice.is_redeem == false failed"); CHECK_AND_ASSERT_MES(hei_alice.amount == hei_bob.amount @@ -157,7 +157,7 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto std::string dummy_origin; bob_b_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, alice_b_wlt_instance->get_account().get_public_address(), - 20, + 100, res_tx, hei_bob.sha256_hash, dummy_origin); r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -168,20 +168,25 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto bob_b_wlt_instance->refresh(); - std::list htlcs_alice_b; + std::list htlcs_alice_b; alice_b_wlt_instance->get_list_of_active_htlc(htlcs_alice_b, false); CHECK_AND_ASSERT_MES(htlcs_alice_b.size() == 1, false, "htlcs_alice_b.size() == 1 failed"); - std::list htlcs_bob_b; + std::list htlcs_bob_b; bob_b_wlt_instance->get_list_of_active_htlc(htlcs_bob_b, false); CHECK_AND_ASSERT_MES(htlcs_bob_b.size() == 1, false, "htlcs_bob_b.size() == 1 failed"); - const wallet_public::htlc_entry_info& hei_bob_b = *htlcs_bob_b.begin(); - CHECK_AND_ASSERT_MES(hei_bob_b.is_redeem == true, false, "hei_bob_b.is_redeem == true failed"); + std::list htlcs_bob_a_2; + bob_a_wlt_instance->get_list_of_active_htlc(htlcs_bob_a_2, false); + CHECK_AND_ASSERT_MES(htlcs_bob_a_2.size() == 1, false, "htlcs_bob_a.size() == 1 failed"); - const wallet_public::htlc_entry_info& hei_alice_b = *htlcs_alice_b.begin(); - CHECK_AND_ASSERT_MES(hei_alice_b.is_redeem == false, false, "hei_alice_b.is_redeem == false failed"); + const tools::wallet_public::htlc_entry_info& hei_bob_b = *htlcs_bob_b.begin(); + CHECK_AND_ASSERT_MES(hei_bob_b.is_redeem == false, false, "hei_bob_b.is_redeem == true failed"); + + + const tools::wallet_public::htlc_entry_info& hei_alice_b = *htlcs_alice_b.begin(); + CHECK_AND_ASSERT_MES(hei_alice_b.is_redeem == true, false, "hei_alice_b.is_redeem == false failed"); CHECK_AND_ASSERT_MES(hei_alice_b.amount == hei_bob_b.amount && hei_alice_b.sha256_hash == hei_bob_b.sha256_hash @@ -197,19 +202,17 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto bob_a_wlt_instance->refresh(); bob_b_wlt_instance->refresh(); - //htlcs_alice_b.clear(); - //alice_b_wlt_instance->get_list_of_active_htlc(htlcs_alice_b, false); - //CHECK_AND_ASSERT_MES(htlcs_alice_b.size() == 0, false, "htlcs_alice_b.size() == 1 failed"); + std::list htlcs_bob_a_3; + bob_a_wlt_instance->get_list_of_active_htlc(htlcs_bob_a_3, false); + CHECK_AND_ASSERT_MES(htlcs_bob_a_3.size() == 1, false, "htlcs_bob_a.size() == 1 failed"); + - //htlcs_bob_b.clear(); - //bob_b_wlt_instance->get_list_of_active_htlc(htlcs_bob_b, false); - //CHECK_AND_ASSERT_MES(htlcs_bob_b.size() == 0, false, "htlcs_bob_b.size() == 1 failed"); std::string bob_detected_origin; r = bob_b_wlt_instance->check_htlc_redeemed(hei_bob_b.tx_id, bob_detected_origin); CHECK_AND_ASSERT_MES(r, false, "bob_a_wlt_instance->check_htlc_redeemed(hei_bob_b.tx_id, bob_detected_origin); returned false"); CHECK_AND_ASSERT_MES(bob_detected_origin == alice_origin, false, "bob_detected_origin == alice_origin failed"); - bob_b_wlt_instance->redeem_htlc(hei_bob.tx_id, bob_detected_origin); + bob_a_wlt_instance->redeem_htlc(hei_bob.tx_id, bob_detected_origin); r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); @@ -219,6 +222,13 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto bob_a_wlt_instance->refresh(); bob_b_wlt_instance->refresh(); + //now we have to check if all balances to make sure that atomic swap passed properly + CHECK_AND_FORCE_ASSERT_MES(alice_a_wlt_instance->balance() == 0, false, "Incorrect balance"); + CHECK_AND_FORCE_ASSERT_MES(bob_b_wlt_instance->balance() == 0, false, "Incorrect balance"); + + CHECK_AND_FORCE_ASSERT_MES(alice_b_wlt_instance->balance() == transfer_amount - TESTS_DEFAULT_FEE, false, "Incorrect balance"); + CHECK_AND_FORCE_ASSERT_MES(bob_a_wlt_instance->balance() == transfer_amount - TESTS_DEFAULT_FEE, false, "Incorrect balance"); + return r; }