1
0
Fork 0
forked from lthn/blockchain

core: HF4 coinage rule is not checked while adding a tx to the pool, letting it being confirmed later

This commit is contained in:
sowle 2025-06-09 05:43:39 +02:00
parent 2a13a63eb7
commit 2bb1113b43
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
5 changed files with 67 additions and 38 deletions

View file

@ -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<crypto::public_key> 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

View file

@ -150,9 +150,19 @@ namespace currency
struct scan_for_keys_context
{
bool htlc_is_expired;
std::list<txout_htlc> htlc_outs;
std::list<tx_out_zarcanum> zc_outs;
bool check_hf4_coinage_rule = true; // input
bool htlc_is_expired; // output
std::list<txout_htlc> htlc_outs; // output, legacy
std::list<tx_out_zarcanum> 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

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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<const txin_zc_input>(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<const txin_zc_input>(tx.vin.front()), get_transaction_hash(tx), ctic), false);
}
return true;