implemnting htlc inputs/outputs handling in core(work in progress)

This commit is contained in:
cryptozoidberg 2020-12-29 23:42:37 +01:00
parent 6bee7541d6
commit 4ee72ef7ac
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
3 changed files with 120 additions and 57 deletions

View file

@ -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_to_htlc>(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<crypto::public_key> 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<crypto::public_key >& 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<crypto::public_key>& 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<txout_htlc>(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<txout_to_key>(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<crypto::public_key>& 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<txout_v>& key_offsets, std::vector<crypto::public_key>& 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<crypto::public_key >& m_results_collector;
const blockchain_storage& m_bch;
uint64_t& m_source_max_unlock_time_for_pos_coinbase;
outputs_visitor(std::vector<crypto::public_key>& 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<txout_to_key>(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<txout_v>& key_offsets, std::vector<crypto::public_key>& 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<crypto::signature>& 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<crypto::public_key> output_keys;
std::vector<txout_v> 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<const crypto::public_key *> 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

View file

@ -133,6 +133,11 @@ namespace currency
}
};
struct scan_for_keys_context
{
std::list<txout_htlc> 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<class visitor_t>
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<class visitor_t>
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<crypto::signature>& 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<crypto::signature>& 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<crypto::signature>& 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<crypto::signature>& 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<crypto::public_key>& 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<txout_v>& key_offsets, std::vector<crypto::public_key>& 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<txout_v>& key_offsets, std::vector<crypto::public_key>& 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<crypto::signature>& sig, const std::vector<const crypto::public_key*>& 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<class visitor_t>
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<txout_v>& 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<txout_v> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
std::vector<txout_v> 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<uint32_t>(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<uint32_t>(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))

View file

@ -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_gen, txin_to_key, txin_multisig> txin_v;
typedef boost::variant<txin_gen, txin_to_key, txin_multisig, txin_to_htlc> txin_v;
typedef boost::variant<txout_to_key, txout_multisig> txout_target_v;
typedef boost::variant<txout_to_key, txout_multisig, txout_htlc> txout_target_v;
//typedef std::pair<uint64_t, txout> 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