2018-12-27 18:50:45 +03:00
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// 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 "pos_validation.h"
# include "tx_builder.h"
# include "pos_block_builder.h"
2022-08-03 13:53:14 +02:00
# include "random_helper.h"
2018-12-27 18:50:45 +03:00
using namespace epee ;
using namespace crypto ;
using namespace currency ;
pos_validation : : pos_validation ( )
: m_invalid_block_index ( std : : numeric_limits < size_t > : : max ( ) )
, m_invalid_tx_index ( std : : numeric_limits < size_t > : : max ( ) )
{
REGISTER_CALLBACK_METHOD ( pos_validation , configure_core ) ;
REGISTER_CALLBACK_METHOD ( pos_validation , mark_invalid_tx ) ;
REGISTER_CALLBACK_METHOD ( pos_validation , mark_invalid_block ) ;
}
bool pos_validation : : 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 ; //four blocks
pc . pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH ; //four blocks
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
bool gen_pos_coinstake_already_spent : : generate ( std : : vector < test_event_entry > & events ) const
{
//test_gentime_settings s = test_generator::get_test_gentime_settings();
//s.miner_tx_max_outs = 11; // limit genesis outs for speed-up, as we going to spend all premine
//test_generator::set_test_gentime_settings(s);
GENERATE_ACCOUNT ( preminer ) ;
GENERATE_ACCOUNT ( miner ) ;
GENERATE_ACCOUNT ( alice ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer , test_core_time : : get_time ( ) ) ;
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 ) ;
// spend all money earned from block 1
uint64_t amount = get_outs_money_amount ( blk_1 . miner_tx ) ;
MAKE_TX ( events , tx_0 , miner , alice , amount - TESTS_DEFAULT_FEE , blk_1r ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_2 , blk_1r , alice , tx_0 ) ;
CREATE_TEST_WALLET ( miner_wlt , miner , blk_0 ) ;
REFRESH_TEST_WALLET_AT_GEN_TIME ( events , miner_wlt , blk_2 , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ) ;
LOG_PRINT_L0 ( " miner's transfers: " < < ENDL < < miner_wlt - > dump_trunsfers ( false ) ) ;
// Legend: (n) - PoW block, [m] - PoS block
// 0 1 11 12 13 <-- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10)
// (0 )--(1 )--(1r)--(2 )--[3 ] main chain
// try to make a PoS block referring to genesis' miner tx out, that is already spent
// create PoS block manually
block & prev_block = blk_2 ;
crypto : : hash prev_id = get_block_hash ( prev_block ) ;
size_t height = get_block_height ( prev_block ) + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
const transaction & stake = blk_1 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
uint64_t stake_output_gidx = 0 ;
bool r = find_global_index_for_output ( events , prev_id , stake , stake_output_idx , stake_output_gidx ) ;
CHECK_AND_ASSERT_MES ( r , false , " find_global_index_for_output failed " ) ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , prev_block . timestamp ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( prev_block ) , alice . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_3 = pb . m_block ;
// EXPECTED: blk_2 is rejected as invalid, because coinstake refers to already spent output
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_3 ) ;
return true ;
}
bool gen_pos_incorrect_timestamp : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t genesis_ts = 1450000000 ;
GENERATE_ACCOUNT ( miner ) ;
GENERATE_ACCOUNT ( alice ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , genesis_ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner , BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ) ;
// Legend: (n) - PoW block, [m] - PoS block
// 0 60 61 <-- blockchain height (assuming BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW == 60)
// (0 )--(0r)--[1 ] main chain
/* // se
uint64_t tiny_little_money = 1 ;
MAKE_TX ( events , tx_0 , miner , alice , tiny_little_money , blk_0r ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_1 , blk_0r , miner , tx_0 ) ;
*/
// make a PoS block manually with incorrect timestamp
crypto : : hash prev_id = get_block_hash ( blk_0r ) ;
size_t height = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
const transaction & stake = blk_0 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
size_t stake_output_gidx = 0 ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
// use incorrect timestamp_step
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , blk_0r . timestamp - 1 , POS_SCAN_WINDOW , POS_SCAN_STEP - 1 ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( blk_0r ) , alice . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_1 = pb . m_block ;
// EXPECTED: blk_1 is rejected as invalid, because it's timestamps % POS_SCAN_STEP != 0
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_1 ) ;
// shift core time a little
uint64_t ts = blk_0r . timestamp + 60 ;
events . push_back ( event_core_time ( ts ) ) ;
// Now try PoS timestamp window boundaries.
pb . clear ( ) ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
// move timestamp to the future
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , ts + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT + 1 , POS_SCAN_WINDOW , POS_SCAN_STEP ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( blk_0r ) , alice . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_2 = pb . m_block ;
// EXPECTED: blk_2 is rejected as invalid, because its timestamp is beyond upper limit
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_2 ) ;
// lower limit
pb . clear ( ) ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
// move timestamp to the future
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , genesis_ts - POS_SCAN_WINDOW , POS_SCAN_WINDOW , POS_SCAN_STEP ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( blk_0r ) , alice . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_3 = pb . m_block ;
// EXPECTED: blk_3 is rejected as invalid, because its timestamp is beyond lower limit
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_3 ) ;
return true ;
}
//------------------------------------------------------------------
gen_pos_too_early_pos_block : : gen_pos_too_early_pos_block ( )
: m_pos_min_height ( CURRENCY_MINED_MONEY_UNLOCK_WINDOW + TESTS_POS_CONFIG_POS_MINIMUM_HEIGH )
{
REGISTER_CALLBACK_METHOD ( gen_pos_too_early_pos_block , configure_core ) ;
}
bool gen_pos_too_early_pos_block : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts = time ( NULL ) ;
GENERATE_ACCOUNT ( miner ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N ( events , blk_0r , blk_0 , miner , m_pos_min_height - 2 ) ;
// Legend: (n) - PoW block, [m] - PoS block
// 0 N N+1 <-- blockchain height (N= CURRENCY_MINED_MONEY_UNLOCK_WINDOW + TESTS_POS_CONFIG_POS_MINIMUM_HEIGH - 2, now is 12)
// (0 )--(0r)--[1 ] main chain
// EXPECTED: blk_1 is rejected as invalid, because PoS blocks are not allowed on that height
DO_CALLBACK ( events , " mark_invalid_block " ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_1 , blk_0r , miner , std : : list < account_base > ( 1 , miner ) ) ;
return true ;
}
bool gen_pos_too_early_pos_block : : 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 = m_pos_min_height ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
//------------------------------------------------------------------
bool gen_pos_extra_nonce : : generate ( std : : vector < test_event_entry > & events ) const
{
uint64_t ts = time ( NULL ) ;
GENERATE_ACCOUNT ( miner ) ;
GENERATE_ACCOUNT ( alice ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , ts ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS ( events , blk_0r , blk_0 , miner ) ;
// Legend: (n) - PoW block, [m] - PoS block
// 0 10 11 <-- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10)
// (0 )--(0r)--[1 ] main chain
// make a PoS block manually with incorrect timestamp
crypto : : hash prev_id = get_block_hash ( blk_0r ) ;
size_t height = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
const transaction & stake = blk_0 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
size_t stake_output_gidx = 0 ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , blk_0r . timestamp ) ;
// use biggest possible extra nonce (255 bytes) + largest alias
2022-11-03 01:25:44 +01:00
currency : : blobdata extra_nonce ( 255 , ' x ' ) ;
//currency::extra_alias_entry alias = AUTO_VAL_INIT(alias); // TODO: this alias entry was ignored for a long time, now I commented it out, make sure it's okay -- sowle
//alias.m_alias = std::string(255, 'a');
//alias.m_address = miner.get_keys().account_address;
//alias.m_text_comment = std::string(255, 'y');
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( blk_0r ) , alice . get_public_address ( ) , extra_nonce , CURRENCY_MINER_TX_MAX_OUTS ) ;
2018-12-27 18:50:45 +03:00
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_1 = pb . m_block ;
// EXPECTED: blk_1 is accepted
events . push_back ( blk_1 ) ;
return true ;
}
gen_pos_min_allowed_height : : gen_pos_min_allowed_height ( )
{
REGISTER_CALLBACK_METHOD ( gen_pos_min_allowed_height , configure_core ) ;
}
bool gen_pos_min_allowed_height : : generate ( std : : vector < test_event_entry > & events ) const
{
GENERATE_ACCOUNT ( miner ) ;
GENERATE_ACCOUNT ( alice ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , alice , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// Legend: (n) - PoW block, [m] - PoS block
// 0 N N+1 <-- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == N)
// (0 )--(0r)--[1 ] main chain
MAKE_TX ( events , tx_1 , alice , miner , MK_TEST_COINS ( 1 ) , blk_0r ) ;
crypto : : hash prev_id = get_block_hash ( blk_0r ) ;
size_t height = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
const transaction & stake = blk_0 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
size_t stake_output_gidx = 0 ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( 1 , tx_1 ) ) ;
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , blk_0r . timestamp ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( blk_0r ) , alice . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner ) ;
block blk_1 = pb . m_block ;
// EXPECTED: blk_1 is accepted
events . push_back ( blk_1 ) ;
return true ;
}
bool gen_pos_min_allowed_height : : 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 ; //four blocks
pc . pos_minimum_heigh = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
//------------------------------------------------------------------
bool gen_pos_invalid_coinbase : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: make a PoS block with zero signature in miner_tx (exploits a bug).
GENERATE_ACCOUNT ( miner_acc ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// create PoS block manually
const block & prev_block = blk_0r ;
crypto : : hash prev_id = get_block_hash ( prev_block ) ;
size_t height = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1 ;
currency : : wide_difficulty_type diff = generator . get_difficulty_for_next_block ( prev_id , false ) ;
const transaction & stake = blk_0 . miner_tx ;
crypto : : public_key stake_tx_pub_key = get_tx_pub_key_from_extra ( stake ) ;
size_t stake_output_idx = 0 ;
size_t stake_output_gidx = 0 ;
2022-05-20 21:32:27 +02:00
uint64_t stake_output_amount = boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . amount ;
2018-12-27 18:50:45 +03:00
crypto : : key_image stake_output_key_image ;
keypair kp ;
generate_key_image_helper ( miner_acc . get_keys ( ) , stake_tx_pub_key , stake_output_idx , kp , stake_output_key_image ) ;
2022-05-20 21:32:27 +02:00
crypto : : public_key stake_output_pubkey = boost : : get < txout_to_key > ( boost : : get < currency : : tx_out_bare > ( stake . vout [ stake_output_idx ] ) . target ) . key ;
2018-12-27 18:50:45 +03:00
pos_block_builder pb ;
2022-10-22 21:00:14 +02:00
pb . step1_init_header ( generator . get_hardforks ( ) , height , prev_id ) ;
2018-12-27 18:50:45 +03:00
pb . step2_set_txs ( std : : vector < transaction > ( ) ) ;
pb . step3_build_stake_kernel ( stake_output_amount , stake_output_gidx , stake_output_key_image , diff , prev_id , null_hash , prev_block . timestamp ) ;
pb . step4_generate_coinbase_tx ( generator . get_timestamps_median ( prev_id ) , generator . get_already_generated_coins ( prev_block ) , alice_acc . get_public_address ( ) ) ;
pb . step5_sign ( stake_tx_pub_key , stake_output_idx , stake_output_pubkey , miner_acc ) ;
2022-06-19 19:47:43 +02:00
pb . m_block . miner_tx . signatures . clear ( ) ; // remove signatures
2018-12-27 18:50:45 +03:00
block blk_1 = pb . m_block ;
// EXPECTED: blk_1 is rejected as invalid, because coinbase cointains no signatures
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_1 ) ;
return true ;
}
//------------------------------------------------------------------
void decompose_amount_into_exact_number_of_pos_entries ( uint64_t amount , size_t pos_entries_count , std : : vector < uint64_t > & pos_amounts )
{
std : : list < uint64_t > pos_amounts_list ;
// decompose algorithm: we need exactly 'pos_entries_count' entries of power-10 which will give 'amount' in sum
auto f = [ & pos_amounts_list ] ( uint64_t a ) { pos_amounts_list . push_back ( a ) ; } ;
decompose_amount_into_digits ( amount , DEFAULT_DUST_THRESHOLD , f , f ) ; // get some initial decomposition
while ( pos_amounts_list . size ( ) < pos_entries_count )
{
for ( auto it = pos_amounts_list . begin ( ) ; it ! = pos_amounts_list . end ( ) & & pos_amounts_list . size ( ) < pos_entries_count ; /* nothing */ )
{
2019-04-25 23:37:43 +02:00
if ( * it > = 2 * TESTS_DEFAULT_FEE )
2018-12-27 18:50:45 +03:00
{
// each iteration pops one element 'a' and pushes two elements: c1 and c2, so that a == c1 + c2 (sum is invariant)
uint64_t a = * it ;
// remove zeros form the tail of 'a': a = 1230000 -> c = 123 * pow = 10000
uint64_t c = a , pow = 1 ;
for ( ; ; )
{
uint64_t tc = c / 10 , tpow = pow * 10 ;
if ( tc * tpow ! = a )
break ;
c = tc ;
pow = tpow ;
}
if ( c = = 1 )
{
c = 10 ;
pow / = 10 ;
}
// randomly split 'a' into sum of c1 and c2
// c in [2; 10], pow in [1...10^^n]
uint64_t c1 = random_in_range ( 1 , c - 1 ) ; // c1: [1; c-1]
uint64_t c2 = c - c1 ; // c2: [c1; c], c1 + c2 == c
pos_amounts_list . push_front ( c1 * pow ) ;
pos_amounts_list . push_front ( c2 * pow ) ;
it = pos_amounts_list . erase ( it ) ;
continue ;
}
+ + it ;
}
}
// random shuffle pos amounts to get rid of any kind of order
pos_amounts . assign ( pos_amounts_list . begin ( ) , pos_amounts_list . end ( ) ) ;
std : : shuffle ( pos_amounts . begin ( ) , pos_amounts . end ( ) , crypto : : uniform_random_bit_generator ( ) ) ;
}
bool populate_wallet_with_stake_coins ( std : : shared_ptr < tools : : wallet2 > w , std : : shared_ptr < tools : : wallet2 > money_source_w , size_t pos_entries_count , uint64_t amount , const std : : vector < uint64_t > & pos_amounts )
{
# define WALLET_TRY_CATCH(statement) \
try \
{ \
statement ; \
} \
catch ( std : : exception & e ) \
{ \
LOG_ERROR ( " Wallet operation failed: " < < e . what ( ) ) ; \
return false ; \
}
LOG_PRINT_L0 ( " populate_wallet_with_stake_coins: amount: " < < print_money ( amount ) < < " , pos_entries_count: " < < pos_entries_count ) ;
// make sure w is empty
w - > refresh ( ) ;
uint64_t balance_unlocked = 0 ;
uint64_t balance = w - > balance ( balance_unlocked ) ;
CHECK_AND_ASSERT_MES ( balance = = balance_unlocked , false , " WARNING: balance is " < < balance < < " NOT EQUAL TO unlocked balance, which is " < < balance_unlocked ) ;
2019-04-25 23:37:43 +02:00
if ( balance_unlocked > TESTS_DEFAULT_FEE )
2018-12-27 18:50:45 +03:00
WALLET_TRY_CATCH ( w - > transfer ( balance_unlocked , money_source_w - > get_account ( ) . get_public_address ( ) ) ) ;
uint64_t sum = 0 ;
for ( auto a : pos_amounts )
sum + = a ;
CHECK_AND_ASSERT_MES ( pos_amounts . size ( ) = = pos_entries_count & & sum = = amount , false , " Failed to decompose " < < print_money ( amount ) < < " into " < < pos_entries_count < < " entries: pos_amounts.size is " < < pos_amounts . size ( ) < < " , sum is " < < sum ) ;
// populate current wallet with pos entries from scratch
money_source_w - > refresh ( ) ;
balance = money_source_w - > balance ( balance_unlocked ) ;
2019-04-25 23:37:43 +02:00
CHECK_AND_ASSERT_MES ( balance_unlocked > amount + TESTS_DEFAULT_FEE * pos_entries_count , false , " source wallet has not enough money: balance_unlocked: " < < balance_unlocked < < " , balance: " < < balance < < " , required amount: " < < amount + TESTS_DEFAULT_FEE * pos_entries_count ) ;
2018-12-27 18:50:45 +03:00
const account_public_address & wallet_address = w - > get_account ( ) . get_public_address ( ) ;
std : : vector < tx_destination_entry > destinations ;
destinations . reserve ( pos_amounts . size ( ) ) ;
for ( auto pos_amount : pos_amounts )
destinations . push_back ( tx_destination_entry ( pos_amount , wallet_address ) ) ;
2019-04-25 23:37:43 +02:00
WALLET_TRY_CATCH ( money_source_w - > transfer ( destinations , 0 , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment ) ) ;
2018-12-27 18:50:45 +03:00
return true ;
# undef WALLET_TRY_CATCH
}
bool pos_wallet_minting_same_amount_diff_outs : : prepare_wallets_0 ( currency : : core & c , const std : : vector < test_event_entry > & events , std : : vector < wlt_info_t > & minting_wallets , std : : vector < size_t > & minting_wallets_order )
{
// Prepare equal wallets: same amount, same transfers (count and amount), same pos entries
// This is to test evenness of simulation environment and the stuff beyound wallet pos entries.
bool r = false ;
block b = AUTO_VAL_INIT ( b ) ;
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX ) ; // Alice is preminer
size_t pos_entries_count = 990 ;
std : : vector < uint64_t > pos_amounts ; // use the same stake decomposition for all wallets
decompose_amount_into_exact_number_of_pos_entries ( m_wallet_stake_amount , pos_entries_count , pos_amounts ) ;
std : : vector < pos_entry > first_wallet_pos_entries ;
// populate minting wallets with money
for ( size_t i = 0 ; i < m_minting_wallets_count ; + + i )
{
minting_wallets_order [ i ] = i ;
auto & w = minting_wallets [ i ] ;
w . w = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX + 1 + i ) ;
//w.pos_entries_count = random_in_range(m_pos_entries_count_min, m_pos_entries_count_max);
w . pos_entries_count = pos_entries_count ;
r = populate_wallet_with_stake_coins ( w . w , alice_wlt , w . pos_entries_count , m_wallet_stake_amount , pos_amounts ) ;
CHECK_AND_ASSERT_MES ( r , false , " populate_wallet_with_stake_coins failed " ) ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_blocks_in_playtime ( alice_wlt - > get_account ( ) . get_public_address ( ) , c , WALLET_DEFAULT_TX_SPENDABLE_AGE + 1 ) ; // to preventing run out of the money for Alice
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
c . get_blockchain_storage ( ) . get_top_block ( b ) ;
uint64_t ts = b . timestamp ;
test_core_time : : adjust ( ts ) ;
// check pos entries actually available
w . w - > refresh ( ) ;
2022-09-05 12:30:08 +02:00
std : : vector < pos_entry > pos_entries ;
r = w . w - > get_pos_entries ( pos_entries ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " get_pos_entries failed " ) ;
2022-09-05 12:30:08 +02:00
CHECK_AND_ASSERT_MES ( pos_entries . size ( ) = = w . pos_entries_count , false , " incorrect pos entries count in the wallet: " < < pos_entries . size ( ) < < " , expected: " < < w . pos_entries_count ) ;
2018-12-27 18:50:45 +03:00
uint64_t balance_unlocked = 0 ;
w . w - > balance ( balance_unlocked ) ;
CHECK_AND_ASSERT_MES ( balance_unlocked = = m_wallet_stake_amount , false , " incorrect wallet unlocked balance: " < < print_money ( balance_unlocked ) < < " , expected: " < < print_money ( m_wallet_stake_amount ) ) ;
// make sure all wallets has the same distribution of pos_entries
if ( i = = 0 )
{
2022-09-05 12:30:08 +02:00
first_wallet_pos_entries = pos_entries ;
2018-12-27 18:50:45 +03:00
}
else
{
for ( size_t i = 0 ; i < pos_amounts . size ( ) ; + + i )
{
2022-09-05 12:30:08 +02:00
CHECK_AND_ASSERT_MES ( pos_entries [ i ] . amount = = first_wallet_pos_entries [ i ] . amount , false , " Wallet pos entry # " < < i < < " has incorrect amount: " < < print_money ( pos_entries [ i ] . amount ) < < " , expected (as in 1st wallet): " < < print_money ( first_wallet_pos_entries [ i ] . amount ) ) ;
2018-12-27 18:50:45 +03:00
}
}
}
return true ;
}
bool pos_wallet_minting_same_amount_diff_outs : : prepare_wallets_1 ( currency : : core & c , const std : : vector < test_event_entry > & events , std : : vector < wlt_info_t > & minting_wallets , std : : vector < size_t > & minting_wallets_order )
{
// prepare five wallets with the same total amount but different pos entries distribution to test how it influence minting efficiency
size_t pos_entries_count_values [ ] = { 100 , 1000 , 300 , 990 , 2000 } ;
CHECK_AND_ASSERT_MES ( m_minting_wallets_count = = ( sizeof pos_entries_count_values / sizeof pos_entries_count_values [ 0 ] ) , false , " sizeof pos_entries_count_values != m_minting_wallets_count " ) ;
bool r = false ;
block b = AUTO_VAL_INIT ( b ) ;
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX ) ; // Alice is preminer
// populate minting wallets with money
for ( size_t i = 0 ; i < m_minting_wallets_count ; + + i )
{
minting_wallets_order [ i ] = i ;
auto & w = minting_wallets [ i ] ;
w . w = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX + 1 + i ) ;
//w.pos_entries_count = random_in_range(m_pos_entries_count_min, m_pos_entries_count_max);
w . pos_entries_count = pos_entries_count_values [ i % ( sizeof pos_entries_count_values / sizeof pos_entries_count_values [ 0 ] ) ] ;
std : : vector < uint64_t > pos_amounts ;
decompose_amount_into_exact_number_of_pos_entries ( m_wallet_stake_amount , w . pos_entries_count , pos_amounts ) ;
r = populate_wallet_with_stake_coins ( w . w , alice_wlt , w . pos_entries_count , m_wallet_stake_amount , pos_amounts ) ;
CHECK_AND_ASSERT_MES ( r , false , " populate_wallet_with_stake_coins failed " ) ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_blocks_in_playtime ( alice_wlt - > get_account ( ) . get_public_address ( ) , c , WALLET_DEFAULT_TX_SPENDABLE_AGE + 1 ) ; // to preventing run out of the money for Alice
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
c . get_blockchain_storage ( ) . get_top_block ( b ) ;
uint64_t ts = b . timestamp ;
test_core_time : : adjust ( ts ) ;
// check pos entries actually available
w . w - > refresh ( ) ;
2022-09-05 12:30:08 +02:00
size_t pos_entries_count = w . w - > get_pos_entries_count ( ) ;
CHECK_AND_ASSERT_MES ( pos_entries_count = = w . pos_entries_count , false , " incorrect pos entries count in the wallet: " < < pos_entries_count < < " , expected: " < < w . pos_entries_count ) ;
2018-12-27 18:50:45 +03:00
uint64_t balance_unlocked = 0 ;
w . w - > balance ( balance_unlocked ) ;
CHECK_AND_ASSERT_MES ( balance_unlocked = = m_wallet_stake_amount , false , " incorrect wallet unlocked balance: " < < print_money ( balance_unlocked ) < < " , expected: " < < print_money ( m_wallet_stake_amount ) ) ;
}
return true ;
}
pos_wallet_minting_same_amount_diff_outs : : pos_wallet_minting_same_amount_diff_outs ( )
{
REGISTER_CALLBACK_METHOD ( pos_wallet_minting_same_amount_diff_outs , c1 ) ;
m_minting_wallets_count = 5 ;
m_wallet_stake_amount = 1000 * COIN ;
}
bool pos_wallet_minting_same_amount_diff_outs : : generate ( std : : vector < test_event_entry > & events ) const
{
GENERATE_ACCOUNT ( miner_acc ) ;
GENERATE_ACCOUNT ( alice_acc ) ;
m_accounts . resize ( ALICE_ACC_IDX + 1 + m_minting_wallets_count ) ;
m_accounts [ MINER_ACC_IDX ] = miner_acc ;
m_accounts [ ALICE_ACC_IDX ] = alice_acc ;
for ( size_t i = 0 ; i < m_minting_wallets_count ; + + i )
m_accounts [ ALICE_ACC_IDX + 1 + i ] . generate ( ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , alice_acc , test_core_time : : get_time ( ) ) ; // Alice is a preminer!
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool pos_wallet_minting_same_amount_diff_outs : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
// Test idea: few wallets with the same total amount but different transfers are mining simultaneously.
// All generated money is calculated and goes to Miner, so wallets' balances are constant.
// Generated amounts per each wallet should be more or less the same, as far they have the same stakes.
uint64_t ts = test_core_time : : get_time ( ) ;
bool r = false ;
block b = AUTO_VAL_INIT ( b ) ;
//
// 1/2 prepare wallets
//
std : : vector < size_t > minting_wallets_order ( m_minting_wallets_count ) ; // to iterate wallets in random order while minting
std : : vector < wlt_info_t > minting_wallets ( m_minting_wallets_count ) ;
r = prepare_wallets_0 ( c , events , minting_wallets , minting_wallets_order ) ;
CHECK_AND_ASSERT_MES ( r , false , " prepare wallets failed " ) ;
//
// 2/2 simulate the mining process
//
uint64_t pos_target = DIFFICULTY_POS_TARGET ;
uint64_t pow_target = DIFFICULTY_POW_TARGET ;
uint64_t last_pow_block_ts = ts - pow_target ;
//uint64_t last_pos_block_ts = ts - pos_target; // block_header::ts
//uint64_t last_pos_block_ts_real = ts - pos_target; // real time at the moment of successfull minting
wide_difficulty_type last_pos_diff = 0 ;
size_t pos_blocks = 0 ;
uint64_t total_minted_coins = 0 ;
# define LOG1_FILENAME "pos_wallet_minting_same_amount_diff_outs.log" // use custom log to store data sheet-friendly
// print header to the log
std : : stringstream ss ;
ss < < " \t " < < " height " < < " \t " < < " pos % " < < " \t " < < " mnt.coins/pos blks " < < " \t " < < " last_pos_diff " < < " \t " < < " stake_to_diff_coeff " < < " \t " < < " pow_seq_factor " < < " \t " < < " pos_seq_factor " ;
for ( size_t w_idx = 0 ; w_idx < m_minting_wallets_count ; + + w_idx )
ss < < " \t w# " < < w_idx < < " _ " < < minting_wallets [ w_idx ] . pos_entries_count ;
LOG_PRINT2 ( LOG1_FILENAME , ss . str ( ) , LOG_LEVEL_0 ) ;
for ( size_t n = 0 ; n < 30000 ; + + n )
{
// log status
c . get_blockchain_storage ( ) . get_top_block ( b ) ;
size_t height = get_block_height ( b ) ;
size_t pow_seq_factor = c . get_blockchain_storage ( ) . get_current_sequence_factor ( false ) ;
size_t pos_seq_factor = c . get_blockchain_storage ( ) . get_current_sequence_factor ( true ) ;
double pos_total_stake_to_diff_coeff = static_cast < double > ( last_pos_diff / COIN ) / static_cast < double > ( m_minting_wallets_count * m_wallet_stake_amount / COIN ) ;
float pos_blocks_proportion = static_cast < float > ( pos_blocks ) / height ;
uint64_t minted_coins_per_pos_block = total_minted_coins / height ;
ss . str ( " " ) ; // <-- just a wired way to clear stringstream, have a good day!
ss < < " \t " < < height < < " \t " < < pos_blocks_proportion < < " \t " < < print_money ( minted_coins_per_pos_block ) < < " \t " < < last_pos_diff < < " \t " < < pos_total_stake_to_diff_coeff < < " \t " < < pow_seq_factor < < " \t " < < pos_seq_factor ;
for ( size_t w_idx = 0 ; w_idx < m_minting_wallets_count ; + + w_idx )
ss < < " \t " < < print_money ( minting_wallets [ w_idx ] . mined_coins ) ;
LOG_PRINT2 ( LOG1_FILENAME , ss . str ( ) , LOG_LEVEL_0 ) ;
if ( n % 250 = = 0 )
dump_wallets_entries ( minting_wallets ) ;
// pow
uint64_t ideal_next_pow_block_ts = last_pow_block_ts + pow_target ;
if ( ts < ideal_next_pow_block_ts )
ts = ideal_next_pow_block_ts ; // "wait" until ideal_next_pow_block_ts if it was not already happened (fast forward but don't wayback the time)
test_core_time : : adjust ( ts ) ;
size_t tx_count_before = c . get_pool_transactions_count ( ) ;
2019-03-25 01:30:20 +01:00
r = mine_next_pow_block_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_block_in_playtime failed " ) ;
CHECK_AND_ASSERT_MES ( tx_count_before = = 0 | | c . get_pool_transactions_count ( ) < tx_count_before , false , " invalid number of txs in tx pool: " < < c . get_pool_transactions_count ( ) < < " , was: " < < tx_count_before ) ;
last_pow_block_ts = ts ;
// pos
uint64_t pos_ts_upper_bound = ts + pos_target / 2 ;
while ( ts < = pos_ts_upper_bound )
{
// every POS_SCAN_STEP all wallets tries to mint a PoS block simultaneously
std : : shuffle ( minting_wallets_order . begin ( ) , minting_wallets_order . end ( ) , crypto : : uniform_random_bit_generator ( ) ) ; // shuffle to iterate in random order
bool a_block_was_minted = true ;
do
{
a_block_was_minted = false ;
for ( size_t i = 0 ; i < m_minting_wallets_count ; + + i )
{
size_t w_idx = minting_wallets_order [ i ] ;
LOG_PRINT_L0 ( " Minting with wallet # " < < w_idx ) ;
auto & current_wallet = minting_wallets [ w_idx ] ;
current_wallet . w - > refresh ( ) ;
if ( n % 10 = = 0 ) // check balances every 10 PoW blocks in a sake of speed
{
uint64_t balance = current_wallet . w - > balance ( ) ;
CHECK_AND_ASSERT_MES ( balance = = m_wallet_stake_amount , false , " Wallet # " < < w_idx < < " has incorrect balance: " < < print_money ( balance ) < < " , expected: " < < print_money ( m_wallet_stake_amount ) ) ;
}
last_pos_diff = c . get_blockchain_storage ( ) . get_next_diff_conditional ( true ) ;
uint64_t bc_height_before = c . get_blockchain_storage ( ) . get_current_blockchain_size ( ) ;
size_t pos_entries_count = 0 ;
if ( mine_next_pos_block_in_playtime_with_wallet ( * current_wallet . w . get ( ) , m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , pos_entries_count ) )
{
+ + pos_blocks ;
a_block_was_minted = true ;
height = c . get_blockchain_storage ( ) . get_current_blockchain_size ( ) ;
CHECK_AND_ASSERT_MES ( bc_height_before < height , false , " didn't mint a block! " ) ;
LOG_PRINT_GREEN ( " >>>>>>>>>>>>>> Minted a PoS block by wallet # " < < w_idx < < " at height " < < height < < " and time " < < ts < < " with difficuly: " < < last_pos_diff < < " using " < < pos_entries_count < < " pos entries " , LOG_LEVEL_0 ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " invalid number of txs in tx pool: " < < c . get_pool_transactions_count ( ) ) ;
c . get_blockchain_storage ( ) . get_top_block ( b ) ;
//last_pos_block_ts = b.timestamp;
//last_pos_block_ts_real = ts;
CHECK_AND_ASSERT_MES ( is_pos_block ( b ) , false , " Top block is not a PoS block! " ) ;
uint64_t generated_coins = get_outs_money_amount ( b . miner_tx ) - boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) . amount ;
current_wallet . mined_coins + = generated_coins ;
total_minted_coins + = generated_coins ;
}
}
}
while ( a_block_was_minted ) ;
ts + = POS_SCAN_STEP ;
test_core_time : : adjust ( ts ) ;
}
}
// Todo: add generated money comparation
return true ;
# undef LOG1_FILENAME
}
void pos_wallet_minting_same_amount_diff_outs : : dump_wallets_entries ( const std : : vector < wlt_info_t > & minting_wallets )
{
# define LOG2_FILENAME "pos_wallet_minting_same_amount_diff_outs.wallets_dumps.log"
LOG_PRINT2 ( LOG2_FILENAME , ENDL < < ENDL < < ENDL < < ENDL < < ENDL < < ENDL , LOG_LEVEL_0 ) ;
for ( size_t i = 0 ; i < minting_wallets . size ( ) ; + + i )
{
LOG_PRINT2 ( LOG2_FILENAME , " wallet # " < < i < < " : " < < ENDL < < minting_wallets [ i ] . w - > dump_trunsfers ( ) < < ENDL , LOG_LEVEL_0 ) ;
}
# undef LOG2_FILENAME
}
//------------------------------------------------------------------
pos_wallet_big_block_test : : pos_wallet_big_block_test ( )
{
REGISTER_CALLBACK_METHOD ( pos_wallet_big_block_test , c1 ) ;
REGISTER_CALLBACK_METHOD ( pos_wallet_big_block_test , configure_core ) ;
}
bool pos_wallet_big_block_test : : generate ( std : : vector < test_event_entry > & events ) const
{
// Test idea: check PoS mining via wallet for block size greather than median (of sizes of last N block)
GENERATE_ACCOUNT ( preminer_acc ) ;
GENERATE_ACCOUNT ( miner_acc ) ;
m_accounts . push_back ( miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , preminer_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3 ) ;
// make a couple of huge txs
bool r = false ;
size_t padding_size = CURRENCY_MAX_TRANSACTION_BLOB_SIZE * 5 / 8 ; // to get over one median (of sizes of last N blocks) but not to exceed 2 medians
extra_padding padding = AUTO_VAL_INIT ( padding ) ;
padding . buff . resize ( padding_size ) ;
std : : vector < extra_v > extra ( { padding } ) ;
std : : vector < tx_source_entry > sources_1 , sources_2 ;
r = fill_tx_sources ( sources_1 , events , blk_0r , miner_acc . get_keys ( ) , MK_TEST_COINS ( 3 ) , 0 ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources failed " ) ;
r = fill_tx_sources ( sources_2 , events , blk_0r , miner_acc . get_keys ( ) , MK_TEST_COINS ( 3 ) , 0 , sources_1 ) ;
CHECK_AND_ASSERT_MES ( r , false , " fill_tx_sources failed " ) ;
std : : vector < tx_destination_entry > destinations ( { tx_destination_entry ( MK_TEST_COINS ( 1 ) , miner_acc . get_public_address ( ) ) } ) ; // yep, it will be bigger fee than usual, don't mind
crypto : : secret_key stub ;
transaction tx_1 = AUTO_VAL_INIT ( tx_1 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources_1 , destinations , extra , empty_attachment , tx_1 , get_tx_version_from_events ( events ) , stub , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
size_t tx_size = get_object_blobsize ( tx_1 ) ;
CHECK_AND_ASSERT_MES ( tx_size > padding_size , false , " Tx size is: " < < tx_size < < " , expected to be bigger than: " < < padding_size ) ;
events . push_back ( tx_1 ) ; // push it to the pool
transaction tx_2 = AUTO_VAL_INIT ( tx_2 ) ;
2022-05-11 23:52:33 +02:00
r = construct_tx ( miner_acc . get_keys ( ) , sources_2 , destinations , extra , empty_attachment , tx_2 , get_tx_version_from_events ( events ) , stub , 0 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_tx failed " ) ;
tx_size = get_object_blobsize ( tx_2 ) ;
CHECK_AND_ASSERT_MES ( tx_size > padding_size , false , " Tx size is: " < < tx_size < < " , expected to be bigger than: " < < padding_size ) ;
events . push_back ( tx_2 ) ; // push it to the pool
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool pos_wallet_big_block_test : : 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 ; //four blocks
pc . pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH ; //four blocks
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
bool pos_wallet_big_block_test : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
bool r = false , stub_bool ;
std : : shared_ptr < tools : : wallet2 > miner_wlt = init_playtime_test_wallet ( events , c , MINER_ACC_IDX ) ;
miner_wlt - > refresh ( ) ;
miner_wlt - > scan_tx_pool ( stub_bool ) ;
LOG_PRINT_L0 ( " miner transfers: " < < ENDL < < miner_wlt - > dump_trunsfers ( false ) ) ;
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 2 , false , " Incorrect number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
try
{
r = miner_wlt - > try_mint_pos ( ) ;
}
catch ( std : : exception & e )
{
LOG_ERROR ( " Got exception in try_mint_pos(): " < < e . what ( ) ) ;
return false ;
}
CHECK_AND_ASSERT_MES ( r , false , " try_mint_pos failed " ) ;
// make sure the tx has gone
CHECK_AND_ASSERT_MES ( c . get_pool_transactions_count ( ) = = 0 , false , " Incorrect number of txs in the pool: " < < c . get_pool_transactions_count ( ) ) ;
// check top block - should be PoS with certain size
block b = AUTO_VAL_INIT ( b ) ;
r = c . get_blockchain_storage ( ) . get_top_block ( b ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_top_block failed " ) ;
CHECK_AND_ASSERT_MES ( is_pos_block ( b ) , false , " top block is not PoS " ) ;
size_t block_size = get_object_blobsize ( b . miner_tx ) ;
for ( const crypto : : hash & h : b . tx_hashes )
{
transaction t = AUTO_VAL_INIT ( t ) ;
r = c . get_transaction ( h , t ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_transaction failed " ) ;
block_size + = get_object_blobsize ( t ) ;
}
uint64_t block_size_median = c . get_blockchain_storage ( ) . get_current_comulative_blocksize_limit ( ) / 2 ;
CHECK_AND_ASSERT_MES ( block_size_median < block_size & & block_size < 2 * block_size_median , false , " Incorrect PoS block size: " < < block_size < < " , expected to be between median and 2 medinas: " < < block_size_median ) ;
return true ;
}
//------------------------------------------------------------------
pos_altblocks_validation : : pos_altblocks_validation ( )
{
}
bool pos_altblocks_validation : : 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 = 1 ;
pc . pos_minimum_heigh = 1 ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
bool pos_altblocks_validation : : generate ( std : : vector < test_event_entry > & events ) const
{
bool r = false ;
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 ( ) ;
std : : list < account_base > miner_acc_lst ( 1 , miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
MAKE_NEXT_BLOCK ( events , blk_1 , blk_0 , alice_acc ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_1r , blk_1 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
MAKE_NEXT_BLOCK ( events , blk_2 , blk_1r , miner_acc ) ;
const transaction & stake_tx = blk_1 . miner_tx ;
uint64_t alice_money = get_outs_money_amount ( stake_tx ) ;
uint64_t stake_tx_out_id = 0 ;
// select stake_tx_out_id as an output with the biggest amount
for ( size_t i = 1 ; i < stake_tx . vout . size ( ) ; + + i )
{
2022-05-20 21:32:27 +02:00
if ( boost : : get < currency : : tx_out_bare > ( stake_tx . vout [ i ] ) . amount > boost : : get < currency : : tx_out_bare > ( stake_tx . vout [ stake_tx_out_id ] ) . amount )
2018-12-27 18:50:45 +03:00
stake_tx_out_id = i ;
}
2019-04-25 23:37:43 +02:00
MAKE_TX_FEE ( events , tx_0 , alice_acc , alice_acc , alice_money - TESTS_DEFAULT_FEE * 17 , TESTS_DEFAULT_FEE * 17 , blk_2 ) ;
2018-12-27 18:50:45 +03:00
// tx_0 transfers all Alice's money, so it effectevily spends all outputs in stake_ts, make sure it does
CHECK_AND_ASSERT_MES ( tx_0 . vin . size ( ) = = stake_tx . vout . size ( ) , false , " probably, tx_0 doesn't spend all Alice's money as expected, tx_0.vin.size()= " < < tx_0 . vin . size ( ) < < " , stake_tx.vout.size()= " < < stake_tx . vout . size ( ) ) ;
MAKE_NEXT_BLOCK_TX1 ( events , blk_3 , blk_2 , miner_acc , tx_0 ) ;
// remember some tx_0's output info for further reference
size_t tx_0_some_output_idx = 0 ;
uint64_t tx_0_some_output_gindex = UINT64_MAX ;
r = find_global_index_for_output ( events , get_block_hash ( blk_3 ) , tx_0 , tx_0_some_output_idx , tx_0_some_output_gindex ) ;
CHECK_AND_ASSERT_MES ( r , false , " find_global_index_for_output failed " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_3r , blk_3 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
MAKE_NEXT_POS_BLOCK ( events , blk_4 , blk_3r , miner_acc , miner_acc_lst ) ;
MAKE_NEXT_BLOCK ( events , blk_5 , blk_4 , miner_acc ) ;
// 0 1 11 12 13 23 24 25 <- height
//
// +------- blk_1 mined by Alice
// |
// (0 )- (1 )-...(1r)- (2 )- (3 )-...(3r)- (4 )- (5 )- <- main chain
// | tx_0
// +---<<---uses-blk_1-out--+
// Case 1 (should pass)
// alt PoS block (blk_2a) refers in its coinstake to an output (stake_tx_out_id) already spent in the main chain
block blk_2a = AUTO_VAL_INIT ( blk_2a ) ;
r = generate_pos_block_with_given_coinstake ( generator , events , alice_acc , blk_1r , stake_tx , stake_tx_out_id , blk_2a ) ;
CHECK_AND_ASSERT_MES ( r , false , " generate_pos_block_with_given_coinstake failed " ) ;
events . push_back ( blk_2a ) ;
CHECK_AND_ASSERT_MES ( generator . add_block_info ( blk_2a , std : : list < transaction > ( ) ) , false , " add_block_info failed " ) ;
// 0 1 11 12 13 23 24 25 <- height
//
// +-----------------------+
// | tx_0 tx_0 spends all outputs in blk_1 (main chain)
// (0 )- (1 )-...(1r)- (2 )- (3 )-...(3r)- (4 )- (5 )- <- main chain
2020-08-20 17:05:03 +03:00
// | \
2018-12-27 18:50:45 +03:00
// | \- (2a)- <- alt chain
// +--------------+ PoS block 2a uses stake already spent in main chain
// Case 2 (should fail)
// alt PoS block (blk_3a) refers in its coinstake to an output (stake_tx_out_id) already spent in this alt chain (in blk_2a)
block blk_3a = AUTO_VAL_INIT ( blk_3a ) ;
r = generate_pos_block_with_given_coinstake ( generator , events , alice_acc , blk_2a , stake_tx , stake_tx_out_id , blk_3a ) ;
CHECK_AND_ASSERT_MES ( r , false , " generate_pos_block_with_given_coinstake failed " ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_3a ) ;
// 0 1 11 12 13 23 24 25 <- height
//
// +-----------------------+
// | tx_0 tx_0 spends all outputs in blk_1 (main chain)
// (0 )- (1 )-...(1r)- (2 )- (3 )-...(3r)- (4 )- (5 )- <- main chain
2020-08-20 17:05:03 +03:00
// || \
2018-12-27 18:50:45 +03:00
// || \- (2a)- #3a#- <- alt chain
// |+--------------+ | PoS block 2a uses stake already spent in main chain (okay)
// +-----------------------+ PoS block 3a uses stake already spent in current alt chain (fail)
REWIND_BLOCKS_N_WITH_TIME ( events , blk_2br , blk_2a , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
// Case 3 (should pass)
// alt PoS block (blk_3b) in its coinstake refers to an output from current altchain (from blk_2a)
block blk_3b = AUTO_VAL_INIT ( blk_3b ) ;
r = generate_pos_block_with_given_coinstake ( generator , events , alice_acc , blk_2br , blk_2a . miner_tx , 0 , blk_3b ) ;
CHECK_AND_ASSERT_MES ( r , false , " generate_pos_block_with_given_coinstake failed " ) ;
events . push_back ( blk_3b ) ;
CHECK_AND_ASSERT_MES ( generator . add_block_info ( blk_3b , std : : list < transaction > ( ) ) , false , " add_block_info failed " ) ;
// 0 1 11 12 13 22 23 24 25 <- height
//
// +-----------------------+
// | tx_0 tx_0 spends all outputs in blk_1 (main chain)
// (0 )- (1 )-...(1r)- (2 )- (3 )- ........ (3r)- (4 )- (5 )- <- main chain
2020-08-20 17:05:03 +03:00
// || \
2018-12-27 18:50:45 +03:00
// || \- (2a)- #3a#- <- alt chain
// |+--------------+ \ | PoS block 2a uses stake already spent in main chain (okay)
// +---------------|-------+ PoS block 3a uses stake already spent in current alt chain (fail)
2020-08-20 17:05:03 +03:00
// | \
2018-12-27 18:50:45 +03:00
// | \ ...... (2br)- (3b)- <- alt chain
// | |
// +-----------------------+ PoS block 3b uses as stake an output, created in current alt chain (2a)
// Case 4 (should fail)
// alt PoS block (blk_4b) in its coinstake refers to an output (tx_0) that appeared in the main chain (blk_3) above split height
block blk_4b = AUTO_VAL_INIT ( blk_4b ) ;
r = generate_pos_block_with_given_coinstake ( generator , events , alice_acc , blk_3b , tx_0 , tx_0_some_output_idx , blk_4b , tx_0_some_output_gindex ) ;
CHECK_AND_ASSERT_MES ( r , false , " generate_pos_block_with_given_coinstake failed " ) ;
DO_CALLBACK ( events , " mark_invalid_block " ) ;
events . push_back ( blk_4b ) ;
// 0 1 11 12 13 22 23 24 25 <- height
// +------------------+
// +-----------------------+ | |
// | tx_0 | tx_0 spends all outputs in blk_1 (main chain)
// (0 )- (1 )-...(1r)- (2 )- (3 )- ........ (3r)- (4 )- (5 )- <- main chain
// || \ |
// || \- (2a)- #3a#- | <- alt chain
// |+--------------+ \ | \ PoS block 2a uses stake already spent in main chain (okay)
// +---------------|-------+ \ PoS block 3a uses stake already spent in current alt chain (fail)
2020-08-20 17:05:03 +03:00
// | \ \
2018-12-27 18:50:45 +03:00
// | \ ...... (2br)- (3b)- #4b# <- alt chain
// | |
// +-----------------------+ PoS block 3b uses as stake an output, created in current alt chain (2a)
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( get_block_height ( blk_5 ) , get_block_hash ( blk_5 ) ) ) ;
// Final check: switch the chains
MAKE_NEXT_BLOCK ( events , blk_4c , blk_3b , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_5c , blk_4c , miner_acc ) ;
MAKE_NEXT_BLOCK ( events , blk_6c , blk_5c , miner_acc ) ;
DO_CALLBACK_PARAMS ( events , " check_top_block " , params_top_block ( get_block_height ( blk_6c ) , get_block_hash ( blk_6c ) ) ) ;
size_t txs_count = 1 ;
DO_CALLBACK_PARAMS ( events , " check_tx_pool_count " , txs_count ) ; // tx_0 should left in the pool
return true ;
}
2019-12-06 19:03:12 +03:00
//------------------------------------------------------------------
pos_minting_tx_packing : : pos_minting_tx_packing ( )
: m_pos_mint_packing_size ( 5 )
{
REGISTER_CALLBACK_METHOD ( pos_minting_tx_packing , configure_core ) ;
REGISTER_CALLBACK_METHOD ( pos_minting_tx_packing , c1 ) ;
}
bool pos_minting_tx_packing : : 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 = 1 ;
pc . pos_minimum_heigh = 1 ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( pc ) ;
return true ;
}
bool pos_minting_tx_packing : : pos_minting_tx_packing : : generate ( std : : vector < test_event_entry > & events ) const
{
bool r = false ;
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 ( ) ;
std : : list < account_base > miner_acc_lst ( 1 , miner_acc ) ;
MAKE_GENESIS_BLOCK ( events , blk_0 , miner_acc , test_core_time : : get_time ( ) ) ;
DO_CALLBACK ( events , " configure_core " ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_0r , blk_0 , miner_acc , CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5 ) ;
m_alice_start_amount = 10 * CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size ; // +TESTS_DEFAULT_FEE;
2020-02-21 03:00:56 +03:00
// 10 outputs each of (CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size) coins
2019-12-06 19:03:12 +03:00
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 ( ) , m_alice_start_amount , 10 , TESTS_DEFAULT_FEE , tx_1 ) ;
2019-12-06 19:03:12 +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 ) ;
REWIND_BLOCKS_N_WITH_TIME ( events , blk_1r , blk_1 , miner_acc , WALLET_DEFAULT_TX_SPENDABLE_AGE ) ;
DO_CALLBACK ( events , " c1 " ) ;
return true ;
}
bool pos_minting_tx_packing : : c1 ( currency : : core & c , size_t ev_index , const std : : vector < test_event_entry > & events )
{
bool r = false ;
std : : shared_ptr < tools : : wallet2 > alice_wlt = init_playtime_test_wallet ( events , c , ALICE_ACC_IDX ) ;
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt , m_alice_start_amount , true , UINT64_MAX , m_alice_start_amount ) , false , " " ) ;
size_t pos_entries_count = 0 ;
for ( size_t i = 0 ; i < m_pos_mint_packing_size ; + + i )
{
r = mine_next_pos_block_in_playtime_with_wallet ( * alice_wlt , m_accounts [ ALICE_ACC_IDX ] . get_public_address ( ) , pos_entries_count ) ;
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pos_block_in_playtime_with_wallet failed " ) ;
alice_wlt - > refresh ( ) ;
}
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt , m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size , true , UINT64_MAX ) , false , " " ) ;
r = mine_next_pow_blocks_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c , WALLET_DEFAULT_TX_SPENDABLE_AGE ) ;
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
2019-12-11 20:49:26 +03:00
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size , // total
true ,
UINT64_MAX ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked
) , false , " " ) ;
2019-12-06 19:03:12 +03:00
2019-12-11 20:49:26 +03:00
alice_wlt - > set_pos_mint_packing_size ( m_pos_mint_packing_size ) ;
2019-12-06 19:03:12 +03:00
2020-02-21 03:00:56 +03:00
// no coinbase tx outputs should be packed
2019-12-06 19:03:12 +03:00
r = alice_wlt - > try_mint_pos ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " try_mint_pos failed " ) ;
2019-12-11 20:49:26 +03:00
// make sure the wallet has only received new locked incoming reward
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 1 ) , // total
true ,
UINT64_MAX ,
m_alice_start_amount // unlocked (one output with amount == CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size was spent as stake)
) , false , " " ) ;
r = mine_next_pow_blocks_in_playtime ( m_accounts [ MINER_ACC_IDX ] . get_public_address ( ) , c , WALLET_DEFAULT_TX_SPENDABLE_AGE ) ;
CHECK_AND_ASSERT_MES ( r , false , " mine_next_pow_blocks_in_playtime failed " ) ;
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 1 ) , // total
true ,
UINT64_MAX ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 1 ) // unlocked
) , false , " " ) ;
// coinbase tx outputs should be packed now, there's enough coinbase outputs (> m_pos_mint_packing_size)
r = alice_wlt - > try_mint_pos ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " try_mint_pos failed " ) ;
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt ,
2020-02-21 03:00:56 +03:00
m_alice_start_amount + CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 2 ) , // total (+1 one block reward)
2019-12-11 20:49:26 +03:00
true ,
UINT64_MAX ,
2020-02-21 03:00:56 +03:00
// CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size locked for stake
// CURRENCY_BLOCK_REWARD * (m_pos_mint_packing_size + 1) locked for packing tx
m_alice_start_amount + CURRENCY_BLOCK_REWARD - CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 1 ) // unlocked
2019-12-11 20:49:26 +03:00
) , false , " " ) ;
2019-12-06 19:03:12 +03:00
2020-02-21 03:00:56 +03:00
r = alice_wlt - > try_mint_pos ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " try_mint_pos failed " ) ;
CHECK_AND_ASSERT_MES ( refresh_wallet_and_check_balance ( " " , " Alice " , alice_wlt ,
m_alice_start_amount + CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 3 ) , // total (+1 one block reward)
true ,
UINT64_MAX ,
// CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size locked for stake
m_alice_start_amount + CURRENCY_BLOCK_REWARD - CURRENCY_BLOCK_REWARD * ( m_pos_mint_packing_size + 1 ) - CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked
) , false , " " ) ;
2019-12-06 19:03:12 +03:00
return true ;
}