diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 74992272..7a81b008 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -590,7 +590,19 @@ namespace currency // return true; // } //--------------------------------------------------------------- - bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, uint8_t tx_outs_attr) + std::string generate_origin_for_htlc(cons crypto::public_key& redeem, cons crypto::public_key& refund, const account_keys& acc_keys) + { + std::string blob; + string_tools::apped_pod_to_strbuff(blob, redeem); + string_tools::apped_pod_to_strbuff(blob, refund); + string_tools::apped_pod_to_strbuff(blob, acc_keys.spend_secret_key); + crypto::hash origin_hs = cn_fast_hash(blob.data(), blob.size()); + std::string origin_blob; + string_tools::apped_pod_to_strbuff(origin_blob, origin_hs); + return origin_hs; + } + //--------------------------------------------------------------- + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, uint8_t tx_outs_attr, const account_keys& self) { CHECK_AND_ASSERT_MES(de.addr.size() == 1 || (de.addr.size() > 1 && de.minimum_sigs <= de.addr.size()), false, "Invalid destination entry: amount: " << de.amount << " minimum_sigs: " << de.minimum_sigs << " addr.size(): " << de.addr.size()); @@ -622,10 +634,38 @@ namespace currency tx_out out; out.amount = de.amount; - if (target_keys.size() == 1) + if (de.htlc) + { + //out htlc + CHECK_AND_ASSERT_MES(target_keys.size() == 1, false, "Unexpected htl keys count = " << target_keys.size() << ", expected ==1"); + txout_htlc htlc = AUTO_VAL_INIT(htlc); + htlc.expiration = de.unlock_time; + htlc.flags = 0; //0 - SHA256, 1 - RIPEMD160, by default leave SHA256 + //receiver key + htlc.pkey_redeem = *target_keys.begin(); + //generate refund key + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + bool r = derive_public_key_from_target_address(self.account_address, tx_sec_key, output_index, out_eph_public_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address"); + htlc.pkey_refund = out_eph_public_key; + //we use deterministic origin, to make possible access origin on different wallets copies + std::string hltc_origin = generate_origin_for_htlc(htlc.pkey_redeem, htlc.pkey_refund, self); + //calculate hash + + if (htlc.flags&CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK) + { + htlc.htlc_hash = crypto::sha256_hash(hltc_origin.data(), hltc_origin.size()); + } + else + { + crypto::hash160 h160 = crypto::RIPEMD160_hash(hltc_origin.data(), hltc_origin.size()); + std::memcpy(&htlc.htlc_hash, &h160, sizeof(h160)); + } + } + else if (target_keys.size() == 1) { //out to key - txout_to_key tk; + txout_to_key tk = AUTO_VAL_INIT(tk); tk.key = target_keys.back(); if (de.addr.front().is_auditable()) // check only the first address because there's only one in this branch @@ -1178,7 +1218,7 @@ namespace currency for(const tx_destination_entry& dst_entr : shuffled_dsts) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); - bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, tx_outs_attr); + bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, tx_outs_attr, sender_account_keys.account_address); CHECK_AND_ASSERT_MES(r, false, "Failed to construc tx out"); output_index++; summary_outs_money += dst_entr.amount; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 67fc8f97..aa90549b 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -163,7 +163,7 @@ namespace currency //--------------------------------------------------------------- uint64_t get_string_uint64_hash(const std::string& str); - bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED, const account_public_address& self = account_public_address()); bool validate_alias_name(const std::string& al); bool validate_password(const std::string& password); void get_attachment_extra_info_details(const std::vector& attachment, extra_attachment_info& eai); @@ -298,7 +298,7 @@ namespace currency void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map& gindices); std::string get_word_from_timstamp(uint64_t timestamp, bool use_password); uint64_t get_timstamp_from_word(std::string word, bool& password_used); - + std::string generate_origin_for_htlc(cons crypto::public_key& redeem, cons crypto::public_key& refund, const account_keys& acc_keys); template typename std::conditional::value, const std::vector, std::vector >::type& get_txin_etc_options(t_txin_v& in) { diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 49bc3f10..bc94083b 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -52,11 +52,12 @@ namespace currency size_t minimum_sigs; // if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used uint64_t amount_to_provide; //amount money that provided by initial creator of tx, used with partially created transactions uint64_t unlock_time; + bool htlc; //if this flag is set, then creating htlc out, unlock_time -> number of blocks that htlc proposal is active - tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0), unlock_time(0){} - tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(0){} - tx_destination_entry(uint64_t a, const account_public_address& ad, uint64_t ut) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(ut) {} - tx_destination_entry(uint64_t a, const std::list& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0), unlock_time(0){} + tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0), unlock_time(0), htlc(false){} + tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(0), htlc(false){} + tx_destination_entry(uint64_t a, const account_public_address& ad, uint64_t ut) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(ut), htlc(false){} + tx_destination_entry(uint64_t a, const std::list& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0), unlock_time(0), htlc(false){} BEGIN_SERIALIZE_OBJECT() FIELD(amount) @@ -64,6 +65,7 @@ namespace currency FIELD(minimum_sigs) FIELD(amount_to_provide) FIELD(unlock_time) + FIELD(htlc) END_SERIALIZE() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 51110bcd..f222aeb9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -584,6 +584,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t //active htlc auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index); m_active_htlcs[amount_gindex_pair] = transfer_index; + m_active_htlcs_txid[get_transaction_hash(tx)] = transfer_index; } size_t transfer_index = m_transfers.size()-1; if (td.m_key_image != currency::null_ki) @@ -1328,11 +1329,21 @@ void wallet2::process_htlc_triggers_on_block_added(uint64_t height) auto it_active_htlc = m_active_htlcs.find(std::make_pair(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].amount, tr.m_global_output_index)); if (it_active_htlc == m_active_htlcs.end()) { - LOG_ERROR("Erasing htlc, but it seems to be already erased"); + LOG_ERROR("Erasing active htlc(m_active_htlcs), but it seems to be already erased"); } else { + const transfer_details& td = m_transfers[it->second]; m_active_htlcs.erase(it); + auto it_tx = m_active_htlcs_txid.find(td.tx_hash()); + if (it_tx == m_active_htlcs_txid.end()) + { + LOG_ERROR("Erasing active htlc(;), but it seems to be already erased"); + } + else + { + m_active_htlcs_txid.erase(it_tx); + } } } } @@ -2206,6 +2217,7 @@ void wallet2::detach_blockchain(uint64_t including_height) const txout_htlc& htlc = boost::get(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].target); auto amount_gindex_pair = std::make_pair(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].amount, tr.m_global_output_index); m_active_htlcs[amount_gindex_pair] = i; + m_active_htlcs_txid[tr.tx_hash()] = i; } } } @@ -3993,6 +4005,43 @@ void wallet2::send_escrow_proposal(const bc_services::contract_private_details& add_sent_tx_detailed_info(tx, ftp.prepared_destinations, ftp.selected_transfers); print_tx_sent_message(tx, "(from multisig)", fee); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::create_htlc_proposal(uint64_t amount, account_public_address& addr, uint64_t lock_blocks_count, currency::transaction &tx) +{ + std::vector extra; + std::vector attachments; + + std::vector dst; + dst.resize(1); + dst.back().addr.push_back(addr); + dst.back().amount = amount; + dst.back().htlc = true; + dst.back().unlock_time = 740; //about 12 hours + + transaction result_tx = AUTO_VAL_INIT(result_tx); + this->transfer(dst, 0, 0, TX_DEFAULT_FEE, extra, attachments, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), result_tx); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs) +{ + for (auto htlc_entry : m_active_htlcs_txid) + { + htlc_entry_info entry = AUTO_VAL_INIT(entry); + entry.tx_id = htlc_entry.first; + const transfer_details& td = m_transfers[htlc_entry.second]; + entry.amount = td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].amount; + 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), + "[get_list_of_active_htlc]Internal error: unexpected type of out"); + const txout_htlc& htlc = boost::get(td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index].target); + entry.sha256_hash = htlc.htlc_hash; + htlcs.push_back(entry); + } +} +//---------------------------------------------------------------------------------------------------- +void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin) +{ + } //---------------------------------------------------------------------------------------------------- bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector& sources, std::vector& selected_indicies, uint64_t& found_money) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3ae9bcdd..bb3f05ea 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -740,6 +740,7 @@ namespace tools return; a & m_htlcs; + a & m_active_htlcs; } @@ -815,6 +816,10 @@ namespace tools uint64_t get_sync_progress(); uint64_t get_wallet_file_size()const; void set_use_deffered_global_outputs(bool use); + void create_htlc_proposal(uint64_t amount, account_public_address& addr, uint64_t lock_blocks_count, + account_public_address& addr, currency::transaction &tx); + void get_list_of_active_htlc(bool only_redeem_txs, std::list& htlcs); + void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin); private: @@ -983,7 +988,8 @@ private: uint64_t transfer_index; }; std::multimap m_htlcs; //uint64_t -> height of expiration - amount_gindex_to_transfer_id_container m_active_htlcs; // map [amount; gindex] -> tid + amount_gindex_to_transfer_id_container m_active_htlcs; // map [amount; gindex] -> transfer index + std::unordered_map m_active_htlcs_txid; // map [txid] -> transfer index, limitation: 1 transactiom -> 1 htlc std::shared_ptr m_core_proxy; std::shared_ptr m_wcallback; diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 7b0c344e..dc12e5ea 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -974,6 +974,20 @@ namespace wallet_public }; }; + struct htlc_entry_info + { + crypto::hash sha256_hash; + crypto::hash tx_id; + uint64_t amount; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(sha256_hash) + KV_SERIALIZE(tx_id) + END_KV_SERIALIZE_MAP() + }; + + inline std::string get_escrow_contract_state_name(uint32_t state)