From 447b09f400e9b05cab12df1a1d9bfe02b29b5042 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 7 Apr 2023 06:04:00 +0200 Subject: [PATCH] check_tx_explicit_asset_id_rules(), is_asset_emitting_transaction() --- src/currency_core/blockchain_storage.cpp | 50 ++++++++++++++++--- src/currency_core/blockchain_storage.h | 2 +- .../currency_format_utils_transactions.cpp | 19 +++++++ .../currency_format_utils_transactions.h | 1 + 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 53d37580..be011871 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4425,6 +4425,34 @@ bool blockchain_storage::print_tx_outputs_lookup(const crypto::hash& tx_id)const return true; } //------------------------------------------------------------------ +bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_have_explicit_asset_ids) +{ + if (tx.version <= TRANSACTION_VERSION_PRE_HF4) + return true; + + // ( assuming that post-HF4 txs can only have tx_out_zarcanum outs ) + + bool r = false; + // if all tx inputs have explicit asset id AND it does not emit a new asset THEN all outputs must have explicit asset id (native coin) + if (all_tx_ins_have_explicit_asset_ids && !is_asset_emitting_transaction(tx)) + { + for(size_t j = 0, k = tx.vout.size(); j < k; ++j) + { + r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() == native_coin_asset_id; + CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has a non-explicit asset id"); + } + } + else // otherwise all outputs must have hidden asset id + { + for(size_t j = 0, k = tx.vout.size(); j < k; ++j) + { + r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id; + CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has an explicit asset id"); + } + } + return true; +} +//------------------------------------------------------------------ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const { // check all tx's inputs for being already spent @@ -4465,6 +4493,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha { size_t sig_index = 0; max_used_block_height = 0; + bool all_tx_ins_have_explicit_asset_ids = true; auto local_check_key_image = [&](const crypto::key_image& ki) -> bool { @@ -4526,7 +4555,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha if (!local_check_key_image(in_zc.k_image)) return false; - if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height)) + if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height, all_tx_ins_have_explicit_asset_ids)) { LOG_ERROR("Failed to validate zc input #" << sig_index << " in tx: " << tx_prefix_hash); return false; @@ -4548,6 +4577,8 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha bool r = validate_attachment_info(tx.extra, tx.attachment, false); CHECK_AND_ASSERT_MES(r, false, "Failed to validate attachments in tx " << tx_prefix_hash << ": incorrect extra_attachment_info in tx.extra"); } + + CHECK_AND_ASSERT_MES(check_tx_explicit_asset_id_rules(tx, all_tx_ins_have_explicit_asset_ids), false, "tx does not comply with explicit asset id rules"); } TIME_MEASURE_FINISH_PD(tx_check_inputs_attachment_check); return true; @@ -4929,7 +4960,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, return check_input_signature(tx, in_index, txin.amount, txin.k_image, txin.etc_details, tx_prefix_hash, output_keys_ptrs); } //------------------------------------------------------------------ -bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height) const +bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, + uint64_t& max_related_block_height, bool& all_tx_ins_have_explicit_asset_ids) const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -4947,7 +4979,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, return false; } - // @#@ + if (m_is_in_checkpoint_zone) // TODO @#@# reconsider placement of this check + return true; // here we don't need to check zc_in.k_image validity because it is checked in verify_CLSAG_GG() @@ -4959,7 +4992,11 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, vector ring; ring.reserve(scan_contex.zc_outs.size()); for(auto& zc_out : scan_contex.zc_outs) + { ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.blinded_asset_id); + if (all_tx_ins_have_explicit_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id) + all_tx_ins_have_explicit_asset_ids = false; + } // calculate corresponding tx prefix hash crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, in_index, tx_prefix_hash); @@ -4967,12 +5004,12 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const ZC_sig& sig = boost::get(tx.signatures[in_index]); - //TIME_MEASURE_START_PD(tx_input_check_clsag_gg); + //TIME_MEASURE_START_PD(tx_input_check_clsag_ggx); bool r = crypto::verify_CLSAG_GGX(tx_hash_for_signature, ring, sig.pseudo_out_amount_commitment, sig.pseudo_out_blinded_asset_id, zc_in.k_image, sig.clsags_ggx); CHECK_AND_ASSERT_MES(r, false, "verify_CLSAG_GGX failed"); - //TIME_MEASURE_FINISH_PD(tx_input_check_clsag_gg); + //TIME_MEASURE_FINISH_PD(tx_input_check_clsag_ggx); return true; } @@ -7035,7 +7072,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, else { uint64_t max_related_block_height = 0; - r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height); + bool all_tx_ins_have_explicit_asset_ids = true; // stub for now, TODO @#@# + r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height, all_tx_ins_have_explicit_asset_ids); CHECK_AND_ASSERT_MES(r, false, "check_tx_input failed"); } VARIANT_CASE_OTHER() diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 6262a815..d7d46185 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -293,7 +293,7 @@ namespace currency bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_htlc& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; - bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height) const; + bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, bool& tx_has_explicit_asset_ids_in_all_ins) const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height)const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)const; diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 24ec80d5..e7071e0c 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -319,6 +319,25 @@ namespace currency return true; } //--------------------------------------------------------------- + bool is_asset_emitting_transaction(const transaction& tx, asset_descriptor_operation* p_ado /* = nullptr */) + { + if (tx.version <= TRANSACTION_VERSION_PRE_HF4) + return false; + + asset_descriptor_operation local_ado{}; + if (p_ado == nullptr) + p_ado = &local_ado; + + if (!get_type_in_variant_container(tx.extra, *p_ado)) + return false; + + // TODO @#@# change to ASSET_DESCRIPTOR_OPERATION_EMMIT ! + if (p_ado->operation_type != ASSET_DESCRIPTOR_OPERATION_REGISTER) + return false; + + return true; + } + //--------------------------------------------------------------- // Prepapres vector of output_entry to be used in key_offsets in a transaction input: // 1) sort all entries by gindex (while moving all ref_by_id to the end, keeping they relative order) // 2) convert absolute global indices to relative key_offsets diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 4237c0ed..a2ba6080 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -201,6 +201,7 @@ namespace currency bool tx_to_blob(const transaction& b, blobdata& b_blob); bool read_keyimages_from_tx(const transaction& tx, std::list& kil); bool validate_inputs_sorting(const transaction& tx); + bool is_asset_emitting_transaction(const transaction& tx, asset_descriptor_operation* p_ado = nullptr); std::vector prepare_outputs_entries_for_key_offsets(const std::vector& outputs, size_t old_real_index, size_t& new_real_index) noexcept;