From 6f93fbd7a051d24c2a8eb7fc03b392f112999c3b Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 5 Sep 2019 19:08:10 +0300 Subject: [PATCH] fix for PoS locking issue + enhanced naming --- src/currency_core/blockchain_storage.cpp | 43 +++++++++++-------- src/currency_core/blockchain_storage.h | 6 +-- .../currency_format_utils_transactions.h | 3 +- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 827adc65..86c16f4d 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3910,14 +3910,14 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const } //------------------------------------------------------------------ -bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const +bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const { CRITICAL_REGION_LOCAL(m_read_lock); //TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop); std::vector output_keys; - if(!get_output_keys_for_input_with_checks(tx, txin, output_keys, max_related_block_height, max_unlock_time)) + if(!get_output_keys_for_input_with_checks(tx, txin, output_keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase)) { LOG_PRINT_L0("Failed to get output keys for input #" << in_index << " (amount = " << print_money(txin.amount) << ", key_offset.size = " << txin.key_offsets.size() << ")"); return false; @@ -3936,7 +3936,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, // 1) source tx unlock time validity // 2) mixin restrictions // 3) general gindex/ref_by_id corectness -bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const +bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -3944,10 +3944,13 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction { std::vector& m_results_collector; const blockchain_storage& m_bch; - uint64_t& m_max_unlock_time; + uint64_t& m_source_max_unlock_time_for_pos_coinbase; outputs_visitor(std::vector& results_collector, const blockchain_storage& bch, - uint64_t& max_unlock_time) :m_results_collector(results_collector), m_bch(bch), m_max_unlock_time(max_unlock_time) + uint64_t& source_max_unlock_time_for_pos_coinbase) + : m_results_collector(results_collector) + , m_bch(bch) + , m_source_max_unlock_time_for_pos_coinbase(source_max_unlock_time_for_pos_coinbase) {} bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i) { @@ -3956,8 +3959,9 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction //let coinbase sources for PoS block to have locked inputs, the outputs supposed to be locked same way, except the reward if (is_coinbase(validated_tx) && is_pos_block(validated_tx)) { - if (source_out_unlock_time > m_max_unlock_time) - m_max_unlock_time = source_out_unlock_time; + CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(source_out_unlock_time), false, "source output #" << out_i << " is locked by time, not by height, which is not allowed for PoS coinbase"); + if (source_out_unlock_time > m_source_max_unlock_time_for_pos_coinbase) + m_source_max_unlock_time_for_pos_coinbase = source_out_unlock_time; } else { @@ -3968,7 +3972,6 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction } } - if(out.target.type() != typeid(txout_to_key)) { LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); @@ -3980,7 +3983,7 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction } }; - outputs_visitor vi(output_keys, *this, max_unlock_time); + outputs_visitor vi(output_keys, *this, source_max_unlock_time_for_pos_coinbase); return scan_outputkeys_for_indexes(tx, txin, vi, max_related_block_height); } //------------------------------------------------------------------ @@ -4438,13 +4441,13 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti return true; } //------------------------------------------------------------------ -bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const +bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const { uint64_t major_unlock_time = get_tx_x_detail(miner_tx); if (major_unlock_time) { //if there was etc_tx_details_unlock_time present in tx, then ignore etc_tx_details_unlock_time2 - if (major_unlock_time < max_unlock_time) + if (major_unlock_time < source_max_unlock_time) return false; else return true; @@ -4458,11 +4461,13 @@ bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transactio CHECK_AND_ASSERT_MES(ut2.unlock_time_array.size() == miner_tx.vout.size(), false, "ut2.unlock_time_array.size()<" << ut2.unlock_time_array.size() << "> != miner_tx.vout.size()<" << miner_tx.vout.size() << ">"); - uint64_t amount_of_coins_in_unlock_in_range = 0; + uint64_t amount_of_coins_in_unlock_in_range = 0; // amount of outputs locked for at least the same time for (uint64_t i = 0; i != miner_tx.vout.size(); i++) { - if (ut2.unlock_time_array[i] >= max_unlock_time) + uint64_t unlock_value = ut2.unlock_time_array[i]; + CHECK_AND_ASSERT_MES(should_unlock_value_be_treated_as_block_height(unlock_value), false, "output #" << i << " is locked by time, not buy height, which is not allowed for PoS coinbase"); + if (unlock_value >= source_max_unlock_time) amount_of_coins_in_unlock_in_range += miner_tx.vout[i].amount; } @@ -4549,22 +4554,22 @@ bool blockchain_storage::validate_pos_block(const block& b, { // Do coinstake input validation for main chain only. // Txs in alternative PoS blocks (including miner_tx) are validated by validate_alt_block_txs() - uint64_t max_unlock_time = 0; - r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], max_related_block_height, max_unlock_time); + uint64_t source_max_unlock_time_for_pos_coinbase = 0; + r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], max_related_block_height, source_max_unlock_time_for_pos_coinbase); CHECK_AND_ASSERT_MES(r, false, "Failed to validate coinstake input in miner tx, block_id = " << get_block_hash(b)); if (get_block_height(b) > m_core_runtime_config.hard_fork1_starts_after_height) { uint64_t last_pow_h = get_last_x_block_height(false); - CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to failed to validate coinbase in pos block, condition failed: max_related_block_height(" << max_related_block_height << ") < last_pow_h(" << last_pow_h << ")"); + CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to validate coinbase in PoS block, condition failed: max_related_block_height(" << max_related_block_height << ") <= last_pow_h(" << last_pow_h << ")"); //let's check that coinbase amount and unlock time - r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, max_unlock_time); + r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, source_max_unlock_time_for_pos_coinbase); CHECK_AND_ASSERT_MES(r, false, "Failed to validate_pos_coinbase_outs_unlock_time() in miner tx, block_id = " << get_block_hash(b) - << "max_unlock_time=" << max_unlock_time); + << "source_max_unlock_time_for_pos_coinbase=" << source_max_unlock_time_for_pos_coinbase); } else { - CHECK_AND_ASSERT_MES(is_tx_spendtime_unlocked(max_unlock_time), false, "Failed to failed to validate coinbase in pos block, condition failed: is_tx_spendtime_unlocked(max_unlock_time)(" << max_unlock_time << ")"); + CHECK_AND_ASSERT_MES(is_tx_spendtime_unlocked(source_max_unlock_time_for_pos_coinbase), false, "Failed to validate coinbase in PoS block, condition failed: is_tx_spendtime_unlocked(source_max_unlock_time_for_pos_coinbase)(" << source_max_unlock_time_for_pos_coinbase << ")"); } } diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 9a215268..6297cb23 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -267,14 +267,14 @@ namespace currency uint64_t get_aliases_count()const; uint64_t get_block_h_older_then(uint64_t timestamp) const; bool validate_tx_service_attachmens_in_services(const tx_service_attachment& a, size_t i, const transaction& tx)const; - bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time)const; + bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, 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, const std::vector& sig, uint64_t& max_related_block_height)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; bool check_ms_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, const transaction& source_tx, size_t out_n) const; bool validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id, uint64_t block_height) const; - bool get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const; + bool get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const; bool check_tokey_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, const std::vector& output_keys_ptrs) const; uint64_t get_current_comulative_blocksize_limit()const; uint64_t get_current_hashrate(size_t aprox_count)const; @@ -312,7 +312,7 @@ namespace currency bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash *p_last_block_hash = nullptr) const; bool scan_pos(const COMMAND_RPC_SCAN_POS::request& sp, COMMAND_RPC_SCAN_POS::response& rsp)const; - bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const; + bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const; bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const; bool validate_pos_block(const block& b, wide_difficulty_type basic_diff, const crypto::hash& id, bool for_altchain)const; bool validate_pos_block(const block& b, diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 9ce1b000..4b993394 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -85,6 +85,7 @@ namespace currency uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i); uint64_t get_tx_max_unlock_time(const transaction& tx); bool get_tx_max_min_unlock_time(const transaction& tx, uint64_t& max_unlock_time, uint64_t& min_unlock_time); + inline bool should_unlock_value_be_treated_as_block_height(uint64_t v) { return v < CURRENCY_MAX_BLOCK_NUMBER; } inline uint64_t get_tx_flags(const transaction& tx) { return get_tx_x_detail(tx); } inline uint64_t get_tx_expiration_time(const transaction& tx) {return get_tx_x_detail(tx); } inline void set_tx_unlock_time(transaction& tx, uint64_t v) { set_tx_x_detail(tx, v); } @@ -106,4 +107,4 @@ namespace currency size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size); blobdata tx_to_blob(const transaction& b); bool tx_to_blob(const transaction& b, blobdata& b_blob); -} \ No newline at end of file +}