From f9b54f305a0d3a44470643e45eae2b4291436544 Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 21 Feb 2023 01:41:33 +0100 Subject: [PATCH] ca: wallet refactoring to support confidential assets (WIP) --- src/currency_core/currency_format_utils.h | 2 + .../currency_format_utils_transactions.cpp | 2 +- .../currency_format_utils_transactions.h | 2 +- src/wallet/wallet2.cpp | 87 ++++++++++++------- src/wallet/wallet2.h | 23 +++-- src/wallet/wallet_errors.h | 2 +- 6 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 49ff7c3f..302d348f 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -218,6 +218,8 @@ namespace currency crypto::scalar_t amount_blinding_mask = 0; crypto::scalar_t asset_id_blinding_mask = 0; crypto::public_key asset_id = currency::native_coin_asset_id; // use point_t instead as this is for internal use only? + + bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; } }; diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index c0e06a3b..6ab5de36 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -51,7 +51,7 @@ namespace currency res += o.amount; } VARIANT_CASE_CONST(tx_out_zarcanum, o) - //@#@ + //@#@# TODO obtain info about public burn of native coins in ZC outputs VARIANT_CASE_THROW_ON_OTHER(); VARIANT_SWITCH_END(); } diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 2e041d3b..6bed1af2 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -60,7 +60,7 @@ namespace currency crypto::public_key asset_id = currency::native_coin_asset_id; //asset id (not blinded, not premultiplied by 1/8) TODO @#@# consider changing to crypto::point_t bool is_multisig() const { return ms_sigs_count > 0; } - bool is_zarcanum() const { return !real_out_amount_blinding_mask.is_zero(); } + bool is_zc() const { return !real_out_amount_blinding_mask.is_zero(); } bool is_native_coin() const { return asset_id == currency::native_coin_asset_id; } BEGIN_SERIALIZE_OBJECT() diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6bffdc4a..05350e89 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -499,7 +499,7 @@ 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; // TODO: @#@# correctly calculate tx_money_got_in_outs for post-HF4 + uint64_t sum_of_native_outs = 0; // TODO: @#@# correctly calculate tx_money_got_in_outs for post-HF4 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); @@ -507,10 +507,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t //check for transaction income crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); std::list htlc_info_list; - r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs, derivation, htlc_info_list); + r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, sum_of_native_outs, derivation, htlc_info_list); THROW_IF_TRUE_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); - if(!outs.empty() /*&& tx_money_got_in_outs*/) + if (!outs.empty()) { //good news - got money! take care about it //usually we have only one transfer for user in transaction @@ -545,16 +545,17 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t { const wallet_out_info& out = outs[i_in_outs]; size_t o = out.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()); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(o < tx.vout.size(), "wrong out in transaction: internal index: " << o << ", tx.vout.size(): " << tx.vout.size()); { const currency::tx_out_v& out_v = tx.vout[o]; + bool out_type_zc = out_is_zc(out_v); + bool out_type_to_key = out_is_to_key(out_v); + bool out_type_htlc = out_is_to_htlc(out_v); + bool out_type_multisig = out_is_multisig(out_v); - bool out_type_zc = out_is_zc(out_v); - bool out_type_to_key = out_is_to_key(out_v); - if (out_type_zc || out_type_to_key || out_is_to_htlc(out_v)) + if (out_type_zc || out_type_to_key || out_type_htlc) { - crypto::public_key out_key = out_get_pub_key(out_v, htlc_info_list); - //const currency::txout_to_key& otk = boost::get(out.target); + crypto::public_key out_key = out_get_pub_key(out_v, htlc_info_list); // htlc_info_list contains information about which one, redeem or refund key is ours for an htlc output // obtain key image for this output crypto::key_image ki = currency::null_ki; @@ -600,8 +601,11 @@ 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 >= 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; + if (out.is_native_coin()) + { + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(sum_of_native_outs >= out.amount, "sum_of_native_outs: " << sum_of_native_outs << ", out.amount:" << out.amount); + sum_of_native_outs -= out.amount; + } continue; // skip the output } } @@ -611,11 +615,15 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t { std::stringstream ss; 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."; + << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)out_get_mixin_attr(out_v) << ". Output is 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; + if (out.is_native_coin()) + { + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(sum_of_native_outs >= out.amount, "sum_of_native_outs: " << sum_of_native_outs << ", out.amount:" << out.amount); + sum_of_native_outs -= out.amount; + } continue; // skip the output } @@ -643,7 +651,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t if (ptc.coin_base_tx) { //last out in coinbase tx supposed to be change from coinstake - if (!(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase)) + if (!(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase)) // TODO: @#@# reconsider this condition { td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER; } @@ -655,7 +663,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)) + if (out_type_htlc) { const currency::txout_htlc& hltc = out_get_htlc(out_v); //mark this as spent @@ -702,8 +710,8 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t 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(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"); + auto amount_gindex_pair = std::make_pair(td.amount_for_global_output_index(), 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_for_global_output_index: " << td.amount_for_global_output_index() << ", gindex: " << td.m_global_output_index << " already exists"); m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index; } @@ -712,14 +720,22 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t if (out_type_to_key || out_type_zc) { - WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money_brief(td.amount()) << (out_type_zc ? " (hidden)" : "") << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); + if (td.is_native_coin()) + { + WLT_LOG_L0("Received native coins, transfer #" << transfer_index << ", amount: " << print_money_brief(td.amount()) << (out_type_zc ? " (hidden)" : "") << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); + } + else + { + // TODO @#@# output asset's ticker/name + WLT_LOG_L0("Received asset " << print16(td.get_asset_id()) << ", transfer #" << transfer_index << ", amount: " << print_money_brief(td.amount()) << (out_type_zc ? " (hidden)" : "") << ", with tx: " << get_transaction_hash(tx) << ", at height " << height); + } } - else if (out_is_to_htlc(out_v)) + else if (out_type_htlc) { 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_is_multisig(out_v)) + else if (out_type_multisig) { 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"); @@ -729,15 +745,22 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t tdb.m_amount = outs[i_in_outs].amount; WLT_LOG_L0("Received multisig, multisig out id: " << multisig_id << ", amount: " << tdb.amount() << ", with tx: " << get_transaction_hash(tx)); } + else + { + WLT_LOG_YELLOW("Unexpected output type: " << out_v.type().name() << ", out index: " << o << " in tx " << get_transaction_hash(tx), LOG_LEVEL_0); + } } } } std::string payment_id; - if (tx_money_got_in_outs && get_payment_id_from_tx(tx.attachment, payment_id)) + if (sum_of_native_outs != 0 && get_payment_id_from_tx(tx.attachment, payment_id)) { - 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; + // TODO @#@# this code takes care only of native coins + // we need to add assets support + + uint64_t received = (ptc.sum_of_own_native_inputs < sum_of_native_outs) ? sum_of_native_outs - ptc.sum_of_own_native_inputs : 0; if (0 < received && payment_id.size()) { payment_details payment; @@ -750,7 +773,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t } } - if (ptc.tx_money_spent_in_ins) + if (ptc.sum_of_own_native_inputs) { //check if there are asset_registration that belong to this wallet asset_descriptor_operation ado = AUTO_VAL_INIT(ado); @@ -798,25 +821,27 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t } } - if (ptc.tx_money_spent_in_ins) + if (ptc.sum_of_own_native_inputs) {//this actually is transfer transaction, notify about spend - if (ptc.tx_money_spent_in_ins > tx_money_got_in_outs) + if (ptc.sum_of_own_native_inputs > sum_of_native_outs) {//usual transfer - 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); + handle_money_spent2(b, tx, ptc.sum_of_own_native_inputs - (sum_of_native_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: " << ptc.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) << ", sum_of_native_inputs: " << ptc.sum_of_own_native_inputs << ", sum_of_native_outs: " << sum_of_native_outs, LOG_LEVEL_0); } - handle_money_received2(b, tx, (tx_money_got_in_outs - (ptc.tx_money_spent_in_ins - get_tx_fee(tx))), ptc.mtd); + handle_money_received2(b, tx, (sum_of_native_outs - (ptc.sum_of_own_native_inputs - get_tx_fee(tx))), ptc.mtd); } } else { - if(tx_money_got_in_outs) - handle_money_received2(b, tx, tx_money_got_in_outs, ptc.mtd); + if (sum_of_native_outs != 0) + { + handle_money_received2(b, tx, sum_of_native_outs, ptc.mtd); + } else if (currency::is_derivation_used_to_encrypt(tx, derivation)) { //transaction doesn't transfer actually money, bud bring some information @@ -5777,7 +5802,7 @@ void wallet2::print_source_entry(std::stringstream& output, const currency::tx_s for(auto& el : src.outputs) ss << el.out_reference << " "; - output << "amount: " << print_money_brief(src.amount) << (src.is_zarcanum() ? " (hidden)" : "") + output << "amount: " << print_money_brief(src.amount) << (src.is_zc() ? " (hidden)" : "") << ", real_output: " << src.real_output << ", real_output_in_tx_index: " << src.real_output_in_tx_index << ", indexes: " << ss.str(); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c73eaad4..d54a0ca2 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -364,7 +364,8 @@ namespace tools boost::shared_ptr m_zc_info_ptr; uint64_t amount() const { return m_amount; } - + uint64_t amount_for_global_output_index() const { return is_zc() ? 0 : m_amount; } // amount value for global outputs index, it's zero for outputs with hidden amounts + // @#@ 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 //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]); } @@ -377,6 +378,7 @@ namespace tools bool is_reserved_for_escrow() const { return ( (m_flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION) != 0 ); } bool is_zc() const { return m_zc_info_ptr.get(); } const crypto::public_key& get_asset_id() const { if (m_zc_info_ptr.get()) { return m_zc_info_ptr->asset_id; } else { return currency::native_coin_asset_id; } } + bool is_native_coin() const { return m_zc_info_ptr.get() ? (m_zc_info_ptr->asset_id == currency::native_coin_asset_id) : true; } BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CUSTOM(m_ptx_wallet_info, const transaction_wallet_info&, tools::wallet2::transform_ptr_to_value, tools::wallet2::transform_value_to_ptr) @@ -504,7 +506,7 @@ namespace tools struct process_transaction_context { - uint64_t tx_money_spent_in_ins = 0; + uint64_t sum_of_own_native_inputs = 0; // old-fashioned bare inputs or ZC inputs referring to native coin asset_id // check all outputs for spending (compare key images) money_transfer2_details mtd; bool is_pos_coinbase = false; @@ -1330,7 +1332,8 @@ namespace tools if (tr_index != UINT64_MAX) { transfer_details& td = m_transfers[tr_index]; - ptc.tx_money_spent_in_ins += td.amount(); + if (td.is_native_coin()) + ptc.sum_of_own_native_inputs += td.m_amount; uint32_t flags_before = td.m_flags; td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; td.m_spent_height = ptc.height; @@ -1338,8 +1341,18 @@ namespace tools ptc.is_derived_from_coinbase = true; else ptc.is_derived_from_coinbase = false; - WLT_LOG_L0("Spent key out, transfer #" << tr_index << ", amount: " << currency::print_money_brief(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << ptc.height << - "; flags: " << flags_before << " -> " << td.m_flags); + + if (td.is_native_coin()) + { + WLT_LOG_L0("Spent native coins, transfer #" << tr_index << ", amount: " << currency::print_money_brief(td.amount()) << (td.is_zc() ? " (hidden), with tx: " : ", with tx: ") << get_transaction_hash(tx) << ", at height " << ptc.height << + "; flags: " << flags_before << " -> " << td.m_flags); + } + else + { + WLT_LOG_L0("Spent asset " << print16(td.get_asset_id()) << " , transfer #" << tr_index << ", amount: " << currency::print_money_brief(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); } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 69c961b8..3d118c92 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -444,7 +444,7 @@ namespace tools const currency::tx_source_entry& src = m_sources[i]; ss << "\n " << i << ": "; ss << " amount: " << std::setw(21) << currency::print_money(src.amount); - ss << (src.is_zarcanum() ? " ZC " : " old "); + ss << (src.is_zc() ? " ZC " : " old "); ss << " asset_id: " << src.asset_id; for (size_t j = 0; j < src.outputs.size(); ++j) {