1
0
Fork 0
forked from lthn/blockchain

moved tx semantics validation to separate funtion, added to pool validation and to core validation

This commit is contained in:
cryptozoidberg 2019-11-15 23:58:09 +01:00
parent d1e6ef429b
commit 499f822e97
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
7 changed files with 242 additions and 122 deletions

View file

@ -32,6 +32,7 @@
#include "storages/portable_storage_template_helper.h"
#include "basic_pow_helpers.h"
#include "version.h"
#include "tx_semantic_validation.h"
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "core"
@ -4762,6 +4763,18 @@ wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise
return 0;
}
//------------------------------------------------------------------
bool get_tx_from_cache(const crypto::hash& tx_id, std::unordered_map<crypto::hash, transaction>& tx_cache, transaction& tx, size_t& blob_size, uint64_t fee)
{
auto it = tx_cache.find(tx_id);
if (it == tx_cache.end())
return false;
tx = it->second;
blob_size = get_object_blobsize(tx);
fee = get_tx_fee(tx);
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
{
TIME_MEASURE_START_PD_MS(block_processing_time_0_ms);
@ -4882,7 +4895,10 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
transaction tx;
size_t blob_size = 0;
uint64_t fee = 0;
if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee))
bool taken_from_cache = get_tx_from_cache(tx_id, bvc.m_onboard_transactions, tx, blob_size, fee);
bool taken_from_pool = m_tx_pool.take_tx(tx_id, tx, blob_size, fee);
if(!taken_from_cache && !taken_from_pool)
{
LOG_PRINT_L0("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
purge_block_data_from_blockchain(bl, tx_processed_count);
@ -4891,6 +4907,15 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
if (!validate_tx_semantic(tx, blob_size))
{
LOG_PRINT_L0("Block with id: " << id << " has at least one transaction with wrong semantic, tx_id: " << tx_id);
purge_block_data_from_blockchain(bl, tx_processed_count);
//add_block_as_invalid(bl, id);
bvc.m_verification_failed = true;
return false;
}
append_per_block_increments_for_tx(tx, gindices);
//If we under checkpoints, ring signatures should be pruned
@ -4905,9 +4930,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
{
LOG_PRINT_L0("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
if (taken_from_pool)
{
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
}
purge_block_data_from_blockchain(bl, tx_processed_count);
add_block_as_invalid(bl, id);
LOG_PRINT_L0("Block with id " << id << " added as invalid because of wrong inputs in transactions");
@ -4925,10 +4953,13 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
if(!add_transaction_from_block(tx, tx_id, id, current_bc_size, actual_timestamp))
{
LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage");
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
if (taken_from_pool)
{
currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
bool add_res = m_tx_pool.add_tx(tx, tvc, true, true);
m_tx_pool.add_transaction_to_black_list(tx);
CHECK_AND_ASSERT_MES_NO_RET(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
}
purge_block_data_from_blockchain(bl, tx_processed_count);
bvc.m_verification_failed = true;
return false;

View file

@ -19,6 +19,7 @@ using namespace epee;
#include "currency_format_utils.h"
#include "misc_language.h"
#include "string_coding.h"
#include "tx_semantic_validation.h"
#define MINIMUM_REQUIRED_FREE_SPACE_BYTES (1024 * 1024 * 100)
@ -185,13 +186,15 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool kept_by_block)
{
CHECK_AND_ASSERT_MES(!kept_by_block, false, "Transaction associated with block came throw handle_incoming_tx!(not allowed anymore)");
tvc = boost::value_initialized<tx_verification_context>();
//want to process all transactions sequentially
TIME_MEASURE_START_MS(wait_lock_time);
CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
TIME_MEASURE_FINISH_MS(wait_lock_time);
if(tx_blob.size() > get_max_tx_size())
if(tx_blob.size() > CURRENCY_MAX_TRANSACTION_BLOB_SIZE)
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected");
tvc.m_verification_failed = true;
@ -210,19 +213,10 @@ namespace currency
TIME_MEASURE_FINISH_MS(parse_tx_time);
TIME_MEASURE_START_MS(check_tx_syntax_time);
if(!check_tx_syntax(tx))
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected");
tvc.m_verification_failed = true;
return false;
}
TIME_MEASURE_FINISH_MS(check_tx_syntax_time);
TIME_MEASURE_START_MS(check_tx_semantic_time);
if(!check_tx_semantic(tx, kept_by_block))
if(!validate_tx_semantic(tx, tx_blob.size()))
{
LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
LOG_PRINT_L0("WRONG TRANSACTION SEMANTICS, Failed to check tx " << tx_hash << " semantic, rejected");
tvc.m_verification_failed = true;
return false;
}
@ -243,7 +237,6 @@ namespace currency
}
LOG_PRINT_L2("[CORE HANDLE_INCOMING_TX]: timing " << wait_lock_time
<< "/" << parse_tx_time
<< "/" << check_tx_syntax_time
<< "/" << check_tx_semantic_time
<< "/" << add_new_tx_time);
return r;
@ -296,88 +289,9 @@ namespace currency
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_semantic(const transaction& tx, bool kept_by_block)
{
if(!tx.vin.size())
{
LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_inputs_types_supported(tx))
{
LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_outs_valid(tx))
{
LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!check_money_overflow(tx))
{
LOG_PRINT_RED_L0("tx has money overflow, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
uint64_t amount_in = 0;
get_inputs_money_amount(tx, amount_in);
uint64_t amount_out = get_outs_money_amount(tx);
if(amount_in < amount_out)
{
LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if(!kept_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CURRENCY_COINBASE_BLOB_RESERVED_SIZE)
{
LOG_PRINT_RED_L0("tx has too big size " << get_object_blobsize(tx) << ", expected no bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CURRENCY_COINBASE_BLOB_RESERVED_SIZE);
return false;
}
//check if tx use different key images
if(!check_tx_inputs_keyimages_diff(tx))
{
LOG_PRINT_RED_L0("tx inputs have the same key images");
return false;
}
if(!check_tx_extra(tx))
{
LOG_PRINT_RED_L0("tx has wrong extra, rejected");
return false;
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_extra(const transaction& tx)
{
tx_extra_info ei = AUTO_VAL_INIT(ei);
bool r = parse_and_validate_tx_extra(tx, ei);
if(!r)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_diff(const transaction& tx)
{
std::unordered_set<crypto::key_image> ki;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if (!ki.insert(tokey_in.k_image).second)
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool kept_by_block)
{
@ -627,11 +541,6 @@ namespace currency
{
return parse_and_validate_tx_from_blob(blob, tx, tx_hash);
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_syntax(const transaction& tx)
{
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transactions(std::list<transaction>& txs)
{

View file

@ -118,12 +118,6 @@ namespace currency
bool add_new_block(const block& b, block_verification_context& bvc);
bool load_state_data();
bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob);
bool check_tx_extra(const transaction& tx);
bool check_tx_syntax(const transaction& tx);
//check correct values, amounts and all lightweight checks not related with database
bool check_tx_semantic(const transaction& tx, bool kept_by_block);
//check if tx already in memory pool or in main blockchain
bool is_key_image_spent(const crypto::key_image& key_im);
@ -132,7 +126,6 @@ namespace currency
bool update_miner_block_template();
bool handle_command_line(const boost::program_options::variables_map& vm);
bool on_update_blocktemplate_interval();
bool check_tx_inputs_keyimages_diff(const transaction& tx);
void notify_blockchain_update_listeners();

View file

@ -2426,15 +2426,7 @@ namespace currency
return true;
return false;
}
size_t get_max_block_size()
{
return CURRENCY_MAX_BLOCK_SIZE;
}
//-----------------------------------------------------------------------------------------------
size_t get_max_tx_size()
{
return CURRENCY_MAX_TRANSACTION_BLOB_SIZE;
}
//-----------------------------------------------------------------------------------------------
uint64_t get_base_block_reward(bool is_pos, const boost::multiprecision::uint128_t& already_generated_coins, uint64_t height)
{

View file

@ -92,7 +92,7 @@ namespace currency
inline void set_tx_flags(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_flags>(tx, v); }
inline void set_tx_expiration_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_expiration_time>(tx, v); }
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations);
//-----------------------------------------------------------------------------------------------
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
uint64_t get_burned_amount(const transaction& tx);

View file

@ -0,0 +1,179 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "tx_semantic_validation.h"
#include "currency_format_utils.h"
namespace currency
{
//-----------------------------------------------------------------------------------------------
bool check_tx_extra(const transaction& tx)
{
tx_extra_info ei = AUTO_VAL_INIT(ei);
bool r = parse_and_validate_tx_extra(tx, ei);
if (!r)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_tx_inputs_keyimages_diff(const transaction& tx)
{
std::unordered_set<crypto::key_image> ki;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if (!ki.insert(tokey_in.k_image).second)
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool validate_tx_semantic(const transaction& tx, size_t tx_block_size)
{
if (!tx.vin.size())
{
LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_inputs_types_supported(tx))
{
LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_outs_valid(tx))
{
LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (!check_money_overflow(tx))
{
LOG_PRINT_RED_L0("tx has money overflow, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
uint64_t amount_in = 0;
get_inputs_money_amount(tx, amount_in);
uint64_t amount_out = get_outs_money_amount(tx);
if (amount_in < amount_out)
{
LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
return false;
}
if (tx_block_size >= CURRENCY_MAX_TRANSACTION_BLOB_SIZE)
{
LOG_PRINT_RED_L0("tx has too big size " << tx_block_size << ", expected no bigger than " << CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE);
return false;
}
//check if tx use different key images
if (!check_tx_inputs_keyimages_diff(tx))
{
LOG_PRINT_RED_L0("tx inputs have the same key images");
return false;
}
if (!check_tx_extra(tx))
{
LOG_PRINT_RED_L0("tx has wrong extra, rejected");
return false;
}
return true;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
uint64_t blob_size = 0;
return get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, uint64_t& blob_size)
{
blob_size = 0;
bool r = get_object_hash(static_cast<const transaction_prefix&>(t), res, blob_size);
blob_size = get_object_blobsize(t, blob_size);
return r;
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t)
{
size_t tx_blob_size = get_object_blobsize(static_cast<const transaction_prefix&>(t));
return get_object_blobsize(t, tx_blob_size);
}
//---------------------------------------------------------------
size_t get_objects_blobsize(const std::list<transaction>& ls)
{
size_t total = 0;
for (const auto& tx : ls)
{
total += get_object_blobsize(tx);
}
return total;
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size)
{
size_t tx_blob_size = prefix_blob_size;
if (is_coinbase(t))
return tx_blob_size;
// for purged tx, with empty signatures and attachments, this function should return the blob size
// which the tx would have if the signatures and attachments were correctly filled with actual data
// 1. signatures
bool separately_signed_tx = get_tx_flags(t) & TX_FLAG_SIGNATURE_MODE_SEPARATE;
tx_blob_size += tools::get_varint_packed_size(t.vin.size()); // size of transaction::signatures (equals to total inputs count)
for (size_t i = 0; i != t.vin.size(); i++)
{
size_t sig_count = get_input_expected_signatures_count(t.vin[i]);
if (separately_signed_tx && i == t.vin.size() - 1)
++sig_count; // count in one more signature for the last input in a complete separately signed tx
tx_blob_size += tools::get_varint_packed_size(sig_count); // size of transaction::signatures[i]
tx_blob_size += sizeof(crypto::signature) * sig_count; // size of signatures' data itself
}
// 2. attachments (try to find extra_attachment_info in tx prefix and count it in if succeed)
extra_attachment_info eai = AUTO_VAL_INIT(eai);
bool got_eai = false;
if (separately_signed_tx)
{
// for separately-signed tx, try to obtain extra_attachment_info from the last input's etc_details
const std::vector<txin_etc_details_v>* p_etc_details = get_input_etc_details(t.vin.back());
got_eai = p_etc_details != nullptr && get_type_in_variant_container(*p_etc_details, eai);
}
if (!got_eai)
got_eai = get_type_in_variant_container(t.extra, eai); // then from the extra
if (got_eai)
tx_blob_size += eai.sz; // sz is a size of whole serialized attachment blob, including attachments vector size
else
tx_blob_size += tools::get_varint_packed_size(static_cast<size_t>(0)); // no extra_attachment_info found - just add zero vector's size, 'cause it's serialized anyway
return tx_blob_size;
}
//---------------------------------------------------------------
blobdata tx_to_blob(const transaction& tx)
{
return t_serializable_object_to_blob(tx);
}
//---------------------------------------------------------------
bool tx_to_blob(const transaction& tx, blobdata& b_blob)
{
return t_serializable_object_to_blob(tx, b_blob);
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) 2018-2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "include_base_utils.h"
#include "currency_format_utils_transactions.h"
namespace currency
{
//check correct values, amounts and all lightweight checks not related with database
bool validate_tx_semantic(const transaction& tx, size_t tx_block_size);
}