2018-12-27 18:50:45 +03:00
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "chaingen.h"
# include "tx_validation.h"
# include "offers_tests_common.h"
# include "tx_builder.h"
# include "chaingen_helpers.h"
2024-09-30 05:06:17 +02:00
# include "../../src/currency_core/tx_semantic_validation.h"
2018-12-27 18:50:45 +03:00
using namespace epee ;
using namespace crypto ;
using namespace currency ;
//----------------------------------------------------------------------------------------------------------------------
// Tests
bool gen_tx_big_version : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( CURRENT_TRANSACTION_VERSION + 1 , 0 ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_unlock_time : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS_N ( events , blk_1 , blk_0 , miner_account , 25 ) ;
REWIND_BLOCKS ( events , blk_1r , blk_1 , miner_account ) ;
auto make_tx_with_unlock_time = [ & ] ( uint64_t unlock_time ) - > transaction
{
return tx_builder : : make_simple_tx_with_unlock_time ( events , blk_1r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , unlock_time ) ;
} ;
std : : list < transaction > txs_0 ;
txs_0 . push_back ( make_tx_with_unlock_time ( 0 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( get_block_height ( blk_1r ) - 1 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( get_block_height ( blk_1r ) ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( get_block_height ( blk_1r ) + 1 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( get_block_height ( blk_1r ) + 2 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( ts_start - 1 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
txs_0 . push_back ( make_tx_with_unlock_time ( time ( 0 ) + 60 * 60 ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_2 , blk_1r , miner_account , txs_0 ) ;
return true ;
}
bool gen_tx_input_is_not_txin_to_key : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
MAKE_NEXT_BLOCK ( events , blk_tmp , blk_0r , miner_account ) ;
events . pop_back ( ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( blk_tmp . miner_tx ) ;
/*
auto make_tx_with_input = [ & ] ( const txin_v & tx_input ) - > transaction
{
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . m_tx . vin . push_back ( tx_input ) ;
builder . step3_fill_outputs ( destinations ) ;
return builder . m_tx ;
} ;
*/
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
//fix script here
//events.push_back(make_tx_with_input(txin_to_script()));
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
//TODO: fix script here
//events.push_back(make_tx_with_input(txin_to_scripthash()));
return true ;
}
bool gen_tx_no_inputs_no_outputs : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
tx_builder builder ;
builder . step1_init ( ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_no_inputs_has_outputs : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step3_fill_outputs ( destinations ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_has_inputs_no_outputs : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
destinations . clear ( ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
events . push_back ( builder . m_tx ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_account , builder . m_tx ) ;
return true ;
}
bool gen_tx_invalid_input_amount : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
bool r = fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
sources . front ( ) . amount + + ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_input_wo_key_offsets : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
txin_to_key & in_to_key = boost : : get < txin_to_key > ( builder . m_tx . vin . front ( ) ) ;
uint64_t key_offset = boost : : get < uint64_t > ( in_to_key . key_offsets . front ( ) ) ;
in_to_key . key_offsets . pop_back ( ) ;
CHECK_AND_ASSERT_MES ( in_to_key . key_offsets . empty ( ) , false , " txin contained more than one key_offset " ) ;
builder . step4_calc_hash ( ) ;
in_to_key . key_offsets . push_back ( key_offset ) ;
builder . step5_sign ( sources ) ;
in_to_key . key_offsets . pop_back ( ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_key_offest_points_to_foreign_key : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ;
REWIND_BLOCKS ( events , blk_1r , blk_1 , miner_account ) ;
MAKE_ACCOUNT ( events , alice_account ) ;
MAKE_ACCOUNT ( events , bob_account ) ;
MAKE_TX_LIST_START ( events , txs_0 , miner_account , bob_account , MK_TEST_COINS ( 60 ) + 1 , blk_1r ) ;
MAKE_TX_LIST ( events , txs_0 , miner_account , alice_account , MK_TEST_COINS ( 60 ) + 1 , blk_1r ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_2 , blk_1r , miner_account , txs_0 ) ;
std : : vector < tx_source_entry > sources_bob ;
std : : vector < tx_destination_entry > destinations_bob ;
fill_tx_sources_and_destinations ( events , blk_2 , bob_account , miner_account , MK_TEST_COINS ( 60 ) + 1 - TESTS_DEFAULT_FEE , TESTS_DEFAULT_FEE , 0 , sources_bob , destinations_bob ) ;
std : : vector < tx_source_entry > sources_alice ;
std : : vector < tx_destination_entry > destinations_alice ;
fill_tx_sources_and_destinations ( events , blk_2 , alice_account , miner_account , MK_TEST_COINS ( 60 ) + 1 - TESTS_DEFAULT_FEE , TESTS_DEFAULT_FEE , 0 , sources_alice , destinations_alice ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( bob_account . get_keys ( ) , sources_bob ) ;
txin_to_key & in_to_key = boost : : get < txin_to_key > ( builder . m_tx . vin . front ( ) ) ;
2022-07-13 17:17:04 +02:00
in_to_key . key_offsets . front ( ) = sources_alice . front ( ) . outputs . front ( ) . out_reference ;
2018-12-27 18:50:45 +03:00
builder . step3_fill_outputs ( destinations_bob ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources_bob ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_sender_key_offest_not_exist : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
txin_to_key & in_to_key = boost : : get < txin_to_key > ( builder . m_tx . vin . front ( ) ) ;
in_to_key . key_offsets . front ( ) = std : : numeric_limits < uint64_t > : : max ( ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_mixed_key_offest_not_exist : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ;
REWIND_BLOCKS ( events , blk_1r , blk_1 , miner_account ) ;
MAKE_ACCOUNT ( events , alice_account ) ;
MAKE_ACCOUNT ( events , bob_account ) ;
MAKE_TX_LIST_START ( events , txs_0 , miner_account , bob_account , MK_TEST_COINS ( 1 ) + TESTS_DEFAULT_FEE , blk_1r ) ;
MAKE_TX_LIST ( events , txs_0 , miner_account , alice_account , MK_TEST_COINS ( 1 ) + TESTS_DEFAULT_FEE , blk_1r ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_2 , blk_1r , miner_account , txs_0 ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_2 , bob_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 1 , sources , destinations ) ;
2022-07-13 17:17:04 +02:00
sources . front ( ) . outputs [ ( sources . front ( ) . real_output + 1 ) % 2 ] . out_reference = std : : numeric_limits < uint64_t > : : max ( ) ;
2018-12-27 18:50:45 +03:00
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( bob_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_key_image_not_derive_from_tx_key : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
txin_to_key & in_to_key = boost : : get < txin_to_key > ( builder . m_tx . vin . front ( ) ) ;
keypair kp = keypair : : generate ( ) ;
key_image another_ki ;
crypto : : generate_key_image ( kp . pub , kp . sec , another_ki ) ;
in_to_key . k_image = another_ki ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
// Tx with invalid key image can't be subscribed, so create empty signature
2022-06-19 19:47:43 +02:00
builder . m_tx . signatures . resize ( 1 ) ;
boost : : get < currency : : NLSAG_sig > ( builder . m_tx . signatures [ 0 ] ) . s . resize ( 1 ) ;
boost : : get < currency : : NLSAG_sig > ( builder . m_tx . signatures [ 0 ] ) . s [ 0 ] = boost : : value_initialized < crypto : : signature > ( ) ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_key_image_is_invalid : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
txin_to_key & in_to_key = boost : : get < txin_to_key > ( builder . m_tx . vin . front ( ) ) ;
crypto : : public_key pub = tx_builder : : generate_invalid_pub_key ( ) ;
memcpy ( & in_to_key . k_image , & pub , sizeof ( crypto : : ec_point ) ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
// Tx with invalid key image can't be subscribed, so create empty signature
2022-06-19 19:47:43 +02:00
builder . m_tx . signatures . resize ( 1 ) ;
boost : : get < currency : : NLSAG_sig > ( builder . m_tx . signatures [ 0 ] ) . s . resize ( 1 ) ;
boost : : get < currency : : NLSAG_sig > ( builder . m_tx . signatures [ 0 ] ) . s [ 0 ] = boost : : value_initialized < crypto : : signature > ( ) ;
2018-12-27 18:50:45 +03:00
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_check_input_unlock_time : : generate ( std : : vector < test_event_entry > & events ) const
{
static const size_t tests_count = 6 ;
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS_N ( events , blk_1 , blk_0 , miner_account , tests_count - 1 ) ;
REWIND_BLOCKS ( events , blk_1r , blk_1 , miner_account ) ;
std : : array < account_base , tests_count > accounts ;
for ( size_t i = 0 ; i < tests_count ; + + i )
{
MAKE_ACCOUNT ( events , acc ) ;
accounts [ i ] = acc ;
}
std : : list < transaction > txs_0 ;
auto make_tx_to_acc = [ & ] ( size_t acc_idx , uint64_t unlock_time )
{
txs_0 . push_back ( tx_builder : : make_simple_tx_with_unlock_time ( events , blk_1r , miner_account , accounts [ acc_idx ] ,
MK_TEST_COINS ( 1 ) + TESTS_DEFAULT_FEE , unlock_time ) ) ;
events . push_back ( txs_0 . back ( ) ) ;
} ;
uint64_t blk_3_height = get_block_height ( blk_1r ) + 2 ;
make_tx_to_acc ( 0 , 0 ) ;
make_tx_to_acc ( 1 , blk_3_height - 1 ) ;
make_tx_to_acc ( 2 , blk_3_height ) ;
make_tx_to_acc ( 3 , blk_3_height + 10 + 1 ) ;
make_tx_to_acc ( 4 , time ( 0 ) - 1 ) ;
make_tx_to_acc ( 5 , time ( 0 ) + 60 * 60 ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_2 , blk_1r , miner_account , txs_0 ) ;
REWIND_BLOCKS ( events , blk_3 , blk_2 , miner_account ) ;
std : : list < transaction > txs_1 ;
auto make_tx_from_acc = [ & ] ( size_t acc_idx , bool invalid )
{
transaction tx = tx_builder : : make_simple_tx_with_unlock_time ( events , blk_3 , accounts [ acc_idx ] , miner_account , MK_TEST_COINS ( 1 ) , 0 , false ) ;
if ( invalid )
{
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
}
else
{
txs_1 . push_back ( tx ) ;
}
events . push_back ( tx ) ;
LOG_PRINT_L0 ( " [gen_tx_check_input_unlock_time]make_tx_from_acc( " < < acc_idx < < " , " < < invalid < < " ) as event[ " < < events . size ( ) - 1 < < " ] " ) ;
} ;
make_tx_from_acc ( 0 , false ) ;
make_tx_from_acc ( 1 , false ) ;
make_tx_from_acc ( 2 , false ) ;
make_tx_from_acc ( 3 , true ) ;
make_tx_from_acc ( 4 , false ) ;
make_tx_from_acc ( 5 , true ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_4 , blk_3 , miner_account , txs_1 ) ;
return true ;
}
bool gen_tx_txout_to_key_has_invalid_key : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
2022-05-25 22:31:23 +02:00
txout_to_key & out_to_key = boost : : get < txout_to_key > ( boost : : get < tx_out_bare > ( builder . m_tx . vout . front ( ) ) . target ) ;
2018-12-27 18:50:45 +03:00
out_to_key . key = tx_builder : : generate_invalid_pub_key ( ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_output_with_zero_amount : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner_account ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
2022-05-25 22:31:23 +02:00
boost : : get < tx_out_bare > ( builder . m_tx . vout . front ( ) ) . amount = 0 ;
2018-12-27 18:50:45 +03:00
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
bool gen_tx_output_is_not_txout_to_key : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
return true ;
// this test is not needed anymore, to be deleted
/*REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . m_tx . vout . push_back ( tx_out ( ) ) ;
builder . m_tx . vout . back ( ) . amount = 1 ;
//TODO: fix test
//builder.m_tx.vout.back().target = txout_to_script();
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . m_tx . vout . push_back ( tx_out ( ) ) ;
builder . m_tx . vout . back ( ) . amount = 1 ;
//TODO: fix test
//builder.m_tx.vout.back().target = txout_to_scripthash();
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
*/
}
void check_broken_tx ( std : : vector < test_event_entry > & events , const transaction & broken_tx , const block & head_block , const account_base & miner_acc , test_generator & generator )
{
// 1. make sure broken_tx will be rejected by the tx_pool
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( broken_tx ) ;
// 2. make sure it will be rejected by blockchain_storage even if it is accepted by the pool
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block | event_visitor_settings : : set_skip_txs_blobsize_check , true , true ) ) ; // to avoid many checks in the pool
events . push_back ( broken_tx ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block | event_visitor_settings : : set_skip_txs_blobsize_check , false , false ) ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , broken_block , head_block , miner_acc , broken_tx ) ;
// clear the pool of broken_tx
DO_CALLBACK ( events , " clear_tx_pool " ) ;
}
bool gen_tx_signatures_are_invalid : : generate ( std : : vector < test_event_entry > & events ) const
{
GENERATE_ACCOUNT ( preminer_account ) ;
GENERATE_ACCOUNT ( miner_account ) ;
GENERATE_ACCOUNT ( alice_account ) ;
GENERATE_ACCOUNT ( bob_account ) ;
GENERATE_ACCOUNT ( carol_account ) ;
GENERATE_ACCOUNT ( dan_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_account , test_core_time : : get_time ( ) ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ;
REWIND_BLOCKS_N ( events , blk_1r , blk_1 , miner_account , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
2019-04-25 23:37:43 +02:00
uint64_t amount = TESTS_DEFAULT_FEE * 80 ;
2018-12-27 18:50:45 +03:00
// prepare needed outputs by transferring them to Carol and Dan
MAKE_TX ( events , tx_a , miner_account , carol_account , amount / 2 , blk_1r ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_a , blk_1r , miner_account , tx_a ) ;
MAKE_TX ( events , tx_b , miner_account , carol_account , amount / 2 , blk_1r_a ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_b , blk_1r_a , miner_account , tx_b ) ;
MAKE_TX ( events , tx_c , miner_account , carol_account , amount / 2 , blk_1r_b ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_c , blk_1r_b , miner_account , tx_c ) ;
MAKE_TX ( events , tx_d , miner_account , dan_account , amount / 2 , blk_1r_c ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_d , blk_1r_c , miner_account , tx_d ) ;
MAKE_TX ( events , tx_e , miner_account , dan_account , amount / 2 , blk_1r_d ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_e , blk_1r_d , miner_account , tx_e ) ;
MAKE_TX ( events , tx_f , miner_account , dan_account , amount / 2 , blk_1r_e ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1r_f , blk_1r_e , miner_account , tx_f ) ;
// create reference transaction tx_0, Carol->Alice, nmix=0
MAKE_TX ( events , tx_0 , carol_account , alice_account , amount , blk_1r_f ) ;
events . pop_back ( ) ;
2022-06-19 19:47:43 +02:00
CHECK_AND_ASSERT_MES ( tx_0 . vin . size ( ) > 1 & & tx_0 . vout . size ( ) > 1 & & tx_0 . signatures . size ( ) > 1 , false , " tx_0 is incorrect " ) ; // need > 1 for this test
2018-12-27 18:50:45 +03:00
// create reference transaction tx_1, Dan->Alice, nmix=1
MAKE_TX_MIX ( events , tx_1 , dan_account , alice_account , amount , 1 , blk_1r_f ) ;
events . pop_back ( ) ;
2022-06-19 19:47:43 +02:00
CHECK_AND_ASSERT_MES ( tx_1 . vin . size ( ) > 1 & & tx_1 . vout . size ( ) > 1 & & tx_1 . signatures . size ( ) > 1 , false , " tx_1 is incorrect " ) ; // need > 1 for this test
2018-12-27 18:50:45 +03:00
const block & prev_block = blk_1r_f ;
transaction broken_tx ;
// Tx with nmix = 0 without signatures
broken_tx = tx_0 ;
2022-06-19 19:47:43 +02:00
broken_tx . signatures . clear ( ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 0 have a few inputs, and not enough signatures
broken_tx = tx_0 ;
2022-06-19 19:47:43 +02:00
broken_tx . signatures . resize ( broken_tx . signatures . size ( ) - 1 ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 0 have a few inputs, and too many signatures (1/2)
broken_tx = tx_0 ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . push_back ( invalid_signature ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 0 have a few inputs, and too many signatures (2/2)
broken_tx = tx_0 ;
2022-06-25 00:06:26 +02:00
broken_tx . signatures . push_back ( currency : : NLSAG_sig ( ) ) ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . push_back ( invalid_signature ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 1 without signatures
broken_tx = tx_1 ;
2022-06-19 19:47:43 +02:00
broken_tx . signatures . clear ( ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 1 have not enough signatures (1/2)
broken_tx = tx_1 ;
2022-06-19 19:47:43 +02:00
broken_tx . signatures . resize ( broken_tx . signatures . size ( ) - 1 ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 1 have not enough signatures (2/2)
broken_tx = tx_1 ;
2022-06-25 00:06:26 +02:00
boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . resize ( boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . size ( ) - 1 ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 1 have too many signatures (1/2)
broken_tx = tx_1 ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . push_back ( invalid_signature ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Tx with nmix = 1 have too many signatures (2/2)
broken_tx = tx_1 ;
2022-06-25 00:06:26 +02:00
broken_tx . signatures . push_back ( currency : : NLSAG_sig ( ) ) ;
2022-06-19 19:47:43 +02:00
boost : : get < currency : : NLSAG_sig > ( broken_tx . signatures . back ( ) ) . s . push_back ( invalid_signature ) ;
2018-12-27 18:50:45 +03:00
check_broken_tx ( events , broken_tx , prev_block , miner_account , generator ) ;
// Finally check that the original transactions are accepted and passed
events . push_back ( tx_0 ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_3 , prev_block , miner_account , std : : list < transaction > ( { tx_0 , tx_1 } ) ) ;
// spend all Alice's money to make sure she has successfully received it with 'tx'
2019-04-25 23:37:43 +02:00
MAKE_TX ( events , tx_2 , alice_account , bob_account , amount * 2 - TESTS_DEFAULT_FEE , blk_3 ) ;
2018-12-27 18:50:45 +03:00
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_3 , miner_account , tx_2 ) ;
return true ;
}
void fill_test_offer_ ( bc_services : : offer_details & od )
{
od . offer_type = OFFER_TYPE_PRIMARY_TO_TARGET ;
od . amount_primary = 1000000000 ;
od . amount_target = 22222222 ;
od . target = " USD " ;
od . location_country = " US " ;
od . location_city = " New York City " ;
od . contacts = " skype: zina; icq: 12313212; email: zina@zina.com; mobile: +621234567834 " ;
od . comment = " The best ever rate in NYC!!! " ;
od . payment_types = " BTC;BANK;CASH " ;
od . expiration_time = 10 ;
}
bool gen_broken_attachments : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
//
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_account ) ;
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2 , miner_account ) ;
MAKE_NEXT_BLOCK ( events , blk_4 , blk_3 , miner_account ) ;
REWIND_BLOCKS ( events , blk_5 , blk_4 , miner_account ) ;
REWIND_BLOCKS_N ( events , blk_5r , blk_5 , miner_account , 20 ) ;
bc_services : : offer_details od = AUTO_VAL_INIT ( od ) ;
fill_test_offer_ ( od ) ;
std : : vector < currency : : attachment_v > attachments ;
bc_services : : put_offer_into_attachment ( od , attachments ) ;
std : : list < currency : : transaction > txs_set ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
construct_broken_tx ( txs_set , events , miner_account , miner_account , blk_5r , attachments , [ ] ( transaction & tx )
{
//don't put attachments info into
return true ;
} ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
construct_broken_tx ( txs_set , events , miner_account , miner_account , blk_5r , std : : vector < currency : : attachment_v > ( ) , [ & ] ( transaction & tx )
{
extra_attachment_info eai = extra_attachment_info ( ) ;
//put hash into extra
std : : stringstream ss ;
binary_archive < true > oar ( ss ) ;
bool r = : : do_serialize ( oar , const_cast < std : : vector < attachment_v > & > ( attachments ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " do_serialize failed " ) ;
std : : string buff = ss . str ( ) ;
eai . sz = buff . size ( ) ;
eai . hsh = get_blob_hash ( buff ) ;
tx . extra . push_back ( eai ) ;
return true ;
} ) ;
return true ;
}
2021-03-12 07:13:30 +03:00
//------------------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
gen_crypted_attachments : : gen_crypted_attachments ( )
{
REGISTER_CALLBACK ( " check_crypted_tx " , gen_crypted_attachments : : check_crypted_tx ) ;
REGISTER_CALLBACK ( " set_blockchain_height " , gen_crypted_attachments : : set_blockchain_height ) ;
REGISTER_CALLBACK ( " set_crypted_tx_height " , gen_crypted_attachments : : set_crypted_tx_height ) ;
REGISTER_CALLBACK ( " check_offers_count_befor_cancel " , gen_crypted_attachments : : check_offers_count_befor_cancel ) ;
REGISTER_CALLBACK ( " check_offers_count_after_cancel " , gen_crypted_attachments : : check_offers_count_after_cancel ) ;
2022-05-26 16:53:40 +02:00
m_hardforks . set_hardfork_height ( 1 , 0 ) ;
m_hardforks . set_hardfork_height ( 2 , 0 ) ; // tx_payer is allowed only after HF2
2021-03-12 07:14:28 +03:00
}
2018-12-27 18:50:45 +03:00
bool gen_crypted_attachments : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1338224400 ;
GENERATE_ACCOUNT ( miner_account ) ;
//
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
2021-03-12 07:14:28 +03:00
set_hard_fork_heights_to_generator ( generator ) ;
DO_CALLBACK ( events , " configure_core " ) ;
2018-12-27 18:50:45 +03:00
MAKE_ACCOUNT ( events , bob_account ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_account ) ;
MAKE_NEXT_BLOCK ( events , blk_4 , blk_1 , miner_account ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_5 , blk_4 , miner_account , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " set_blockchain_height " ) ;
2020-04-23 15:41:40 +03:00
pr . acc_addr = miner_account . get_keys ( ) . account_address ;
2018-12-27 18:50:45 +03:00
cm . comment = " Comandante Che Guevara " ;
2019-09-27 17:16:18 +02:00
//ms.msg = "Hasta Siempre, Comandante";
2018-12-27 18:50:45 +03:00
std : : vector < currency : : attachment_v > attachments ;
attachments . push_back ( pr ) ;
attachments . push_back ( cm ) ;
2019-09-27 17:16:18 +02:00
//attachments.push_back(ms);
2018-12-27 18:50:45 +03:00
MAKE_TX_LIST_START ( events , txs_list , miner_account , bob_account , MK_TEST_COINS ( 1 ) , blk_5 ) ;
MAKE_TX_LIST_ATTACH ( events , txs_list , miner_account , bob_account , MK_TEST_COINS ( 1 ) , blk_5 , attachments ) ;
DO_CALLBACK ( events , " set_crypted_tx_height " ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_6 , blk_5 , miner_account , txs_list ) ;
DO_CALLBACK ( events , " check_crypted_tx " ) ;
MAKE_TX_LIST_START ( events , txs_list2 , miner_account , bob_account , MK_TEST_COINS ( 1 ) , blk_6 ) ;
//create two offers
currency : : transaction tx ;
std : : vector < currency : : attachment_v > attachments2 ;
bc_services : : offer_details od = AUTO_VAL_INIT ( od ) ;
fill_test_offer_ ( od ) ;
bc_services : : put_offer_into_attachment ( od , attachments2 ) ;
bc_services : : put_offer_into_attachment ( od , attachments2 ) ;
2022-05-11 23:52:33 +02:00
construct_tx_to_key ( m_hardforks , events , tx , blk_6 , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , CURRENCY_TO_KEY_OUT_RELAXED , std : : vector < currency : : extra_v > ( ) , attachments2 ) ;
2018-12-27 18:50:45 +03:00
txs_list2 . push_back ( tx ) ;
events . push_back ( tx ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_7 , blk_6 , miner_account , txs_list2 ) ;
DO_CALLBACK ( events , " check_offers_count_befor_cancel " ) ;
//create two cancel offers
MAKE_TX_LIST_START ( events , txs_list3 , miner_account , bob_account , MK_TEST_COINS ( 1 ) , blk_7 ) ;
std : : vector < currency : : attachment_v > attachments3 ;
bc_services : : cancel_offer co = AUTO_VAL_INIT ( co ) ;
co . tx_id = get_transaction_hash ( tx ) ;
co . offer_index = 0 ;
crypto : : public_key related_tx_pub_key = get_tx_pub_key_from_extra ( tx ) ;
crypto : : public_key related_tx_offer_pub_key = bc_services : : get_offer_secure_key_by_index_from_tx ( tx , 0 ) ;
currency : : keypair ephemeral ;
bool r = currency : : derive_ephemeral_key_helper ( miner_account . get_keys ( ) , related_tx_pub_key , 0 , ephemeral ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to derive_ephemeral_key_helper " ) ;
CHECK_AND_ASSERT_THROW_MES ( ephemeral . pub = = related_tx_offer_pub_key , " Failed to derive_ephemeral_key_helper(pub keys missmatch) " ) ;
blobdata bl_for_sig = bc_services : : make_offer_sig_blob ( co ) ;
crypto : : generate_signature ( crypto : : cn_fast_hash ( bl_for_sig . data ( ) , bl_for_sig . size ( ) ) , ephemeral . pub , ephemeral . sec , co . sig ) ;
// blobdata bl_for_sig = currency::make_offer_sig_blob(co);
// crypto::generate_signature(crypto::cn_fast_hash(bl_for_sig.data(), bl_for_sig.size()),
// currency::get_tx_pub_key_from_extra(tx),
// off_key_pair.sec, co.sig);
bc_services : : put_offer_into_attachment ( co , attachments3 ) ;
co . offer_index = 1 ;
crypto : : public_key related_tx_pub_key_1 = get_tx_pub_key_from_extra ( tx ) ;
crypto : : public_key related_tx_offer_pub_key_1 = bc_services : : get_offer_secure_key_by_index_from_tx ( tx , 1 ) ;
currency : : keypair ephemeral_1 ;
r = currency : : derive_ephemeral_key_helper ( miner_account . get_keys ( ) , related_tx_pub_key_1 , 1 , ephemeral_1 ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Failed to derive_ephemeral_key_helper " ) ;
CHECK_AND_ASSERT_THROW_MES ( ephemeral_1 . pub = = related_tx_offer_pub_key_1 , " Failed to derive_ephemeral_key_helper(pub keys missmatch) " ) ;
bl_for_sig = bc_services : : make_offer_sig_blob ( co ) ;
crypto : : generate_signature ( crypto : : cn_fast_hash ( bl_for_sig . data ( ) , bl_for_sig . size ( ) ) , ephemeral_1 . pub , ephemeral_1 . sec , co . sig ) ;
// bl_for_sig = currency::make_offer_sig_blob(co);
// crypto::generate_signature(crypto::cn_fast_hash(bl_for_sig.data(), bl_for_sig.size()),
// currency::get_tx_pub_key_from_extra(tx),
// off_key_pair.sec, co.sig);
bc_services : : put_offer_into_attachment ( co , attachments3 ) ;
2022-05-11 23:52:33 +02:00
construct_tx_to_key ( m_hardforks , events , tx , blk_7 , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , CURRENCY_TO_KEY_OUT_RELAXED , std : : vector < currency : : extra_v > ( ) , attachments3 ) ;
2018-12-27 18:50:45 +03:00
txs_list3 . push_back ( tx ) ;
events . push_back ( tx ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_8 , blk_7 , miner_account , txs_list3 ) ;
DO_CALLBACK ( events , " check_offers_count_after_cancel " ) ;
return true ;
}
bool gen_crypted_attachments : : set_blockchain_height ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & /*events*/ )
{
bc_height_before = c . get_current_blockchain_size ( ) ;
return true ;
}
bool gen_crypted_attachments : : set_crypted_tx_height ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
crypted_tx_height = ev_index - 1 ;
return true ;
}
bool gen_crypted_attachments : : check_offers_count_befor_cancel ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & /*events*/ )
{
std : : list < bc_services : : offer_details_ex > od ;
get_all_offers ( c , od ) ;
offers_before_canel = od . size ( ) ;
return true ;
}
bool gen_crypted_attachments : : check_offers_count_after_cancel ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & /*events*/ )
{
std : : list < bc_services : : offer_details_ex > od ;
get_all_offers ( c , od ) ;
CHECK_EQ ( offers_before_canel - 2 , od . size ( ) ) ;
return true ;
}
bool gen_crypted_attachments : : check_crypted_tx ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
//// check cha cha
std : : string test_key = " ssssss " ;
std : : vector < std : : string > test_array ;
test_array . push_back ( " qweqweqwqw " ) ;
test_array . push_back ( " 4g45 " ) ;
test_array . push_back ( " qweqwe56575qwqw " ) ;
test_array . push_back ( " q3f34f4 " ) ;
std : : vector < std : : string > test_array2 = test_array ;
for ( auto & v : test_array2 )
chacha_crypt ( v , test_key ) ;
for ( auto & v : test_array2 )
chacha_crypt ( v , test_key ) ;
if ( test_array2 ! = test_array )
{
LOG_ERROR ( " cha cha broken " ) ;
return false ;
}
//
const currency : : transaction & tx = boost : : get < currency : : transaction > ( events [ crypted_tx_height ] ) ;
2021-03-12 07:14:28 +03:00
const currency : : account_base & bob_acc = boost : : get < currency : : account_base > ( events [ 2 ] ) ;
2018-12-27 18:50:45 +03:00
CHECK_EQ ( c . get_current_blockchain_size ( ) , bc_height_before + 1 ) ;
auto ptx_from_bc = c . get_blockchain_storage ( ) . get_tx ( currency : : get_transaction_hash ( tx ) ) ;
CHECK_TEST_CONDITION ( ptx_from_bc ) ;
std : : vector < payload_items_v > at ;
bool r = currency : : decrypt_payload_items ( true , * ptx_from_bc , bob_acc . get_keys ( ) , at ) ;
CHECK_EQ ( r , true ) ;
2024-03-14 22:04:38 +01:00
if ( at . size ( ) ! = 16 )
{
std : : stringstream ss ;
for ( auto & el : at )
ss < < " " < < el . type ( ) . name ( ) < < ENDL ;
LOG_PRINT_RED ( " at.size() = " < < at . size ( ) < < " : " < < ENDL < < ss . str ( ) , LOG_LEVEL_0 ) ;
// expected items:
// public_key
// etc_tx_flags16_t
// tx_derivation_hint x 10
// extra_attachment_info
// tx_payer
// tx_comment
// tx_crypto_checksum
//
// total: 16
CHECK_AND_ASSERT_MES ( false , false , " unexpected number of decrypted items " ) ;
}
2018-12-27 18:50:45 +03:00
currency : : tx_payer decrypted_pr = AUTO_VAL_INIT ( decrypted_pr ) ;
r = get_type_in_variant_container ( at , decrypted_pr ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_type_in_variant_container failed " ) ;
if ( pr . acc_addr ! = decrypted_pr . acc_addr )
{
LOG_ERROR ( " decrypted wrong data: "
< < epee : : string_tools : : pod_to_hex ( decrypted_pr . acc_addr )
< < " expected: " < < epee : : string_tools : : pod_to_hex ( pr . acc_addr ) ) ;
return false ;
}
currency : : tx_comment decrypted_comment = AUTO_VAL_INIT ( decrypted_comment ) ;
r = get_type_in_variant_container ( at , decrypted_comment ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_type_in_variant_container failed " ) ;
if ( cm . comment ! = decrypted_comment . comment )
{
LOG_ERROR ( " decrypted wrong data: "
< < decrypted_comment . comment
< < " expected: " < < cm . comment ) ;
return false ;
}
2019-09-27 17:16:18 +02:00
// currency::tx_message decrypted_message = AUTO_VAL_INIT(decrypted_message);
// r = get_type_in_variant_container(at, decrypted_message);
// CHECK_AND_ASSERT_MES(r, false, "get_type_in_variant_container failed");
// if (ms.msg != decrypted_message.msg)
// {
// LOG_ERROR("decrypted wrong data: "
// << decrypted_message.msg
// << "expected:" << ms.msg);
// return false;
// }
2018-12-27 18:50:45 +03:00
// now cancel attachment
return true ;
}
2021-03-11 19:43:39 +03:00
gen_tx_extra_double_entry : : gen_tx_extra_double_entry ( )
{
REGISTER_CALLBACK_METHOD ( gen_tx_extra_double_entry , configure_core ) ;
}
bool gen_tx_extra_double_entry : : configure_core ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
currency : : core_runtime_config pc = c . get_blockchain_storage ( ) . get_core_runtime_config ( ) ;
pc . min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE ;
pc . pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH ;
2022-05-26 16:53:40 +02:00
pc . hard_forks . set_hardfork_height ( 1 , 0 ) ;
pc . hard_forks . set_hardfork_height ( 2 , 0 ) ;
2021-03-11 19:43:39 +03:00
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
2018-12-27 18:50:45 +03:00
bool gen_tx_extra_double_entry : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts_start = 1450000000 ;
bool r = false ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
2021-03-12 07:14:28 +03:00
generator . set_hardfork_height ( 1 , 0 ) ;
generator . set_hardfork_height ( 2 , 0 ) ; // extra_alias_entry is only allowed after HF2, so switch it on here
2021-03-11 19:43:39 +03:00
DO_CALLBACK ( events , " configure_core " ) ;
2018-12-27 18:50:45 +03:00
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_account , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
tx_builder builder ;
// 1. double pubkey
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
add_tx_pub_key_to_extra ( builder . m_tx , builder . m_tx_key . pub ) ; // add redundant second pubkey to tx extra
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_1 are rejected
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_account , builder . m_tx ) ;
// 2. double attachment
bc_services : : offer_details od = AUTO_VAL_INIT ( od ) ;
fill_test_offer_ ( od ) ;
//std::vector<currency::attachment_v> attachments;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
bc_services : : put_offer_into_attachment ( od , builder . m_tx . attachment ) ;
add_attachments_info_to_extra ( builder . m_tx . extra , builder . m_tx . attachment ) ;
add_attachments_info_to_extra ( builder . m_tx . extra , builder . m_tx . attachment ) ; // two attachment entry
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_2 are rejected
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_0r , miner_account , builder . m_tx ) ;
// 3. double alias
extra_alias_entry ai ;
ai . m_alias = " some-alias " ;
ai . m_address = miner_account . get_public_address ( ) ;
ai . m_text_comment = " Mandame unas monedas, por favor! " ;
uint64_t alias_reward = currency : : get_alias_coast_from_fee ( ai . m_alias , TESTS_DEFAULT_FEE ) ;
sources . clear ( ) ;
destinations . clear ( ) ;
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE + alias_reward , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
tx_destination_entry de ;
de . addr . push_back ( null_pub_addr ) ;
de . amount = alias_reward ;
destinations . push_back ( de ) ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
add_tx_extra_alias ( builder . m_tx , ai ) ;
add_tx_extra_alias ( builder . m_tx , ai ) ; // second alias entry in the extra
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_3 are rejected
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_0r , miner_account , builder . m_tx ) ;
// 4. double extra_user_data
sources . clear ( ) ;
destinations . clear ( ) ;
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
extra_user_data eud ;
eud . buff = std : : string ( 2049 , ' $ ' ) ;
builder . m_tx . extra . push_back ( eud ) ;
builder . m_tx . extra . push_back ( eud ) ; // second extra user data entry
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_4 are rejected
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_4 , blk_0r , miner_account , builder . m_tx ) ;
// 5. double extra_padding
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
extra_padding padding ;
padding . buff . resize ( 2049 ) ;
builder . m_tx . extra . push_back ( padding ) ;
builder . m_tx . extra . push_back ( padding ) ; // extra_padding entry
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_5 are rejected
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_0r , miner_account , builder . m_tx ) ;
// 6. each entry is added only once
ai = AUTO_VAL_INIT ( ai ) ;
ai . m_alias = " uwndjckforptifmslf " ;
ai . m_address = miner_account . get_public_address ( ) ;
ai . m_text_comment = " Mandame unas monedas, por favor! " ;
sources . clear ( ) ;
destinations . clear ( ) ;
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE + alias_reward , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
de = AUTO_VAL_INIT ( de ) ;
de . addr . push_back ( currency : : account_public_address ( ) ) ;
de . amount = alias_reward ;
destinations . push_back ( de ) ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
bc_services : : put_offer_into_attachment ( od , builder . m_tx . attachment ) ;
add_attachments_info_to_extra ( builder . m_tx . extra , builder . m_tx . attachment ) ; // attachment
add_tx_extra_alias ( builder . m_tx , ai ) ; // alias
builder . m_tx . extra . push_back ( eud ) ; // user data
builder . m_tx . extra . push_back ( padding ) ; // padding
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
// EXPECTED: tx and blk_6 are accepted
events . push_back ( builder . m_tx ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_65 , blk_0r , miner_account , builder . m_tx ) ;
return true ;
}
bool gen_tx_double_key_image : : generate ( std : : vector < test_event_entry > & events ) const
{
// Make a tx with correct, but used more then once key_image in inputs. Should be invalid tx.
uint64_t ts_start = test_core_time : : get_time ( ) ;
GENERATE_ACCOUNT ( miner_account ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_account , ts_start ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_account , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
fill_tx_sources_and_destinations ( events , blk_0r , miner_account , miner_account , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
sources . push_back ( sources . front ( ) ) ; // dobule source entry => double imput => double key image
tx_builder builder ;
builder . step1_init ( ) ;
builder . step2_fill_inputs ( miner_account . get_keys ( ) , sources ) ;
builder . step3_fill_outputs ( destinations ) ;
builder . step4_calc_hash ( ) ;
builder . step5_sign ( sources ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( builder . m_tx ) ;
return true ;
}
//------------------------------------------------------------------
bool tx_expiration_time : : generate ( std : : vector < test_event_entry > & events ) const
{
bool r = false ;
GENERATE_ACCOUNT ( miner_acc ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// transfer to Alice some coins in chunks
uint64_t alice_amount = MK_TEST_COINS ( 10 ) ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx_with_many_outputs ( m_hardforks , events , blk_0r , miner_acc . get_keys ( ) , alice_acc . get_public_address ( ) , alice_amount , 10 , TESTS_DEFAULT_FEE , tx_1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx_with_many_outputs failed " ) ;
events . push_back ( tx_1 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
// check Alice's balance
std : : shared_ptr < tools : : wallet2 > alice_wlt ;
r = generator . init_test_wallet ( alice_acc , get_block_hash ( blk_0 ) , alice_wlt ) ;
CHECK_AND_ASSERT_MES ( r , false , " init_test_wallet failed " ) ;
r = generator . refresh_test_wallet ( events , alice_wlt . get ( ) , get_block_hash ( blk_2 ) , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " refresh_test_wallet failed " ) ;
CHECK_AND_ASSERT_MES ( check_balance_via_wallet ( * alice_wlt . get ( ) , " alice " , alice_amount , 0 , 0 , 0 , 0 ) , false , " " ) ;
uint64_t ts_median = generator . get_timestamps_median ( get_block_hash ( blk_2 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
LOG_PRINT_YELLOW ( " %%%%%%% ts_median = " < < ts_median , LOG_LEVEL_0 ) ;
/// core tx expiration condition:
/// expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW)
// 1/3
// create normal tx, then add expiration time entry to the extra and then resign
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
r = fill_tx_sources_and_destinations ( events , blk_2 , alice_acc . get_keys ( ) , bob_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2024-12-27 07:27:43 +01:00
transaction tx_2 { } ;
2025-04-07 04:47:55 +03:00
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , events , this , tx_2 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
set_tx_expiration_time ( tx_2 , ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 1 ) ; // one second greather than minimum allowed
r = resign_tx ( alice_acc . get_keys ( ) , sources , tx_2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
// should pass normally
events . push_back ( tx_2 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_2 ) ;
// 2/3
// normal tx with expiration time entry equal to median minus shift - should not pass at tx_pool
ts_median = generator . get_timestamps_median ( get_block_hash ( blk_3 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
sources . clear ( ) ;
destinations . clear ( ) ;
r = fill_tx_sources_and_destinations ( events , blk_3 , alice_acc . get_keys ( ) , bob_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2025-04-07 04:47:55 +03:00
transaction tx_3 { } ;
r = construct_tx ( alice_acc . get_keys ( ) , sources , destinations , events , this , tx_3 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
set_tx_expiration_time ( tx_3 , ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 0 ) ; // exact expiration time, should not pass (see core condition above)
r = resign_tx ( alice_acc . get_keys ( ) , sources , tx_3 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
// should be rejected at tx_pool
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_3 ) ;
// 3/3
// normal tx with expiration time entry equal to median minus shift - should not pass at the core
// create a new block, but do not add it to the events vector for a while
MAKE_NEXT_BLOCK ( events , blk_4 , blk_3 , miner_acc ) ;
events . pop_back ( ) ;
// update median
ts_median = generator . get_timestamps_median ( get_block_hash ( blk_4 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
// prepare transaction - use tx_3 change it's expiration time and resign
set_tx_expiration_time ( tx_3 , ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 0 ) ; // exact expiration time, should not pass (see core condition above)
r = resign_tx ( alice_acc . get_keys ( ) , sources , tx_3 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
// should be accepted by the pool
events . push_back ( tx_3 ) ;
// add blk_4, this step was deferred in order to skip expiration condition at tx_memory_pool::add_tx
events . push_back ( blk_4 ) ;
// blk_5 contain already expired tx_3 and thus should be rejected by the core
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_4 , miner_acc , tx_3 ) ;
return true ;
}
//------------------------------------------------------------------
tx_expiration_time_and_block_template : : tx_expiration_time_and_block_template ( )
{
REGISTER_CALLBACK_METHOD ( tx_expiration_time_and_block_template , c1 ) ;
}
bool tx_expiration_time_and_block_template : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: make sure expired transaction, waiting in tx pool, won't be included into a block template, making it invalid
GENERATE_ACCOUNT ( miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
uint64_t ts_median = generator . get_timestamps_median ( get_block_hash ( blk_0r ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
LOG_PRINT_YELLOW ( " %%%%%%% ts_median = " < < ts_median , LOG_LEVEL_0 ) ;
/// core tx expiration condition:
/// expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW)
// create normal tx, then add expiration time entry to the extra and then resign
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
bool r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , miner_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2024-12-27 07:27:43 +01:00
transaction tx_1 { } ;
2025-04-07 04:47:55 +03:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , events , this , tx_1 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
uint64_t tx_1_expiration_time = ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 1 ; // one second greather than minimum allowed
set_tx_expiration_time ( tx_1 , tx_1_expiration_time ) ;
r = resign_tx ( miner_acc . get_keys ( ) , sources , tx_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
events . push_back ( tx_1 ) ; // should normally pass into tx pool
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0r , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
ts_median = generator . get_timestamps_median ( get_block_hash ( blk_2 ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
// make sure tx_1 is expired with this median
CHECK_AND_ASSERT_MES ( tx_1_expiration_time - TX_EXPIRATION_MEDIAN_SHIFT < = ts_median , false , " tx_1 didn't expire yet as expected " ) ;
// okay, now we have expired transaction in tx pool, try to construct block_template and mine a block
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool tx_expiration_time_and_block_template : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect tx count in the pool: " < < c . get_pool_transactions_count ( ) ) ;
account_public_address addr = AUTO_VAL_INIT ( addr ) ;
2019-03-25 01:30:20 +01:00
bool r = mine_next_pow_block_in_playtime ( addr , c ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_block_in_playtime failed " ) ;
// tx MAY stay in the pool, check it as forced condition (may change in future)
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Incorrect tx count in the pool: " < < c . get_pool_transactions_count ( ) ) ;
LOG_PRINT ( " tx pool: " < < ENDL < < c . get_tx_pool ( ) . print_pool ( true ) , LOG_LEVEL_0 ) ;
LOG_PRINT_YELLOW ( " tx_pool::on_idle() " , LOG_LEVEL_0 ) ;
c . get_tx_pool ( ) . on_idle ( ) ;
LOG_PRINT ( " tx pool: " < < ENDL < < c . get_tx_pool ( ) . print_pool ( true ) , LOG_LEVEL_0 ) ;
return true ;
}
//------------------------------------------------------------------
bool tx_expiration_time_and_chain_switching : : generate ( std : : vector < test_event_entry > & events ) const
{
// test idea: check border case when tx with expiration time are moved back from blockchain to the pool
bool r = false ;
GENERATE_ACCOUNT ( miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
//
// chain A
//
// 0 ... 20 21 22 23 24 <- height
// (0 )- (0r)- (1 )- (2 )- <- chain A
// tx_1
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner_acc , static_cast < size_t > ( std : : max ( CURRENCY_MINED_MONEY_UNLOCK_WINDOW , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ) ) ;
uint64_t ts_median = generator . get_timestamps_median ( get_block_hash ( blk_0r ) , TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
LOG_PRINT_YELLOW ( " %%%%%%% ts_median = " < < ts_median < < " for the last " < < TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW < < " blocks " , LOG_LEVEL_0 ) ;
/// core tx expiration GOOD condition:
/// expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW)
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , miner_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2024-12-27 07:27:43 +01:00
transaction tx_0 { } ;
2025-04-07 04:47:55 +03:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , events , this , tx_0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
uint64_t tx_0_expiration_time = ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 0 ; // one second less than minimum allowed (see condition above)
set_tx_expiration_time ( tx_0 , tx_0_expiration_time ) ;
r = resign_tx ( miner_acc . get_keys ( ) , sources , tx_0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_0 ) ; // should not pass at tx pool as it has expiration time exactly at the right boundary of allowed period
// tx_1 is the same except the expiration time
transaction tx_1 = tx_0 ;
uint64_t tx_1_expiration_time = ts_median + TX_EXPIRATION_MEDIAN_SHIFT + 1 ; // minimum allowed ts (see condition above)
set_tx_expiration_time ( tx_1 , tx_1_expiration_time ) ;
r = resign_tx ( miner_acc . get_keys ( ) , sources , tx_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
events . push_back ( tx_1 ) ; // should pass
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_1 ) ; // tx_1 and blk_1 should pass
//
// chain B
//
// 0 ... 20 21 22 23 24 <- height
// (0 )- (0r)- (1 )- <- chain A
// | tx_1
2020-08-20 17:05:03 +03:00
// \
2018-12-27 18:50:45 +03:00
// \ (1b)- (2b)- <- chain B (became main after 2b)
//
MAKE_NEXT_BLOCK ( events , blk_1b , blk_0r , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_2b , blk_1b , miner_acc ) ; // <-- should trigger chain switching
// make sure the chain switching was ok
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( blk_2b ) ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) )
// 0 ... 20 21 22 23 24 <- height
// (0 )- (0r)- (1 )- <- chain A
// | tx_1
2020-08-20 17:05:03 +03:00
// \
2018-12-27 18:50:45 +03:00
// \ (1b)- (2b)- !3b!- <- chain B, block 3b is rejected because tx_1 is already expired
// tx_1
// tx_1 is still in the pool, but can't be added to any block anymore as it was already expired
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3b_bad , blk_2b , miner_acc , tx_1 ) ;
// make sure top block wasn't changed
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( blk_2b ) ) ;
// make sure tx_1 is still in the pool
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) )
//
// chain C
//
// 0 ... 20 21 22 23 24 <- height
// (0 )- (0r)- (1 )- <- chain A
// | tx_1
2020-08-20 17:05:03 +03:00
// |\
2018-12-27 18:50:45 +03:00
// | \ (1b)- (2b)- <- chain B
// |
// \- (1c)- (2c)- (3c)- <- chain C
// tx_1
// should pass as tx_1 can be added to any block on height 21 (it's not yet expired on that height)
MAKE_NEXT_BLOCK_TX1 ( events , blk_1c , blk_0r , miner_acc , tx_1 ) ;
MAKE_NEXT_BLOCK ( events , blk_2c , blk_1c , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_3c , blk_2c , miner_acc ) ; // <- should trigger chain switching
// make sure the chain switching was ok and the pool is empty
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( blk_3c ) ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 0 ) )
//
// switch to chain B again
//
// 0 ... 20 21 22 23 24 <- height
// (0 )- (0r)- (1 )- <- chain A
// | tx_1
2020-08-20 17:05:03 +03:00
// |\
2018-12-27 18:50:45 +03:00
// | \ (1b)- (2b)- (3b)- (4b)- <- chain B
// |
// \- (1c)- (2c)- (3c)- <- chain C
// tx_1
// should pass as tx_1 can be added to any block on height 21 (it's not yet expired on that height)
MAKE_NEXT_BLOCK ( events , blk_3b , blk_2b , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_4b , blk_3b , miner_acc ) ; // <- should trigger chain switching
// make sure the chain switching was ok
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( blk_4b ) ) ;
// tx_1 should be popped out to the pool again
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) )
return true ;
}
2019-10-16 16:34:34 +03:00
//------------------------------------------------------------------
bool tx_key_image_pool_conflict : : generate ( std : : vector < test_event_entry > & events ) const
{
2019-10-17 14:04:13 +03:00
// Test idea: check tx that is stuck in tx pool because one on its key images is already spent in the blockchain
// 1) if it's linked to an alt block -- tx will not be removed as long as linked alt block exists (in order to be able to switch)
// 2) if it's not linked to an alt block -- it will be removed after CONFLICT_KEY_IMAGE_SPENT_DEPTH_TO_REMOVE_TX_FROM_POOL confirmations of conflicted tx
// or it will be removed once tx is old enough (CURRENCY_MEMPOOL_TX_LIVETIME)
2019-10-16 16:34:34 +03:00
bool r = false ;
m_miner_acc . generate ( ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , m_miner_acc , test_core_time : : get_time ( ) ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , m_miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3 ) ;
// make tx_0 : miner -> bob
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
r = fill_tx_sources_and_destinations ( events , blk_0r , m_miner_acc . get_keys ( ) , bob_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
2024-12-27 07:27:43 +01:00
transaction tx_0 { } ;
2025-04-07 04:47:55 +03:00
r = construct_tx ( m_miner_acc . get_keys ( ) , sources , destinations , events , this , tx_0 ) ;
2019-10-16 16:34:34 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
LOG_PRINT_YELLOW ( " tx_0 = " < < get_transaction_hash ( tx_0 ) , LOG_LEVEL_0 ) ;
// do not push tx_0 into events yet
// tx_1 spends the same key image as tx_0
transaction tx_1 = tx_0 ;
keypair kp = keypair : : generate ( ) ;
// change tx pub key to end up with different tx hash
update_or_add_field_to_extra ( tx_1 . extra , kp . pub ) ;
r = resign_tx ( m_miner_acc . get_keys ( ) , sources , tx_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
LOG_PRINT_YELLOW ( " tx_1 = " < < get_transaction_hash ( tx_1 ) , LOG_LEVEL_0 ) ;
// tx_2 spends the same key image as tx_0
transaction tx_2 = tx_0 ;
kp = keypair : : generate ( ) ;
// change tx pub key to end up with different tx hash
update_or_add_field_to_extra ( tx_2 . extra , kp . pub ) ;
r = resign_tx ( m_miner_acc . get_keys ( ) , sources , tx_2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " resign_tx failed " ) ;
LOG_PRINT_YELLOW ( " tx_2 = " < < get_transaction_hash ( tx_2 ) , LOG_LEVEL_0 ) ;
events . push_back ( tx_1 ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
// as long as tx_0 is using the same key image as tx_1, tx_0 and tx_2 can't be added to the pool atm
// make sure that it's true
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_0 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_2 ) ;
// however, tx_0 and tx_2 can be added with kept_by_block flag (to simulate it's going with blk_1)
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
events . push_back ( tx_0 ) ;
events . push_back ( tx_2 ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
// make a block with tx_0 and put tx_0 to the blockchain
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , m_miner_acc , tx_0 ) ;
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
// tx_1 and tx_2 is still in the pool
// it can never be added to any block as long as blk_1 is in the blockchain due to key image conflict
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2_bad , blk_1 , m_miner_acc , tx_1 ) ;
// add tx_1 to alt block, it should go well
MAKE_NEXT_BLOCK_TX1 ( events , blk_1a , blk_0r , m_miner_acc , tx_1 ) ;
// however, it does not remove tx from the pool
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
2019-10-17 14:04:13 +03:00
//
// make sure stuck tx will be removed from the pool when it's too old
//
2019-10-16 16:34:34 +03:00
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , m_miner_acc ) ;
// remove_stuck_txs should not remove anything, tx_1 and tx_2 should be in the pool
2019-10-17 14:04:13 +03:00
DO_CALLBACK ( events , " remove_stuck_txs " ) ;
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
// shift time by CURRENCY_MEMPOOL_TX_LIVETIME
events . push_back ( event_core_time ( CURRENCY_MEMPOOL_TX_LIVETIME + 1 , true ) ) ;
2019-10-17 14:04:13 +03:00
// remove_stuck_txs should have removed tx_2 because it's too old
2019-10-16 16:34:34 +03:00
DO_CALLBACK ( events , " remove_stuck_txs " ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-17 14:04:13 +03:00
//
// make sure stuck tx will be removed from the pool as soon as one of its key images is spent deep enough in the blockchain
// (even if it's not too old to be removed by age)
//
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2 , m_miner_acc ) ;
2019-10-16 16:34:34 +03:00
2019-10-17 14:04:13 +03:00
// re-add tx_2 with kept_by_block flag
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
events . push_back ( tx_2 ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
2019-10-16 16:34:34 +03:00
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
2019-10-17 14:04:13 +03:00
// remove_stuck_txs should not remove anything, tx_1 and tx_2 should be in the pool
DO_CALLBACK ( events , " remove_stuck_txs " ) ;
2019-11-20 23:03:27 +01:00
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
2019-10-16 16:34:34 +03:00
2019-10-17 14:04:13 +03:00
// rewind 50 blocks so tx_0 spending its key image will be deep enough
REWIND_BLOCKS_N_WITH_TIME ( events , blk_3r , blk_3 , m_miner_acc , 50 ) ;
2019-10-16 16:34:34 +03:00
2019-10-17 14:04:13 +03:00
// remove_stuck_txs should remove only tx_2 and left tx_1 (linked to alt block)
DO_CALLBACK ( events , " remove_stuck_txs " ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
DO_CALLBACK ( events , " print_tx_pool " ) ;
2019-10-16 16:34:34 +03:00
return true ;
}
2024-08-01 01:39:13 +02:00
//------------------------------------------------------------------
bool tx_version_against_hardfork : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: make sure that tx with incorrect for the activated HF version won't be accepted.
bool r = false ;
GENERATE_ACCOUNT ( miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ; // default configure_core callback will initialize core runtime config with m_hardforks
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// 0 ... 10 11 12 13 14 <- height
// (0 )- (0r)- (1 )- !2b!- <- chain A, block 2b is invalid
// tx_0 tx_1 tx_0 is accepted, tx_1 is rejected
/ / \
// \(2 ) <- chain B
// tx_1 tx_1 is accepted
uint64_t tx_version_good = 0 , tx_version_bad = 0 ;
2024-12-27 07:27:43 +01:00
size_t tx_hardfork_id = 0 ;
2024-08-01 01:39:13 +02:00
// select good and bad tx versions based on active hardfork
size_t most_recent_hardfork_id = m_hardforks . get_the_most_recent_hardfork_id_for_height ( get_block_height ( blk_0r ) ) ;
switch ( most_recent_hardfork_id )
{
case ZANO_HARDFORK_04_ZARCANUM :
2024-12-27 07:27:43 +01:00
tx_hardfork_id = 0 ;
2025-07-18 20:17:02 +03:00
[[fallthrough]] ;
2024-08-01 01:39:13 +02:00
case ZANO_HARDFORK_05 :
tx_version_good = TRANSACTION_VERSION_POST_HF4 ;
tx_version_bad = TRANSACTION_VERSION_PRE_HF4 ;
2024-12-27 07:27:43 +01:00
tx_hardfork_id = ZANO_HARDFORK_05 ;
2024-08-01 01:39:13 +02:00
break ;
default :
LOG_ERROR ( " hardfork " < < most_recent_hardfork_id < < " is not supported by this test " ) ;
return false ;
}
std : : vector < tx_source_entry > sources ;
std : : vector < tx_destination_entry > destinations ;
//
// 1/2 tx with good version, should go okay
//
r = fill_tx_sources_and_destinations ( events , blk_0r , miner_acc . get_keys ( ) , miner_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_0 { } ;
2024-12-27 07:27:43 +01:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_0 , tx_version_good , tx_hardfork_id , 0 ) ;
2024-08-01 01:39:13 +02:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_0 ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_0 ) ;
sources . clear ( ) ;
destinations . clear ( ) ;
//
// 2/2 tx with bad version, should be rejected by tx pool and by the core
//
r = fill_tx_sources_and_destinations ( events , blk_0 , miner_acc . get_keys ( ) , miner_acc . get_public_address ( ) , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , 0 , sources , destinations ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources_and_destinations failed " ) ;
transaction tx_1 { } ;
2024-12-27 07:27:43 +01:00
r = construct_tx ( miner_acc . get_keys ( ) , sources , destinations , empty_attachment , tx_0 , tx_version_bad , tx_hardfork_id , 0 ) ;
2024-08-01 01:39:13 +02:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
// check tx pool rejection
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
events . push_back ( tx_1 ) ;
// now add tx_1 as onboard transaction (directly to a block, skipping tx pool)
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , true ) ) ;
events . push_back ( tx_1 ) ;
events . push_back ( event_visitor_settings ( event_visitor_settings : : set_txs_kept_by_block , false ) ) ;
// make sure the block with tx_1 is invalid
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2b , blk_1 , miner_acc , tx_1 ) ;
// just one more block to make sure everyting is okay
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1 , miner_acc ) ;
return true ;
}
2024-09-28 06:17:36 +05:00
bool tx_pool_semantic_validation : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: ensure that the checks contained in the function "validate_tx_semantic" body are performed.
GENERATE_ACCOUNT ( miner ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( transaction { } , CURRENCY_MAX_TRANSACTION_BLOB_SIZE ) , false ) ;
// No inputs.
{
transaction tx { } ;
tx . vin = { } ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Unsupported input type.
{
transaction tx { } ;
2024-10-15 05:30:05 +05:00
tx . vin . emplace_back ( txin_gen { } ) ;
2024-09-28 06:17:36 +05:00
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Unsupported output type.
{
transaction tx { } ;
tx . vin . push_back ( txin_to_key { } ) ;
tx . vout . emplace_back ( ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Inputs amount overflow.
{
point_t point_public_key { } ;
txout_to_key target { } ;
std : : array < txin_to_key , 2 > inputs { } ;
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( point_public_key . from_string ( " 499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7 " ) , true ) ;
target . key = point_public_key . to_public_key ( ) ;
inputs . at ( 0 ) . amount = 1 ;
inputs . at ( 1 ) . amount = UINT64_MAX ;
for ( const auto & input : inputs )
{
tx . vin . push_back ( input ) ;
}
CHECK_AND_ASSERT_GREATER ( inputs . at ( 0 ) . amount , inputs . at ( 0 ) . amount + inputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_GREATER ( inputs . at ( 1 ) . amount , inputs . at ( 0 ) . amount + inputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Outputs amount overflow.
{
point_t point_public_key { } ;
txout_to_key target { } ;
std : : array < tx_out_bare , 2 > outputs { } ;
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( point_public_key . from_string ( " 78ef3d9af7b5e3d09556d57820cf68c2b3553a9d8205c01fe40fc70aae86bb4f " ) , true ) ;
target . key = point_public_key . to_public_key ( ) ;
outputs . at ( 0 ) . amount = 1 ;
outputs . at ( 1 ) . amount = UINT64_MAX ;
for ( auto & output : outputs )
{
output . target = target ;
tx . vout . push_back ( output ) ;
}
tx . vin . push_back ( txin_to_key { } ) ;
CHECK_AND_ASSERT_GREATER ( outputs . at ( 0 ) . amount , outputs . at ( 0 ) . amount + outputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_GREATER ( outputs . at ( 1 ) . amount , outputs . at ( 0 ) . amount + outputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Equal key images in inputs.
{
transaction tx { } ;
2024-11-07 20:13:58 +05:00
{
txin_zc_input input_zc { } ;
txin_htlc input_htlc { } ;
txin_to_key input_to_key { } ;
point_t point_key_image { } ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " 93fa59f43fb9cff98e6867d20cf200c98b29cae406acdbde798ffb3e30d3503a " ) , true ) ;
input_zc . k_image = point_key_image . to_key_image ( ) ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " ad1226e3fd1be15e26b119fa80380e580a498e5fa3421b63fded89672b526a44 " ) , true ) ;
input_htlc . k_image = point_key_image . to_key_image ( ) ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " 8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96 " ) , true ) ;
input_to_key . k_image = point_key_image . to_key_image ( ) ;
tx . vin . push_back ( input_zc ) ;
tx . vin . push_back ( input_htlc ) ;
tx . vin . push_back ( input_to_key ) ;
}
2024-09-28 06:17:36 +05:00
{
2024-11-07 20:13:58 +05:00
txin_to_key input { } ;
2024-10-15 05:30:05 +05:00
2024-11-07 20:13:58 +05:00
input . amount = 0 ;
2024-10-15 05:30:05 +05:00
tx . vin . push_back ( input ) ;
2024-09-28 06:17:36 +05:00
}
2024-11-07 20:13:58 +05:00
for ( int8_t step { } ; step < 3 ; + + step )
{
tx . vin . push_back ( tx . vin . front ( ) ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
tx . vin . erase ( tx . vin . begin ( ) , tx . vin . begin ( ) + 1 ) ;
}
2024-10-15 05:30:05 +05:00
2024-11-07 20:13:58 +05:00
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
2025-07-15 23:36:53 +03:00
2024-09-28 06:17:36 +05:00
}
// Two entries of the same type in extra.
{
transaction tx { } ;
std : : array < txin_to_key , 2 > inputs { } ;
std : : array < point_t , 2 > key_image_points { } ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 0 ) . from_string ( " de3c22a62f15e6de8abe6b217085b2aead196daf5ddd67d9c4b366330736fbeb " ) , true ) ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 1 ) . from_string ( " 9f3eef913921ca35239e696725595e3686bb0d69e3e805791c5aa93d5754aa5c " ) , true ) ;
for ( int position { } ; position < 2 ; + + position )
{
inputs . at ( position ) . k_image = key_image_points . at ( position ) . to_key_image ( ) ;
tx . vin . push_back ( inputs . at ( position ) ) ;
}
tx . extra . push_back ( null_pkey ) ;
tx . extra . push_back ( null_pkey ) ;
CHECK_AND_ASSERT_EQ ( tx . extra . size ( ) , 2 ) ;
CHECK_AND_ASSERT_EQ ( typeid ( tx . extra . front ( ) ) , typeid ( tx . extra . back ( ) ) ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs.
{
2024-10-15 05:30:05 +05:00
tx_out_bare output { } ;
2024-09-28 06:17:36 +05:00
transaction tx { } ;
std : : array < point_t , 2 > key_image_points { } ;
std : : array < txin_to_key , 2 > inputs { } ;
output . amount = 3 ;
tx . vout . push_back ( output ) ;
tx . version = 0 ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 0 ) . from_string ( " 8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96 " ) , true ) ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 1 ) . from_string ( " dc48b741dacda5ac026ad0a7d193b816049eb08724907a1ff6f95839cfb0efa5 " ) , true ) ;
for ( int position { } ; position < 2 ; + + position )
{
auto & input { inputs . at ( position ) } ;
input . amount = 1 ;
input . k_image = key_image_points . at ( position ) . to_key_image ( ) ;
tx . vin . push_back ( input ) ;
}
2024-10-15 05:30:05 +05:00
const uint64_t sum_inputs { std : : accumulate ( inputs . begin ( ) , inputs . end ( ) , std : : uint64_t { } , [ ] ( uint64_t sum , const txin_to_key & input ) { return sum + input . amount ; } ) } ;
CHECK_AND_ASSERT_LESS ( sum_inputs , output . amount ) ;
CHECK_AND_ASSERT_EQ ( output . amount - sum_inputs , 1 ) ;
2024-09-28 06:17:36 +05:00
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Semantically valid transaction.
{
tx_out_bare output ;
transaction tx { } ;
std : : array < point_t , 2 > key_image_points { } ;
std : : array < txin_to_key , 2 > inputs { } ;
output . amount = 3'000'000'000'000 ;
tx . version = 0 ;
tx . vout . push_back ( output ) ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 0 ) . from_string ( " fe1ef4a48a69804652324dc071cb72f49c22cd97479583950eaff746c936f72c " ) , true ) ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 1 ) . from_string ( " 0bf1d8bb0988069f2c2b4f0dc89c81830c1178e04450d1da31ef020660732279 " ) , true ) ;
for ( int position { } ; position < 2 ; + + position )
{
auto & input { inputs . at ( position ) } ;
input . amount = 2'000'000'000'000 ;
input . k_image = key_image_points . at ( position ) . to_key_image ( ) ;
tx . vin . push_back ( input ) ;
}
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
2024-10-15 05:30:05 +05:00
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// Construct a valid transaction and then modify it so that the transaction is no longer semantically correct.
// No inputs.
{
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
tx . vin = { } ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Unsupported input type.
{
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
tx . vin . emplace_back ( txin_gen { } ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Unsupported output type.
{
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
tx . vout . emplace_back ( ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Inputs amount overflow.
{
point_t point_public_key { } ;
txout_to_key target { } ;
std : : array < txin_to_key , 2 > inputs { } ;
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
CHECK_AND_ASSERT_EQ ( point_public_key . from_string ( " 499790c3302b9f0514e2db09b390679283d43d971383d33dc24c7991ea4cf6d7 " ) , true ) ;
target . key = point_public_key . to_public_key ( ) ;
inputs . at ( 0 ) . amount = 1 ;
inputs . at ( 1 ) . amount = UINT64_MAX ;
for ( const auto & input : inputs )
{
tx . vin . push_back ( input ) ;
}
CHECK_AND_ASSERT_GREATER ( inputs . at ( 0 ) . amount , inputs . at ( 0 ) . amount + inputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_GREATER ( inputs . at ( 1 ) . amount , inputs . at ( 0 ) . amount + inputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Outputs amount overflow.
{
point_t point_public_key { } ;
txout_to_key target { } ;
std : : array < tx_out_bare , 2 > outputs { } ;
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
CHECK_AND_ASSERT_EQ ( point_public_key . from_string ( " 78ef3d9af7b5e3d09556d57820cf68c2b3553a9d8205c01fe40fc70aae86bb4f " ) , true ) ;
target . key = point_public_key . to_public_key ( ) ;
outputs . at ( 0 ) . amount = 1 ;
outputs . at ( 1 ) . amount = UINT64_MAX ;
for ( auto & output : outputs )
{
output . target = target ;
tx . vout . push_back ( output ) ;
}
tx . vin . push_back ( txin_to_key { } ) ;
CHECK_AND_ASSERT_GREATER ( outputs . at ( 0 ) . amount , outputs . at ( 0 ) . amount + outputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_GREATER ( outputs . at ( 1 ) . amount , outputs . at ( 0 ) . amount + outputs . at ( 1 ) . amount ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// Equal key images in inputs.
{
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
{
2024-11-07 20:13:58 +05:00
txin_zc_input input_zc { } ;
txin_htlc input_htlc { } ;
txin_to_key input_to_key { } ;
point_t point_key_image { } ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " 93fa59f43fb9cff98e6867d20cf200c98b29cae406acdbde798ffb3e30d3503a " ) , true ) ;
input_zc . k_image = point_key_image . to_key_image ( ) ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " ad1226e3fd1be15e26b119fa80380e580a498e5fa3421b63fded89672b526a44 " ) , true ) ;
input_htlc . k_image = point_key_image . to_key_image ( ) ;
CHECK_AND_ASSERT_EQ ( point_key_image . from_string ( " 8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96 " ) , true ) ;
input_to_key . k_image = point_key_image . to_key_image ( ) ;
tx . vin . push_back ( input_zc ) ;
tx . vin . push_back ( input_htlc ) ;
tx . vin . push_back ( input_to_key ) ;
2024-10-15 05:30:05 +05:00
}
2024-11-07 20:13:58 +05:00
for ( int8_t step { } ; step < 3 ; + + step )
2024-10-15 05:30:05 +05:00
{
2024-11-07 20:13:58 +05:00
tx . vin . push_back ( tx . vin . front ( ) ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
tx . vin . erase ( tx . vin . begin ( ) , tx . vin . begin ( ) + 1 ) ;
2024-10-15 05:30:05 +05:00
}
2024-11-07 20:13:58 +05:00
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
2024-10-15 05:30:05 +05:00
}
// Two entries of the same type in extra.
{
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
tx . extra . push_back ( null_pkey ) ;
tx . extra . push_back ( null_pkey ) ;
CHECK_AND_ASSERT_GREATER ( tx . extra . size ( ) , 2 ) ;
CHECK_AND_ASSERT_EQ ( typeid ( tx . extra . back ( ) ) , typeid ( tx . extra . at ( tx . extra . size ( ) - 2 ) ) ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
// tx.version <= TRANSACTION_VERSION_PRE_HF4. Balance check fail: sum of inputs <= sum of outputs.
{
tx_out_bare output { } ;
std : : array < point_t , 2 > key_image_points { } ;
std : : array < txin_to_key , 2 > inputs { } ;
MAKE_TX_FEE ( events , tx , miner , miner , MK_TEST_COINS ( 1 ) , TESTS_DEFAULT_FEE , blk_0r ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , true ) ;
output . amount = 10'000'000'003 ;
tx . vout . push_back ( output ) ;
tx . version = 0 ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 0 ) . from_string ( " 8fc7cbfd1054690767d0c20917a68371b34b190aac5997581641f064b93d1b96 " ) , true ) ;
CHECK_AND_ASSERT_EQ ( key_image_points . at ( 1 ) . from_string ( " dc48b741dacda5ac026ad0a7d193b816049eb08724907a1ff6f95839cfb0efa5 " ) , true ) ;
for ( int position { } ; position < 2 ; + + position )
{
auto & input { inputs . at ( position ) } ;
input . amount = 1 ;
input . k_image = key_image_points . at ( position ) . to_key_image ( ) ;
tx . vin . push_back ( input ) ;
}
const auto inputs_sum { [ ] ( const uint64_t sum , const txin_v & input ) - > uint64_t
{
if ( input . type ( ) = = typeid ( txin_to_key ) )
{
return sum + boost : : get < txin_to_key > ( input ) . amount ;
}
if ( input . type ( ) = = typeid ( txin_multisig ) )
{
return sum + boost : : get < txin_multisig > ( input ) . amount ;
}
2024-10-22 17:34:19 +05:00
return sum ;
2024-10-15 05:30:05 +05:00
}
} ;
const auto outputs_sum { [ ] ( const uint64_t sum , const tx_out_v & output ) - > uint64_t
{
if ( output . type ( ) = = typeid ( tx_out_bare ) )
{
return sum + boost : : get < tx_out_bare > ( output ) . amount ;
}
2024-10-22 17:34:19 +05:00
return sum ;
2024-10-15 05:30:05 +05:00
}
} ;
const uint64_t inputs_amount { std : : accumulate ( tx . vin . begin ( ) , tx . vin . end ( ) , std : : uint64_t { } , inputs_sum ) } ;
const uint64_t outputs_amount { std : : accumulate ( tx . vout . begin ( ) , tx . vout . end ( ) , std : : uint64_t { } , outputs_sum ) } ;
CHECK_AND_ASSERT_LESS ( inputs_amount , outputs_amount ) ;
CHECK_AND_ASSERT_EQ ( outputs_amount - inputs_amount , 1 ) ;
CHECK_AND_ASSERT ( tx . version < = TRANSACTION_VERSION_PRE_HF4 , false ) ;
CHECK_AND_ASSERT_EQ ( get_block_height ( blk_0r ) , 10 ) ;
CHECK_AND_ASSERT_EQ ( m_hardforks . is_hardfork_active_for_height ( ZANO_HARDFORK_03 , 10 ) , true ) ;
CHECK_AND_ASSERT_EQ ( m_hardforks . is_hardfork_active_for_height ( ZANO_HARDFORK_04_ZARCANUM , 10 ) , false ) ;
CHECK_AND_ASSERT_EQ ( validate_tx_semantic ( tx , CURRENCY_MAX_TRANSACTION_BLOB_SIZE - 1 ) , false ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx ) ;
}
2024-09-28 06:17:36 +05:00
return true ;
}
2024-12-09 21:34:28 +05:00
input_refers_to_incompatible_by_type_output : : input_refers_to_incompatible_by_type_output ( )
{
REGISTER_CALLBACK_METHOD ( input_refers_to_incompatible_by_type_output , assert_htlc_input_refers_to_key_output_is_wrong ) ;
REGISTER_CALLBACK_METHOD ( input_refers_to_incompatible_by_type_output , assert_to_key_input_refers_zarcanum_output_is_wrong ) ;
REGISTER_CALLBACK_METHOD ( input_refers_to_incompatible_by_type_output , assert_htlc_input_refers_zarcanum_output_is_wrong ) ;
REGISTER_CALLBACK_METHOD ( input_refers_to_incompatible_by_type_output , assert_zc_input_refers_bare_output_is_wrong ) ;
m_hardforks . set_hardfork_height ( ZANO_HARDFORK_04_ZARCANUM , 14 ) ;
}
bool input_refers_to_incompatible_by_type_output : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: ensure that input and output compatibility checks work.
GENERATE_ACCOUNT ( miner ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner ) ;
block & top { blk_0r } ;
MAKE_TX_FEE ( events , tx_00 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , top , miner , tx_00 ) ;
top = blk_1 ;
}
// An input of the type "txin_htlc" refers by a "ref_by_id" object to an output with a target of the type "txout_to_key".
{
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
transaction tx_1 { } ;
top = blk ;
{
txin_htlc input { } ;
{
ref_by_id reference { } ;
reference . tx_id = get_transaction_hash ( tx_0 ) ;
reference . n = get_tx_out_index_by_amount ( tx_0 , MK_TEST_COINS ( 7 ) ) ;
CHECK_AND_ASSERT_NEQ ( reference . n , UINT64_MAX ) ;
input . key_offsets . push_back ( reference ) ;
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
}
input . k_image = crypto : : point_t { { 0x59 , 0x01 , 0xed , 0xcc , 0x3a , 0xe7 , 0xaa , 0x83 , 0x6c , 0x79 , 0xfb , 0xed , 0x5d , 0x60 , 0x02 , 0xc5 , 0xd0 , 0xbf , 0x65 , 0x85 , 0x7b , 0x65 , 0x25 , 0x0e , 0x22 , 0xcb , 0x63 ,
0x64 , 0x3b , 0x3b , 0x47 , 0x30 } } . to_key_image ( ) ;
input . amount = MK_TEST_COINS ( 7 ) ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
{
tx_out_bare output_bare { } ;
txout_to_key output_to_key { } ;
output_bare . amount = MK_TEST_COINS ( 2 ) ;
output_to_key . key = crypto : : point_t { { 0x2c , 0xdc , 0xc4 , 0x7c , 0x38 , 0x69 , 0xe5 , 0xe2 , 0x4c , 0x5e , 0x10 , 0xb2 , 0xbe , 0x57 , 0xe9 , 0x42 , 0x72 , 0xd8 , 0xf8 , 0xb5 , 0x97 , 0xb9 , 0x02 , 0x41 , 0xba , 0xea ,
0x82 , 0xb3 , 0xaf , 0x0c , 0xf0 , 0x09 } } . to_public_key ( ) ;
output_bare . target = output_to_key ;
tx_1 . vout . push_back ( output_bare ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 1 ) ;
}
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_htlc_input_refers_to_key_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
// An input of the type "txin_htlc" refers by a global output index to an output with a target of the type "txout_to_key".
{
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
transaction tx_1 { } ;
top = blk ;
{
txin_htlc input { } ;
{
uint64_t global_output_index { } ;
CHECK_AND_ASSERT_EQ ( find_global_index_for_output ( events , get_block_hash ( top ) , tx_0 , get_tx_out_index_by_amount ( tx_0 , MK_TEST_COINS ( 7 ) ) , global_output_index ) , true ) ;
CHECK_AND_ASSERT_NEQ ( global_output_index , UINT64_MAX ) ;
input . key_offsets . push_back ( global_output_index ) ;
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
}
input . k_image = crypto : : point_t { { 0xc6 , 0x1c , 0xda , 0xf7 , 0x9e , 0xb7 , 0xd9 , 0xc2 , 0x46 , 0x90 , 0x29 , 0xc8 , 0x8a , 0x8f , 0xb4 , 0x3e , 0x8e , 0xa8 , 0x3b , 0x33 , 0x4c , 0x75 , 0xdf , 0xcb , 0x8b , 0x77 , 0xf7 ,
0x39 , 0xa7 , 0x17 , 0xc9 , 0xb4 } } . to_key_image ( ) ;
input . amount = MK_TEST_COINS ( 7 ) ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
{
tx_out_bare output_bare { } ;
txout_to_key output_to_key { } ;
output_bare . amount = MK_TEST_COINS ( 2 ) ;
output_to_key . key = crypto : : point_t { { 0xc4 , 0x17 , 0xc7 , 0x7f , 0xb2 , 0x5d , 0xcb , 0x4b , 0x29 , 0xdf , 0xea , 0x53 , 0x70 , 0x11 , 0xbb , 0x42 , 0x33 , 0x0d , 0xf1 , 0x22 , 0x2d , 0xe4 , 0x84 , 0x24 , 0x36 , 0xc0 ,
0x06 , 0xd5 , 0x8c , 0xf8 , 0x23 , 0x62 } } . to_public_key ( ) ;
output_bare . target = output_to_key ;
tx_1 . vout . push_back ( output_bare ) ;
}
tx_1 . signatures . push_back ( NLSAG_sig { { crypto : : signature { } } } ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_htlc_input_refers_to_key_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
DO_CALLBACK_PARAMS ( events , " check_hardfork_inactive " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
{
REWIND_BLOCKS_N ( events , blk , top , miner , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
top = blk ;
}
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
MAKE_TX_FEE ( events , tx_01 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_01 ) ;
top = blk ;
}
MAKE_TX_FEE ( events , tx_02 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_02 ) ;
top = blk ;
}
// An input of the type "txin_to_key" refers by a "ref_by_id" object to an output of the type "tx_out_zarcanum".
{
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
top = blk ;
}
{
REWIND_BLOCKS ( events , blk_r , top , miner ) ;
top = blk_r ;
}
transaction tx_1 { } ;
tx_1 . version = 2 ;
{
txin_to_key input { } ;
input . key_offsets . emplace_back ( ref_by_id { get_transaction_hash ( tx_0 ) , 0 } ) ;
CHECK_AND_ASSERT_EQ ( tx_0 . vout . at ( boost : : get < ref_by_id > ( input . key_offsets . front ( ) ) . n ) . type ( ) , typeid ( tx_out_zarcanum ) ) ;
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
input . k_image = crypto : : point_t ( { 0x59 , 0x01 , 0xed , 0xcc , 0x3a , 0xe7 , 0xaa , 0x83 , 0x6c , 0x79 , 0xfb , 0xed , 0x5d , 0x60 , 0x02 , 0xc5 , 0xd0 , 0xbf , 0x65 , 0x85 , 0x7b , 0x65 , 0x25 , 0x0e , 0x22 , 0xcb , 0x63 ,
0x64 , 0x3b , 0x3b , 0x47 , 0x30 } ) . to_key_image ( ) ;
input . amount = MK_TEST_COINS ( 2 ) ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 2 ) ;
tx_1 . extra . push_back ( zarcanum_tx_data_v1 { TESTS_DEFAULT_FEE } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . extra . size ( ) , 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_to_key_input_refers_zarcanum_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
// An input of the type "txin_to_key" refers by a global output index to an output of the type "tx_out_zarcanum".
{
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
top = blk ;
}
{
REWIND_BLOCKS ( events , blk_r , top , miner ) ;
top = blk_r ;
}
transaction tx_1 { } ;
tx_1 . version = 2 ;
{
txin_to_key input { } ;
{
uint64_t global_output_index { } ;
CHECK_AND_ASSERT_EQ ( tx_0 . vout . front ( ) . type ( ) , typeid ( tx_out_zarcanum ) ) ;
CHECK_AND_ASSERT_EQ ( find_global_index_for_output ( events , get_block_hash ( top ) , tx_0 , 0 , global_output_index ) , true ) ;
input . key_offsets . push_back ( global_output_index ) ;
}
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
input . k_image = crypto : : point_t { { 0xbc , 0x2d , 0xdc , 0xc5 , 0x93 , 0x03 , 0x9f , 0x0e , 0xce , 0x76 , 0xee , 0xef , 0xd9 , 0x1c , 0x2c , 0x3e , 0x8c , 0x4a , 0xca , 0x87 , 0x9b , 0x6e , 0x3a , 0xda , 0xaf , 0x0c ,
0x92 , 0x88 , 0xda , 0x88 , 0xc0 , 0xf0 } } . to_key_image ( ) ;
// A container is selected by an amount specified in an input. ZC outputs have .amount equals to 0. Thus, the input has .amount equals to 0.
input . amount = 0 ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 2 ) ;
tx_1 . extra . push_back ( zarcanum_tx_data_v1 { TESTS_DEFAULT_FEE } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . extra . size ( ) , 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_to_key_input_refers_zarcanum_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
// An input of the type "txin_zc_input" refers by a "ref_by_id" object to an output with a target of the type "txout_to_key".
{
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
top = blk ;
}
{
REWIND_BLOCKS ( events , blk_r , top , miner ) ;
top = blk_r ;
}
transaction tx_1 { } ;
tx_1 . version = 2 ;
{
txin_zc_input input { } ;
input . key_offsets . emplace_back ( ref_by_id { get_transaction_hash ( tx_00 ) , 0 } ) ;
{
const auto & output { tx_00 . vout . at ( boost : : get < ref_by_id > ( input . key_offsets . front ( ) ) . n ) } ;
CHECK_AND_ASSERT_EQ ( output . type ( ) , typeid ( tx_out_bare ) ) ;
CHECK_AND_ASSERT_EQ ( boost : : get < tx_out_bare > ( output ) . target . type ( ) , typeid ( txout_to_key ) ) ;
}
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
input . key_offsets . emplace_back ( ref_by_id { get_transaction_hash ( tx_01 ) , 0 } ) ;
{
const auto & output { tx_01 . vout . at ( boost : : get < ref_by_id > ( input . key_offsets . at ( 1 ) ) . n ) } ;
CHECK_AND_ASSERT_EQ ( output . type ( ) , typeid ( tx_out_zarcanum ) ) ;
}
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 2 ) ;
input . k_image = crypto : : point_t { { 0x31 , 0x31 , 0xd0 , 0xf7 , 0x13 , 0x73 , 0xff , 0x21 , 0x14 , 0xe8 , 0x17 , 0x4d , 0x18 , 0x20 , 0x12 , 0x2d , 0x80 , 0x31 , 0xd5 , 0x11 , 0x82 , 0xc0 , 0x37 , 0xad , 0xd2 , 0x7b , 0x8c ,
0xf2 , 0xdd , 0xd4 , 0x34 , 0x9a } } . to_key_image ( ) ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 2 ) ;
tx_1 . extra . push_back ( zarcanum_tx_data_v1 { TESTS_DEFAULT_FEE } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . extra . size ( ) , 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_zc_input_refers_bare_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
// An input of the type "txin_htlc" refers by a "ref_by_id" object to an output of the type "tx_out_zarcanum".
{
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
top = blk ;
}
{
REWIND_BLOCKS_N_WITH_TIME ( events , blk_r , top , miner , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
top = blk_r ;
}
transaction tx_1 { } ;
tx_1 . version = 2 ;
{
txin_htlc input { } ;
input . key_offsets . push_back ( ref_by_id { get_transaction_hash ( tx_0 ) , 0 } ) ;
CHECK_AND_ASSERT_EQ ( tx_0 . vout . at ( boost : : get < ref_by_id > ( input . key_offsets . front ( ) ) . n ) . type ( ) , typeid ( tx_out_zarcanum ) ) ;
input . k_image = crypto : : point_t { { 0x7b , 0xf5 , 0x28 , 0x09 , 0xe8 , 0x7e , 0x9c , 0x71 , 0x0b , 0xad , 0x24 , 0xa1 , 0x9d , 0xb4 , 0xc8 , 0xd7 , 0x96 , 0x72 , 0x18 , 0xe6 , 0x4b , 0x8f , 0x31 , 0x01 , 0xb0 , 0x43 , 0xa0 ,
0xcc , 0xce , 0x72 , 0x8c , 0x7e } } . to_key_image ( ) ;
input . amount = MK_TEST_COINS ( 2 ) ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 2 ) ;
tx_1 . extra . push_back ( zarcanum_tx_data_v1 { TESTS_DEFAULT_FEE } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . extra . size ( ) , 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_htlc_input_refers_zarcanum_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
// An input of the type "txin_htlc" refers by a global output index to an output of the type "tx_out_zarcanum".
{
MAKE_TX_FEE ( events , tx_0 , miner , miner , MK_TEST_COINS ( 2 ) , TESTS_DEFAULT_FEE , top ) ;
{
MAKE_NEXT_BLOCK_TX1 ( events , blk , top , miner , tx_0 ) ;
top = blk ;
}
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
{
REWIND_BLOCKS_N_WITH_TIME ( events , blk_r , top , miner , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
top = blk_r ;
}
transaction tx_1 { } ;
tx_1 . version = 2 ;
{
txin_htlc input { } ;
uint64_t global_output_index { } ;
CHECK_AND_ASSERT_EQ ( tx_0 . vout . front ( ) . type ( ) , typeid ( tx_out_zarcanum ) ) ;
CHECK_AND_ASSERT_EQ ( find_global_index_for_output ( events , get_block_hash ( top ) , tx_0 , 0 , global_output_index ) , true ) ;
input . key_offsets . push_back ( global_output_index ) ;
CHECK_AND_ASSERT_EQ ( input . key_offsets . size ( ) , 1 ) ;
input . k_image = crypto : : point_t { { 0xbc , 0x2d , 0xdc , 0xc5 , 0x93 , 0x03 , 0x9f , 0x0e , 0xce , 0x76 , 0xee , 0xef , 0xd9 , 0x1c , 0x2c , 0x3e , 0x8c , 0x4a , 0xca , 0x87 , 0x9b , 0x6e , 0x3a , 0xda , 0xaf , 0x0c , 0x92 ,
0x88 , 0xda , 0x88 , 0xc0 , 0xf0 } } . to_key_image ( ) ;
// A container is selected by an amount specified in an input. ZC outputs have .amount equals to 0. Thus, the input has .amount equals to 0.
input . amount = 0 ;
tx_1 . vin . push_back ( input ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vin . size ( ) , 1 ) ;
}
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
tx_1 . vout . push_back ( tx_out_zarcanum { } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . vout . size ( ) , 2 ) ;
tx_1 . extra . push_back ( zarcanum_tx_data_v1 { TESTS_DEFAULT_FEE } ) ;
CHECK_AND_ASSERT_EQ ( tx_1 . extra . size ( ) , 1 ) ;
DO_CALLBACK ( events , " mark_invalid_tx " ) ;
ADD_CUSTOM_EVENT ( events , tx_1 ) ;
DO_CALLBACK_PARAMS_STR ( events , " assert_htlc_input_refers_zarcanum_output_is_wrong " , t_serializable_object_to_blob ( tx_1 ) ) ;
}
return true ;
}
bool input_refers_to_incompatible_by_type_output : : assert_htlc_input_refers_to_key_output_is_wrong ( const currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events ) const
{
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( t_unserializable_object_from_blob ( tx , boost : : get < const callback_entry > ( events . at ( ev_index ) ) . callback_params ) , true ) ;
CHECK_AND_ASSERT_EQ ( tx . vin . front ( ) . type ( ) , typeid ( txin_htlc ) ) ;
{
uint64_t max_related_block_height { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . check_tx_input ( tx , 0 , boost : : get < const txin_htlc > ( tx . vin . front ( ) ) , get_transaction_hash ( tx ) , max_related_block_height ) , false ) ;
}
{
std : : vector < public_key > keys { } ;
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . get_output_keys_for_input_with_checks ( tx , tx . vin . front ( ) , keys , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) , false ) ;
}
return true ;
}
bool input_refers_to_incompatible_by_type_output : : assert_to_key_input_refers_zarcanum_output_is_wrong ( const currency : : core & c , const size_t ev_index , const std : : vector < test_event_entry > & events ) const
{
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( t_unserializable_object_from_blob ( tx , boost : : get < const callback_entry > ( events . at ( ev_index ) ) . callback_params ) , true ) ;
CHECK_AND_ASSERT_EQ ( tx . vin . front ( ) . type ( ) , typeid ( txin_to_key ) ) ;
{
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . check_tx_input ( tx , 0 , boost : : get < const txin_to_key > ( tx . vin . front ( ) ) , get_transaction_hash ( tx ) , max_related_block_height ,
source_max_unlock_time_for_pos_coinbase ) , false ) ;
}
{
std : : vector < public_key > keys { } ;
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . get_output_keys_for_input_with_checks ( tx , tx . vin . front ( ) , keys , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) , false ) ;
}
return true ;
}
bool input_refers_to_incompatible_by_type_output : : assert_zc_input_refers_bare_output_is_wrong ( const currency : : core & c , const size_t ev_index , const std : : vector < test_event_entry > & events ) const
{
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( t_unserializable_object_from_blob ( tx , boost : : get < const callback_entry > ( events . at ( ev_index ) ) . callback_params ) , true ) ;
CHECK_AND_ASSERT_EQ ( tx . vin . front ( ) . type ( ) , typeid ( txin_zc_input ) ) ;
{
std : : vector < public_key > keys { } ;
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . get_output_keys_for_input_with_checks ( tx , tx . vin . front ( ) , keys , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) , true ) ;
}
{
2025-06-09 05:43:39 +02:00
blockchain_storage : : check_tx_inputs_context ctic { } ;
2024-12-09 21:34:28 +05:00
2025-06-09 05:43:39 +02:00
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . check_tx_input ( tx , 0 , boost : : get < const txin_zc_input > ( tx . vin . front ( ) ) , get_transaction_hash ( tx ) , ctic ) , false ) ;
2024-12-09 21:34:28 +05:00
}
return true ;
}
bool input_refers_to_incompatible_by_type_output : : assert_htlc_input_refers_zarcanum_output_is_wrong ( const currency : : core & c , const size_t ev_index , const std : : vector < test_event_entry > & events ) const
{
transaction tx { } ;
CHECK_AND_ASSERT_EQ ( t_unserializable_object_from_blob ( tx , boost : : get < const callback_entry > ( events . at ( ev_index ) ) . callback_params ) , true ) ;
CHECK_AND_ASSERT_EQ ( tx . vin . front ( ) . type ( ) , typeid ( txin_htlc ) ) ;
{
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . check_tx_input ( tx , 0 , boost : : get < const txin_htlc > ( tx . vin . front ( ) ) , get_transaction_hash ( tx ) , max_related_block_height ,
source_max_unlock_time_for_pos_coinbase ) , false ) ;
}
{
std : : vector < public_key > keys { } ;
uint64_t max_related_block_height { } ;
uint64_t source_max_unlock_time_for_pos_coinbase { } ;
CHECK_AND_ASSERT_EQ ( c . get_blockchain_storage ( ) . get_output_keys_for_input_with_checks ( tx , tx . vin . front ( ) , keys , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) , false ) ;
}
return true ;
}
2025-06-03 00:01:11 +02:00
//------------------------------------------------------------------
tx_pool_validation_and_chain_switch : : tx_pool_validation_and_chain_switch ( )
{
REGISTER_CALLBACK_METHOD ( tx_pool_validation_and_chain_switch , c1 ) ;
}
bool tx_pool_validation_and_chain_switch : : generate ( std : : vector < test_event_entry > & events ) const
{
2025-06-09 05:39:57 +02:00
// Test idea: make shure that if a tx in the tx pool doesn't satisfy HF4 coinage rule, it won't be added to the block template
// UNTIL the current height is enough so the rule is obeyed.
2025-06-03 00:01:11 +02:00
uint64_t ts = test_core_time : : get_time ( ) ;
m_accounts . resize ( TOTAL_ACCS_COUNT ) ;
account_base & miner_acc = m_accounts [ MINER_ACC_IDX ] ; miner_acc . generate ( ) ;
account_base & alice_acc = m_accounts [ ALICE_ACC_IDX ] ; alice_acc . generate ( ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
std : : list < currency : : account_base > miner_stake_sources ( { miner_acc } ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 ) ;
MAKE_TX ( events , tx_0 , miner_acc , alice_acc , MK_TEST_COINS ( 100 ) , blk_0r ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner_acc , tx_0 ) ;
2025-06-09 05:39:57 +02:00
// 0 ... 20 21 22 23 24 25 26 27 28 29 30 31 <- height
// (0 )- (0r)- {1 }- {2 }- {3 }- {4 }- {5 }- {6 }- {7 }- {8 }- {9 }- {10}- {11}- <- chain M
// tx_0 \ tx_a
// - (4a)- {5a}- (6a)- {7a}- <- chain A
2025-06-03 00:01:11 +02:00
MAKE_NEXT_POS_BLOCK ( events , blk_2 , blk_1 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_3 , blk_2 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_4 , blk_3 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_5 , blk_4 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_6 , blk_5 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_7 , blk_6 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_8 , blk_7 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_9 , blk_8 , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_10 , blk_9 , miner_acc , miner_stake_sources ) ;
// now Alice should be able to spend her coins
MAKE_TX ( events , tx_a , alice_acc , miner_acc , MK_TEST_COINS ( 100 ) - TESTS_DEFAULT_FEE , blk_10 ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
MAKE_NEXT_POS_BLOCK_TX1 ( events , blk_11 , blk_10 , miner_acc , miner_stake_sources , tx_a ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 0 ) ) ;
2025-06-09 05:39:57 +02:00
// construct chain A as PoW-PoS-PoW-PoS, it should win
MAKE_NEXT_BLOCK ( events , blk_4a , blk_3 , miner_acc ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_5a , blk_4a , miner_acc , miner_stake_sources ) ;
MAKE_NEXT_BLOCK ( events , blk_6a , blk_5a , miner_acc ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_7a , blk_6a , miner_acc , miner_stake_sources ) ;
2025-06-03 00:01:11 +02:00
// make sure it won
2025-06-09 05:39:57 +02:00
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( blk_7a ) ) ;
2025-06-03 00:01:11 +02:00
// now tx_a should have been put back to the tx pool
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 1 ) ) ;
// the next block should be rejected, because of max_related_block_height=21 and the current height is 28
2025-06-09 05:39:57 +02:00
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_8a , blk_7a , miner_acc , tx_a ) ;
2025-06-03 00:01:11 +02:00
// and if we clear tx pool ...
2025-06-09 05:39:57 +02:00
DO_CALLBACK ( events , " clear_tx_pool " ) ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , static_cast < size_t > ( 0 ) ) ;
// and re-add the transaction on this height, it should be added
ADD_CUSTOM_EVENT ( events , tx_a ) ;
2025-06-03 00:01:11 +02:00
// however, we need to make sure that tx pool won't be use tx_a in a block template until the height is good enough
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool tx_pool_validation_and_chain_switch : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
2025-06-09 05:39:57 +02:00
// 0 ... 20 21 22 23 24 25 26 27 28 29 30 31 <- height
// (0 )- (0r)- {1 }- {2 }- {3 }- {4 }- {5 }- {6 }- {7 }- {8 }- {9 }- {10}- {11}- <- chain M
// tx_0 \ tx_a
// - (4a)- {5a}- (6a)- {7a}- ( )- ( )- ( )- ( )- <- chain A
// | tx_a tx_a re-added in A chain (in mine_next_pow_block_in_playtime() call)
// c1 <- callback called
2025-06-03 00:01:11 +02:00
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , MINER_ACC_IDX ) ;
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX ) ;
bool r = false ;
alice_wlt - > refresh ( ) ;
// make sure tx_a is still in the pool
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Unexpected number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// and Alice's unconfirmed balance is nonzero
CHECK_AND_ASSERT_MES ( check_balance_via_wallet ( * alice_wlt , " Alice " , MK_TEST_COINS ( 100 ) ) , false , " " ) ;
// mine a block, make sure it is possible and the tx is still in the pool afterwards
r = mine_next_pow_block_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c ) ;
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Unexpected number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// make sure tx_a is still in the pool
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Unexpected number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// and Alice's unconfirmed balance is nonzero
alice_wlt - > refresh ( ) ;
CHECK_AND_ASSERT_MES ( check_balance_via_wallet ( * alice_wlt , " Alice " , MK_TEST_COINS ( 100 ) ) , false , " " ) ;
// mine more
r = mine_next_pow_blocks_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c , 2 ) ;
// make sure tx_a is still in the pool
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 1 , false , " Unexpected number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// and Alice's unconfirmed balance is nonzero
alice_wlt - > refresh ( ) ;
CHECK_AND_ASSERT_MES ( check_balance_via_wallet ( * alice_wlt , " Alice " , MK_TEST_COINS ( 100 ) ) , false , " " ) ;
// the next blocktemplate should include it
r = mine_next_pow_block_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c ) ;
// make sure it did
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Unexpected number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// and Alice's unconfirmed balance is zero
alice_wlt - > refresh ( ) ;
CHECK_AND_ASSERT_MES ( check_balance_via_wallet ( * alice_wlt , " Alice " , MK_TEST_COINS ( 0 ) ) , false , " " ) ;
return true ;
}
2025-07-19 15:49:10 +03:00
// С oinbase transactions must NOT allow the TX_FLAG_SIGNATURE_MODE_SEPARATE flag.
// С hecks that setting this flag for coinbase fails, while a default coinbase (without the flag) succeeds.
bool tx_coinbase_separate_sig_flag : : generate ( std : : vector < test_event_entry > & events ) const
{
GENERATE_ACCOUNT ( miner ) ;
uint64_t ts = test_core_time : : get_time ( ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner ) ;
REWIND_BLOCKS_N ( events , blk_1r , blk_1 , miner , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ) ;
block blk_2 ;
auto coinbase_default_cb = [ ] ( transaction & miner_tx , const keypair & ) - > bool { return true ; } ;
auto coinbase_separate_cb = [ ] ( transaction & miner_tx , const keypair & ) - > bool
{
set_tx_flags ( miner_tx , get_tx_flags ( miner_tx ) | TX_FLAG_SIGNATURE_MODE_SEPARATE ) ;
return true ;
} ;
// с onstruct a block with the forbidden flag, should fail after hf4
bool with_separate_flag = generator . construct_block_gentime_with_coinbase_cb ( blk_1r , miner , coinbase_separate_cb , blk_2 ) ;
CHECK_AND_ASSERT_MES ( with_separate_flag , false , " expected failure because TX_FLAG_SIGNATURE_MODE_SEPARATE is forbidden for coinbase after HF4 " ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_2 ) ;
// construct a default coinbase block, should succeed
bool default_tx = generator . construct_block_gentime_with_coinbase_cb ( blk_1r , miner , coinbase_default_cb , blk_2 ) ;
CHECK_AND_ASSERT_MES ( default_tx , true , " default coinbase must succeed " ) ;
events . push_back ( blk_2 ) ;
return true ;
}
2025-07-15 23:36:53 +03:00
tx_input_mixins : : tx_input_mixins ( )
{
REGISTER_CALLBACK_METHOD ( tx_input_mixins , configure_core ) ;
}
bool tx_input_mixins : : configure_core ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
currency : : core_runtime_config pc = c . get_blockchain_storage ( ) . get_core_runtime_config ( ) ;
pc . min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE ;
pc . pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH ;
pc . hf4_minimum_mixins = 5 ;
pc . hard_forks . set_hardfork_height ( 1 , 0 ) ;
pc . hard_forks . set_hardfork_height ( 2 , 1 ) ;
pc . hard_forks . set_hardfork_height ( 3 , 1 ) ;
pc . hard_forks . set_hardfork_height ( 4 , 31 ) ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
// Tests that post-HF4, legacy txin_to_key inputs accept any mixin count, while new txin_zc inputs enforce a >= 15 mixin minimum
// are in the same vin and work together
bool tx_input_mixins : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts = test_core_time : : get_time ( ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
GENERATE_ACCOUNT ( bob_acc ) ;
m_hardforks . set_hardfork_height ( 1 , 0 ) ;
m_hardforks . set_hardfork_height ( 2 , 1 ) ;
m_hardforks . set_hardfork_height ( 3 , 1 ) ;
m_hardforks . set_hardfork_height ( 4 , 31 ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
// mine one block, then rewind enough to unlock initial coinbase funds
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , miner_acc ) ;
REWIND_BLOCKS_N ( events , blk_1r , blk_1 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW * 2 ) ;
// build tx_a_1: single txin_to_key output of 15 coins to Alice
transaction tx_a_1 ;
bool r = construct_tx_with_many_outputs ( m_hardforks , events , blk_1r , miner_acc . get_keys ( ) ,
alice_acc . get_public_address ( ) , MK_TEST_COINS ( 15 ) , 1 , TESTS_DEFAULT_FEE , tx_a_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx_with_many_outputs 1 failed " ) ;
events . push_back ( tx_a_1 ) ;
// build tx_a_2: 16 outputs of 15 coins each to Bob for mixins
transaction tx_a_2 ;
r = construct_tx_with_many_outputs ( m_hardforks , events , blk_1r , miner_acc . get_keys ( ) ,
bob_acc . get_public_address ( ) , MK_TEST_COINS ( 15 * 16 ) , 16 , TESTS_DEFAULT_FEE , tx_a_2 ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx_with_many_outputs 2 failed " ) ;
events . push_back ( tx_a_2 ) ;
MAKE_NEXT_BLOCK_TX_LIST ( events , blk_1r_1 , blk_1r , miner_acc , std : : list < transaction > ( { tx_a_1 , tx_a_2 } ) ) ;
// mine a couple more blocks and rewind to generate new spendable outputs
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1r_1 , miner_acc ) ;
REWIND_BLOCKS_N ( events , blk_2r , blk_2 , miner_acc , 4 ) ;
MAKE_NEXT_BLOCK ( events , blk_3 , blk_2r , miner_acc ) ;
REWIND_BLOCKS_N ( events , blk_4r , blk_3 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// make sure HF4 is active at 31 height
DO_CALLBACK_PARAMS ( events , " check_hardfork_active " , size_t { ZANO_HARDFORK_04_ZARCANUM } ) ;
// build tx_b with zc hf4 input, send 15 coins from miner -> Alice
std : : vector < currency : : tx_source_entry > sources_b ;
std : : vector < currency : : tx_destination_entry > destinations_b ;
CHECK_AND_ASSERT_MES ( fill_tx_sources_and_destinations (
events , blk_4r , miner_acc , alice_acc , MK_TEST_COINS ( 15 ) , TESTS_DEFAULT_FEE , 2 , sources_b , destinations_b ) ,
false , " fill_tx_sources_and_destinations failed " ) ;
currency : : transaction tx_b { } ;
r = construct_tx ( miner_acc . get_keys ( ) , sources_b , destinations_b , events , this , tx_b ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_b ) ;
// mine tx_b and rewind to unlock its outputs
MAKE_NEXT_BLOCK_TX1 ( events , blk_5 , blk_4r , miner_acc , tx_b ) ;
REWIND_BLOCKS_N ( events , blk_5r , blk_5 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
std : : vector < currency : : tx_source_entry > sources_c ;
std : : vector < currency : : tx_destination_entry > destinations_c ;
// For input #0 use 5 mixins (old-style ring-CT allows any mixins)
// For input #1 use 16 mixins (new-style txin_zc enforces >=15 once HF4 is live)
mixins_per_input nmix_map = { { 0 , 5 } , { 1 , 16 } } ;
CHECK_AND_ASSERT_MES ( fill_tx_sources_and_destinations (
events , blk_5r , alice_acc , bob_acc , MK_TEST_COINS ( 29 ) , TESTS_DEFAULT_FEE , 5 , sources_c , destinations_c ,
true , true , false , & nmix_map ) , false , " fill_tx_sources_and_destinations failed " ) ;
currency : : transaction tx_c { } ;
r = construct_tx ( alice_acc . get_keys ( ) , sources_c , destinations_c , events , this , tx_c ) ;
LOG_PRINT_GREEN ( " tx_c alice -> bob \n " < < obj_to_json_str ( tx_c ) , LOG_LEVEL_0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
events . push_back ( tx_c ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_6 , blk_5r , miner_acc , tx_c ) ;
return true ;
}