diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index c22f9ad9..03052424 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4210,7 +4210,15 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha LOG_ERROR("Failed to validate multisig input #" << sig_index << " (ms out id: " << in_ms.multisig_out_id << ") in tx: " << tx_prefix_hash); return false; } - + } + else if (txin.type() == typeid(txin_to_htlc)) + { + const txin_to_htlc& in_ms_htlc = boost::get(txin); + if (!check_tx_input(tx, sig_index, in_ms, tx_prefix_hash, *psig, 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; + } } sig_index++; } @@ -4243,6 +4251,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, //TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop); std::vector output_keys; + scan_for_keys_context scan_context = AUTO_VAL_INIT(scan_context); 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() << ")"); @@ -4257,61 +4266,77 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, return check_tokey_input(tx, in_index, txin, tx_prefix_hash, sig, output_keys_ptrs); } +//---------------------------------------------------------------- +struct outputs_visitor +{ + std::vector& m_results_collector; + blockchain_storage::scan_for_keys_context& m_scan_context; + const blockchain_storage& m_bch; + uint64_t& m_source_max_unlock_time_for_pos_coinbase; + outputs_visitor(std::vector& results_collector, + const blockchain_storage& bch, + uint64_t& source_max_unlock_time_for_pos_coinbase, + blockchain_storage::scan_for_keys_context& scan_context) + : m_results_collector(results_collector) + , m_bch(bch) + , m_source_max_unlock_time_for_pos_coinbase(source_max_unlock_time_for_pos_coinbase), + , m_scan_context(scan_context) + {} + bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i) + { + //check tx unlock time + uint64_t source_out_unlock_time = get_tx_unlock_time(source_tx, out_i); + //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)) + { + 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 + { + if (!m_bch.is_tx_spendtime_unlocked(source_out_unlock_time)) + { + LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(source_tx, out_i)); + return false; + } + } + + if (out.target.type() == typeid(txout_htlc)) + { + m_scan_context.htlc_outs.push_back(boost::get(out.target)); + + }else if (out.target.type() != typeid(txout_to_key)) + { + LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); + return false; + } + + crypto::public_key pk = boost::get(out.target).key; + m_results_collector.push_back(pk); + return true; + } +}; + //------------------------------------------------------------------ // Checks each referenced output for: // 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& source_max_unlock_time_for_pos_coinbase) const +bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, uint64_t amount, const std::vector& key_offsets, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase, scan_for_keys_context& scan_context) const { CRITICAL_REGION_LOCAL(m_read_lock); - struct outputs_visitor - { - std::vector& m_results_collector; - const blockchain_storage& m_bch; - uint64_t& m_source_max_unlock_time_for_pos_coinbase; - outputs_visitor(std::vector& results_collector, - const blockchain_storage& bch, - 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) - { - //check tx unlock time - uint64_t source_out_unlock_time = get_tx_unlock_time(source_tx, out_i); - //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)) - { - 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 - { - if (!m_bch.is_tx_spendtime_unlocked(source_out_unlock_time)) - { - LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(source_tx, out_i)); - return false; - } - } - - if(out.target.type() != typeid(txout_to_key)) - { - LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); - return false; - } - crypto::public_key pk = boost::get(out.target).key; - m_results_collector.push_back(pk); - return true; - } - }; - 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); + return scan_outputkeys_for_indexes(tx, amount, key_offsets, vi, max_related_block_height, scan_context); } +//------------------------------------------------------------------ +bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, uint64_t amount, const std::vector& key_offsets, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase) const +{ + scan_for_keys_context scan_context_dummy = AUTO_VAL_INIT(); + return get_output_keys_for_input_with_checks(tx, amount, key_offsets, output_keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase, scan_context_dummy); +} + //------------------------------------------------------------------ // Note: this function can be used for checking to_key inputs against either main chain or alt chain, that's why it has output_keys_ptrs parameter // Doesn't check spent flags, the caller must check it. @@ -4491,6 +4516,32 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, return true; #undef LOC_CHK +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_htlc& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t& max_related_block_height)const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + + //TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop); + + std::vector output_keys; + std::vector key_offsets(txin.key_offset, 1); + scan_for_keys_context scan_contex = AUTO_VAL_INIT(); + if (!get_output_keys_for_input_with_checks(tx, txin.amount, key_offsets, output_keys, max_related_block_height, source_max_unlock_time_for_pos_coinbase, scan_contex)) + { + 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; + } + //TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_ch_in_get_keys_loop); + + //TODO: add caludating hash + + std::vector output_keys_ptrs; + output_keys_ptrs.reserve(output_keys.size()); + for (auto& ptr : output_keys) + output_keys_ptrs.push_back(&ptr); + + return check_tokey_input(tx, in_index, txin, tx_prefix_hash, sig, output_keys_ptrs); } //------------------------------------------------------------------ uint64_t blockchain_storage::get_adjusted_time() const diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 5dea7182..5142e72d 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -133,6 +133,11 @@ namespace currency } }; + struct scan_for_keys_context + { + std::list htlc_outs; + }; + // == Output indexes local lookup table conception == // Main chain gindex table (outputs_container) contains data which is valid only for the most recent block. // Thus it can't be used to get output's global index for any arbitrary height because there's no height data. @@ -232,7 +237,7 @@ namespace currency template bool scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis) { uint64_t stub = 0; return scan_outputkeys_for_indexes(validated_tx, tx_in_to_key, vis, stub); } template - bool scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t& max_related_block_height) const ; + bool scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t& max_related_block_height, scan_for_keys_context& scan_context) const ; uint64_t get_current_blockchain_size() const; uint64_t get_top_block_height() const; @@ -273,12 +278,14 @@ namespace currency 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& 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_input(const transaction& tx, size_t in_index, const txin_to_htlc& 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& source_max_unlock_time_for_pos_coinbase) const; + bool get_output_keys_for_input_with_checks(const transaction& tx, uint64_t amount, const std::vector& key_offsets, std::vector& output_keys, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase, scan_for_keys_context& scan_context) const; + bool get_output_keys_for_input_with_checks(const transaction& tx, uint64_t amount, const std::vector& key_offsets, 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; @@ -664,17 +671,17 @@ namespace currency //------------------------------------------------------------------ //------------------------------------------------------------------ template - bool blockchain_storage::scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t& max_related_block_height) const + bool blockchain_storage::scan_outputkeys_for_indexes(const transaction &validated_tx, uint64_t amount, const std::vector& key_offsets, visitor_t& vis, uint64_t& max_related_block_height, scan_for_keys_context& /*scan_context*/) const { CRITICAL_REGION_LOCAL(m_read_lock); TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_get_item_size); - uint64_t outs_count_for_amount = m_db_outputs.get_item_size(tx_in_to_key.amount); + uint64_t outs_count_for_amount = m_db_outputs.get_item_size(amount); TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_get_item_size); if (!outs_count_for_amount) return false; TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_relative_to_absolute); - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); + std::vector absolute_offsets = relative_output_offsets_to_absolute(key_offsets); TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_relative_to_absolute); TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop); size_t output_index = 0; @@ -696,7 +703,7 @@ namespace currency LOG_ERROR("Wrong index in transaction inputs: " << i << ", expected maximum " << outs_count_for_amount - 1); return false; } - auto out_ptr = m_db_outputs.get_subitem(tx_in_to_key.amount, i); + auto out_ptr = m_db_outputs.get_subitem(amount, i); tx_id = out_ptr->tx_id; n = out_ptr->out_no; TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_get_subitem); @@ -715,9 +722,9 @@ namespace currency - CHECK_AND_ASSERT_MES(tx_in_to_key.key_offsets.size() >= 1, false, "internal error: tx input has empty key_offsets"); // should never happen as input correctness must be handled by the caller - bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(outtk.mix_attr, tx_in_to_key.key_offsets.size() - 1); - CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx output #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast(outtk.mix_attr) << ", key_offsets.size = " << tx_in_to_key.key_offsets.size()); + CHECK_AND_ASSERT_MES(key_offsets.size() >= 1, false, "internal error: tx input has empty key_offsets"); // should never happen as input correctness must be handled by the caller + bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(outtk.mix_attr, key_offsets.size() - 1); + CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx output #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast(outtk.mix_attr) << ", key_offsets.size = " << key_offsets.size()); TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output); if (!vis.handle_output(tx_ptr->tx, validated_tx, tx_ptr->tx.vout[n], n)) diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 4acf9a9f..f20f1a66 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -225,6 +225,7 @@ namespace currency struct txin_to_htlc { + uint64_t amount; crypto::hash hltc_origin; txout_v key_offset; crypto::key_image k_image; // double spending protection @@ -279,9 +280,9 @@ namespace currency END_SERIALIZE() }; - typedef boost::variant txin_v; + typedef boost::variant txin_v; - typedef boost::variant txout_target_v; + typedef boost::variant txout_target_v; //typedef std::pair out_t; struct tx_out @@ -797,6 +798,10 @@ SET_VARIANT_TAGS(currency::tx_receiver, 32, "receiver2"); // @#@ TODO @#@ SET_VARIANT_TAGS(currency::extra_alias_entry, 33, "alias_entry2"); +//htlc +SET_VARIANT_TAGS(currency::txin_to_htlc, 34, "txin_to_htlc"); +SET_VARIANT_TAGS(currency::txout_htlc, 35, "txout_htlc"); + #undef SET_VARIANT_TAGS