From 75e05e2d2e4bbf91839a6f0a6f88a3e374d8affa Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Tue, 28 Jun 2022 22:27:36 +0200 Subject: [PATCH 01/13] hidden amounts in wallet: process_new_transaction refactoring --- src/currency_core/blockchain_storage.cpp | 6 +- src/currency_core/currency_basic.h | 6 +- src/wallet/wallet2.cpp | 136 ++++++++++------------- src/wallet/wallet2.h | 65 ++++++++++- 4 files changed, 130 insertions(+), 83 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 3489b306..2d1ca631 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1651,7 +1651,7 @@ bool blockchain_storage::purge_altblock_keyimages_from_big_heap(const block& b, // TODO @#@# consider refactoring const txin_zarcanum_inputs& zins = boost::get(tx.vin[n]); for(const auto& el : zins.elements) - purge_keyimage_from_big_heap(el.key_image, block_id); + purge_keyimage_from_big_heap(el.k_image, block_id); } } } @@ -4294,7 +4294,7 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const const auto& zins = boost::get(in); for(auto& el: zins.elements) { - if (have_tx_keyimg_as_spent(el.key_image)) + if (have_tx_keyimg_as_spent(el.k_image)) return true; } } @@ -4910,7 +4910,7 @@ std::shared_ptr blockchain_storage::find_key_imag const auto& zins = boost::get(in); for(auto& el: zins.elements) { - if (el.key_image == ki) + if (el.k_image == ki) { id_result = tx_id; return tx_chain_entry; diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index b7c1de14..16701909 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -310,15 +310,15 @@ namespace currency zarcanum_input(const zarcanum_input&) = default; zarcanum_input& operator=(const zarcanum_input&)= default; - crypto::key_image key_image; + crypto::key_image k_image; BEGIN_SERIALIZE_OBJECT() - FIELD(key_image) + FIELD(k_image) FIELD(key_offsets) // referring_input END_SERIALIZE() BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(key_image) + BOOST_SERIALIZE(k_image) BOOST_SERIALIZE(key_offsets) // referring_input END_BOOST_SERIALIZATION() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 70583de8..4feef0ff 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -329,78 +329,45 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t std::vector recipients, remote_aliases; process_unconfirmed(tx, recipients, remote_aliases); - std::vector outs; - uint64_t tx_money_got_in_outs = 0; - crypto::public_key tx_pub_key = null_pkey; - bool r = parse_and_validate_tx_extra(tx, tx_pub_key); - THROW_IF_TRUE_WALLET_EX(!r, error::tx_extra_parse_error, tx); - //check for transaction spends - uint64_t tx_money_spent_in_ins = 0; + process_transaction_context ptc = AUTO_VAL_INIT(ptc); + // check all outputs for spending (compare key images) - money_transfer2_details mtd; - size_t i = 0; - bool is_pos_coinbase = false; - bool coin_base_tx = is_coinbase(tx, is_pos_coinbase); + ptc.coin_base_tx = is_coinbase(tx, ptc.is_pos_coinbase); //PoW block don't have change, so all outs supposed to be marked as "mined" - bool is_derived_from_coinbase = !is_pos_coinbase; + ptc.is_derived_from_coinbase = !ptc.is_pos_coinbase; + ptc.height = height; for(auto& in : tx.vin) { - if (in.type() == typeid(currency::txin_to_key)) + ptc.sub_i = 0; + VARIANT_SWITCH_BEGIN(in); + VARIANT_CASE_CONST(currency::txin_to_key, intk) { - const currency::txin_to_key& intk = boost::get(in); - - // check if this input spends our output - uint64_t tid = UINT64_MAX; - - if (is_auditable() && is_watch_only()) - { - // tracking wallet, assuming all outputs are spent directly because of mix_attr = 1 - tid = get_directly_spent_transfer_id_by_input_in_tracking_wallet(intk); - } - else - { - // wallet with spend secret key -- we can calculate own key images and then search by them - auto it = m_key_images.find(intk.k_image); - if (it != m_key_images.end()) - { - tid = it->second; - } - } - - if (tid != UINT64_MAX) - { - tx_money_spent_in_ins += intk.amount; - transfer_details& td = m_transfers[tid]; - uint32_t flags_before = td.m_flags; - td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; - td.m_spent_height = height; - if (coin_base_tx && td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER) - is_derived_from_coinbase = true; - else - is_derived_from_coinbase = false; - WLT_LOG_L0("Spent key out, transfer #" << tid << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height << - "; flags: " << flags_before << " -> " << td.m_flags); - mtd.spent_indices.push_back(i); - remove_transfer_from_expiration_list(tid); - } + process_input_t(intk, ptc, tx); } - else if (in.type() == typeid(currency::txin_multisig)) + VARIANT_CASE_CONST(currency::txin_zarcanum_inputs, tinz) { - crypto::hash multisig_id = boost::get(in).multisig_out_id; + for (const auto& e : tinz.elements) + { + process_input_t(e, ptc, tx); + ptc.sub_i++; + } + } + VARIANT_CASE_CONST(currency::txin_multisig, inms) + { + crypto::hash multisig_id = inms.multisig_out_id; auto it = m_multisig_transfers.find(multisig_id); if (it != m_multisig_transfers.end()) { it->second.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; it->second.m_spent_height = height; WLT_LOG_L0("Spent multisig out: " << multisig_id << ", amount: " << print_money(currency::get_amount_from_variant(in)) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); - mtd.spent_indices.push_back(i); + ptc.mtd.spent_indices.push_back(ptc.i); } } - else if (in.type() == typeid(currency::txin_htlc)) + VARIANT_CASE_CONST(currency::txin_htlc, in_htlc) { - const currency::txin_htlc& in_htlc = boost::get(in); if (in_htlc.key_offsets.size() != 1) { LOG_ERROR("in_htlc.key_offsets.size() != 1, skip inout"); @@ -428,7 +395,8 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t tdeohi.redeem_tx_id = get_transaction_hash(tx); } } - i++; + VARIANT_SWITCH_END(); + ptc.i++; } /* @@ -438,6 +406,12 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t */ uint64_t max_out_unlock_time = 0; + std::vector outs; + uint64_t tx_money_got_in_outs = 0; + crypto::public_key tx_pub_key = null_pkey; + bool r = parse_and_validate_tx_extra(tx, tx_pub_key); + THROW_IF_TRUE_WALLET_EX(!r, error::tx_extra_parse_error, tx); + //check for transaction income crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); std::list htlc_info_list; @@ -572,7 +546,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t continue; // skip the output } - mtd.receive_indices.push_back(o); + ptc.mtd.receive_indices.push_back(o); m_transfers.push_back(boost::value_initialized()); transfer_details& td = m_transfers.back(); @@ -592,10 +566,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pglobal_indexes->size() > o, "pglobal_indexes size()(" << pglobal_indexes->size() << ") <= o " << o); td.m_global_output_index = (*pglobal_indexes)[o]; } - if (coin_base_tx) + if (ptc.coin_base_tx) { //last out in coinbase tx supposed to be change from coinstake - if (!(o == tx.vout.size() - 1 && !is_derived_from_coinbase)) + if (!(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase)) { td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER; } @@ -685,7 +659,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t std::string payment_id; if (tx_money_got_in_outs && get_payment_id_from_tx(tx.attachment, payment_id)) { - uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; + uint64_t received = (ptc.tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - ptc.tx_money_spent_in_ins : 0; if (0 < received && payment_id.size()) { payment_details payment; @@ -699,34 +673,34 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t } - if (tx_money_spent_in_ins) + if (ptc.tx_money_spent_in_ins) {//this actually is transfer transaction, notify about spend - if (tx_money_spent_in_ins > tx_money_got_in_outs) + if (ptc.tx_money_spent_in_ins > tx_money_got_in_outs) {//usual transfer - handle_money_spent2(b, tx, tx_money_spent_in_ins - (tx_money_got_in_outs+get_tx_fee(tx)), mtd, recipients, remote_aliases); + handle_money_spent2(b, tx, ptc.tx_money_spent_in_ins - (tx_money_got_in_outs+get_tx_fee(tx)), ptc.mtd, recipients, remote_aliases); } else {//strange transfer, seems that in one transaction have transfers from different wallets. if (!is_coinbase(tx)) { - WLT_LOG_RED("Unusual transaction " << currency::get_transaction_hash(tx) << ", tx_money_spent_in_ins: " << tx_money_spent_in_ins << ", tx_money_got_in_outs: " << tx_money_got_in_outs, LOG_LEVEL_0); + WLT_LOG_RED("Unusual transaction " << currency::get_transaction_hash(tx) << ", tx_money_spent_in_ins: " << ptc.tx_money_spent_in_ins << ", tx_money_got_in_outs: " << tx_money_got_in_outs, LOG_LEVEL_0); } - handle_money_received2(b, tx, (tx_money_got_in_outs - (tx_money_spent_in_ins - get_tx_fee(tx))), mtd); + handle_money_received2(b, tx, (tx_money_got_in_outs - (ptc.tx_money_spent_in_ins - get_tx_fee(tx))), ptc.mtd); } } else { if(tx_money_got_in_outs) - handle_money_received2(b, tx, tx_money_got_in_outs, mtd); + handle_money_received2(b, tx, tx_money_got_in_outs, ptc.mtd); else if (currency::is_derivation_used_to_encrypt(tx, derivation)) { //transaction doesn't transfer actually money, bud bring some information - handle_money_received2(b, tx, 0, mtd); + handle_money_received2(b, tx, 0, ptc.mtd); } - else if (mtd.spent_indices.size()) + else if (ptc.mtd.spent_indices.size()) { // multisig spend detected - handle_money_spent2(b, tx, 0, mtd, recipients, remote_aliases); + handle_money_spent2(b, tx, 0, ptc.mtd, recipients, remote_aliases); } } } @@ -1832,32 +1806,42 @@ bool wallet2::has_related_alias_entry_unconfirmed(const currency::transaction& t return false; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_directly_spent_transfer_id_by_input_in_tracking_wallet(const currency::txin_to_key& intk) +uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk) +{ + return get_directly_spent_transfer_index_by_input_in_tracking_wallet(intk.amount, intk.key_offsets); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk) +{ + return get_directly_spent_transfer_index_by_input_in_tracking_wallet(0, inzk.key_offsets); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(uint64_t amount, const std::vector & key_offsets) { WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(is_auditable() && is_watch_only(), "this is not an auditable-watch-only (tracking) wallet"); uint64_t tid = UINT64_MAX; // try to find a reference among own UTXOs - std::vector abs_key_offsets = relative_output_offsets_to_absolute(intk.key_offsets); // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid. + std::vector abs_key_offsets = relative_output_offsets_to_absolute(key_offsets); // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid. for (auto v : abs_key_offsets) { if (v.type() != typeid(uint64_t)) continue; uint64_t gindex = boost::get(v); - auto it = m_amount_gindex_to_transfer_id.find(std::make_pair(intk.amount, gindex)); + auto it = m_amount_gindex_to_transfer_id.find(std::make_pair(amount, gindex)); if (it != m_amount_gindex_to_transfer_id.end()) { tid = it->second; - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tid < m_transfers.size(), "invalid tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tid < m_transfers.size(), "invalid tid: " << tid << ", ref from input with amount: " << amount << ", gindex: " << gindex); auto& td = m_transfers[it->second]; - if (intk.key_offsets.size() != 1) + if (key_offsets.size() != 1) { // own output was used in non-direct transaction // the core should not allow this to happen, the only way it may happen - mixing in own output that was sent without mix_attr == 1 // log strange situation std::stringstream ss; - ss << "own transfer tid=" << tid << " tx=" << td.tx_hash() << " mix_attr=" << td.mix_attr() << ", is referenced by a transaction with mixins, ref from input with amount: " << intk.amount << ", gindex: " << gindex; + ss << "own transfer tid=" << tid << " tx=" << td.tx_hash() << " mix_attr=" << td.mix_attr() << ", is referenced by a transaction with mixins, ref from input with amount: " << amount << ", gindex: " << gindex; WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0); if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); @@ -1867,7 +1851,7 @@ uint64_t wallet2::get_directly_spent_transfer_id_by_input_in_tracking_wallet(con tid = UINT64_MAX; continue; } - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(td.m_spent_height == 0, "transfer is spent in blockchain, tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(td.m_spent_height == 0, "transfer is spent in blockchain, tid: " << tid << ", ref from input with amount: " << amount << ", gindex: " << gindex); // okay, own output is being spent, return it break; } @@ -1961,7 +1945,7 @@ void wallet2::scan_tx_pool(bool& has_related_alias_in_unconfirmed) if (is_auditable() && is_watch_only()) { // tracking wallet, assuming all outputs are spent directly because of mix_attr = 1 - tid = get_directly_spent_transfer_id_by_input_in_tracking_wallet(intk); + tid = get_directly_spent_transfer_index_by_input_in_tracking_wallet(intk.amount, intk.key_offsets); } else { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index bd0c657d..2ed310e5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -416,10 +416,15 @@ namespace tools crypto::key_image m_key_image; //TODO: key_image stored twice :( std::vector varian_options; + //v2 + uint64_t m_amount; //@#@ need conversion from old files format + + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_global_output_index) KV_SERIALIZE_POD_AS_HEX_STRING(m_key_image) KV_CHAIN_BASE(transfer_details_base) + KV_SERIALIZE(m_amount) END_KV_SERIALIZE_MAP() }; @@ -499,6 +504,21 @@ namespace tools END_SERIALIZE() }; + struct process_transaction_context + { + uint64_t tx_money_spent_in_ins; + // check all outputs for spending (compare key images) + money_transfer2_details mtd; + bool is_pos_coinbase; + bool coin_base_tx; + //PoW block don't have change, so all outs supposed to be marked as "mined" + bool is_derived_from_coinbase; + size_t i; + size_t sub_i; + uint64_t height; + }; + + void assign_account(const currency::account_base& acc); void generate(const std::wstring& path, const std::string& password, bool auditable_wallet); void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password); @@ -964,6 +984,8 @@ private: void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const wallet_public::wallet_transfer_info& wti) const; void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const std::string& reason = "internal intention") const; + template + bool process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx); const construct_tx_param& get_default_construct_tx_param(); @@ -1015,7 +1037,9 @@ private: //void check_if_block_matched(uint64_t i, const crypto::hash& id, bool& block_found, bool& block_matched, bool& full_reset_needed); uint64_t detach_from_block_ids(uint64_t height); uint64_t get_wallet_minimum_height(); - uint64_t get_directly_spent_transfer_id_by_input_in_tracking_wallet(const currency::txin_to_key& intk); + uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(uint64_t amount, const std::vector & key_offsets); + uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk); + uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk); bool is_in_hardfork_zone(uint64_t hardfork_index); void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector& extra); @@ -1217,6 +1241,45 @@ namespace boost namespace tools { + template + bool wallet2::process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx) + { + // check if this input spends our output + uint64_t tr_index = UINT64_MAX; + + if (this->is_auditable() && this->is_watch_only()) + { + // tracking wallet, assuming all outputs are spent directly because of mix_attr = 1 + tr_index = this->get_directly_spent_transfer_index_by_input_in_tracking_wallet(in_t); + } + else + { + // wallet with spend secret key -- we can calculate own key images and then search by them + auto it = m_key_images.find(in_t.k_image); + if (it != m_key_images.end()) + { + tr_index = it->second; + } + } + + if (tr_index != UINT64_MAX) + { + transfer_details& td = m_transfers[tr_index]; + ptc.tx_money_spent_in_ins += td.amount(); + uint32_t flags_before = td.m_flags; + td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; + td.m_spent_height = ptc.height; + if (ptc.coin_base_tx && td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER) + ptc.is_derived_from_coinbase = true; + else + ptc.is_derived_from_coinbase = false; + WLT_LOG_L0("Spent key out, transfer #" << tr_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << ptc.height << + "; flags: " << flags_before << " -> " << td.m_flags); + ptc.mtd.spent_indices.push_back(ptc.i); + remove_transfer_from_expiration_list(tr_index); + } + } + template //do refresh as external callback bool wallet2::scan_pos(mining_context& cxt, std::atomic& stop, From c2d0167c8a1e696100332fa2118c1efaf19727d3 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Tue, 28 Jun 2022 22:29:52 +0200 Subject: [PATCH 02/13] hidden amounts in wallet: added return value to process_input_t() --- src/wallet/wallet2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2ed310e5..d3bbdfa8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1278,6 +1278,7 @@ namespace tools ptc.mtd.spent_indices.push_back(ptc.i); remove_transfer_from_expiration_list(tr_index); } + return true; } template //do refresh as external callback From c0eb4088d8aea3175ff826c0066b326c1113acb6 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Thu, 30 Jun 2022 19:02:22 +0200 Subject: [PATCH 03/13] hidden amounts in wallet: pricess_new_transaction() refactoring in work --- src/wallet/wallet2.cpp | 94 ++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4feef0ff..55948a69 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -323,6 +323,61 @@ void wallet2::fetch_tx_global_indixes(const std::list(out_t).target.type() == typeid(txout_to_key); + } + return false; +} + +bool out_is_to_htlc(const ¤cy::tx_out_v out_t) +{ + if (out_t.type() == typeid(currency::tx_out_bare)) + { + return boost::get(out_t).target.type() == typeid(currency::txout_htlc); + } + return false; +} +bool out_is_to_zarcanum(const ¤cy::tx_out_v out_t) +{ + return out_t.type() == typeid(currency::tx_out_zarcanum); +} + +const crypto::public_key& out_get_pub_key(const ¤cy::tx_out_v out_t, std::list& htlc_info_list) +{ + if (out_t.type() == typeid(tx_out_bare)) + { + const &tx_out_bare out = boost::get(out_t); + if (out.target.type() == typeid(txout_to_key)) + { + return boost::get(out.target).key; + } + else if (out.target.type() == typeid(txout_htlc)) + { + THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty"); + bool hltc_our_out_is_before_expiration = htlc_info_list.front().hltc_our_out_is_before_expiration; + htlc_info_list.pop_front(); + if (hltc_our_out_is_before_expiration) + { + return boost::get(out.target).pkey_redeem; + } + else + { + return boost::get(out.target).pkey_refund; + } + }else + { + THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out.target.type().name()); + } + } + else if (out_t.type() == typeid(currency::tx_out_zarcanum)) + { + return boost::get(out_t).stealth_address; + } +} + //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) { @@ -453,34 +508,13 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t { size_t o = outs[i_in_outs].index; WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(o < tx.vout.size(), "wrong out in transaction: internal index=" << o << ", total_outs=" << tx.vout.size()); - VARIANT_SWITCH_BEGIN(tx.vout[o]); - VARIANT_CASE_CONST(tx_out_bare, out) { - if (out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc)) + const &tx_out_v out_v = tx.vout[o]; + + if (out_is_to_key(out_v) || out_is_to_htlc(out_v) || out_is_to_zarcanum(out_v)) // out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc)) { bool hltc_our_out_is_before_expiration = false; - crypto::public_key out_key = null_pkey; - if (out.target.type() == typeid(txout_to_key)) - { - out_key = boost::get(out.target).key; - } - else if (out.target.type() == typeid(txout_htlc)) - { - THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty"); - if (htlc_info_list.front().hltc_our_out_is_before_expiration) - { - out_key = boost::get(out.target).pkey_redeem; - } - else - { - out_key = boost::get(out.target).pkey_refund; - } - htlc_info_list.pop_front(); - } - else - { - THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out.target.type().name()); - } + crypto::public_key out_key = out_get_pub_key(out_v); //const currency::txout_to_key& otk = boost::get(out.target); // obtain key image for this output @@ -527,13 +561,13 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0); if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str()); - WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tx_money_got_in_outs >= out.amount, "tx_money_got_in_outs: " << tx_money_got_in_outs << ", out.amount:" << out.amount); - tx_money_got_in_outs -= out.amount; + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tx_money_got_in_outs >= outs[i_in_outs].amount, "tx_money_got_in_outs: " << tx_money_got_in_outs << ", out.amount:" << outs[i_in_outs].amount); + tx_money_got_in_outs -= outs[i_in_outs].amount; continue; // skip the output } } - if (is_auditable() && out.target.type() == typeid(txout_to_key) && + if (is_auditable() && (out_is_to_key(out_v) || out_is_to_zarcanum(out_v)) && boost::get(out.target).mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) { std::stringstream ss; @@ -650,9 +684,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t WLT_LOG_L0("Received multisig, multisig out id: " << multisig_id << ", amount: " << tdb.amount() << ", with tx: " << get_transaction_hash(tx)); } } - VARIANT_CASE_CONST(tx_out_zarcanum, o); - //@#@ - VARIANT_SWITCH_END(); + } } From 9a365940e6faccc2a53161713fe0ee2b6ab072a7 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Thu, 30 Jun 2022 21:47:59 +0200 Subject: [PATCH 04/13] hidden amounts in wallet: pricess_new_transaction(): finished --- src/wallet/wallet2.cpp | 71 ++++++++++++++++++++++++++++++++---------- src/wallet/wallet2.h | 15 ++++++--- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 55948a69..238277a1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -332,6 +332,15 @@ bool out_is_to_key(const &tx_out_v out_t) return false; } +bool out_is_multisig(const &tx_out_v out_t) +{ + if (out_t.type() == typeid(tx_out_bare)) + { + return boost::get(out_t).target.type() == typeid(txout_multisig); + } + return false; +} + bool out_is_to_htlc(const ¤cy::tx_out_v out_t) { if (out_t.type() == typeid(currency::tx_out_bare)) @@ -340,6 +349,34 @@ bool out_is_to_htlc(const ¤cy::tx_out_v out_t) } return false; } +const currency::txout_htlc& out_get_htlc(const ¤cy::tx_out_v out_t) +{ + return boost::get(boost::get(out_t).target); +} + +bool out_get_mixin_attr(const ¤cy::tx_out_v out_t) +{ + if (out_t.type() == typeid(currency::tx_out_bare)) + { + if (boost::get(out_t).target.type() == typeid(currency::txout_to_key)) + { + return boost::get(boost::get(out_t).target).mix_attr; + } + else + { + THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "Unexpected type in out_get_mixin_attr"); + } + } + else if (out_t.type() == typeid(currency::tx_out_zarcanum)) + { + boost::get(out_t).mix_attr; + } + else + { + THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "Unexpected type in out_get_mixin_attr"); + } +} + bool out_is_to_zarcanum(const ¤cy::tx_out_v out_t) { return out_t.type() == typeid(currency::tx_out_zarcanum); @@ -513,7 +550,6 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t if (out_is_to_key(out_v) || out_is_to_htlc(out_v) || out_is_to_zarcanum(out_v)) // out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc)) { - bool hltc_our_out_is_before_expiration = false; crypto::public_key out_key = out_get_pub_key(out_v); //const currency::txout_to_key& otk = boost::get(out.target); @@ -568,15 +604,15 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t } if (is_auditable() && (out_is_to_key(out_v) || out_is_to_zarcanum(out_v)) && - boost::get(out.target).mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) + out_get_mixin_attr(out_v) != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) { std::stringstream ss; - ss << "output #" << o << " from tx " << get_transaction_hash(tx) << " with amount " << print_money_brief(out.amount) - << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)boost::get(out.target).mix_attr << ". Output IGNORED."; + ss << "output #" << o << " from tx " << get_transaction_hash(tx) << " with amount " << print_money_brief(outs[i_in_outs].amount) + << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)out_get_mixin_attr(out_v) << ". Output IGNORED."; WLT_LOG_RED(ss.str(), LOG_LEVEL_0); if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_red, ss.str()); - tx_money_got_in_outs -= out.amount; + tx_money_got_in_outs -= outs[i_in_outs].amount; continue; // skip the output } @@ -587,6 +623,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t td.m_ptx_wallet_info = pwallet_info; td.m_internal_output_index = o; td.m_key_image = ki; + td.m_amount = outs[i_in_outs].amount; if (m_use_deffered_global_outputs) { if (pglobal_indexes && pglobal_indexes->size() > o) @@ -608,11 +645,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER; } } - uint64_t amount = out.amount; size_t transfer_index = m_transfers.size() - 1; - if (out.target.type() == typeid(txout_htlc)) + if (out_is_to_htlc(out_v)) { - const txout_htlc& hltc = boost::get(out.target); + const txout_htlc& hltc = out_get_htlc(out_v); //mark this as spent td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; //create entry for htlc input @@ -628,7 +664,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); + auto amount_gindex_pair = std::make_pair(td.m_amount, td.m_global_output_index); m_active_htlcs[amount_gindex_pair] = transfer_index; m_active_htlcs_txid[get_transaction_hash(tx)] = transfer_index; //add payer to extra options @@ -652,29 +688,29 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t if (td.m_key_image != currency::null_ki) m_key_images[td.m_key_image] = transfer_index; - add_transfer_to_transfers_cache(amount, transfer_index); + add_transfer_to_transfers_cache(td.m_amount, transfer_index); if (is_watch_only() && is_auditable()) { WLT_CHECK_AND_ASSERT_MES_NO_RET(td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED, "td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED validation failed"); - auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index); - WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << amount << ", gindex " << td.m_global_output_index << " already exists"); + auto amount_gindex_pair = std::make_pair(td.m_amount, td.m_global_output_index); + WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << td.m_amount << ", gindex " << td.m_global_output_index << " already exists"); m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index; } if (max_out_unlock_time < get_tx_unlock_time(tx, o)) max_out_unlock_time = get_tx_unlock_time(tx, o); - if (out.target.type() == typeid(txout_to_key)) + if (out_is_to_key(out_v) || out_is_to_zarcanum(out_v)) { WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); } - else if (out.target.type() == typeid(txout_htlc)) + else if (out_is_to_htlc(out_v)) { WLT_LOG_L0("Detected HTLC[" << (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ? "REDEEM" : "REFUND") << "], transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); } } - else if (out.target.type() == typeid(txout_multisig)) + else if (out_is_multisig(out_v)) { crypto::hash multisig_id = currency::get_multisig_out_id(tx, o); WLT_CHECK_AND_ASSERT_MES_NO_RET(m_multisig_transfers.count(multisig_id) == 0, "multisig_id = " << multisig_id << " already in multisig container"); @@ -839,6 +875,7 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept tdb.m_internal_output_index = n; tdb.m_flags &= ~(WALLET_TRANSFER_DETAIL_FLAG_SPENT); //--------------------------------- + //@#@ todo: proper handling with zarcanum_based stuff //figure out fee that was left for release contract THROW_IF_FALSE_WALLET_INT_ERR_EX(tx.vout[n].type() == typeid(tx_out_bare), "Unexpected output type in accept proposal"); THROW_IF_FALSE_WALLET_INT_ERR_EX(boost::get(tx.vout[n]).amount > (contr_it->second.private_detailes.amount_to_pay + @@ -1410,7 +1447,7 @@ void wallet2::unprocess_htlc_triggers_on_block_removed(uint64_t height) } //re-add to active contracts WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type() == typeid(tx_out_bare), std::string("Unexprected type of out in unprocess_htlc_triggers_on_block_removed : ") + tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type().name()); - auto pair_key = std::make_pair(boost::get(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index]).amount, tr.m_global_output_index); + auto pair_key = std::make_pair(tr.m_amount, tr.m_global_output_index); auto it_active_htlc = m_active_htlcs.find(pair_key); if (it_active_htlc != m_active_htlcs.end()) { @@ -1473,7 +1510,7 @@ void wallet2::process_htlc_triggers_on_block_added(uint64_t height) //remove it from active contracts CHECK_AND_ASSERT_MES(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type() == typeid(tx_out_bare), void(), "Unexpected type out in process_htlc_triggers_on_block_added: " << tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type().name()); - uint64_t amount = boost::get(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index]).amount; + uint64_t amount = tr.m_amount; auto it_active_htlc = m_active_htlcs.find(std::make_pair(amount, tr.m_global_output_index)); if (it_active_htlc == m_active_htlcs.end()) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d3bbdfa8..9678898c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -378,10 +378,11 @@ namespace tools uint64_t m_internal_output_index; uint64_t m_spent_height; uint32_t m_flags; + uint64_t m_amount; // @#@ will throw if type is not tx_out_bare, TODO: change according to new model, // need to replace all get_tx_out_bare_from_out_v() to proper code - uint64_t amount() const { return currency::get_tx_out_bare_from_out_v(m_ptx_wallet_info->m_tx.vout[m_internal_output_index]).amount; } + uint64_t amount() const { return m_amount; } const currency::tx_out_bare& output() const { return currency::get_tx_out_bare_from_out_v(m_ptx_wallet_info->m_tx.vout[m_internal_output_index]); } uint8_t mix_attr() const { return output().target.type() == typeid(currency::txout_to_key) ? boost::get(output().target).mix_attr : UINT8_MAX; } crypto::hash tx_hash() const { return get_transaction_hash(m_ptx_wallet_info->m_tx); } @@ -394,6 +395,7 @@ namespace tools KV_SERIALIZE(m_internal_output_index) KV_SERIALIZE(m_spent_height) KV_SERIALIZE(m_flags) + KV_SERIALIZE(m_amount) KV_SERIALIZE_EPHEMERAL_N(uint64_t, tools::wallet2::transfer_details_base_to_amount, "amount") KV_SERIALIZE_EPHEMERAL_N(std::string, tools::wallet2::transfer_details_base_to_tx_hash, "tx_id") END_KV_SERIALIZE_MAP() @@ -417,14 +419,10 @@ namespace tools std::vector varian_options; //v2 - uint64_t m_amount; //@#@ need conversion from old files format - - BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_global_output_index) KV_SERIALIZE_POD_AS_HEX_STRING(m_key_image) KV_CHAIN_BASE(transfer_details_base) - KV_SERIALIZE(m_amount) END_KV_SERIALIZE_MAP() }; @@ -1110,6 +1108,7 @@ private: BOOST_CLASS_VERSION(tools::wallet2, WALLET_FILE_SERIALIZATION_VERSION) BOOST_CLASS_VERSION(tools::wallet_public::wallet_transfer_info, 10) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 3) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details_base, 2) namespace boost { @@ -1130,6 +1129,12 @@ namespace boost a & x.m_internal_output_index; a & x.m_flags; a & x.m_spent_height; + if (ver < 2) + { + x.m_amount = currency::get_tx_out_bare_from_out_v(x.m_ptx_wallet_info->m_tx.vout[x.m_internal_output_index]).amount; + return; + } + a & x.m_amount; } From 464771baecf66bcab36b5914ac9b01e266b8b4f0 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 1 Jul 2022 17:19:57 +0200 Subject: [PATCH 05/13] hidden amounts in wallet: wallet.cpp compilation fixed --- src/wallet/wallet2.cpp | 56 +++++++++++++++++++++++++++--------------- src/wallet/wallet2.h | 3 +++ 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 238277a1..e06c0023 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -323,25 +323,25 @@ void wallet2::fetch_tx_global_indixes(const std::list(out_t).target.type() == typeid(txout_to_key); + return boost::get(out_t).target.type() == typeid(currency::txout_to_key); } return false; } -bool out_is_multisig(const &tx_out_v out_t) +bool out_is_multisig(const currency::tx_out_v& out_t) { - if (out_t.type() == typeid(tx_out_bare)) + if (out_t.type() == typeid(currency::tx_out_bare)) { - return boost::get(out_t).target.type() == typeid(txout_multisig); + return boost::get(out_t).target.type() == typeid(currency::txout_multisig); } return false; } -bool out_is_to_htlc(const ¤cy::tx_out_v out_t) +bool out_is_to_htlc(const currency::tx_out_v& out_t) { if (out_t.type() == typeid(currency::tx_out_bare)) { @@ -349,12 +349,12 @@ bool out_is_to_htlc(const ¤cy::tx_out_v out_t) } return false; } -const currency::txout_htlc& out_get_htlc(const ¤cy::tx_out_v out_t) +const currency::txout_htlc& out_get_htlc(const currency::tx_out_v& out_t) { return boost::get(boost::get(out_t).target); } -bool out_get_mixin_attr(const ¤cy::tx_out_v out_t) +bool wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) { if (out_t.type() == typeid(currency::tx_out_bare)) { @@ -377,21 +377,21 @@ bool out_get_mixin_attr(const ¤cy::tx_out_v out_t) } } -bool out_is_to_zarcanum(const ¤cy::tx_out_v out_t) +bool out_is_to_zarcanum(const currency::tx_out_v& out_t) { return out_t.type() == typeid(currency::tx_out_zarcanum); } -const crypto::public_key& out_get_pub_key(const ¤cy::tx_out_v out_t, std::list& htlc_info_list) +const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out_t, std::list& htlc_info_list) { if (out_t.type() == typeid(tx_out_bare)) { - const &tx_out_bare out = boost::get(out_t); - if (out.target.type() == typeid(txout_to_key)) + const currency::tx_out_bare& out = boost::get(out_t); + if (out.target.type() == typeid(currency::txout_to_key)) { return boost::get(out.target).key; } - else if (out.target.type() == typeid(txout_htlc)) + else if (out.target.type() == typeid(currency::txout_htlc)) { THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty"); bool hltc_our_out_is_before_expiration = htlc_info_list.front().hltc_our_out_is_before_expiration; @@ -411,10 +411,14 @@ const crypto::public_key& out_get_pub_key(const ¤cy::tx_out_v out_t, std:: } else if (out_t.type() == typeid(currency::tx_out_zarcanum)) { - return boost::get(out_t).stealth_address; + return boost::get(out_t).stealth_address; } + else + { + THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out_t.type().name()); + } + } - //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) { @@ -546,11 +550,11 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t size_t o = outs[i_in_outs].index; WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(o < tx.vout.size(), "wrong out in transaction: internal index=" << o << ", total_outs=" << tx.vout.size()); { - const &tx_out_v out_v = tx.vout[o]; + const currency::tx_out_v& out_v = tx.vout[o]; if (out_is_to_key(out_v) || out_is_to_htlc(out_v) || out_is_to_zarcanum(out_v)) // out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc)) { - crypto::public_key out_key = out_get_pub_key(out_v); + crypto::public_key out_key = out_get_pub_key(out_v, htlc_info_list); //const currency::txout_to_key& otk = boost::get(out.target); // obtain key image for this output @@ -648,7 +652,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t size_t transfer_index = m_transfers.size() - 1; if (out_is_to_htlc(out_v)) { - const txout_htlc& hltc = out_get_htlc(out_v); + const currency::txout_htlc& hltc = out_get_htlc(out_v); //mark this as spent td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; //create entry for htlc input @@ -5168,7 +5172,7 @@ bool wallet2::prepare_free_transfers_cache(uint64_t fake_outputs_count) if (is_transfer_able_to_go(td, fake_outputs_count)) { //@#@ - m_found_free_amounts[boost::get(td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index]).amount].insert(i); + m_found_free_amounts[td.amount()].insert(i); count++; } } @@ -5219,6 +5223,18 @@ bool wallet2::read_money_transfer2_details_from_tx(const transaction& tx, const wtd.spn.push_back(in_to_key.amount); } } + else if (i.type() == typeid(currency::txin_zarcanum_inputs)) + { + const currency::txin_zarcanum_inputs& in_to_zc = boost::get(i); + for (auto& e : in_to_zc.elements) + { + auto it = m_key_images.find(e.k_image); + //should we panic if image not found? + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_key_images.end(), "[read_money_transfer2_details_from_tx]Unknown key image in tx: " << get_transaction_hash(tx)); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "[read_money_transfer2_details_from_tx]Index out of range for key image in tx: " << get_transaction_hash(tx)); + wtd.spn.push_back(m_transfers[it->second].amount()); + } + } } return true; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9678898c..d32824b3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1039,6 +1039,9 @@ private: uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk); uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk); bool is_in_hardfork_zone(uint64_t hardfork_index); + bool out_get_mixin_attr(const currency::tx_out_v& out_t); + const crypto::public_key& out_get_pub_key(const currency::tx_out_v& out_t, std::list& htlc_info_list); + void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector& extra); void remove_transfer_from_amount_gindex_map(uint64_t tid); From 045801449a66f30ab37e46b39031ab97cbfbebc9 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 1 Jul 2022 17:20:55 +0200 Subject: [PATCH 06/13] hidden amounts in wallet: fixed warining in out_get_mixin_attr() --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e06c0023..0b8de104 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -369,7 +369,7 @@ bool wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) } else if (out_t.type() == typeid(currency::tx_out_zarcanum)) { - boost::get(out_t).mix_attr; + return boost::get(out_t).mix_attr; } else { From b799aae67f97cc9629190b6904ba4287abed2236 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Mon, 4 Jul 2022 15:26:28 +0200 Subject: [PATCH 07/13] hidden amounts in wallet: syntax fixes --- src/currency_core/blockchain_storage.cpp | 2 +- src/wallet/wallet2.cpp | 20 ++++++++------------ src/wallet/wallet_errors.h | 7 +++++++ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 2d1ca631..e193c6f2 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3935,7 +3935,7 @@ namespace currency // TODO: @#@# should check for hardfork here? for(auto& el : in.elements) { - if (!visit(0, el.key_image, el.key_offsets)) + if (!visit(0, el.k_image, el.key_offsets)) return false; } return true; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 0b8de104..bcaeee42 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -364,7 +364,7 @@ bool wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) } else { - THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "Unexpected type in out_get_mixin_attr"); + THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr"); } } else if (out_t.type() == typeid(currency::tx_out_zarcanum)) @@ -373,8 +373,10 @@ bool wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) } else { - THROW_IF_FALSE_WALLET_INT_ERR_EX(false, "Unexpected type in out_get_mixin_attr"); + THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr"); } + THROW_WALLET_CMN_ERR_EX("Unexpected out type im wallet: " << out_t.type().name()); + return false; } bool out_is_to_zarcanum(const currency::tx_out_v& out_t) @@ -391,8 +393,9 @@ const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out { return boost::get(out.target).key; } - else if (out.target.type() == typeid(currency::txout_htlc)) + else { + THROW_IF_FALSE_WALLET_INT_ERR_EX(out.target.type() == typeid(currency::txout_htlc), "Unexpected out type in target wallet: " << out.target.type().name()); THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty"); bool hltc_our_out_is_before_expiration = htlc_info_list.front().hltc_our_out_is_before_expiration; htlc_info_list.pop_front(); @@ -404,20 +407,13 @@ const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out { return boost::get(out.target).pkey_refund; } - }else - { - THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out.target.type().name()); } } - else if (out_t.type() == typeid(currency::tx_out_zarcanum)) - { - return boost::get(out_t).stealth_address; - } else { - THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out_t.type().name()); + THROW_IF_FALSE_WALLET_INT_ERR_EX(out_t.type() == typeid(currency::tx_out_zarcanum), "Unexpected out type im wallet: " << out_t.type().name()); + return boost::get(out_t).stealth_address; } - } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector* pglobal_indexes) diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 46c93f30..a5de826d 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -751,3 +751,10 @@ if (cond) LOG_ERROR(" (" << #cond << ") is FALSE. THROW EXCEPTION: wallet_common_error"); \ tools::error::throw_wallet_ex(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str()); \ } +#define THROW_WALLET_CMN_ERR_EX(mess) \ + { \ + std::stringstream ss; \ + ss << mess; \ + LOG_ERROR("THROW EXCEPTION: wallet_common_error"); \ + tools::error::throw_wallet_ex(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str()); \ + } \ No newline at end of file From 572f3c01c6f47be27cfb42eaa2b77f73c86e751d Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Mon, 4 Jul 2022 15:27:41 +0200 Subject: [PATCH 08/13] hidden amounts in wallet: fixed return type --- src/wallet/wallet2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d32824b3..e74e459f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1039,7 +1039,7 @@ private: uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk); uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk); bool is_in_hardfork_zone(uint64_t hardfork_index); - bool out_get_mixin_attr(const currency::tx_out_v& out_t); + uint8_t out_get_mixin_attr(const currency::tx_out_v& out_t); const crypto::public_key& out_get_pub_key(const currency::tx_out_v& out_t, std::list& htlc_info_list); From 52de76edd5890dfa4fed651637e7bb3ffe2c4d10 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Wed, 6 Jul 2022 13:22:05 +0200 Subject: [PATCH 09/13] hidden amounts in wallet: fixed types --- src/wallet/wallet2.cpp | 2 +- tests/core_tests/chain_switch_1.cpp | 4 ++-- tests/core_tests/ring_signature_1.cpp | 2 +- tests/core_tests/transaction_tests.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bcaeee42..d929e583 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -354,7 +354,7 @@ const currency::txout_htlc& out_get_htlc(const currency::tx_out_v& out_t) return boost::get(boost::get(out_t).target); } -bool wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) +uint8_t wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t) { if (out_t.type() == typeid(currency::tx_out_bare)) { diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index c0f3d001..a579515f 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -160,7 +160,7 @@ bool gen_chain_switch_1::check_split_not_switched(currency::core& c, size_t ev_i CHECK_TEST_CONDITION(r); CHECK_EQ(1, tx_pool.size()); - std::vector tx_outs; + std::vector tx_outs; uint64_t transfered; crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), tx_outs, transfered, derivation); @@ -223,7 +223,7 @@ bool gen_chain_switch_1::check_split_switched(currency::core& c, size_t ev_index CHECK_EQ(1, tx_pool.size()); CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front())); - std::vector tx_outs; + std::vector tx_outs; uint64_t transfered; crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); lookup_acc_outs(m_recipient_account_2.get_keys(), tx_pool.front(), tx_outs, transfered, derivation); diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index a77b7342..12ea6f9e 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -310,7 +310,7 @@ bool gen_ring_signature_big::check_balances_2(currency::core& c, size_t ev_index CHECK_EQ(balance, get_balance(an_account, chain, mtx)); } - std::vector tx_outs; + std::vector tx_outs; uint64_t transfered; crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); lookup_acc_outs(m_alice_account.get_keys(), boost::get(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get(events[events.size() - 3])), tx_outs, transfered, derivation); diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 29adf651..5fc89e29 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -113,7 +113,7 @@ bool test_transaction_generation_and_ring_signature() r = crypto::check_ring_signature(pref_hash, boost::get(tx_rc1.vin[0]).k_image, output_keys, &boost::get(tx_rc1.signatures[0]).s[0]); CHECK_AND_ASSERT_MES(r, false, "failed to check ring signature"); - std::vector outs; + std::vector outs; uint64_t money = 0; crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money, derivation); From b500a22d599a1f3fedc29ef27ae4e786301d6100 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 8 Jul 2022 15:55:01 +0200 Subject: [PATCH 10/13] hidden amounts in wallet: fixed to proper return code literal --- src/currency_core/currency_format_utils.cpp | 3 ++- src/rpc/core_rpc_server.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index f44b7ae7..ec7ff1c4 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -3459,7 +3459,8 @@ namespace currency //-------------------------------------------------------------------------------- bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, const std::vector& vouts, zarcanum_outs_range_proof& result) - { + { + //TODO: review for Andre CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 392718f9..5a2208a7 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -353,7 +353,7 @@ namespace currency bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx) { CHECK_CORE_READY(); - res.status = "Failed"; + res.status = API_RETURN_CODE_FAIL; if(!m_core.get_random_outs_for_amounts(req, res)) { return true; From 7792605ab98869e7d8fae7fd54030be4087b33b0 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 8 Jul 2022 20:52:04 +0200 Subject: [PATCH 11/13] hidden amounts in wallet: construct_tx deep refactoring(totaly broken yet) --- src/currency_core/currency_format_utils.cpp | 153 ++++++++++++++++++-- src/wallet/wallet2.h | 1 + 2 files changed, 140 insertions(+), 14 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index ec7ff1c4..a057d295 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1313,6 +1313,13 @@ namespace currency return r; } //--------------------------------------------------------------- + // prepare inputs + struct input_generation_context_data + { + keypair in_ephemeral; + //std::vector participants_derived_keys; + }; + bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& ftp, finalized_tx& result) { const std::vector& sources = ftp.sources; @@ -1395,20 +1402,28 @@ namespace currency tx.extra.insert(tx.extra.end(), extra_local.begin(), extra_local.end()); } + //first: separate zarcanum inputs and regular one + const std::vector zc_sources; + const std::vector NLSAG_sources; - - // prepare inputs - struct input_generation_context_data + BOOST_FOREACH(const tx_source_entry& src_entr, sources) { - keypair in_ephemeral; - //std::vector participants_derived_keys; - }; + if (src_entr.is_zarcanum()) + { + zc_sources.push_back(src_entr); + } + else + { + NLSAG_sources.push_back(src_entr); + } + } + std::vector in_contexts; size_t input_starter_index = tx.vin.size(); uint64_t summary_inputs_money = 0; - //fill inputs - for (const tx_source_entry& src_entr : sources) + //fill inputs NLSAG + for (const tx_source_entry& src_entr : NLSAG_sources) { in_contexts.push_back(input_generation_context_data()); if(src_entr.is_multisig()) @@ -1517,6 +1532,78 @@ namespace currency } } + + //fill inputs Zarcanum + if (zc_sources.size()) + { + txin_zarcanum_inputs ins_zc = AUTO_VAL_INIT(ins_zc); + + + for (const tx_source_entry& src_entr : zc_sources) + { + + + + //regular to key out + keypair& in_ephemeral = in_contexts.back().in_ephemeral; + //txin_to_key + if (src_entr.real_output >= src_entr.outputs.size()) + { + LOG_ERROR("real_output index (" << src_entr.real_output << ") greater than or equal to output_keys.size()=" << src_entr.outputs.size()); + return false; + } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + crypto::key_image img; + if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; + + //check that derivated key is equal with real output key + if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second)) + { + LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:" + << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second)); + return false; + } + + //put key image into tx input + txin_v in_v; + txin_to_key* ptokey = nullptr; + if (src_entr.htlc_origin.size()) + { + //add txin_htlc + txin_htlc in_htlc = AUTO_VAL_INIT(in_htlc); + in_htlc.hltc_origin = src_entr.htlc_origin; + in_v = in_htlc; + txin_htlc& in_v_ref = boost::get(in_v); + ptokey = static_cast(&in_v_ref); + } + else + { + in_v = txin_to_key(); + txin_to_key& in_v_ref = boost::get(in_v); + ptokey = &in_v_ref; + } + txin_to_key& input_to_key = *ptokey; + + + input_to_key.amount = src_entr.amount; + input_to_key.k_image = img; + + //fill outputs array and use relative offsets + BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) + input_to_key.key_offsets.push_back(out_entry.first); + + input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + tx.vin.push_back(in_v); + } + } + + + + // "Shuffle" outs std::vector shuffled_dsts(destinations); if (shuffle) @@ -1525,6 +1612,7 @@ namespace currency uint64_t summary_outs_money = 0; //fill outputs size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 + uint64_t range_proof_start_index = output_index; std::set deriv_cache; crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For rnage proof generation @@ -1533,7 +1621,7 @@ namespace currency CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, blinding_masks[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out"); - amounts[output_index] = dst_entr.amount; + amounts[range_proof_start_index - output_index] = dst_entr.amount; output_index++; summary_outs_money += dst_entr.amount; } @@ -1567,6 +1655,15 @@ namespace currency att_count++; } } + if (tx.version > TRANSACTION_VERSION_PRE_HF4) + { + //add range proofs + currency::zarcanum_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs); + bool r = generate_zarcanum_outs_range_proof(range_proof_start_index, amounts.size(), amounts, blinding_masks, tx.vout, range_proofs); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate zarcanum_outs_range_proof()"); + tx.attachment.push_back(range_proofs); + } + if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)) { //take hash from attachment and put into extra @@ -1593,7 +1690,38 @@ namespace currency crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); + + std::stringstream ss_ring_s; + if (tx.version <= TRANSACTION_VERSION_PRE_HF4) + { + bool r = generate_NLSAG_sig(ftp, input_starter_index, tx, tx_prefix_hash, sender_account_keys, in_contexts, txkey, ss_ring_s); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate_NLSAG_sig()"); + }else + { + bool r = generate_hybrid_sig(); + } + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); + + return true; + } + bool generate_hybrid_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) + { + const std::vector& sources = ftp.sources; + bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; + size_t input_index = input_starter_index; + size_t in_context_index = 0; + + + bool r = generate_zarcanum_signature(tx_prefix_hash, zc_sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) + + } + + bool generate_NLSAG_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) + { + const std::vector& sources = ftp.sources; + bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; size_t input_index = input_starter_index; size_t in_context_index = 0; BOOST_FOREACH(const tx_source_entry& src_entr, sources) @@ -1604,7 +1732,7 @@ namespace currency tx.signatures.push_back(NLSAG_sig()); std::vector& sigs = boost::get(tx.signatures.back()).s; - if(src_entr.is_multisig()) + if (src_entr.is_multisig()) { // txin_multisig -- don't sign anything here (see also sign_multisig_input_in_tx()) sigs.resize(src_entr.ms_keys_count, null_sig); // just reserve keys.size() null signatures (NOTE: not minimum_sigs!) @@ -1623,7 +1751,7 @@ namespace currency if (!watch_only_mode) 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; @@ -1641,9 +1769,6 @@ namespace currency input_index++; in_context_index++; } - - LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); - return true; } //--------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e74e459f..88b44b38 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -155,6 +155,7 @@ namespace tools }; #pragma pack(pop) + typedef tools::pod_array_file_container pending_ki_file_container_t; namespace detail From 9fb947b54456a80ae88dfd265fd7b40f41fb3b37 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 9 Jul 2022 17:35:40 +0200 Subject: [PATCH 12/13] hidden amounts in wallet: construct_tx deep refactoring(in progress) --- src/currency_core/currency_format_utils.cpp | 136 +++++++------------- 1 file changed, 43 insertions(+), 93 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 88c8249e..51fe6937 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1402,27 +1402,31 @@ namespace currency tx.extra.insert(tx.extra.end(), extra_local.begin(), extra_local.end()); } - //first: separate zarcanum inputs and regular one - const std::vector zc_sources; - const std::vector NLSAG_sources; +// //first: separate zarcanum inputs and regular one +// const std::vector zc_sources; +// const std::vector NLSAG_sources; +// +// BOOST_FOREACH(const tx_source_entry& src_entr, sources) +// { +// if (src_entr.is_zarcanum()) +// { +// zc_sources.push_back(src_entr); +// } +// else +// { +// NLSAG_sources.push_back(src_entr); +// } +// } - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { - if (src_entr.is_zarcanum()) - { - zc_sources.push_back(src_entr); - } - else - { - NLSAG_sources.push_back(src_entr); - } - } - std::vector in_contexts; + std::vector in_contexts; + + //we'll aggregate Zarcanum outs into one txin_zarcanum_inputs + txin_zarcanum_inputs ins_zc = AUTO_VAL_INIT(ins_zc); size_t input_starter_index = tx.vin.size(); uint64_t summary_inputs_money = 0; - //fill inputs NLSAG + //fill inputs NLSAG and Zarcanum for (const tx_source_entry& src_entr : NLSAG_sources) { in_contexts.push_back(input_generation_context_data()); @@ -1528,82 +1532,27 @@ namespace currency input_to_key.key_offsets.push_back(out_entry.first); input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); - tx.vin.push_back(in_v); + + //TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything) + if (src_entr.is_zarcanum()) + { + zarcanum_input zc_in = AUTO_VAL_INIT(zc_in); + zc_in.k_image = img; + zc_in.key_offsets = input_to_key.key_offsets; + ins_zc.elements.push_back(zc_in); + }else + { + tx.vin.push_back(in_v); + } } } - //fill inputs Zarcanum - if (zc_sources.size()) + if (ins_zc.elements.size()) { - txin_zarcanum_inputs ins_zc = AUTO_VAL_INIT(ins_zc); - - - for (const tx_source_entry& src_entr : zc_sources) - { - - - - //regular to key out - keypair& in_ephemeral = in_contexts.back().in_ephemeral; - //txin_to_key - if (src_entr.real_output >= src_entr.outputs.size()) - { - LOG_ERROR("real_output index (" << src_entr.real_output << ") greater than or equal to output_keys.size()=" << src_entr.outputs.size()); - return false; - } - summary_inputs_money += src_entr.amount; - - //key_derivation recv_derivation; - crypto::key_image img; - if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; - - //check that derivated key is equal with real output key - if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second)) - { - LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:" - << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second)); - return false; - } - - //put key image into tx input - txin_v in_v; - txin_to_key* ptokey = nullptr; - if (src_entr.htlc_origin.size()) - { - //add txin_htlc - txin_htlc in_htlc = AUTO_VAL_INIT(in_htlc); - in_htlc.hltc_origin = src_entr.htlc_origin; - in_v = in_htlc; - txin_htlc& in_v_ref = boost::get(in_v); - ptokey = static_cast(&in_v_ref); - } - else - { - in_v = txin_to_key(); - txin_to_key& in_v_ref = boost::get(in_v); - ptokey = &in_v_ref; - } - txin_to_key& input_to_key = *ptokey; - - - input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; - - //fill outputs array and use relative offsets - BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.first); - - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); - tx.vin.push_back(in_v); - } + tx.vin.push_back(ins_zc); } - - - // "Shuffle" outs std::vector shuffled_dsts(destinations); if (shuffle) @@ -1664,13 +1613,7 @@ namespace currency tx.attachment.push_back(range_proofs); } - if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)) - { - //take hash from attachment and put into extra - if (tx.attachment.size()) - add_attachments_info_to_extra(tx.extra, tx.attachment); - } - else + if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) { // for separately signed tx each input has to contain information about corresponding outputs, extra entries and attachments for (size_t in_index = input_starter_index; in_index != tx.vin.size(); in_index++) @@ -1680,11 +1623,18 @@ namespace currency so.n_outs = tx.vout.size(); so.n_extras = tx.extra.size(); get_txin_etc_options(tx.vin[in_index]).push_back(so); - + // put attachment extra info to each input's details (in case there are attachments) add_attachments_info_to_extra(get_txin_etc_options(tx.vin[in_index]), tx.attachment); } } + else + { + //take hash from attachment and put into extra + if (tx.attachment.size()) + add_attachments_info_to_extra(tx.extra, tx.attachment); + } + //generate ring signatures crypto::hash tx_prefix_hash; From 915bdd1efbb2d2334a9293cd7b1c4143d76851ba Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sun, 10 Jul 2022 20:11:44 +0200 Subject: [PATCH 13/13] hidden amounts in wallet: construct_tx deep refactoring(first version, likely with bugs) --- src/currency_core/currency_format_utils.cpp | 222 ++++++++++---------- 1 file changed, 109 insertions(+), 113 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 51fe6937..8f5bc4f4 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1319,6 +1319,99 @@ namespace currency keypair in_ephemeral; //std::vector participants_derived_keys; }; + //-------------------------------------------------------------------------------- + bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) + { + return true; + } + //-------------------------------------------------------------------------------- + bool generate_zc_sig(const std::vector& sources, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys) + { + //TODO: sender_account_keys is not used? + tx.signatures.push_back(zarcanum_sig()); + CHECK_AND_ASSERT_THROW_MES(tx.vin.back().type() == typeid(txin_zarcanum_inputs), "Unexpected input type in generate_zc_sig"); + crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, tx.vin.size() - 1, tx_prefix_hash); + CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); + + return generate_zarcanum_signature(tx_hash_for_signature, sources, boost::get(tx.vin.back()), boost::get(tx.signatures.back())); + } + //-------------------------------------------------------------------------------- + bool generate_NLSAG_sig(const std::vector& sources, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) + { + bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; + size_t input_index = input_starter_index; + size_t in_context_index = 0; + BOOST_FOREACH(const tx_source_entry* src_entr_ptr, sources) + { + const tx_source_entry& src_entr = *src_entr_ptr; + crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash); + CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); + + tx.signatures.push_back(NLSAG_sig()); + std::vector& sigs = boost::get(tx.signatures.back()).s; + + if (src_entr.is_multisig()) + { + // txin_multisig -- don't sign anything here (see also sign_multisig_input_in_tx()) + sigs.resize(src_entr.ms_keys_count, null_sig); // just reserve keys.size() null signatures (NOTE: not minimum_sigs!) + } + else + { + // regular txin_to_key or htlc + ss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL; + std::vector keys_ptrs; + BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + { + keys_ptrs.push_back(&o.second); + ss_ring_s << o.second << ENDL; + } + sigs.resize(src_entr.outputs.size()); + + if (!watch_only_mode) + 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; + } + if (src_entr.separately_signed_tx_complete) + { + // if separately signed tx is complete, put one more signature to the last bunch using tx secret key, which confirms that transaction has been generated by authorized subject + CHECK_AND_ASSERT_MES(input_index == tx.vin.size() - 1, false, "separately_signed_tx_complete flag is set for source entry #" << input_index << ", allowed only for the last one"); + CHECK_AND_ASSERT_MES(get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE, false, "sorce entry separately_signed_tx_complete flag is set for tx with no TX_FLAG_SIGNATURE_MODE_SEPARATE flag"); + CHECK_AND_ASSERT_MES(tx_hash_for_signature == tx_prefix_hash, false, "internal error: hash_for_sign for the last input of separately signed complete tx expected to be the same as tx prefix hash"); + sigs.resize(sigs.size() + 1); + crypto::generate_signature(tx_prefix_hash, txkey.pub, txkey.sec, sigs.back()); + } + + input_index++; + in_context_index++; + } + return true; + } + //-------------------------------------------------------------------------------- + bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, + const std::vector& vouts, zarcanum_outs_range_proof& result) + { + //TODO: review for Andre + CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); + + std::vector commitments_1div8; + for (size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i) + { + const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected + const crypto::public_key* p = &toz.amount_commitment; + commitments_1div8.push_back(p); + } + + uint8_t err = 0; + bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err); + CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err); + + return true; + } bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& ftp, finalized_tx& result) { @@ -1403,20 +1496,9 @@ namespace currency } // //first: separate zarcanum inputs and regular one -// const std::vector zc_sources; -// const std::vector NLSAG_sources; -// -// BOOST_FOREACH(const tx_source_entry& src_entr, sources) -// { -// if (src_entr.is_zarcanum()) -// { -// zc_sources.push_back(src_entr); -// } -// else -// { -// NLSAG_sources.push_back(src_entr); -// } -// } + std::vector zc_sources; + std::vector NLSAG_sources; + std::vector in_contexts; @@ -1427,7 +1509,7 @@ namespace currency size_t input_starter_index = tx.vin.size(); uint64_t summary_inputs_money = 0; //fill inputs NLSAG and Zarcanum - for (const tx_source_entry& src_entr : NLSAG_sources) + for (const tx_source_entry& src_entr : sources) { in_contexts.push_back(input_generation_context_data()); if(src_entr.is_multisig()) @@ -1534,15 +1616,18 @@ namespace currency input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); //TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything) + //potentially this approach might help to support htlc and multisig without making to complicated code if (src_entr.is_zarcanum()) { zarcanum_input zc_in = AUTO_VAL_INIT(zc_in); zc_in.k_image = img; zc_in.key_offsets = input_to_key.key_offsets; ins_zc.elements.push_back(zc_in); + zc_sources.push_back(&src_entr); }else { tx.vin.push_back(in_v); + NLSAG_sources.push_back(&src_entr); } } @@ -1563,7 +1648,7 @@ namespace currency size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 uint64_t range_proof_start_index = output_index; std::set deriv_cache; - crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation + crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret binging masks for each output. For range proof generation crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For rnage proof generation for(const tx_destination_entry& dst_entr : shuffled_dsts) { @@ -1586,7 +1671,6 @@ namespace currency } - //process offers and put there offers derived keys uint64_t att_count = 0; for (auto& o : tx.attachment) @@ -1641,86 +1725,25 @@ namespace currency get_transaction_prefix_hash(tx, tx_prefix_hash); - std::stringstream ss_ring_s; - if (tx.version <= TRANSACTION_VERSION_PRE_HF4) + if (NLSAG_sources.size()) { - bool r = generate_NLSAG_sig(ftp, input_starter_index, tx, tx_prefix_hash, sender_account_keys, in_contexts, txkey, ss_ring_s); + bool r = generate_NLSAG_sig(NLSAG_sources, input_starter_index, tx, tx_prefix_hash, sender_account_keys, in_contexts, txkey, ss_ring_s); CHECK_AND_ASSERT_MES(r, false, "Failed to generate_NLSAG_sig()"); - }else - { - bool r = generate_hybrid_sig(); } + if (zc_sources.size()) + { + generate_zc_sig(zc_sources, tx, tx_prefix_hash, sender_account_keys); + } + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); return true; } - bool generate_hybrid_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) - { - const std::vector& sources = ftp.sources; - bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; - size_t input_index = input_starter_index; - size_t in_context_index = 0; - - bool r = generate_zarcanum_signature(tx_prefix_hash, zc_sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) - - } - bool generate_NLSAG_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) - { - const std::vector& sources = ftp.sources; - bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; - size_t input_index = input_starter_index; - size_t in_context_index = 0; - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { - crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash); - CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); - - tx.signatures.push_back(NLSAG_sig()); - std::vector& sigs = boost::get(tx.signatures.back()).s; - - if (src_entr.is_multisig()) - { - // txin_multisig -- don't sign anything here (see also sign_multisig_input_in_tx()) - sigs.resize(src_entr.ms_keys_count, null_sig); // just reserve keys.size() null signatures (NOTE: not minimum_sigs!) - } - else - { - // regular txin_to_key or htlc - ss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL; - std::vector keys_ptrs; - BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) - { - keys_ptrs.push_back(&o.second); - ss_ring_s << o.second << ENDL; - } - sigs.resize(src_entr.outputs.size()); - - if (!watch_only_mode) - 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; - } - if (src_entr.separately_signed_tx_complete) - { - // if separately signed tx is complete, put one more signature to the last bunch using tx secret key, which confirms that transaction has been generated by authorized subject - CHECK_AND_ASSERT_MES(input_index == tx.vin.size() - 1, false, "separately_signed_tx_complete flag is set for source entry #" << input_index << ", allowed only for the last one"); - CHECK_AND_ASSERT_MES(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE, false, "sorce entry separately_signed_tx_complete flag is set for tx with no TX_FLAG_SIGNATURE_MODE_SEPARATE flag"); - CHECK_AND_ASSERT_MES(tx_hash_for_signature == tx_prefix_hash, false, "internal error: hash_for_sign for the last input of separately signed complete tx expected to be the same as tx prefix hash"); - sigs.resize(sigs.size() + 1); - crypto::generate_signature(tx_prefix_hash, txkey.pub, txkey.sec, sigs.back()); - } - - input_index++; - in_context_index++; - } - return true; - } //--------------------------------------------------------------- uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd) { @@ -3530,29 +3553,7 @@ namespace currency //@#@ TODO return false; } - //-------------------------------------------------------------------------------- - bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, - const std::vector& vouts, zarcanum_outs_range_proof& result) - { - //TODO: review for Andre - CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); - CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); - CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); - std::vector commitments_1div8; - for(size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i) - { - const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected - const crypto::public_key* p = &toz.amount_commitment; - commitments_1div8.push_back(p); - } - - uint8_t err = 0; - bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err); - CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err); - - return true; - } //-------------------------------------------------------------------------------- struct zarcanum_outs_range_proof_commit_ref_t { @@ -3577,11 +3578,6 @@ namespace currency return true; } //-------------------------------------------------------------------------------- - bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) - { - return true; - } - //-------------------------------------------------------------------------------- boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,