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 <thread>
# include "core_concurrency_test.h"
# include <boost/variant.hpp>
# include "include_base_utils.h"
using namespace epee ;
# include "currency_core/currency_core.h"
# include "rpc/core_rpc_server.h"
2020-02-12 20:36:54 +01:00
# include "wallet/core_fast_rpc_proxy.h"
2018-12-27 18:50:45 +03:00
# include "version.h"
# include "common/command_line.h"
# include "common/boost_serialization_helper.h"
using namespace currency ;
# include "../core_tests/test_core_time.h"
std : : atomic < int64_t > test_core_time : : m_time_shift ;
2022-07-22 05:32:41 +02:00
# include "../core_tests/random_helper.h"
2018-12-27 18:50:45 +03:00
# include "../core_tests/test_core_proxy.h"
# include "../core_tests/chaingen_helpers.h"
# include "../core_tests/core_state_helper.h"
2020-07-11 18:42:59 +02:00
//#define TESTS_DEFAULT_FEE TX_DEFAULT_FEE
2018-12-27 18:50:45 +03:00
2019-04-17 18:35:32 +03:00
static std : : atomic < uint64_t > s_generated_money_total ( 0 ) ; // TODO: consiger changing to boost::multiprecision::uint128_t
2018-12-27 18:50:45 +03:00
static size_t s_wallets_total_count = 10 ; // total number of wallet that will be randomly used to generate transactions
//static size_t s_althchains_minimum_height = 150; // height at which althchaining is started
static size_t s_tx_generation_minimum_height = 100 ; // height at which tx generation is started
static size_t s_tx_count_per_block_max = 0 ; // maximum tx count to be generated per block
static size_t s_althchain_max_depth = 9 ; // maximum possible length of alternative chain to be generated
typedef boost : : variant < currency : : block , currency : : transaction > cct_event_t ; // CCT = core concurrency test
typedef std : : vector < cct_event_t > cct_events_t ;
typedef std : : vector < currency : : account_base > cct_accounts_t ;
typedef std : : vector < std : : shared_ptr < tools : : wallet2 > > cct_wallets_t ;
2020-07-11 18:42:59 +02:00
//static const std::vector<currency::extra_v> empty_extra;
//static const std::vector<currency::attachment_v> empty_attachment;
2018-12-27 18:50:45 +03:00
2019-04-17 18:35:32 +03:00
bool create_block_template_manually ( const currency : : block & prev_block , boost : : multiprecision : : uint128_t already_generated_coins , const std : : vector < const currency : : transaction * > & txs , const currency : : account_public_address & miner_addr , currency : : block & result )
2018-12-27 18:50:45 +03:00
{
result . flags = 0 ;
2021-04-28 21:26:23 +03:00
result . major_version = BLOCK_MAJOR_VERSION_INITIAL ;
2018-12-27 18:50:45 +03:00
result . minor_version = CURRENT_BLOCK_MINOR_VERSION ;
result . nonce = 0 ;
result . prev_id = get_block_hash ( prev_block ) ;
result . timestamp = prev_block . timestamp ! = 0 ? prev_block . timestamp + DIFFICULTY_POW_TARGET : test_core_time : : get_time ( ) ;
uint64_t fee = 0 ;
size_t txs_size = 0 ;
for ( auto & ptx : txs )
{
result . tx_hashes . push_back ( get_transaction_hash ( * ptx ) ) ;
fee + = get_tx_fee ( * ptx ) ;
txs_size + = get_object_blobsize ( * ptx ) ;
}
// make things really simple by assuming block size is less than CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE
size_t median_size = 0 ;
2023-07-13 01:00:17 +02:00
uint64_t block_reward_without_fee = 0 ;
2024-02-09 15:22:43 +01:00
uint64_t block_reward = 0 ;
2023-07-13 01:00:17 +02:00
2023-03-17 23:29:26 +01:00
bool r = construct_miner_tx ( get_block_height ( prev_block ) + 1 ,
median_size ,
already_generated_coins ,
txs_size ,
fee ,
miner_addr ,
miner_addr ,
result . miner_tx ,
2023-07-13 01:00:17 +02:00
block_reward_without_fee ,
2024-02-09 15:22:43 +01:00
block_reward ,
2023-03-17 23:29:26 +01:00
TRANSACTION_VERSION_PRE_HF4 ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " construct_miner_tx failed " ) ;
size_t coinbase_size = get_object_blobsize ( result . miner_tx ) ;
// "- 100" - to reserve room for PoS additions into miner tx
CHECK_AND_ASSERT_MES ( coinbase_size < CURRENCY_COINBASE_BLOB_RESERVED_SIZE - 100 , false , " Failed to get block template (coinbase_size = " < < coinbase_size < < " ) " ) ;
size_t block_size = get_object_blobsize ( result ) ;
CHECK_AND_ASSERT_MES ( block_size < CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE , false , " block is bigger than CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE " ) ;
return true ;
}
bool generate_events ( currency : : core & c , cct_events_t & events , const cct_wallets_t & wallets , size_t blocks_count )
{
blockchain_storage & bcs = c . get_blockchain_storage ( ) ;
CHECK_AND_ASSERT_MES ( c . get_current_blockchain_size ( ) = = 1 , false , " " ) ;
bool r = false ;
uint64_t height = 1 ;
size_t altchain_max_size = 0 ; // used to limit size of alt chains in some cases
size_t last_altchain_block_height = 0 ;
bool alt_chains_enabled = false ;
block_extended_info prev_block = AUTO_VAL_INIT ( prev_block ) , current_block = AUTO_VAL_INIT ( current_block ) ;
r = bcs . get_block_extended_info_by_height ( 0 , prev_block ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_block_extended_info_by_height failed " ) ;
for ( size_t block_index = 1 ; block_index < blocks_count ; + + block_index )
{
//block_extended_info prev_block = AUTO_VAL_INIT(prev_block);
//r = bcs.get_block_extended_info_by_height(height - 1, prev_block);
//CHECK_AND_ASSERT_MES(r, false, "get_block_extended_info_by_height failed");
const bool is_in_main_chain = c . get_current_blockchain_size ( ) = = prev_block . height + 1 & & bcs . get_top_block_id ( ) = = get_block_hash ( prev_block . bl ) ;
const currency : : account_public_address & miner_addr = wallets [ random_in_range ( 0 , wallets . size ( ) - 1 ) ] - > get_account ( ) . get_public_address ( ) ;
currency : : block b = AUTO_VAL_INIT ( b ) ;
if ( is_in_main_chain )
{
blobdata ex_nonce ;
wide_difficulty_type diff = 0 ;
if ( prev_block . height ! = 0 )
test_core_time : : adjust ( prev_block . bl . timestamp + DIFFICULTY_POW_TARGET ) ;
2022-10-20 12:46:41 +02:00
r = bcs . create_block_template ( miner_addr , ex_nonce , b , diff , height ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " create_block_template failed " ) ;
}
else
{
// walk events container backward to collect txs
std : : vector < const currency : : transaction * > txs ;
for ( cct_events_t : : const_reverse_iterator it = events . rbegin ( ) ; it ! = events . rend ( ) ; + + it )
{
if ( it - > type ( ) = = typeid ( currency : : transaction ) )
{
const transaction & tx = boost : : get < const currency : : transaction > ( * it ) ;
uint64_t max_used_block_height = 0 ;
2019-09-11 13:38:04 +03:00
r = bcs . check_tx_inputs ( tx , get_transaction_hash ( tx ) , max_used_block_height ) ;
2018-12-27 18:50:45 +03:00
if ( r & & max_used_block_height < = prev_block . height )
txs . push_back ( & tx ) ; // filter out tx that are using too recent outputs -- yep, some txs will be dropped forever
}
else if ( it - > type ( ) = = typeid ( currency : : block ) )
{
const block & b = boost : : get < currency : : block > ( * it ) ;
size_t h = get_block_height ( b ) ;
if ( h = = prev_block . height )
break ;
CHECK_AND_ASSERT_MES ( h > prev_block . height , false , " internal invariant failed: h: " < < h < < " , prev_block.height: " < < prev_block . height ) ; // should never seen as h < prev_block.height
}
}
r = create_block_template_manually ( prev_block . bl , prev_block . already_generated_coins , txs , miner_addr , b ) ;
CHECK_AND_ASSERT_MES ( r , false , " create_block_template_manually failed " ) ;
}
test_core_time : : adjust ( b . timestamp ) ;
currency : : wide_difficulty_type diff = 0 ;
2019-03-25 01:30:20 +01:00
r = currency : : miner : : find_nonce_for_given_block ( b , diff , height ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " find_nonce_for_given_block failed " ) ;
currency : : block_verification_context bvc = AUTO_VAL_INIT ( bvc ) ;
c . handle_incoming_block ( t_serializable_object_to_blob ( b ) , bvc ) ;
if ( ! is_in_main_chain & & bvc . m_verification_failed )
{
// alt chain gone wild (ex: block triggered reorganization which failed) -- return back to main chain
events . push_back ( b ) ;
LOG_PRINT_CYAN ( " \n ============================================== \n " " EVENT[ " < < events . size ( ) - 1 < < " ]: INVALID BLOCK at " < < current_block . height < < " in alt chain \n ============================================== " , LOG_LEVEL_1 ) ;
bcs . get_block_extended_info_by_height ( bcs . get_top_block_height ( ) , prev_block ) ; // return back to main chain
continue ;
}
CHECK_AND_NO_ASSERT_MES ( ! bvc . m_verification_failed & & ! bvc . m_marked_as_orphaned & & ! bvc . m_already_exists , false , " block verification context check failed " ) ;
if ( is_in_main_chain & & c . get_pool_transactions_count ( ) > 0 )
{
LOG_PRINT ( " !!! txs in the pool: " < < c . get_pool_transactions_count ( ) , LOG_LEVEL_0 ) ;
}
crypto : : hash current_block_hash = get_block_hash ( b ) ;
r = bcs . get_block_extended_info_by_hash ( current_block_hash , current_block ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_block_extended_info_by_hash failed " ) ;
if ( current_block . already_generated_coins = = 0 )
current_block . already_generated_coins = prev_block . already_generated_coins + get_outs_money_amount ( current_block . bl . miner_tx ) ; // workaround for altchains -- BCS does not calculate already generated coins
if ( ! is_in_main_chain )
last_altchain_block_height = current_block . height ;
events . push_back ( b ) ;
LOG_PRINT_CYAN ( " \n ============================================== \n " " EVENT[ " < < events . size ( ) - 1 < < " ]: BLOCK at " < < current_block . height < < " in " < < ( is_in_main_chain ? " MAIN " : " alt " ) < < " chain \n ============================================== " , LOG_LEVEL_1 ) ;
// wait few blocks for mined money to be unlocked and collected
if ( /*is_in_main_chain && */ current_block . height > = s_tx_generation_minimum_height )
{
// generate some txs
std : : shared_ptr < tools : : wallet2 > alice_wlt = wallets [ random_in_range ( 0 , wallets . size ( ) - 1 ) ] ;
alice_wlt - > refresh ( ) ;
size_t txs_to_generate = random_in_range ( 0 , s_tx_count_per_block_max ) ;
for ( size_t i = 0 ; i < txs_to_generate ; + + i )
{
std : : vector < tx_destination_entry > destinations ( { tx_destination_entry ( TESTS_DEFAULT_FEE , miner_addr ) } ) ;
transaction tx = AUTO_VAL_INIT ( tx ) ;
r = true ;
try
{
alice_wlt - > transfer ( destinations , 0 , 0 , TESTS_DEFAULT_FEE , empty_extra , empty_attachment , tx ) ;
}
catch ( std : : exception & e )
{
LOG_ERROR ( " transfer failed with an exception, what = " < < e . what ( ) ) ;
/*
// Try to find key image in BCS for debug purpose...
crypto : : key_image ki = boost : : get < txin_to_key > ( tx . vin [ 0 ] ) . k_image ;
bool found = false ;
auto tx_ki_finder = [ & bcs , & ki , & found ] ( uint64_t i , crypto : : hash tx_id ) - > bool {
transaction_chain_entry tce = AUTO_VAL_INIT ( tce ) ;
if ( ! bcs . get_tx_chain_entry ( tx_id , tce ) )
{
static std : : stringstream ss ;
ss . clear ( ) ; ss < < " get_tx_chain_entry failed with id = " < < tx_id ;
throw std : : exception ( ss . str ( ) . c_str ( ) ) ;
}
size_t inp_idx = 0 ;
for ( auto & in : tce . tx . vin )
{
if ( in . type ( ) = = typeid ( txin_to_key ) & & boost : : get < txin_to_key > ( in ) . k_image = = ki )
{
LOG_ERROR ( " key image " < < ki < < " was already spent by input # " < < inp_idx < < " in tx " < < get_transaction_hash ( tce . tx ) < < " at height " < < tce . m_keeper_block_height ) ;
found = true ;
return false ; // stop enumeration
}
+ + inp_idx ;
}
return true ; // continue enumaration
} ;
bcs . enumerate_transactions ( tx_ki_finder ) ;
if ( ! found )
{
LOG_PRINT ( " tx that used key image " < < ki < < " was NOT found in BCS. Perhaps such tx is in the pool. " , LOG_LEVEL_0 ) ;
}
*/
r = false ;
}
if ( r )
{
events . push_back ( tx ) ;
LOG_PRINT_CYAN ( " \n ============================================== \n " " EVENT[ " < < events . size ( ) - 1 < < " ]: TX " < < get_transaction_hash ( tx ) < < " \n ============================================== " , LOG_LEVEL_1 ) ;
}
}
}
// chain swithcing mechanism
if ( ! alt_chains_enabled & & current_block . height = = s_tx_generation_minimum_height )
alt_chains_enabled = true ; // one-time trigger to enable alt-chaining after height s_tx_generation_minimum_height
prev_block = current_block ; // default behaviour is to continue current chain whatever it is, prev_block is to be overriden below
if ( alt_chains_enabled )
{
// do alt chaining
if ( is_in_main_chain & & last_altchain_block_height + 2 * s_althchain_max_depth < current_block . height & & random_in_range ( 0 , 5 ) = = 0 )
{
// we are in main chain -- desided to start alt chain
size_t altchain_depth = random_in_range ( 1 , s_althchain_max_depth ) ;
altchain_max_size = random_in_range ( 0 , 1 ) = = 0 ? SIZE_MAX : random_in_range ( 1 , altchain_depth ) ; // limit altchain size to certain value (or don't limit so switching do will eventually occur)
// start next block as altchain from old block
bcs . get_block_extended_info_by_height ( current_block . height - altchain_depth , prev_block ) ;
}
else if ( ! is_in_main_chain )
{
// we are in alt chain, check whether we should continue it or stop it and return to the main
if ( altchain_max_size > 0 )
{
// do nothing -- just continue current chain
- - altchain_max_size ;
}
else
{
// return back to main chain
bcs . get_block_extended_info_by_height ( bcs . get_top_block_height ( ) , prev_block ) ;
}
}
LOG_PRINT2 ( " cct_altchains.log " , " tx pool size: \t " < < c . get_pool_transactions_count ( ) < < " \t current height: \t " < < current_block . height < < " \t prev height: \t " < < prev_block . height < < " \t chain: \t " < < ( is_in_main_chain ? " MAIN " : " alt " ) , LOG_LEVEL_0 ) ;
}
}
return true ;
}
bool clean_data_directory ( boost : : program_options : : variables_map & vm )
{
std : : string config_folder = command_line : : get_arg ( vm , command_line : : arg_data_dir ) ;
2020-01-23 03:26:25 +01:00
const std : : string bch_db_folder_path = config_folder + ( " / " CURRENCY_BLOCKCHAINDATA_FOLDERNAME_PREFIX ) + " lmdb " + CURRENCY_BLOCKCHAINDATA_FOLDERNAME_SUFFIX ;
const std : : string pool_db_folder_path = config_folder + ( " / " CURRENCY_POOLDATA_FOLDERNAME_PREFIX ) + " lmdb " + CURRENCY_POOLDATA_FOLDERNAME_SUFFIX ;
2018-12-27 18:50:45 +03:00
2020-01-23 03:26:25 +01:00
static const char * const files [ ] = { bch_db_folder_path . c_str ( ) , pool_db_folder_path . c_str ( ) , MINER_CONFIG_FILENAME } ;
2018-12-27 18:50:45 +03:00
for ( size_t i = 0 ; i < sizeof files / sizeof files [ 0 ] ; + + i )
{
boost : : filesystem : : path filename ( config_folder + " / " + files [ i ] ) ;
if ( boost : : filesystem : : exists ( filename ) )
CHECK_AND_ASSERT_MES ( boost : : filesystem : : remove_all ( filename ) , false , " boost::filesystem::remove failed to remove this: " < < filename ) ;
}
return true ;
}
struct writer_context
{
writer_context ( ) : blocks_total ( 0 ) , blocks_added ( 0 ) , blocks_already_existed ( 0 ) , blocks_failed ( 0 ) { }
size_t blocks_total ;
size_t blocks_added ;
size_t blocks_already_existed ;
size_t blocks_failed ;
} ;
bool replay_events ( currency : : core & c , const cct_events_t & events , size_t thread_index , writer_context & context )
{
//bool r = false;
for ( size_t event_idx = 0 ; event_idx < events . size ( ) ; + + event_idx )
{
if ( events [ event_idx ] . type ( ) = = typeid ( currency : : block ) )
{
const currency : : block & b = boost : : get < const currency : : block > ( events [ event_idx ] ) ;
if ( thread_index = = 0 )
test_core_time : : adjust ( b . timestamp ) ; // only the first thread adjusts time
currency : : block_verification_context bvc = AUTO_VAL_INIT ( bvc ) ;
c . handle_incoming_block ( t_serializable_object_to_blob ( b ) , bvc ) ;
context . blocks_total + + ;
if ( ! bvc . m_verification_failed & & ! bvc . m_marked_as_orphaned & & ! bvc . m_already_exists )
{
context . blocks_added + + ;
size_t sleep_time = random_in_range ( 50 , 70 ) ;
LOG_PRINT_L0 ( " writer thread # " < < thread_index < < " , going to sleep: " < < sleep_time < < " ms " ) ;
epee : : misc_utils : : sleep_no_w ( sleep_time ) ;
}
else if ( bvc . m_already_exists )
context . blocks_already_existed + + ;
else
context . blocks_failed + + ;
//CHECK_AND_NO_ASSERT_MES(!bvc.m_verification_failed && !bvc.m_marked_as_orphaned && !bvc.m_already_exists, false, "block verification context check failed");
}
else if ( events [ event_idx ] . type ( ) = = typeid ( currency : : transaction ) )
{
const currency : : transaction & tx = boost : : get < const currency : : transaction > ( events [ event_idx ] ) ;
currency : : tx_verification_context tvc = AUTO_VAL_INIT ( tvc ) ;
c . handle_incoming_tx ( t_serializable_object_to_blob ( tx ) , tvc , false ) ;
//CHECK_AND_ASSERT_MES(!tvc.m_verification_failed && tvc.m_added_to_pool, false, "tx verification context check failed");
}
}
return true ;
}
void performe_core_reads ( const currency : : core & c , std : : atomic < bool > & stop )
{
NOTIFY_REQUEST_GET_OBJECTS : : request req_objs_req = AUTO_VAL_INIT ( req_objs_req ) ;
req_objs_req . blocks . push_back ( c . get_block_id_by_height ( 0 ) ) ;
NOTIFY_RESPONSE_CHAIN_ENTRY : : request resp = AUTO_VAL_INIT ( resp ) ;
c . find_blockchain_supplement ( req_objs_req . blocks , resp ) ;
NOTIFY_RESPONSE_GET_OBJECTS : : request res_objs_seq = AUTO_VAL_INIT ( res_objs_seq ) ;
currency : : currency_connection_context context = AUTO_VAL_INIT ( context ) ;
c . handle_get_objects ( req_objs_req , res_objs_seq , context ) ;
}
void blockchain_reader ( const currency : : core & c , std : : atomic < bool > & stop )
{
auto & bcs = c . get_blockchain_storage ( ) ;
size_t prev_block_height = 0 ;
uint64_t steps_to_stop = UINT_MAX ;
while ( - - steps_to_stop > 0 )
{
if ( steps_to_stop > 100 & & stop )
steps_to_stop = 2 ;
size_t top_block_height = bcs . get_top_block_height ( ) ;
if ( top_block_height = = prev_block_height )
{
std : : this_thread : : yield ( ) ;
continue ;
}
performe_core_reads ( c , stop ) ;
for ( size_t h = ( prev_block_height = = 0 ? 0 : prev_block_height + 1 ) ; h < = top_block_height ; + + h )
{
block b = AUTO_VAL_INIT ( b ) ;
if ( bcs . get_block_by_height ( h , b ) )
{
uint64_t generated_money = get_outs_money_amount ( b . miner_tx ) ;
s_generated_money_total . fetch_add ( generated_money , std : : memory_order_relaxed ) ;
prev_block_height = h ; // update prev_block_height only if get_block_by_height succeeded, retry on the next step otherwise
}
}
}
}
class core_checker : public test_core_listener
{
public :
virtual void before_tx_pushed_to_core ( const currency : : transaction & tx , const currency : : blobdata & blob , currency : : core & c , bool invalid_tx = false ) override // invalid_tx is true when processing a tx, marked as invalid in a test
{
}
virtual void before_block_pushed_to_core ( const currency : : block & block , const currency : : blobdata & blob , currency : : core & c ) override
{
}
} ;
struct test_context
{
cct_events_t events ;
core_state_helper core_state_after_generation ;
} ;
namespace boost
{
namespace serialization
{
template < class archive_t >
void serialize ( archive_t & ar , test_context & tc , const unsigned int version )
{
ar & tc . events ;
ar & tc . core_state_after_generation ;
}
template < class archive_t >
void serialize ( archive_t & ar , core_state_helper & csh , const unsigned int version )
{
ar & csh . blocks_hashes ;
ar & csh . pool_txs_hashes ;
ar & csh . txs_hashes ;
}
}
}
bool core_concurrency_test ( boost : : program_options : : variables_map & vm , size_t wthreads , size_t rthreads , size_t blocks_count )
{
log_space : : get_set_log_detalisation_level ( true , LOG_LEVEL_0 ) ;
//epee::debug::get_set_enable_assert(true, false);
log_space : : get_set_need_thread_id ( true , true ) ;
cct_accounts_t accounts ( s_wallets_total_count ) ;
for ( auto & a : accounts )
a . generate ( ) ;
test_context tc = AUTO_VAL_INIT ( tc ) ;
cct_events_t & events ( tc . events ) ;
core_state_helper & core_state_after_generation ( tc . core_state_after_generation ) ;
core_state_helper core_state_after_playback ;
// Stage 1.
if ( ! command_line : : has_arg ( vm , arg_test_core_load_and_replay ) )
{
// Generate events
clean_data_directory ( vm ) ;
currency : : core c ( nullptr ) ;
std : : shared_ptr < core_checker > core_listener ( new core_checker ) ;
test_protocol_handler protocol_handler ( c , core_listener . get ( ) ) ;
c . set_currency_protocol ( & protocol_handler ) ;
2020-03-20 04:19:25 +01:00
if ( ! c . init ( vm ) )
2018-12-27 18:50:45 +03:00
{
LOG_ERROR ( " Failed to init core " ) ;
return false ;
}
test_core_time : : init ( ) ;
currency : : core_runtime_config crc = c . get_blockchain_storage ( ) . get_core_runtime_config ( ) ;
crc . get_core_time = & test_core_time : : get_time ;
crc . tx_pool_min_fee = TESTS_DEFAULT_FEE ;
crc . tx_default_fee = TESTS_DEFAULT_FEE ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( crc ) ;
test_node_server p2psrv ( protocol_handler ) ;
bc_services : : bc_offers_service offers_service ( nullptr ) ;
currency : : core_rpc_server rpc_server ( c , p2psrv , offers_service ) ;
std : : shared_ptr < tools : : core_fast_rpc_proxy > fast_proxy ( new tools : : core_fast_rpc_proxy ( rpc_server ) ) ;
crypto : : hash genesis_hash = c . get_block_id_by_height ( 0 ) ;
cct_wallets_t wallets ;
for ( size_t i = 0 ; i < s_wallets_total_count ; + + i )
{
std : : shared_ptr < tools : : wallet2 > w ( new tools : : wallet2 ( ) ) ;
w - > set_core_runtime_config ( c . get_blockchain_storage ( ) . get_core_runtime_config ( ) ) ;
w - > assign_account ( accounts [ i ] ) ;
w - > set_genesis ( genesis_hash ) ;
w - > set_core_proxy ( fast_proxy ) ;
wallets . push_back ( w ) ;
}
bool r = generate_events ( c , events , wallets , blocks_count ) ;
core_state_after_generation . fill ( c ) ;
c . deinit ( ) ;
if ( ! r )
{
LOG_ERROR ( " generate_events failed " ) ;
return false ;
}
} else
{
// Load events
std : : string generated_test_context_file = command_line : : get_arg ( vm , arg_test_core_load_and_replay ) ;
bool r = tools : : unserialize_obj_from_file ( tc , generated_test_context_file ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to load test context " ) ;
LOG_PRINT_GREEN ( " EVENTS SUCCESSEFUL LOADED " , LOG_LEVEL_0 ) ;
}
// Stage 2.
if ( command_line : : has_arg ( vm , arg_test_core_prepare_and_store ) )
{
// Store context
std : : string generated_test_context_file = command_line : : get_arg ( vm , arg_test_core_prepare_and_store ) ;
bool r = tools : : serialize_obj_to_file ( tc , generated_test_context_file ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to load test context " ) ;
LOG_PRINT_GREEN ( " EVENTS SUCCESSEFUL STORED " , LOG_LEVEL_0 ) ;
return true ;
}
else
{
// Replay events
clean_data_directory ( vm ) ;
currency : : core c ( nullptr ) ;
std : : shared_ptr < core_checker > core_listener ( new core_checker ) ;
test_protocol_handler protocol_handler ( c , core_listener . get ( ) ) ;
c . set_currency_protocol ( & protocol_handler ) ;
2020-03-20 04:19:25 +01:00
if ( ! c . init ( vm ) )
2018-12-27 18:50:45 +03:00
{
LOG_ERROR ( " Failed to init core " ) ;
return false ;
}
test_core_time : : init ( ) ;
currency : : core_runtime_config crc = c . get_blockchain_storage ( ) . get_core_runtime_config ( ) ;
crc . get_core_time = & test_core_time : : get_time ;
crc . tx_pool_min_fee = TESTS_DEFAULT_FEE ;
crc . tx_default_fee = TESTS_DEFAULT_FEE ;
c . get_blockchain_storage ( ) . set_core_runtime_config ( crc ) ;
LOG_PRINT ( " \n \n \n \n \n \n \n " , LOG_LEVEL_0 ) ;
LOG_PRINT ( " Start replaing " < < events . size ( ) < < " events with " < < wthreads < < " writing threads and " < < rthreads < < " reading threads, logging will be switched off now... " , LOG_LEVEL_0 ) ;
int log_level_before = log_space : : get_set_log_detalisation_level ( false ) ;
// log_space::get_set_log_detalisation_level(true, LOG_LEVEL_SILENT);
TIME_MEASURE_START_MS ( replay_time_ms ) ;
std : : vector < writer_context > writers_contexts ( wthreads ) ;
std : : atomic < bool > stop_readers ( false ) ;
std : : vector < std : : thread > writing_threads , reading_threads ;
for ( size_t thread_index = 0 ; thread_index < wthreads ; + + thread_index )
writing_threads . emplace_back ( std : : thread ( replay_events , std : : ref ( c ) , std : : cref ( events ) , thread_index , std : : ref ( writers_contexts [ thread_index ] ) ) ) ;
for ( size_t thread_index = 0 ; thread_index < rthreads ; + + thread_index )
reading_threads . emplace_back ( std : : thread ( blockchain_reader , std : : cref ( c ) , std : : ref ( stop_readers ) ) ) ;
for ( auto & t : writing_threads )
t . join ( ) ;
stop_readers = true ;
for ( auto & t : reading_threads )
t . join ( ) ;
TIME_MEASURE_FINISH_MS ( replay_time_ms ) ;
log_space : : get_set_log_detalisation_level ( true , log_level_before ) ;
LOG_PRINT ( " logging switched on " , LOG_LEVEL_0 ) ;
LOG_PRINT_YELLOW ( " Total replay time: " < < replay_time_ms < < " ms ( " < < misc_utils : : get_time_interval_string ( replay_time_ms / ( uint64_t ) 1000 ) < < " ) with an average of "
< < replay_time_ms / ( events . empty ( ) ? 1 : events . size ( ) ) < < " ms per event, " < < events . size ( ) < < " events total " , LOG_LEVEL_0 ) ;
core_state_after_playback . fill ( c ) ;
2019-04-17 18:35:32 +03:00
boost : : multiprecision : : uint128_t already_generated_coins = 0 ;
2018-12-27 18:50:45 +03:00
{
block_extended_info bei = AUTO_VAL_INIT ( bei ) ;
c . get_blockchain_storage ( ) . get_block_extended_info_by_hash ( c . get_blockchain_storage ( ) . get_top_block_id ( ) , bei ) ;
already_generated_coins = bei . already_generated_coins ;
}
c . deinit ( ) ;
if ( rthreads > 0 )
{
s_generated_money_total = s_generated_money_total / rthreads ;
2019-04-17 18:35:32 +03:00
LOG_PRINT ( " Generated coins: " < < print_money ( already_generated_coins ) < < " , counted by readers (with fee): " < < print_money ( s_generated_money_total . load ( ) ) , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
}
LOG_PRINT ( " Writers' stats: " , LOG_LEVEL_0 ) ;
for ( size_t ti = 0 ; ti < wthreads ; + + ti )
{
auto & w = writers_contexts [ ti ] ;
LOG_PRINT_L0 ( " writer thread # " < < ti < <
" : blocks added: " < < std : : setw ( 5 ) < < w . blocks_added < <
" , already existed: " < < std : : setw ( 5 ) < < w . blocks_already_existed < <
" , failed: " < < std : : setw ( 5 ) < < w . blocks_failed < <
" , total: " < < std : : setw ( 5 ) < < w . blocks_total ) ;
}
if ( core_state_after_generation = = core_state_after_playback )
{
LOG_PRINT_GREEN ( " SUCCESS! Core state is correct! " , LOG_LEVEL_0 ) ;
return true ;
}
else
{
LOG_ERROR ( " Core state after events playback is incorrect! " ) ;
return false ;
}
}
}