forked from lthn/blockchain
moved tx semantics validation to separate funtion, added to pool validation and to core validation
This commit is contained in:
parent
d1e6ef429b
commit
499f822e97
7 changed files with 242 additions and 122 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
179
src/currency_core/tx_semantic_validation.cpp
Normal file
179
src/currency_core/tx_semantic_validation.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/currency_core/tx_semantic_validation.h
Normal file
16
src/currency_core/tx_semantic_validation.h
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue