1
0
Fork 0
forked from lthn/blockchain

validate_alt_block_input deep refactoring related to htlc

This commit is contained in:
cryptozoidberg 2021-01-20 23:16:28 +01:00
parent 16f37ed4c2
commit 79040fa9b3
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
2 changed files with 162 additions and 40 deletions

View file

@ -6184,6 +6184,9 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
CHECK_AND_ASSERT_MES(pub_keys.size() == abs_key_offsets.size(), false, "pub_keys.size()==" << pub_keys.size() << " != abs_key_offsets.size()==" << abs_key_offsets.size()); // just a little bit of paranoia
std::vector<const crypto::public_key*> pub_key_pointers;
uint64_t height_of_current_alt_block = alt_chain.size() ? alt_chain.back()->second.height + 1 : split_height + 1;
for (size_t pk_n = 0; pk_n < pub_keys.size(); ++pk_n)
{
crypto::public_key& pk = pub_keys[pk_n];
@ -6218,7 +6221,34 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
uint64_t local_offset = offset_gindex - it_aag->second;
auto& alt_keys = (*alt_it)->second.outputs_pub_keys;
CHECK_AND_ASSERT_MES(local_offset < alt_keys[input_to_key.amount].size(), false, "Internal error: local_offset=" << local_offset << " while alt_keys[" << input_to_key.amount << " ].size()=" << alt_keys.size());
pk = alt_keys[input_to_key.amount][local_offset];
const output_key_or_htlc_v& out_in_alt = alt_keys[input_to_key.amount][local_offset];
/*
here we do validation against compatibility of input and output type
TxOutput | TxInput | Allowed
----------------------------
HTLC | HTLC | ONLY IF HTLC NOT EXPIRED
HTLC | TO_KEY | ONLY IF HTLC IS EXPIRED
TO_KEY | HTLC | NOT
TO_KEY | TO_KEY | YES
*/
uint64_t height_of_source_block = (*alt_it)->second.height;
CHECK_AND_ASSERT_MES(height_of_current_alt_block > height_of_source_block, false, "Intenral error: height_of_current_alt_block > height_of_source_block failed");
bool r = is_output_allowed_for_input(out_in_alt, input_v, height_of_current_alt_block - height_of_source_block);
CHECK_AND_ASSERT_MES(r, false, "Input and output incompatible type");
if (out_in_alt.type() == typeid(crypto::public_key))
{
pk = boost::get<crypto::public_key>(out_in_alt);
}
else
{
const txout_htlc& out_htlc = boost::get<txout_htlc>(out_in_alt);
bool htlc_expired = htlc.expiration > (height_of_current_alt_block - height_of_source_block) ? false:true;
pk = htlc_expired ? out_htlc.pkey_after_expiration : out_htlc.pkey_before_expiration;
//input_v
}
pub_key_pointers.push_back(&pk);
found_the_key = true;
break;
@ -6242,34 +6272,37 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
auto it = alt_chain_tx_ids.find(tx_id);
if (it != alt_chain_tx_ids.end())
{
uint64_t height_of_source_block = it->second.second;
CHECK_AND_ASSERT_MES(height_of_current_alt_block > height_of_source_block, false, "Intenral error: height_of_current_alt_block > height_of_source_block failed");
/*
here we do validation against compatibility of input and output type
TxOutput | TxInput | Allowed
----------------------------
HTLC | HTLC | ONLY IF HTLC NOT EXPIRED
HTLC | TO_KEY | ONLY IF HTLC IS EXPIRED
TO_KEY | HTLC | NOT
TO_KEY | TO_KEY | YES
*/
bool r = is_output_allowed_for_input(out_target_v, input_v, height_of_current_alt_block - height_of_source_block);
CHECK_AND_ASSERT_MES(r, false, "Input and output incompatible type");
//source tx found in altchain
CHECK_AND_ASSERT_MES(it->second.first.vout.size() > out_n, false, "Internal error: out_n(" << out_n << ") >= it->second.vout.size()(" << it->second.first.vout.size() << ")");
txout_target_v out_target_v = it->second.first.vout[out_n].target;
if (out_target_v.type() == typeid(txout_htlc))
{
//source is hltc out
const txout_htlc& htlc = boost::get<txout_to_key>(out_target_v);
uint64_t height_of_source_block = it->second.second;
uint64_t height_of_current_alt_block = alt_chain.size() ? alt_chain.back()->second.height + 1 : split_height + 1;
CHECK_AND_ASSERT_MES(height_of_current_alt_block > height_of_source_block, false, "Intenral error: height_of_current_alt_block > height_of_source_block failed");
if (htlc.expiration > height_of_current_alt_block - height_of_source_block)
{
//HTLC IS NOT expired, can be used ONLY by pkey_before_expiration and ONLY by HTLC input
CHECK_AND_ASSERT_MES(input_v.type() == typeid(txin_htlc), false, "[TXOUT_HTLC]: Unexpected output type of non-HTLC input");
pk = htlc.pkey_before_expiration;
}
else
{
//HTLC IS expired, can be used ONLY by pkey_after_expiration and ONLY by to_key input
CHECK_AND_ASSERT_MES(input_v.type() == typeid(txin_to_key), false, "[TXOUT_HTLC]: Unexpected output type of HTLC input");
pk = htlc.pkey_after_expiration;
}
const txout_htlc& htlc = boost::get<txout_htlc>(out_target_v);
bool htlc_expired = htlc.expiration > (height_of_current_alt_block - height_of_source_block) ? false : true;
pk = htlc_expired ? htlc.pkey_after_expiration : htlc.pkey_before_expiration;
pub_key_pointers.push_back(&pk);
continue;
}
else if (out_target_v.type() == typeid(txout_to_key))
{
CHECK_AND_ASSERT_MES(input_v.type() != typeid(txin_htlc), false, "Forbidden output type referenced by in tx ( input txin_htlc refered to txout_to_key )");
//source is to_key out
pk = boost::get<txout_to_key>(out_target_v).key;
pub_key_pointers.push_back(&pk);
@ -6279,14 +6312,6 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
{
ASSERT_MES_AND_THROW("Unexpected out type for tx_in in altblock: " << out_target_v.type().name());
}
//let's validate against htlc&to_key
pk = alt_keys[input_to_key.amount][local_offset];
pub_key_pointers.push_back(&pk);
found_the_key = true;
}
}
@ -6294,11 +6319,36 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
auto p = m_db_transactions.get(tx_id);
CHECK_AND_ASSERT_MES(p != nullptr && out_n < p->tx.vout.size(), false, "can't find output #" << out_n << " for tx " << tx_id << " referred by offset #" << pk_n);
auto &t = p->tx.vout[out_n].target;
CHECK_AND_ASSERT_MES(t.type() == typeid(txout_to_key), false, "txin_to_key input offset #" << pk_n << " refers to incorrect output type " << t.type().name());
auto& out_tk = boost::get<txout_to_key>(t);
pk = out_tk.key;
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(out_tk.mix_attr, abs_key_offsets.size() - 1);
CHECK_AND_ASSERT_MES(mixattr_ok, false, "input offset #" << pk_n << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(out_tk.mix_attr) << ", input's key_offsets.size = " << abs_key_offsets.size());
/*
here we do validation against compatibility of input and output type
TxOutput | TxInput | Allowed
----------------------------
HTLC | HTLC | ONLY IF HTLC NOT EXPIRED
HTLC | TO_KEY | ONLY IF HTLC IS EXPIRED
TO_KEY | HTLC | NOT
TO_KEY | TO_KEY | YES
*/
uint64_t height_of_source_block = p->m_keeper_block_height;
bool r = is_output_allowed_for_input(t, input_v, height_of_current_alt_block - height_of_source_block);
CHECK_AND_ASSERT_MES(r, false, "Input and output incompatible type");
if (t.type() == typeid(txout_to_key))
{
const txout_to_key& out_tk = boost::get<txout_to_key>(t);
pk = out_tk.key;
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(out_tk.mix_attr, abs_key_offsets.size() - 1);
CHECK_AND_ASSERT_MES(mixattr_ok, false, "input offset #" << pk_n << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(out_tk.mix_attr) << ", input's key_offsets.size = " << abs_key_offsets.size());
}
else if (t.type() == typeid(txout_htlc))
{
const txout_htlc& htlc = boost::get<txout_htlc>(t);
bool htlc_expired = htlc.expiration > (height_of_current_alt_block - height_of_source_block) ? false : true;
pk = htlc_expired ? htlc.pkey_after_expiration : htlc.pkey_before_expiration;
}
// case b4 (make sure source tx in the main chain is preceding split point, otherwise this referece is invalid)
CHECK_AND_ASSERT_MES(p->m_keeper_block_height < split_height, false, "input offset #" << pk_n << " refers to main chain tx " << tx_id << " at height " << p->m_keeper_block_height << " while split height is " << split_height);
@ -6324,6 +6374,58 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::is_output_allowed_for_input(const txout_target_v& out_v, const txin_v& in_v, uint64_t top_minus_source_height)
{
/*
TxOutput | TxInput | Allowed
----------------------------
HTLC | HTLC | ONLY IF HTLC NOT EXPIRED
HTLC | TO_KEY | ONLY IF HTLC IS EXPIRED
TO_KEY | HTLC | NOT
TO_KEY | TO_KEY | YES
*/
if (out_v.type() == typeid(txout_to_key))
{
return is_output_allowed_for_input(boost::get<txout_to_key>(out_v), in_v);
}
else if (out_v.type() == typeid(txout_htlc))
{
return is_output_allowed_for_input(boost::get<txout_htlc>(out_v), in_v, top_minus_source_height);
}
else
{
LOG_ERROR("[scan_outputkeys_for_indexes]: Wrong output type in : " << out_v.type().name());
return false;
}
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::is_output_allowed_for_input(const txout_htlc& out_v, const txin_v& in_v, uint64_t top_minus_source_height)
{
bool htlc_expired = out_v.expiration > (top_minus_source_height) ? false : true;
if (!hltc_expired)
{
//HTLC IS NOT expired, can be used ONLY by pkey_before_expiration and ONLY by HTLC input
CHECK_AND_ASSERT_MES(in_v.type() == typeid(txin_htlc), false, "[TXOUT_HTLC]: Unexpected output type of non-HTLC input");
}
else
{
//HTLC IS expired, can be used ONLY by pkey_after_expiration and ONLY by to_key input
CHECK_AND_ASSERT_MES(in_v.type() == typeid(txin_to_key), false, "[TXOUT_HTLC]: Unexpected output type of HTLC input");
}
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::is_output_allowed_for_input(const txout_to_key& out_v, const txin_v& in_v)
{
//HTLC input CAN'T refer to regular to_key output
CHECK_AND_ASSERT_MES(in_v.type() != typeid(txin_htlc), false, "[TXOUT_TO_KEY]: Unexpected output type of HTLC input");
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_alt_block_ms_input(const transaction& input_tx, const crypto::hash& input_tx_hash, size_t input_index, const std::vector<crypto::signature>& input_sigs, uint64_t split_height, const alt_chain_type& alt_chain) const
{
// Main and alt chain outline:
@ -6465,7 +6567,7 @@ bool blockchain_storage::update_alt_out_indexes_for_tx_in_block(const transactio
//add tx outputs to gindex_lookup_table
for (auto o : tx.vout)
{
if (o.target.type() == typeid(txout_to_key))
if (o.target.type() == typeid(txout_to_key) || o.target.type() == typeid(txout_htlc))
{
//LOG_PRINT_MAGENTA("ALT_OUT KEY ON H[" << abei.height << "] AMOUNT: " << o.amount, LOG_LEVEL_0);
// first, look at local gindexes tables
@ -6475,7 +6577,15 @@ bool blockchain_storage::update_alt_out_indexes_for_tx_in_block(const transactio
abei.gindex_lookup_table[o.amount] = m_db_outputs.get_item_size(o.amount);
//LOG_PRINT_MAGENTA("FIRST TOUCH: size=" << abei.gindex_lookup_table[o.amount], LOG_LEVEL_0);
}
abei.outputs_pub_keys[o.amount].push_back(boost::get<txout_to_key>(o.target).key);
if (o.target.type() == typeid(txout_to_key))
{
abei.outputs_pub_keys[o.amount].push_back(boost::get<txout_to_key>(o.target).key);
}
else
{
abei.outputs_pub_keys[o.amount].push_back(boost::get<txout_htlc>(o.target));
}
//TODO: At the moment we ignore check of mix_attr again mixing to simplify alt chain check, but in future consider it for stronger validation
}
}

View file

@ -150,7 +150,9 @@ namespace currency
// retrieve gindex from local_gindex_lookup_table # there are outputs having given amount after the given height
// else:
// retrieve gindex from main chain gindex table # not outputs having given amount are present after the given height
//
//
typedef boost::variant<crypto::public_key, txout_htlc> output_key_or_htlc_v;
struct alt_block_extended_info: public block_extended_info
{
@ -158,7 +160,7 @@ namespace currency
std::map<uint64_t, uint64_t> gindex_lookup_table;
// {amount -> pub_keys} map of outputs' pub_keys appeared in this alt block ( index_in_vector == output_gindex - gindex_lookup_table[output_amount] )
std::map<uint64_t, std::vector<crypto::public_key> > outputs_pub_keys;
std::map<uint64_t, std::vector<output_key_or_htlc_v> > outputs_pub_keys;
//date added to alt chain storage
uint64_t timestamp;
@ -641,6 +643,9 @@ namespace currency
void calculate_local_gindex_lookup_table_for_height(uint64_t split_height, std::map<uint64_t, uint64_t>& increments) const;
void do_erase_altblock(alt_chain_container::iterator it);
uint64_t get_blockchain_launch_timestamp()const;
bool is_output_allowed_for_input(const txout_target_v& out_v, const txin_v& in_v, uint64_t top_minus_source_height);
bool is_output_allowed_for_input(const txout_to_key& out_v, const txin_v& in_v);
bool is_output_allowed_for_input(const txout_htlc& out_v, const txin_v& in_v, uint64_t top_minus_source_height);
@ -739,11 +744,20 @@ namespace currency
//CHECKED_GET_SPECIFIC_VARIANT(tx_ptr->tx.vout[n].target, const txout_to_key, outtk, false);
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
/*
TxOutput | TxInput | Allowed
----------------------------
HTLC | HTLC | ONLY IF HTLC NOT EXPIRED
HTLC | TO_KEY | ONLY IF HTLC IS EXPIRED
TO_KEY | HTLC | NOT
TO_KEY | TO_KEY | YES
*/
bool r = is_output_allowed_for_input(tx_ptr->tx.vout[n].target, verified_input, get_current_blockchain_size() - tx_ptr->m_keeper_block_height);
CHECK_AND_ASSERT_MES(r, false, "Input and output incompatible type");
if (tx_ptr->tx.vout[n].target.type() == typeid(txout_to_key))
{
//HTLC input CAN'T refer to regular to_key output
CHECK_AND_ASSERT_MES(verified_input.type() != typeid(txin_htlc), false, "[TXOUT_TO_KEY]: Unexpected output type of HTLC input");
CHECKED_GET_SPECIFIC_VARIANT(tx_ptr->tx.vout[n].target, const txout_to_key, outtk, false);
//fix for burned money
patch_out_if_needed(const_cast<txout_to_key&>(outtk), tx_id, n);
@ -757,13 +771,11 @@ namespace currency
if (htlc_out.expiration > get_current_blockchain_size() - tx_ptr->m_keeper_block_height)
{
//HTLC IS NOT expired, can be used ONLY by pkey_before_expiration and ONLY by HTLC input
CHECK_AND_ASSERT_MES(verified_input.type() == typeid(txin_htlc), false, "[TXOUT_HTLC]: Unexpected output type of non-HTLC input");
scan_context.htlc_is_expired = false;
}
else
{
//HTLC IS expired, can be used ONLY by pkey_after_expiration and ONLY by to_key input
CHECK_AND_ASSERT_MES(verified_input.type() == typeid(txin_to_key), false, "[TXOUT_HTLC]: Unexpected output type of HTLC input");
scan_context.htlc_is_expired = true;
}
}else