diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index ad7b4ea4..88082943 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -5118,10 +5118,13 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std:: bool blockchain_storage::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 { CRITICAL_REGION_LOCAL(m_read_lock); - bool res = check_tx_inputs(tx, tx_prefix_hash, max_used_block_height); - if(!res) return false; - CHECK_AND_ASSERT_MES(max_used_block_height < m_db_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less than blockchain size = " << m_db_blocks.size()); - get_block_hash(m_db_blocks[max_used_block_height]->bl, max_used_block_id); + check_tx_inputs_context ctic{}; + ctic.calculate_max_used_block_id = true; + bool res = check_tx_inputs(tx, tx_prefix_hash, ctic); + if (!res) + return false; + max_used_block_height = ctic.max_used_block_height; + max_used_block_id = ctic.max_used_block_id; return true; } //------------------------------------------------------------------ @@ -5397,15 +5400,16 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const //------------------------------------------------------------------ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const { - uint64_t stub = 0; - return check_tx_inputs(tx, tx_prefix_hash, stub); + check_tx_inputs_context ctic{}; + return check_tx_inputs(tx, tx_prefix_hash, ctic); } //------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height) const +bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, check_tx_inputs_context& ctic) const { + CRITICAL_REGION_LOCAL(m_read_lock); + size_t sig_index = 0; - max_used_block_height = 0; - bool all_tx_ins_have_explicit_native_asset_ids = true; + ctic.max_used_block_height = 0; auto local_check_key_image = [&](const crypto::key_image& ki) -> bool { @@ -5430,7 +5434,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha return false; uint64_t max_unlock_time = 0; - if (!check_tx_input(tx, sig_index, in_to_key, tx_prefix_hash, max_used_block_height, max_unlock_time)) + if (!check_tx_input(tx, sig_index, in_to_key, tx_prefix_hash, ctic.max_used_block_height, max_unlock_time)) { LOG_ERROR("Failed to validate input #" << sig_index << " tx: " << tx_prefix_hash); return false; @@ -5438,7 +5442,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha } VARIANT_CASE_CONST(txin_multisig, in_ms) { - if (!check_tx_input(tx, sig_index, in_ms, tx_prefix_hash, max_used_block_height)) + if (!check_tx_input(tx, sig_index, in_ms, tx_prefix_hash, ctic.max_used_block_height)) { LOG_ERROR("Failed to validate multisig input #" << sig_index << " (ms out id: " << in_ms.multisig_out_id << ") in tx: " << tx_prefix_hash); return false; @@ -5450,7 +5454,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha if (!local_check_key_image(in_htlc.k_image)) return false; - if (!check_tx_input(tx, sig_index, in_htlc, tx_prefix_hash, max_used_block_height)) + if (!check_tx_input(tx, sig_index, in_htlc, tx_prefix_hash, ctic.max_used_block_height)) { LOG_ERROR("Failed to validate htlc input #" << sig_index << " in tx: " << tx_prefix_hash << ", htlc json: " << ENDL << obj_to_json_str(in_htlc)); return false; @@ -5461,7 +5465,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, all_tx_ins_have_explicit_native_asset_ids)) + if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, ctic)) { LOG_ERROR("Failed to validate zc input #" << sig_index << " in tx: " << tx_prefix_hash); return false; @@ -5484,9 +5488,16 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha 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_native_asset_ids), false, "tx does not comply with explicit asset id rules"); + CHECK_AND_ASSERT_MES(check_tx_explicit_asset_id_rules(tx, ctic.all_tx_ins_have_explicit_native_asset_ids), false, "tx does not comply with explicit asset id rules"); } TIME_MEASURE_FINISH_PD(tx_check_inputs_attachment_check); + + if (ctic.calculate_max_used_block_id) + { + CHECK_AND_ASSERT_MES(ctic.max_used_block_height < m_db_blocks.size(), false, "internal error: max used block index=" << ctic.max_used_block_height << " is not less than blockchain size = " << m_db_blocks.size()); + get_block_hash(m_db_blocks[ctic.max_used_block_height]->bl, ctic.max_used_block_id); + } + return true; } //------------------------------------------------------------------ @@ -5620,7 +5631,7 @@ struct outputs_visitor }; //------------------------------------------------------------------ -// Checks each referenced output for: +// Checks each referenced output against the current blockchain state for: // 1) source tx unlock time validity // 2) mixin restrictions // 3) general gindex/ref_by_id corectness @@ -5908,8 +5919,7 @@ 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, bool& all_tx_ins_have_explicit_native_asset_ids) 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, check_tx_inputs_context& ctic) const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -5919,9 +5929,10 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, // std::vector dummy_output_keys; // won't be used uint64_t dummy_source_max_unlock_time_for_pos_coinbase_dummy = 0; // won't be used - scan_for_keys_context scan_contex = AUTO_VAL_INIT(scan_contex); + scan_for_keys_context scan_contex{}; + scan_contex.check_hf4_coinage_rule = ctic.check_hf4_coinage_rule; - if (!get_output_keys_for_input_with_checks(tx, zc_in, dummy_output_keys, max_related_block_height, dummy_source_max_unlock_time_for_pos_coinbase_dummy, scan_contex)) + if (!get_output_keys_for_input_with_checks(tx, zc_in, dummy_output_keys, ctic.max_used_block_height, dummy_source_max_unlock_time_for_pos_coinbase_dummy, scan_contex)) { LOG_PRINT_L0("get_output_keys_for_input_with_checks failed for input #" << in_index << ", key_offset.size = " << zc_in.key_offsets.size() << ")"); return false; @@ -5942,8 +5953,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, 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_native_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id) - all_tx_ins_have_explicit_native_asset_ids = false; + if (ctic.all_tx_ins_have_explicit_native_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id) + ctic.all_tx_ins_have_explicit_native_asset_ids = false; } // calculate corresponding tx prefix hash diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 075bc789..65dbc07a 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -150,9 +150,19 @@ namespace currency struct scan_for_keys_context { - bool htlc_is_expired; - std::list htlc_outs; - std::list zc_outs; + bool check_hf4_coinage_rule = true; // input + bool htlc_is_expired; // output + std::list htlc_outs; // output, legacy + std::list zc_outs; // output + }; + + struct check_tx_inputs_context + { + bool check_hf4_coinage_rule = true; // input + bool calculate_max_used_block_id = false; // input + crypto::hash max_used_block_id{}; // output + uint64_t max_used_block_height = 0; // output + bool all_tx_ins_have_explicit_native_asset_ids = true; // output }; // == Output indexes local lookup table conception == @@ -316,8 +326,8 @@ 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, bool& all_tx_ins_have_explicit_native_asset_ids) const; - bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_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, check_tx_inputs_context& ctic) const; + bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, check_tx_inputs_context& ctic)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; bool check_ms_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const transaction& source_tx, size_t out_n) const; @@ -927,7 +937,7 @@ namespace currency ++output_index; } - if (m_core_runtime_config.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, this->get_current_blockchain_size())) + if (scan_context.check_hf4_coinage_rule && m_core_runtime_config.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, this->get_current_blockchain_size())) { //with hard fork 4 make it network rule to have at least 10 confirmations diff --git a/src/currency_core/tx_pool.cpp b/src/currency_core/tx_pool.cpp index b99ba859..ed2edf08 100644 --- a/src/currency_core/tx_pool.cpp +++ b/src/currency_core/tx_pool.cpp @@ -93,7 +93,7 @@ namespace currency return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::check_tx_fee(const transaction &tx, uint64_t amount_fee) + bool tx_memory_pool::check_tx_fee(const transaction &tx, uint64_t amount_fee) const { if (amount_fee < m_blockchain.get_core_runtime_config().tx_pool_min_fee) return false; @@ -227,9 +227,10 @@ namespace currency TIME_MEASURE_FINISH_PD(check_keyimages_ws_ms_time); TIME_MEASURE_START_PD(check_inputs_time); - crypto::hash max_used_block_id = null_hash; - uint64_t max_used_block_height = 0; - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, id, max_used_block_height, max_used_block_id); + blockchain_storage::check_tx_inputs_context ctic{}; + ctic.calculate_max_used_block_id = true; + ctic.check_hf4_coinage_rule = false; + bool ch_inp_res = m_blockchain.check_tx_inputs(tx, id, ctic); if (!ch_inp_res && !kept_by_block && !from_core) { LOG_PRINT_L0("check_tx_inputs failed, tx rejected"); @@ -255,7 +256,7 @@ namespace currency } } - do_insert_transaction(tx, id, blob_size, kept_by_block, tx_fee, ch_inp_res ? max_used_block_id : null_hash, ch_inp_res ? max_used_block_height : 0); + do_insert_transaction(tx, id, blob_size, kept_by_block, tx_fee, ch_inp_res ? ctic.max_used_block_id : null_hash, ch_inp_res ? ctic.max_used_block_height : 0); TIME_MEASURE_FINISH_PD(tx_processing_time); tvc.m_added_to_pool = true; @@ -935,12 +936,16 @@ namespace currency if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_size() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) return false;//we already sure that this tx is broken for this height - if(!m_blockchain.check_tx_inputs(txd.tx, id, txd.max_used_block_height, txd.max_used_block_id)) + blockchain_storage::check_tx_inputs_context ctic{}; + ctic.check_hf4_coinage_rule = false; + if(!m_blockchain.check_tx_inputs(txd.tx, id, ctic)) { txd.last_failed_height = m_blockchain.get_top_block_height(); txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); return false; } + txd.max_used_block_height = ctic.max_used_block_height; + txd.max_used_block_id = ctic.max_used_block_id; } else { @@ -952,12 +957,16 @@ namespace currency if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) return false; //check ring signature again, it is possible (with very small chance) that this transaction become again valid - if(!m_blockchain.check_tx_inputs(txd.tx, id, txd.max_used_block_height, txd.max_used_block_id)) + blockchain_storage::check_tx_inputs_context ctic{}; + ctic.check_hf4_coinage_rule = false; + if(!m_blockchain.check_tx_inputs(txd.tx, id, ctic)) { txd.last_failed_height = m_blockchain.get_top_block_height(); txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); return false; } + txd.max_used_block_height = ctic.max_used_block_height; + txd.max_used_block_id = ctic.max_used_block_id; } } diff --git a/src/currency_core/tx_pool.h b/src/currency_core/tx_pool.h index e573d2b5..321f0c48 100644 --- a/src/currency_core/tx_pool.h +++ b/src/currency_core/tx_pool.h @@ -154,7 +154,7 @@ namespace currency bool remove_key_images(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block); bool insert_alias_info(const transaction& tx); bool remove_alias_info(const transaction& tx); - bool check_tx_fee(const transaction &tx, uint64_t amount_fee); + bool check_tx_fee(const transaction &tx, uint64_t amount_fee) const; std::string get_blacklisted_txs_string() const; bool is_valid_contract_finalization_tx(const transaction &tx)const; diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index c11157bc..91274083 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -2568,10 +2568,9 @@ bool input_refers_to_incompatible_by_type_output::assert_zc_input_refers_bare_ou { bool all_inputs_have_explicit_native_asset_id{}; - uint64_t max_related_block_height{}; + blockchain_storage::check_tx_inputs_context ctic{}; - CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), max_related_block_height, - all_inputs_have_explicit_native_asset_id), false); + CHECK_AND_ASSERT_EQ(c.get_blockchain_storage().check_tx_input(tx, 0, boost::get(tx.vin.front()), get_transaction_hash(tx), ctic), false); } return true;