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
// Copyright (c) 2012-2013 The Boolberry developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <set>
# include <algorithm>
# include <cstdio>
# include <boost/archive/binary_oarchive.hpp>
# include <boost/archive/binary_iarchive.hpp>
# include <boost/algorithm/string/replace.hpp>
# include "include_base_utils.h"
# include "common/db_backend_lmdb.h"
# include "common/command_line.h"
# include "blockchain_storage.h"
# include "currency_format_utils.h"
# include "currency_boost_serialization.h"
# include "currency_core/currency_config.h"
# include "miner.h"
# include "misc_language.h"
# include "profile_tools.h"
# include "file_io_utils.h"
# include "common/boost_serialization_helper.h"
# include "warnings.h"
# include "crypto/hash.h"
# include "miner_common.h"
# include "storages/portable_storage_template_helper.h"
# include "common/db_backend_lmdb.h"
2019-03-25 01:30:20 +01:00
# include "basic_pow_helpers.h"
2018-12-27 18:50:45 +03:00
# include "version.h"
# undef LOG_DEFAULT_CHANNEL
# define LOG_DEFAULT_CHANNEL "core"
ENABLE_CHANNEL_BY_DEFAULT ( " core " ) ;
using namespace std ;
using namespace epee ;
using namespace currency ;
# define BLOCKCHAIN_STORAGE_CONTAINER_BLOCKS "blocks"
# define BLOCKCHAIN_STORAGE_CONTAINER_BLOCKS_INDEX "blocks_index"
# define BLOCKCHAIN_STORAGE_CONTAINER_TRANSACTIONS "transactions"
# define BLOCKCHAIN_STORAGE_CONTAINER_SPENT_KEYS "spent_keys"
# define BLOCKCHAIN_STORAGE_CONTAINER_OUTPUTS "outputs"
# define BLOCKCHAIN_STORAGE_CONTAINER_MULTISIG_OUTS "multisig_outs"
# define BLOCKCHAIN_STORAGE_CONTAINER_INVALID_BLOCKS "invalid_blocks"
# define BLOCKCHAIN_STORAGE_CONTAINER_SOLO_OPTIONS "solo"
# define BLOCKCHAIN_STORAGE_CONTAINER_ALIASES "aliases"
# define BLOCKCHAIN_STORAGE_CONTAINER_ADDR_TO_ALIAS "addr_to_alias"
# define BLOCKCHAIN_STORAGE_CONTAINER_TX_FEE_MEDIAN "median_fee2"
# define BLOCKCHAIN_STORAGE_CONTAINER_GINDEX_INCS "gindex_increments"
# define BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_BLOCK_CUMUL_SZ_LIMIT 0
# define BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_PRUNED_RS_HEIGHT 1
# define BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION 2
2019-08-06 20:23:05 +02:00
# define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION 3 //DON'T CHANGE THIS, if you need to resync db change BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION
2018-12-27 18:50:45 +03:00
# define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION 4 //mismatch here means some reinitializations
2019-04-18 23:29:38 +02:00
# define TARGETDATA_CACHE_SIZE DIFFICULTY_WINDOW + 10
2018-12-27 18:50:45 +03:00
2019-06-17 16:25:40 +03:00
# ifndef TESTNET
# define BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION 57000
# else
# define BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION 18000
# endif
2019-08-07 08:03:44 +03:00
# define BLOCK_POS_STRICT_SEQUENCE_LIMIT 20
2018-12-27 18:50:45 +03:00
2019-06-18 01:15:33 +03:00
2018-12-27 18:50:45 +03:00
DISABLE_VS_WARNINGS ( 4267 )
namespace
{
2019-08-28 17:58:35 +03:00
const command_line : : arg_descriptor < uint32_t > arg_db_cache_l1 = { " db-cache-l1 " , " Specify size of memory mapped db cache file " , 0 , true } ;
2018-12-27 18:50:45 +03:00
const command_line : : arg_descriptor < uint32_t > arg_db_cache_l2 = { " db-cache-l2 " , " Specify cached elements in db helpers " , 0 , true } ;
}
//------------------------------------------------------------------
blockchain_storage : : blockchain_storage ( tx_memory_pool & tx_pool ) : m_db ( std : : shared_ptr < tools : : db : : i_db_backend > ( new tools : : db : : lmdb_db_backend ) , m_rw_lock ) ,
m_db_blocks ( m_db ) ,
m_db_blocks_index ( m_db ) ,
m_db_transactions ( m_db ) ,
m_db_spent_keys ( m_db ) ,
m_db_outputs ( m_db ) ,
m_db_multisig_outs ( m_db ) ,
m_db_solo_options ( m_db ) ,
m_db_aliases ( m_db ) ,
m_db_addr_to_alias ( m_db ) ,
m_read_lock ( m_rw_lock ) ,
m_db_current_block_cumul_sz_limit ( BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_BLOCK_CUMUL_SZ_LIMIT , m_db_solo_options ) ,
m_db_current_pruned_rs_height ( BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_PRUNED_RS_HEIGHT , m_db_solo_options ) ,
m_db_last_worked_version ( BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION , m_db_solo_options ) ,
m_db_storage_major_compatibility_version ( BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION , m_db_solo_options ) ,
m_db_storage_minor_compatibility_version ( BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION , m_db_solo_options ) ,
m_db_per_block_gindex_incs ( m_db ) ,
m_tx_pool ( tx_pool ) ,
m_is_in_checkpoint_zone ( false ) ,
m_is_blockchain_storing ( false ) ,
m_core_runtime_config ( get_default_core_runtime_config ( ) ) ,
//m_bei_stub(AUTO_VAL_INIT(m_bei_stub)),
m_event_handler ( & m_event_handler_stub ) ,
m_services_mgr ( nullptr ) ,
m_interprocess_locker_file ( 0 ) ,
m_current_fee_median ( 0 ) ,
m_current_fee_median_effective_index ( 0 ) ,
2019-01-14 19:51:32 +03:00
m_is_reorganize_in_process ( false ) ,
2019-07-15 20:10:50 +02:00
m_deinit_is_done ( false ) ,
m_cached_next_pow_difficulty ( 0 ) ,
m_cached_next_pos_difficulty ( 0 )
2018-12-27 18:50:45 +03:00
{
m_services_mgr . set_core_runtime_config ( m_core_runtime_config ) ;
m_performance_data . epic_failure_happend = false ;
}
2019-01-14 19:51:32 +03:00
blockchain_storage : : ~ blockchain_storage ( )
{
if ( ! m_deinit_is_done )
deinit ( ) ;
}
2018-12-27 18:50:45 +03:00
//------------------------------------------------------------------
bool blockchain_storage : : have_tx ( const crypto : : hash & id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_transactions . find ( id ) ! = m_db_transactions . end ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : have_tx_keyimg_as_spent ( const crypto : : key_image & key_im , uint64_t before_height /* = UINT64_MAX */ ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto it_ptr = m_db_spent_keys . get ( key_im ) ;
if ( ! it_ptr )
return false ;
return * it_ptr < before_height ;
}
//------------------------------------------------------------------
std : : shared_ptr < transaction > blockchain_storage : : get_tx ( const crypto : : hash & id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto it = m_db_transactions . find ( id ) ;
if ( it = = m_db_transactions . end ( ) )
return std : : shared_ptr < transaction > ( nullptr ) ;
return std : : make_shared < transaction > ( it - > tx ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : init_options ( boost : : program_options : : options_description & desc )
{
2019-08-28 17:58:35 +03:00
command_line : : add_arg ( desc , arg_db_cache_l1 ) ;
2018-12-27 18:50:45 +03:00
command_line : : add_arg ( desc , arg_db_cache_l2 ) ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_block_h_older_then ( uint64_t timestamp ) const
{
// get avarage block position
uint64_t last_block_timestamp = m_db_blocks . back ( ) - > bl . timestamp ;
if ( timestamp > = last_block_timestamp )
return get_top_block_height ( ) ;
uint64_t difference = last_block_timestamp - timestamp ;
uint64_t n_blocks = difference / ( DIFFICULTY_TOTAL_TARGET ) ;
if ( n_blocks > = get_top_block_height ( ) )
return 0 ;
uint64_t index = get_top_block_height ( ) - n_blocks ;
while ( true )
{
if ( index = = 0 )
return 0 ;
if ( m_db_blocks [ index ] - > bl . timestamp < timestamp )
return index ;
index - - ;
}
return 0 ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_current_blockchain_size ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_blocks . size ( ) ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_top_block_height ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_blocks . size ( ) - 1 ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_instance ( const std : : string & path )
{
std : : string locker_name = path + " / " + std : : string ( CURRENCY_CORE_INSTANCE_LOCK_FILE ) ;
bool r = epee : : file_io_utils : : open_and_lock_file ( locker_name , m_interprocess_locker_file ) ;
if ( r )
return true ;
else
{
LOG_ERROR ( " Failed to initialize db: some other instance is already running " ) ;
return false ;
}
}
//------------------------------------------------------------------
bool blockchain_storage : : init ( const std : : string & config_folder , const boost : : program_options : : variables_map & vm )
{
// CRITICAL_REGION_LOCAL(m_read_lock);
if ( ! validate_instance ( config_folder ) )
{
LOG_ERROR ( " Failed to initialize instance " ) ;
return false ;
}
2019-08-28 17:58:35 +03:00
uint64_t cache_size_l1 = CACHE_SIZE ;
if ( command_line : : has_arg ( vm , arg_db_cache_l1 ) )
{
cache_size_l1 = command_line : : get_arg ( vm , arg_db_cache_l1 ) ;
}
LOG_PRINT_GREEN ( " Using db file cache size(L1): " < < cache_size_l1 , LOG_LEVEL_0 ) ;
2018-12-27 18:50:45 +03:00
m_config_folder = config_folder ;
2019-08-28 17:58:35 +03:00
2019-09-07 00:46:03 +02:00
// remove old incompatible DB
2019-08-28 17:58:35 +03:00
const std : : string old_db_folder_path = m_config_folder + " / " CURRENCY_BLOCKCHAINDATA_FOLDERNAME_OLD ;
2019-09-25 17:09:38 +03:00
if ( boost : : filesystem : : exists ( epee : : string_encoding : : utf8_to_wstring ( old_db_folder_path ) ) )
2019-08-28 17:58:35 +03:00
{
LOG_PRINT_YELLOW ( " Removing old DB in " < < old_db_folder_path < < " ... " , LOG_LEVEL_0 ) ;
2019-09-25 17:09:38 +03:00
boost : : filesystem : : remove_all ( epee : : string_encoding : : utf8_to_wstring ( old_db_folder_path ) ) ;
2019-08-28 17:58:35 +03:00
}
2019-08-17 07:10:37 +03:00
const std : : string db_folder_path = m_config_folder + " / " CURRENCY_BLOCKCHAINDATA_FOLDERNAME ;
LOG_PRINT_L0 ( " Loading blockchain from " < < db_folder_path ) ;
bool db_opened_okay = false ;
for ( size_t loading_attempt_no = 0 ; loading_attempt_no < 2 ; + + loading_attempt_no )
2018-12-27 18:50:45 +03:00
{
2019-08-28 17:58:35 +03:00
bool res = m_db . open ( db_folder_path , cache_size_l1 ) ;
2019-08-17 07:10:37 +03:00
if ( ! res )
{
// if DB could not be opened -- try to remove the whole folder and re-open DB
LOG_PRINT_YELLOW ( " Failed to initialize database in folder: " < < db_folder_path < < " , first attempt " , LOG_LEVEL_0 ) ;
2019-09-25 17:09:38 +03:00
boost : : filesystem : : remove_all ( epee : : string_encoding : : utf8_to_wstring ( db_folder_path ) ) ;
2019-08-28 17:58:35 +03:00
res = m_db . open ( db_folder_path , cache_size_l1 ) ;
2019-08-17 07:10:37 +03:00
CHECK_AND_ASSERT_MES ( res , false , " Failed to initialize database in folder: " < < db_folder_path < < " , second attempt " ) ;
}
res = m_db_blocks . init ( BLOCKCHAIN_STORAGE_CONTAINER_BLOCKS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_blocks_index . init ( BLOCKCHAIN_STORAGE_CONTAINER_BLOCKS_INDEX ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_transactions . init ( BLOCKCHAIN_STORAGE_CONTAINER_TRANSACTIONS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_spent_keys . init ( BLOCKCHAIN_STORAGE_CONTAINER_SPENT_KEYS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_outputs . init ( BLOCKCHAIN_STORAGE_CONTAINER_OUTPUTS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_multisig_outs . init ( BLOCKCHAIN_STORAGE_CONTAINER_MULTISIG_OUTS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_solo_options . init ( BLOCKCHAIN_STORAGE_CONTAINER_SOLO_OPTIONS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_aliases . init ( BLOCKCHAIN_STORAGE_CONTAINER_ALIASES ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_addr_to_alias . init ( BLOCKCHAIN_STORAGE_CONTAINER_ADDR_TO_ALIAS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
res = m_db_per_block_gindex_incs . init ( BLOCKCHAIN_STORAGE_CONTAINER_GINDEX_INCS ) ;
CHECK_AND_ASSERT_MES ( res , false , " Unable to init db container " ) ;
if ( command_line : : has_arg ( vm , arg_db_cache_l2 ) )
{
uint64_t cache_size = command_line : : get_arg ( vm , arg_db_cache_l2 ) ;
LOG_PRINT_GREEN ( " Using db items cache size(L2): " < < cache_size , LOG_LEVEL_0 ) ;
m_db_blocks_index . set_cache_size ( cache_size ) ;
m_db_blocks . set_cache_size ( cache_size ) ;
m_db_blocks_index . set_cache_size ( cache_size ) ;
m_db_transactions . set_cache_size ( cache_size ) ;
m_db_spent_keys . set_cache_size ( cache_size ) ;
//m_db_outputs.set_cache_size(cache_size);
m_db_multisig_outs . set_cache_size ( cache_size ) ;
m_db_solo_options . set_cache_size ( cache_size ) ;
m_db_aliases . set_cache_size ( cache_size ) ;
m_db_addr_to_alias . set_cache_size ( cache_size ) ;
}
bool need_reinit = false ;
if ( m_db_blocks . size ( ) ! = 0 )
{
if ( m_db_storage_major_compatibility_version ! = BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION )
{
need_reinit = true ;
LOG_PRINT_MAGENTA ( " DB storage needs reinit because it has major compatibility ver " < < m_db_storage_major_compatibility_version < < " , expected : " < < BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION , LOG_LEVEL_0 ) ;
}
else if ( m_db_storage_minor_compatibility_version ! = BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION )
{
// nothing
}
}
if ( need_reinit )
{
LOG_PRINT_L1 ( " DB at " < < db_folder_path < < " is about to be deleted and re-created... " ) ;
m_db_blocks . deinit ( ) ;
m_db_blocks_index . deinit ( ) ;
m_db_transactions . deinit ( ) ;
m_db_spent_keys . deinit ( ) ;
m_db_outputs . deinit ( ) ;
m_db_multisig_outs . deinit ( ) ;
m_db_solo_options . deinit ( ) ;
m_db_aliases . deinit ( ) ;
m_db_addr_to_alias . deinit ( ) ;
m_db_per_block_gindex_incs . deinit ( ) ;
m_db . close ( ) ;
2019-09-25 17:09:38 +03:00
size_t files_removed = boost : : filesystem : : remove_all ( epee : : string_encoding : : utf8_to_wstring ( db_folder_path ) ) ;
2019-08-17 07:10:37 +03:00
LOG_PRINT_L1 ( files_removed < < " files at " < < db_folder_path < < " removed " ) ;
// try to re-create DB and re-init containers
continue ;
}
db_opened_okay = true ;
break ;
2018-12-27 18:50:45 +03:00
}
2019-08-17 07:10:37 +03:00
CHECK_AND_ASSERT_MES ( db_opened_okay , false , " All attempts to open DB at " < < db_folder_path < < " failed " ) ;
2018-12-27 18:50:45 +03:00
if ( ! m_db_blocks . size ( ) )
{
2019-08-17 07:10:37 +03:00
// empty DB: generate and add genesis block
2018-12-27 18:50:45 +03:00
block bl = boost : : value_initialized < block > ( ) ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
generate_genesis_block ( bl ) ;
add_new_block ( bl , bvc ) ;
CHECK_AND_ASSERT_MES ( ! bvc . m_verification_failed , false , " Failed to add genesis block to blockchain " ) ;
LOG_PRINT_MAGENTA ( " Storage initialized with genesis " , LOG_LEVEL_0 ) ;
}
2019-08-17 07:10:37 +03:00
store_db_solo_options_values ( ) ;
2018-12-27 18:50:45 +03:00
m_services_mgr . init ( config_folder , vm ) ;
//print information message
uint64_t timestamp_diff = m_core_runtime_config . get_core_time ( ) - m_db_blocks . back ( ) - > bl . timestamp ;
if ( ! m_db_blocks . back ( ) - > bl . timestamp )
timestamp_diff = m_core_runtime_config . get_core_time ( ) - 1341378000 ;
2019-07-09 19:22:57 +02:00
m_db . begin_transaction ( ) ;
set_lost_tx_unmixable ( ) ;
m_db . commit_transaction ( ) ;
2018-12-27 18:50:45 +03:00
LOG_PRINT_GREEN ( " Blockchain initialized. (v: " < < m_db_storage_major_compatibility_version < < " ) last block: " < < m_db_blocks . size ( ) - 1 < < ENDL
< < " genesis: " < < get_block_hash ( m_db_blocks [ 0 ] - > bl ) < < ENDL
< < " last block: " < < m_db_blocks . size ( ) - 1 < < " , " < < misc_utils : : get_time_interval_string ( timestamp_diff ) < < " time ago " < < ENDL
< < " current pos difficulty: " < < get_next_diff_conditional ( true ) < < ENDL
< < " current pow difficulty: " < < get_next_diff_conditional ( false ) < < ENDL
2019-05-07 00:51:34 +02:00
< < " total transactions: " < < m_db_transactions . size ( ) ,
2018-12-27 18:50:45 +03:00
LOG_LEVEL_0 ) ;
return true ;
}
2019-07-09 19:22:57 +02:00
//------------------------------------------------------------------
bool blockchain_storage : : set_lost_tx_unmixable_for_height ( uint64_t height )
{
2019-10-07 08:18:58 +03:00
# ifndef TESTNET
2019-07-09 19:22:57 +02:00
if ( height = = 75738 )
return set_lost_tx_unmixable ( ) ;
2019-10-07 08:18:58 +03:00
# endif
2019-07-09 19:22:57 +02:00
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : set_lost_tx_unmixable ( )
{
2019-10-07 08:18:58 +03:00
# ifndef TESTNET
2019-07-09 19:22:57 +02:00
if ( m_db_blocks . size ( ) > 75738 )
{
crypto : : hash tx_id_1 = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " c2a2229d614e7c026433efbcfdbd0be1f68d9b419220336df3e2c209f5d57314 " ) ;
crypto : : hash tx_id_2 = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " 647f936c6ffbd136f5c95d9a90ad554bdb4c01541c6eb5755ad40b984d80da67 " ) ;
auto tx_ptr_1 = m_db_transactions . find ( tx_id_1 ) ;
CHECK_AND_ASSERT_MES ( tx_ptr_1 , false , " Internal error: filed to find lost tx " ) ;
transaction_chain_entry tx1_local_entry ( * tx_ptr_1 ) ;
for ( size_t i = 0 ; i ! = tx1_local_entry . m_spent_flags . size ( ) ; i + + )
{
tx1_local_entry . m_spent_flags [ i ] = true ;
}
m_db_transactions . set ( tx_id_1 , tx1_local_entry ) ;
auto tx_ptr_2 = m_db_transactions . find ( tx_id_2 ) ;
transaction_chain_entry tx2_local_entry ( * tx_ptr_2 ) ;
CHECK_AND_ASSERT_MES ( tx_ptr_1 , false , " Internal error: filed to find lost tx " ) ;
for ( size_t i = 0 ; i ! = tx2_local_entry . m_spent_flags . size ( ) ; i + + )
{
tx2_local_entry . m_spent_flags [ i ] = true ;
}
m_db_transactions . set ( tx_id_2 , tx2_local_entry ) ;
}
2019-10-07 08:18:58 +03:00
# endif
2019-07-09 19:22:57 +02:00
return true ;
}
//------------------------------------------------------------------
void blockchain_storage : : patch_out_if_needed ( txout_to_key & out , const crypto : : hash & tx_id , uint64_t n ) const
{
2019-10-07 08:18:58 +03:00
# ifndef TESTNET
2019-08-09 16:57:18 +02:00
static crypto : : hash tx_id_1 = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " c2a2229d614e7c026433efbcfdbd0be1f68d9b419220336df3e2c209f5d57314 " ) ;
static crypto : : hash tx_id_2 = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " 647f936c6ffbd136f5c95d9a90ad554bdb4c01541c6eb5755ad40b984d80da67 " ) ;
if ( tx_id = = tx_id_1 & & n = = 12 )
{
out . mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX ;
} else if ( tx_id = = tx_id_2 & & n = = 5 )
{
out . mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX ;
}
2019-10-07 08:18:58 +03:00
# endif
2019-07-09 19:22:57 +02:00
}
2018-12-27 18:50:45 +03:00
//------------------------------------------------------------------
2019-08-17 07:10:37 +03:00
void blockchain_storage : : store_db_solo_options_values ( )
2018-12-27 18:50:45 +03:00
{
m_db . begin_transaction ( ) ;
m_db_storage_major_compatibility_version = BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION ;
m_db_storage_minor_compatibility_version = BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION ;
m_db_last_worked_version = std : : string ( PROJECT_VERSION_LONG ) ;
m_db . commit_transaction ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : deinit ( )
{
m_db . close ( ) ;
epee : : file_io_utils : : unlock_and_close_file ( m_interprocess_locker_file ) ;
2019-01-14 19:51:32 +03:00
m_deinit_is_done = true ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : pop_block_from_blockchain ( )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( m_db_blocks . size ( ) > 1 , false , " pop_block_from_blockchain: can't pop from blockchain with size = " < < m_db_blocks . size ( ) ) ;
size_t h = m_db_blocks . size ( ) - 1 ;
auto bei_ptr = m_db_blocks [ h ] ;
CHECK_AND_ASSERT_MES ( bei_ptr . get ( ) , false , " pop_block_from_blockchain: can't pop from blockchain " ) ;
uint64_t fee_total = 0 ;
bool r = purge_block_data_from_blockchain ( bei_ptr - > bl , bei_ptr - > bl . tx_hashes . size ( ) , fee_total ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to purge_block_data_from_blockchain for block " < < get_block_hash ( bei_ptr - > bl ) < < " on height " < < h ) ;
pop_block_from_per_block_increments ( bei_ptr - > height ) ;
//remove from index
r = m_db_blocks_index . erase_validate ( get_block_hash ( bei_ptr - > bl ) ) ;
CHECK_AND_ASSERT_MES_NO_RET ( r , " pop_block_from_blockchain: block id not found in m_blocks_index while trying to delete it " ) ;
//pop block from core
m_db_blocks . pop_back ( ) ;
on_block_removed ( * bei_ptr ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : set_checkpoints ( checkpoints & & chk_pts )
{
m_checkpoints = chk_pts ;
try
{
m_db . begin_transaction ( ) ;
if ( m_db_blocks . size ( ) < m_checkpoints . get_top_checkpoint_height ( ) )
m_is_in_checkpoint_zone = true ;
prune_ring_signatures_and_attachments_if_need ( ) ;
m_db . commit_transaction ( ) ;
return true ;
}
catch ( const std : : exception & ex )
{
m_db . abort_transaction ( ) ;
LOG_ERROR ( " UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK: " < < ex . what ( ) ) ;
return false ;
}
catch ( . . . )
{
m_db . abort_transaction ( ) ;
LOG_ERROR ( " UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK. " ) ;
return false ;
}
}
//------------------------------------------------------------------
bool blockchain_storage : : prune_ring_signatures_and_attachments ( uint64_t height , uint64_t & transactions_pruned , uint64_t & signatures_pruned , uint64_t & attachments_pruned )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( height < m_db_blocks . size ( ) , false , " prune_ring_signatures called with wrong parameter: " < < height < < " , m_blocks.size() " < < m_db_blocks . size ( ) ) ;
auto vptr = m_db_blocks [ height ] ;
CHECK_AND_ASSERT_MES ( vptr . get ( ) , false , " Failed to get block on height " ) ;
for ( const auto & h : vptr - > bl . tx_hashes )
{
auto it = m_db_transactions . find ( h ) ;
CHECK_AND_ASSERT_MES ( it ! = m_db_transactions . end ( ) , false , " failed to find transaction " < < h < < " in blockchain index, in block on height = " < < height ) ;
CHECK_AND_ASSERT_MES ( it - > m_keeper_block_height = = height , false ,
" failed to validate extra check, it->second.m_keeper_block_height = " < < it - > m_keeper_block_height < <
" is mot equal to height = " < < height < < " in blockchain index, for block on height = " < < height ) ;
transaction_chain_entry lolcal_chain_entry = * it ;
signatures_pruned + = lolcal_chain_entry . tx . signatures . size ( ) ;
attachments_pruned + = lolcal_chain_entry . tx . attachment . size ( ) ;
lolcal_chain_entry . tx . signatures . clear ( ) ;
lolcal_chain_entry . tx . attachment . clear ( ) ;
//reassign to db
m_db_transactions . set ( h , lolcal_chain_entry ) ;
+ + transactions_pruned ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : prune_ring_signatures_and_attachments_if_need ( )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( m_db_blocks . size ( ) & & m_checkpoints . get_top_checkpoint_height ( ) & & m_checkpoints . get_top_checkpoint_height ( ) > m_db_current_pruned_rs_height )
{
LOG_PRINT_CYAN ( " Starting pruning ring signatues and attachments... " , LOG_LEVEL_0 ) ;
uint64_t tx_count = 0 , sig_count = 0 , attach_count = 0 ;
for ( uint64_t height = m_db_current_pruned_rs_height ; height < m_db_blocks . size ( ) & & height < = m_checkpoints . get_top_checkpoint_height ( ) ; height + + )
{
bool res = prune_ring_signatures_and_attachments ( height , tx_count , sig_count , attach_count ) ;
CHECK_AND_ASSERT_MES ( res , false , " failed to prune_ring_signatures_and_attachments for height = " < < height ) ;
}
m_db_current_pruned_rs_height = m_checkpoints . get_top_checkpoint_height ( ) ;
LOG_PRINT_CYAN ( " Transaction pruning finished: " < < sig_count < < " signatures and " < < attach_count < < " attachments released in " < < tx_count < < " transactions. " , LOG_LEVEL_0 ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : clear ( )
{
//CRITICAL_REGION_LOCAL(m_read_lock);
m_db . begin_transaction ( ) ;
m_db_blocks . clear ( ) ;
m_db_blocks_index . clear ( ) ;
m_db_transactions . clear ( ) ;
m_db_spent_keys . clear ( ) ;
m_db_solo_options . clear ( ) ;
2019-08-17 07:10:37 +03:00
store_db_solo_options_values ( ) ;
2018-12-27 18:50:45 +03:00
m_db_outputs . clear ( ) ;
m_db_multisig_outs . clear ( ) ;
m_db_aliases . clear ( ) ;
m_db_addr_to_alias . clear ( ) ;
m_db_per_block_gindex_incs . clear ( ) ;
2019-04-18 23:29:38 +02:00
m_pos_targetdata_cache . clear ( ) ;
m_pow_targetdata_cache . clear ( ) ;
2018-12-27 18:50:45 +03:00
m_db . commit_transaction ( ) ;
{
CRITICAL_REGION_LOCAL ( m_invalid_blocks_lock ) ;
m_invalid_blocks . clear ( ) ; // crypto::hash -> block_extended_info
}
{
CRITICAL_REGION_LOCAL ( m_alternative_chains_lock ) ;
m_alternative_chains . clear ( ) ;
m_altblocks_keyimages . clear ( ) ;
m_alternative_chains_txs . clear ( ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : reset_and_set_genesis_block ( const block & b )
{
clear ( ) ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
add_new_block ( b , bvc ) ;
if ( ! bvc . m_added_to_main_chain | | bvc . m_verification_failed )
{
LOG_ERROR ( " Blockchain reset failed. " )
return false ;
}
LOG_PRINT_GREEN ( " Blockchain reset. Genesis block: " < < get_block_hash ( b ) < < " , " < < misc_utils : : get_time_interval_string ( m_core_runtime_config . get_core_time ( ) - b . timestamp ) < < " ago " , LOG_LEVEL_0 ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_transaction_keyimages_from_blockchain ( const transaction & tx , bool strict_check )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
struct purge_transaction_visitor : public boost : : static_visitor < bool >
{
blockchain_storage & m_bcs ;
key_images_container & m_spent_keys ;
bool m_strict_check ;
purge_transaction_visitor ( blockchain_storage & bcs , key_images_container & spent_keys , bool strict_check ) :
m_bcs ( bcs ) ,
m_spent_keys ( spent_keys ) ,
m_strict_check ( strict_check ) { }
bool operator ( ) ( const txin_to_key & inp ) const
{
bool r = m_spent_keys . erase_validate ( inp . k_image ) ;
CHECK_AND_ASSERT_MES ( ! ( ! r & & m_strict_check ) , false , " purge_transaction_keyimages_from_blockchain: key image " < < inp . k_image < < " was not found " ) ;
if ( inp . key_offsets . size ( ) = = 1 )
{
//direct spend detected
if ( ! m_bcs . update_spent_tx_flags_for_input ( inp . amount , inp . key_offsets [ 0 ] , false ) )
{
//internal error
LOG_PRINT_L0 ( " Failed to update_spent_tx_flags_for_input " ) ;
return false ;
}
}
return true ;
}
bool operator ( ) ( const txin_gen & inp ) const
{
return true ;
}
bool operator ( ) ( const txin_multisig & inp ) const
{
if ( ! m_bcs . update_spent_tx_flags_for_input ( inp . multisig_out_id , 0 ) )
{
LOG_PRINT_L0 ( " update_spent_tx_flags_for_input failed for multisig id " < < inp . multisig_out_id < < " amount: " < < inp . amount ) ;
return false ;
}
return true ;
}
} ;
BOOST_FOREACH ( const txin_v & in , tx . vin )
{
bool r = boost : : apply_visitor ( purge_transaction_visitor ( * this , m_db_spent_keys , strict_check ) , in ) ;
CHECK_AND_ASSERT_MES ( ! strict_check | | r , false , " failed to process purge_transaction_visitor " ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_transaction_from_blockchain ( const crypto : : hash & tx_id , uint64_t & fee )
{
fee = 0 ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto tx_res_ptr = m_db_transactions . find ( tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_res_ptr ! = m_db_transactions . end ( ) , false , " transaction " < < tx_id < < " is not found in blockchain index!! " ) ;
const transaction & tx = tx_res_ptr - > tx ;
fee = get_tx_fee ( tx_res_ptr - > tx ) ;
purge_transaction_keyimages_from_blockchain ( tx , true ) ;
bool r = unprocess_blockchain_tx_extra ( tx ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to unprocess_blockchain_tx_extra for tx " < < tx_id ) ;
r = unprocess_blockchain_tx_attachments ( tx , get_current_blockchain_size ( ) , 0 /*TODO: add valid timestamp here in future if need*/ ) ;
bool added_to_the_pool = false ;
if ( ! is_coinbase ( tx ) )
{
currency : : tx_verification_context tvc = AUTO_VAL_INIT ( tvc ) ;
added_to_the_pool = m_tx_pool . add_tx ( tx , tvc , true , true ) ;
CHECK_AND_ASSERT_MES ( added_to_the_pool , false , " failed to add transaction " < < tx_id < < " to transaction pool " ) ;
}
bool res = pop_transaction_from_global_index ( tx , tx_id ) ;
CHECK_AND_ASSERT_MES_NO_RET ( res , " pop_transaction_from_global_index failed for tx " < < tx_id ) ;
bool res_erase = m_db_transactions . erase_validate ( tx_id ) ;
CHECK_AND_ASSERT_MES_NO_RET ( res_erase , " Failed to m_transactions.erase with id = " < < tx_id ) ;
LOG_PRINT_L1 ( " transaction " < < tx_id < < ( added_to_the_pool ? " was removed from blockchain history -> to the pool " : " was removed from blockchain history " ) ) ;
return res ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_block_data_from_blockchain ( const block & b , size_t processed_tx_count )
{
uint64_t total_fee = 0 ;
return purge_block_data_from_blockchain ( b , processed_tx_count , total_fee ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_block_data_from_blockchain ( const block & bl , size_t processed_tx_count , uint64_t & fee_total )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
fee_total = 0 ;
uint64_t fee = 0 ;
bool res = true ;
CHECK_AND_ASSERT_MES ( processed_tx_count < = bl . tx_hashes . size ( ) , false , " wrong processed_tx_count in purge_block_data_from_blockchain " ) ;
for ( size_t count = 0 ; count ! = processed_tx_count ; count + + )
{
res = purge_transaction_from_blockchain ( bl . tx_hashes [ ( processed_tx_count - 1 ) - count ] , fee ) & & res ;
fee_total + = fee ;
}
res = purge_transaction_from_blockchain ( get_transaction_hash ( bl . miner_tx ) , fee ) & & res ;
return res ;
}
//------------------------------------------------------------------
crypto : : hash blockchain_storage : : get_top_block_id ( uint64_t & height ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
height = get_top_block_height ( ) ;
return get_top_block_id ( ) ;
}
//------------------------------------------------------------------
crypto : : hash blockchain_storage : : get_top_block_id ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
crypto : : hash id = null_hash ;
if ( m_db_blocks . size ( ) )
{
auto val_ptr = m_db_blocks . back ( ) ;
CHECK_AND_ASSERT_MES ( val_ptr , null_hash , " m_blocks.back() returned null " ) ;
get_block_hash ( val_ptr - > bl , id ) ;
}
return id ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_top_block ( block & b ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( m_db_blocks . size ( ) , false , " Wrong blockchain state, m_blocks.size()=0! " ) ;
auto val_ptr = m_db_blocks . back ( ) ;
CHECK_AND_ASSERT_MES ( val_ptr . get ( ) , false , " m_blocks.back() returned null " ) ;
b = val_ptr - > bl ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_short_chain_history ( std : : list < crypto : : hash > & ids ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
size_t i = 0 ;
size_t current_multiplier = 1 ;
size_t sz = m_db_blocks . size ( ) ;
if ( ! sz )
return true ;
size_t current_back_offset = 1 ;
bool genesis_included = false ;
while ( current_back_offset < sz )
{
ids . push_back ( get_block_hash ( m_db_blocks [ sz - current_back_offset ] - > bl ) ) ;
if ( sz - current_back_offset = = 0 )
genesis_included = true ;
if ( i < 10 )
{
+ + current_back_offset ;
} else
{
current_back_offset + = current_multiplier * = 2 ;
}
+ + i ;
}
if ( ! genesis_included )
ids . push_back ( get_block_hash ( m_db_blocks [ 0 ] - > bl ) ) ;
return true ;
}
//------------------------------------------------------------------
crypto : : hash blockchain_storage : : get_block_id_by_height ( uint64_t height ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( height > = m_db_blocks . size ( ) )
return null_hash ;
return get_block_hash ( m_db_blocks [ height ] - > bl ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_block_by_hash ( const crypto : : hash & h , block & blk ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
// try to find block in main chain
auto it = m_db_blocks_index . find ( h ) ;
if ( m_db_blocks_index . end ( ) ! = it )
{
blk = m_db_blocks [ * it ] - > bl ;
return true ;
}
// try to find block in alternative chain
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
auto it_alt = m_alternative_chains . find ( h ) ;
if ( m_alternative_chains . end ( ) ! = it_alt )
{
blk = it_alt - > second . bl ;
return true ;
}
return false ;
}
bool blockchain_storage : : is_tx_related_to_altblock ( crypto : : hash tx_id ) const
{
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
auto it = m_alternative_chains_txs . find ( tx_id ) ;
return it ! = m_alternative_chains_txs . end ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_block_extended_info_by_hash ( const crypto : : hash & h , block_extended_info & blk ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
// try to find block in main chain
auto vptr = m_db_blocks_index . find ( h ) ;
if ( vptr )
{
return get_block_extended_info_by_height ( * vptr , blk ) ;
}
// try to find block in alternative chain
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
auto it_alt = m_alternative_chains . find ( h ) ;
if ( m_alternative_chains . end ( ) ! = it_alt )
{
blk = it_alt - > second ;
return true ;
}
return false ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_block_extended_info_by_height ( uint64_t h , block_extended_info & blk ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( h > = m_db_blocks . size ( ) )
return false ;
blk = * m_db_blocks [ h ] ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_block_by_height ( uint64_t h , block & blk ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( h > = m_db_blocks . size ( ) )
return false ;
blk = m_db_blocks [ h ] - > bl ;
return true ;
}
//------------------------------------------------------------------
// void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const
// {
// CRITICAL_REGION_LOCAL(m_blockchain_lock);
//
// for (auto &v : m_blocks_index)
// main.push_back(v.first);
//
// for (auto &v : m_alternative_chains)
// alt.push_back(v.first);
//
// for(auto &v: m_invalid_blocks)
// invalid.push_back(v.first);
// }
//------------------------------------------------------------------
bool blockchain_storage : : rollback_blockchain_switching ( std : : list < block > & original_chain , size_t rollback_height )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
//remove failed subchain
for ( size_t i = m_db_blocks . size ( ) - 1 ; i > = rollback_height ; i - - )
{
bool r = pop_block_from_blockchain ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " PANIC!!! failed to remove block while chain switching during the rollback! " ) ;
}
//return back original chain
BOOST_FOREACH ( auto & bl , original_chain )
{
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
bool r = handle_block_to_main_chain ( bl , bvc ) ;
CHECK_AND_ASSERT_MES ( r & & bvc . m_added_to_main_chain , false , " PANIC!!! failed to add (again) block while chain switching during the rollback! " ) ;
}
LOG_PRINT_L0 ( " Rollback success. " ) ;
return true ;
}
//------------------------------------------------------------------
void blockchain_storage : : add_alt_block_txs_hashs ( const block & b )
{
CRITICAL_REGION_LOCAL ( m_alternative_chains_lock ) ;
for ( const auto & tx_hash : b . tx_hashes )
{
m_alternative_chains_txs [ tx_hash ] + + ;
}
}
//------------------------------------------------------------------
void blockchain_storage : : purge_alt_block_txs_hashs ( const block & b )
{
CRITICAL_REGION_LOCAL ( m_alternative_chains_lock ) ;
for ( const auto & h : b . tx_hashes )
{
auto it = m_alternative_chains_txs . find ( h ) ;
if ( it = = m_alternative_chains_txs . end ( ) )
{
LOG_ERROR ( " Internal error: tx with hash " < < h < < " not found in m_alternative_chains_txs while removing block " < < get_block_hash ( b ) ) ;
continue ;
}
if ( it - > second > = 1 )
{
it - > second - - ;
}
else
{
LOG_ERROR ( " Internal error: tx with hash " < < h < < " has invalid m_alternative_chains_txs entry (zero count) while removing block " < < get_block_hash ( b ) ) ;
}
if ( it - > second = = 0 )
m_alternative_chains_txs . erase ( it ) ;
}
}
//------------------------------------------------------------------
void blockchain_storage : : do_erase_altblock ( alt_chain_container : : iterator it )
{
purge_altblock_keyimages_from_big_heap ( it - > second . bl , get_block_hash ( it - > second . bl ) ) ;
purge_alt_block_txs_hashs ( it - > second . bl ) ;
m_alternative_chains . erase ( it ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : switch_to_alternative_blockchain ( alt_chain_type & alt_chain )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
CHECK_AND_ASSERT_MES ( alt_chain . size ( ) , false , " switch_to_alternative_blockchain: empty chain passed " ) ;
size_t split_height = alt_chain . front ( ) - > second . height ;
CHECK_AND_ASSERT_MES ( m_db_blocks . size ( ) > = split_height , false , " switch_to_alternative_blockchain: blockchain size is lower than split height " < < ENDL
< < " alt chain: " < < ENDL < < print_alt_chain ( alt_chain ) < < ENDL
< < " main chain: " < < ENDL < < get_blockchain_string ( m_db_blocks . size ( ) - 10 , CURRENCY_MAX_BLOCK_NUMBER )
) ;
//disconnecting old chain
std : : list < block > disconnected_chain ;
for ( size_t i = m_db_blocks . size ( ) - 1 ; i > = split_height ; i - - )
{
block b = m_db_blocks [ i ] - > bl ;
bool r = pop_block_from_blockchain ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to remove block " < < get_block_hash ( b ) < < " @ " < < get_block_height ( b ) < < " on chain switching " ) ;
disconnected_chain . push_front ( b ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
}
//connecting new alternative chain
for ( auto alt_ch_iter = alt_chain . begin ( ) ; alt_ch_iter ! = alt_chain . end ( ) ; alt_ch_iter + + )
{
auto ch_ent = * alt_ch_iter ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
bool r = handle_block_to_main_chain ( ch_ent - > second . bl , bvc ) ;
if ( ! r | | ! bvc . m_added_to_main_chain )
{
LOG_PRINT_L0 ( " Failed to switch to alternative blockchain " ) ;
rollback_blockchain_switching ( disconnected_chain , split_height ) ;
LOG_PRINT_L0 ( " The block was inserted as invalid while connecting new alternative chain, block_id: " < < get_block_hash ( ch_ent - > second . bl ) ) ;
for ( ; alt_ch_iter ! = alt_chain . end ( ) ; + + alt_ch_iter )
{
add_block_as_invalid ( ( * alt_ch_iter ) - > second , ( * alt_ch_iter ) - > first ) ;
do_erase_altblock ( * alt_ch_iter ) ;
}
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
return false ;
}
}
//pushing old chain as alternative chain
for ( auto & old_ch_ent : disconnected_chain )
{
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
bool r = handle_alternative_block ( old_ch_ent , get_block_hash ( old_ch_ent ) , bvc ) ;
if ( ! r )
{
LOG_ERROR ( " Failed to push ex-main chain blocks to alternative chain " ) ;
rollback_blockchain_switching ( disconnected_chain , split_height ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
2019-04-30 02:30:42 +02:00
//can't do return false here, because of the risc to get stuck in "PANIC" mode when nor of
//new chain nor altchain can be inserted into main chain. Got core caught in this trap when
//when machine time was wrongly set for a few hours back, then blocks which was detached from main chain
//couldn't be added as alternative due to timestamps validation(timestamps assumed as from future)
//thanks @Gigabyted for reporting this problem
break ;
2018-12-27 18:50:45 +03:00
}
}
//removing all_chain entries from alternative chain
for ( auto ch_ent : alt_chain )
{
do_erase_altblock ( ch_ent ) ;
}
LOG_PRINT_GREEN ( " REORGANIZE SUCCESS! on height: " < < split_height < < " , new blockchain size: " < < m_db_blocks . size ( ) , LOG_LEVEL_0 ) ;
return true ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_next_diff_conditional ( bool pos ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
std : : vector < uint64_t > timestamps ;
std : : vector < wide_difficulty_type > commulative_difficulties ;
if ( ! m_db_blocks . size ( ) )
return DIFFICULTY_STARTER ;
//skip genesis timestamp
2019-04-18 02:45:43 +02:00
TIME_MEASURE_START_PD ( target_calculating_enum_blocks ) ;
2019-05-09 13:43:04 +02:00
CRITICAL_REGION_BEGIN ( m_targetdata_cache_lock ) ;
2019-04-18 23:29:38 +02:00
std : : list < std : : pair < wide_difficulty_type , uint64_t > > & targetdata_cache = pos ? m_pos_targetdata_cache : m_pow_targetdata_cache ;
2019-05-09 00:19:05 +02:00
//if (targetdata_cache.empty())
load_targetdata_cache ( pos ) ;
2019-04-18 02:45:43 +02:00
2019-04-18 23:29:38 +02:00
size_t count = 0 ;
for ( auto it = targetdata_cache . rbegin ( ) ; it ! = targetdata_cache . rend ( ) & & count < DIFFICULTY_WINDOW ; it + + )
2018-12-27 18:50:45 +03:00
{
2019-04-18 23:29:38 +02:00
timestamps . push_back ( it - > second ) ;
commulative_difficulties . push_back ( it - > first ) ;
2018-12-27 18:50:45 +03:00
+ + count ;
}
2019-05-09 13:43:04 +02:00
CRITICAL_REGION_END ( ) ;
2019-04-18 02:45:43 +02:00
2018-12-27 18:50:45 +03:00
wide_difficulty_type & dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty ;
2019-04-18 02:45:43 +02:00
TIME_MEASURE_FINISH_PD ( target_calculating_enum_blocks ) ;
TIME_MEASURE_START_PD ( target_calculating_calc ) ;
2019-07-20 14:36:12 +02:00
if ( m_db_blocks . size ( ) > m_core_runtime_config . hard_fork1_starts_after_height )
2019-07-19 18:39:00 +02:00
{
dif = next_difficulty_2 ( timestamps , commulative_difficulties , pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ) ;
}
else
{
dif = next_difficulty_1 ( timestamps , commulative_difficulties , pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ) ;
}
2019-04-18 02:45:43 +02:00
TIME_MEASURE_FINISH_PD ( target_calculating_calc ) ;
return dif ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
2019-07-19 18:39:00 +02:00
wide_difficulty_type blockchain_storage : : get_next_diff_conditional2 ( bool pos , const alt_chain_type & alt_chain , uint64_t split_height , const alt_block_extended_info & abei ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
std : : vector < uint64_t > timestamps ;
std : : vector < wide_difficulty_type > commulative_difficulties ;
size_t count = 0 ;
if ( ! m_db_blocks . size ( ) )
return DIFFICULTY_STARTER ;
auto cb = [ & ] ( const block_extended_info & bei , bool is_main ) {
if ( ! bei . height )
return false ;
bool is_pos_bl = is_pos_block ( bei . bl ) ;
if ( pos ! = is_pos_bl )
return true ;
timestamps . push_back ( bei . bl . timestamp ) ;
commulative_difficulties . push_back ( bei . cumulative_diff_precise ) ;
+ + count ;
if ( count > = DIFFICULTY_WINDOW )
return false ;
return true ;
} ;
enum_blockchain ( cb , alt_chain , split_height ) ;
2019-07-19 18:39:00 +02:00
wide_difficulty_type diff = 0 ;
2019-07-20 14:36:12 +02:00
if ( abei . height > m_core_runtime_config . hard_fork1_starts_after_height )
2019-07-19 18:39:00 +02:00
diff = next_difficulty_2 ( timestamps , commulative_difficulties , pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ) ;
else
diff = next_difficulty_1 ( timestamps , commulative_difficulties , pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ) ;
return diff ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_cached_next_difficulty ( bool pos ) const
{
wide_difficulty_type res = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty ;
if ( ! res )
{
get_next_diff_conditional ( pos ) ;
res = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty ;
}
return res ;
}
//------------------------------------------------------------------
std : : shared_ptr < block_extended_info > blockchain_storage : : get_last_block_of_type ( bool looking_for_pos , const alt_chain_type & alt_chain , uint64_t split_height ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
std : : shared_ptr < block_extended_info > pbei ( nullptr ) ;
auto cb = [ & ] ( const block_extended_info & bei_local , bool is_main ) {
if ( looking_for_pos = = is_pos_block ( bei_local . bl ) )
{
pbei . reset ( new block_extended_info ( bei_local ) ) ;
return false ;
}
return true ;
} ;
enum_blockchain ( cb , alt_chain , split_height ) ;
return pbei ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_next_difficulty_for_alternative_chain ( const alt_chain_type & alt_chain , block_extended_info & bei , bool pos ) const
{
std : : vector < uint64_t > timestamps ;
std : : vector < wide_difficulty_type > commulative_difficulties ;
for ( auto it = alt_chain . rbegin ( ) ; it ! = alt_chain . rend ( ) & & timestamps . size ( ) < DIFFICULTY_BLOCKS_COUNT ; it + + )
{
bool is_pos_bl = is_pos_block ( ( * it ) - > second . bl ) ;
if ( pos ! = is_pos_bl )
continue ;
timestamps . push_back ( ( * it ) - > second . bl . timestamp ) ;
commulative_difficulties . push_back ( ( * it ) - > second . cumulative_diff_precise ) ;
}
size_t main_chain_start_offset = ( alt_chain . size ( ) ? alt_chain . front ( ) - > second . height : bei . height ) - 1 ;
for ( uint64_t i = main_chain_start_offset ; i ! = 0 & & timestamps . size ( ) < DIFFICULTY_BLOCKS_COUNT ; - - i )
{
bool is_pos_bl = is_pos_block ( m_db_blocks [ i ] - > bl ) ;
if ( pos ! = is_pos_bl )
continue ;
timestamps . push_back ( m_db_blocks [ i ] - > bl . timestamp ) ;
commulative_difficulties . push_back ( m_db_blocks [ i ] - > cumulative_diff_precise ) ;
}
2019-07-19 18:39:00 +02:00
return next_difficulty_1 ( timestamps , commulative_difficulties , pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ) ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
bool blockchain_storage : : prevalidate_miner_transaction ( const block & b , uint64_t height , bool pos ) const
{
CHECK_AND_ASSERT_MES ( ( pos ? ( b . miner_tx . vin . size ( ) = = 2 ) : ( b . miner_tx . vin . size ( ) = = 1 ) ) , false , " coinbase transaction in the block has no inputs " ) ;
CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 0 ] . type ( ) = = typeid ( txin_gen ) , false , " coinbase transaction in the block has the wrong type " ) ;
if ( boost : : get < txin_gen > ( b . miner_tx . vin [ 0 ] ) . height ! = height )
{
LOG_PRINT_RED_L0 ( " The miner transaction in block has invalid height: " < < boost : : get < txin_gen > ( b . miner_tx . vin [ 0 ] ) . height < < " , expected: " < < height ) ;
return false ;
}
if ( pos )
{
CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( txin_to_key ) , false , " coinstake transaction in the block has the wrong type " ) ;
}
2019-07-22 23:54:38 +02:00
if ( height > m_core_runtime_config . hard_fork1_starts_after_height )
{
// new rules that allow different unlock time in coinbase outputs
uint64_t max_unlock_time = 0 ;
uint64_t min_unlock_time = 0 ;
bool r = get_tx_max_min_unlock_time ( b . miner_tx , max_unlock_time , min_unlock_time ) ;
CHECK_AND_ASSERT_MES ( r & & min_unlock_time > = height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW ,
false ,
" coinbase transaction has wrong min_unlock_time: " < < min_unlock_time < < " , expected: " < < height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
}
else
{
//------------------------------------------------------------------
//bool blockchain_storage::
// pre-hard fork rules that don't allow different unlock time in coinbase outputs
uint64_t max_unlock_time = 0 ;
uint64_t min_unlock_time = 0 ;
bool r = get_tx_max_min_unlock_time ( b . miner_tx , max_unlock_time , min_unlock_time ) ;
CHECK_AND_ASSERT_MES ( r & & max_unlock_time = = min_unlock_time & & min_unlock_time = = height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW ,
false ,
" coinbase transaction has wrong min_unlock_time: " < < min_unlock_time < < " , expected: " < < height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW ) ;
}
2018-12-27 18:50:45 +03:00
//check outs overflow
if ( ! check_outs_overflow ( b . miner_tx ) )
{
LOG_PRINT_RED_L0 ( " miner transaction have money overflow in block " < < get_block_hash ( b ) ) ;
return false ;
}
CHECK_AND_ASSERT_MES ( b . miner_tx . attachment . empty ( ) , false , " coinbase transaction has attachments; attachments are not allowed for coinbase transactions. " ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_miner_transaction ( const block & b ,
size_t cumulative_block_size ,
uint64_t fee ,
uint64_t & base_reward ,
2019-04-10 03:00:29 +02:00
const boost : : multiprecision : : uint128_t & already_generated_coins ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
//validate reward
uint64_t money_in_use = get_outs_money_amount ( b . miner_tx ) ;
uint64_t pos_income = 0 ;
if ( is_pos_block ( b ) )
{
CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( txin_to_key ) , false , " Wrong miner tx_in " ) ;
pos_income = boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) . amount ;
}
std : : vector < size_t > last_blocks_sizes ;
get_last_n_blocks_sizes ( last_blocks_sizes , CURRENCY_REWARD_BLOCKS_WINDOW ) ;
size_t blocks_size_median = misc_utils : : median ( last_blocks_sizes ) ;
if ( ! get_block_reward ( is_pos_block ( b ) , blocks_size_median , cumulative_block_size , already_generated_coins , base_reward , get_block_height ( b ) ) )
{
LOG_PRINT_L0 ( " block size " < < cumulative_block_size < < " is bigger than allowed for this blockchain " ) ;
return false ;
}
if ( base_reward + pos_income + fee < money_in_use )
{
LOG_ERROR ( " coinbase transaction spend too much money ( " < < print_money ( money_in_use ) < < " ). Block reward is " < < print_money ( base_reward + pos_income + fee ) < < " ( " < < print_money ( base_reward ) < < " + " < < print_money ( pos_income ) < < " + " < < print_money ( fee )
< < " , blocks_size_median = " < < blocks_size_median
< < " , cumulative_block_size = " < < cumulative_block_size
< < " , fee = " < < fee
< < " , already_generated_coins = " < < already_generated_coins
< < " ), tx: " ) ;
LOG_PRINT_L0 ( currency : : obj_to_json_str ( b . miner_tx ) ) ;
return false ;
}
if ( base_reward + pos_income + fee ! = money_in_use )
{
LOG_ERROR ( " coinbase transaction doesn't use full amount of block reward: spent: ( " < < print_money ( money_in_use ) < < " ). Block reward is " < < print_money ( base_reward + pos_income + fee ) < < " ( " < < print_money ( base_reward ) < < " + " < < print_money ( pos_income ) < < " + " < < print_money ( fee )
< < " , blocks_size_median = " < < blocks_size_median
< < " , cumulative_block_size = " < < cumulative_block_size
< < " , fee = " < < fee
< < " , already_generated_coins = " < < already_generated_coins
< < " ), tx: " ) ;
LOG_PRINT_L0 ( currency : : obj_to_json_str ( b . miner_tx ) ) ;
return false ;
}
LOG_PRINT_MAGENTA ( " Mining tx verification ok, blocks_size_median = " < < blocks_size_median , LOG_LEVEL_2 ) ;
return true ;
}
//------------------------------------------------------------------
blockchain_storage : : performnce_data & blockchain_storage : : get_performnce_data ( ) const
{
m_db . get_backend ( ) - > get_stat_info ( m_performance_data . si ) ;
return m_performance_data ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_backward_blocks_sizes ( size_t from_height , std : : vector < size_t > & sz , size_t count ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( from_height < m_db_blocks . size ( ) , false , " Internal error: get_backward_blocks_sizes called with from_height= " < < from_height < < " , blockchain height = " < < m_db_blocks . size ( ) ) ;
size_t start_offset = ( from_height + 1 ) - std : : min ( ( from_height + 1 ) , count ) ;
for ( size_t i = start_offset ; i ! = from_height + 1 ; i + + )
sz . push_back ( m_db_blocks [ i ] - > block_cumulative_size ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_last_n_blocks_sizes ( std : : vector < size_t > & sz , size_t count ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! m_db_blocks . size ( ) )
return true ;
return get_backward_blocks_sizes ( m_db_blocks . size ( ) - 1 , sz , count ) ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_current_comulative_blocksize_limit ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_current_block_cumul_sz_limit ;
}
//------------------------------------------------------------------
2019-03-25 01:30:20 +01:00
bool blockchain_storage : : create_block_template ( block & b ,
2018-12-27 18:50:45 +03:00
const account_public_address & miner_address ,
wide_difficulty_type & diffic ,
uint64_t & height ,
const blobdata & ex_nonce ) const
{
2019-03-25 01:30:20 +01:00
return create_block_template ( b , miner_address , miner_address , diffic , height , ex_nonce , false , pos_entry ( ) ) ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
2019-03-25 01:30:20 +01:00
bool blockchain_storage : : create_block_template ( block & b ,
2018-12-27 18:50:45 +03:00
const account_public_address & miner_address ,
const account_public_address & stakeholder_address ,
wide_difficulty_type & diffic ,
uint64_t & height ,
const blobdata & ex_nonce ,
bool pos ,
const pos_entry & pe ,
fill_block_template_func_t custom_fill_block_template_func /* = nullptr */ ) const
{
size_t median_size ;
2019-04-10 03:00:29 +02:00
boost : : multiprecision : : uint128_t already_generated_coins ;
2018-12-27 18:50:45 +03:00
CRITICAL_REGION_BEGIN ( m_read_lock ) ;
2019-07-19 18:39:00 +02:00
height = m_db_blocks . size ( ) ;
if ( height < = m_core_runtime_config . hard_fork1_starts_after_height )
b . major_version = BLOCK_MAJOR_VERSION_INITAL ;
else
b . major_version = CURRENT_BLOCK_MAJOR_VERSION ;
2018-12-27 18:50:45 +03:00
b . minor_version = CURRENT_BLOCK_MINOR_VERSION ;
b . prev_id = get_top_block_id ( ) ;
b . timestamp = m_core_runtime_config . get_core_time ( ) ;
b . nonce = 0 ;
b . flags = 0 ;
if ( pos )
{
b . flags | = CURRENCY_BLOCK_FLAG_POS_BLOCK ;
b . timestamp = 0 ;
}
diffic = get_next_diff_conditional ( pos ) ;
CHECK_AND_ASSERT_MES ( diffic , false , " difficulty owverhead. " ) ;
2019-07-19 18:39:00 +02:00
2018-12-27 18:50:45 +03:00
median_size = m_db_current_block_cumul_sz_limit / 2 ;
already_generated_coins = m_db_blocks . back ( ) - > already_generated_coins ;
CRITICAL_REGION_END ( ) ;
size_t txs_size ;
uint64_t fee ;
bool block_filled = false ;
if ( custom_fill_block_template_func = = nullptr )
block_filled = m_tx_pool . fill_block_template ( b , pos , median_size , already_generated_coins , txs_size , fee , height ) ;
else
block_filled = ( * custom_fill_block_template_func ) ( b , pos , median_size , already_generated_coins , txs_size , fee , height ) ;
if ( ! block_filled )
return false ;
/*
instead of complicated two - phase template construction and adjustment of cumulative size with block reward we
use CURRENCY_COINBASE_BLOB_RESERVED_SIZE as penalty - free coinbase transaction reservation .
*/
bool r = construct_miner_tx ( height , median_size , already_generated_coins ,
txs_size ,
fee ,
miner_address ,
stakeholder_address ,
b . miner_tx , ex_nonce ,
CURRENCY_MINER_TX_MAX_OUTS ,
pos ,
pe ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to construc miner tx, first chance " ) ;
uint64_t coinbase_size = get_object_blobsize ( b . 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 < < " ) << coinbase structue: "
< < ENDL < < obj_to_json_str ( b . miner_tx ) ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : print_transactions_statistics ( ) const
{
LOG_ERROR ( " print_transactions_statistics not implemented yet " ) ;
// CRITICAL_REGION_LOCAL(m_blockchain_lock);
// LOG_PRINT_L0("Started to collect transaction statistics, pleas wait...");
// size_t total_count = 0;
// size_t coinbase_count = 0;
// size_t total_full_blob = 0;
// size_t total_cropped_blob = 0;
// for(auto tx_entry: m_db_transactions)
// {
// ++total_count;
// if(is_coinbase(tx_entry.second.tx))
// ++coinbase_count;
// else
// {
// total_full_blob += get_object_blobsize<transaction>(tx_entry.second.tx);
// transaction tx = tx_entry.second.tx;
// tx.signatures.clear();
// total_cropped_blob += get_object_blobsize<transaction>(tx);
// }
// }
// LOG_PRINT_L0("Done" << ENDL
// << "total transactions: " << total_count << ENDL
// << "coinbase transactions: " << coinbase_count << ENDL
// << "avarage size of transaction: " << total_full_blob/(total_count-coinbase_count) << ENDL
// << "avarage size of transaction without ring signatures: " << total_cropped_blob/(total_count-coinbase_count) << ENDL
// );
return true ;
}
void blockchain_storage : : reset_db_cache ( ) const
{
m_db_blocks . clear_cache ( ) ;
m_db_blocks_index . clear_cache ( ) ;
m_db_transactions . clear_cache ( ) ;
m_db_spent_keys . clear_cache ( ) ;
m_db_solo_options . clear_cache ( ) ;
m_db_multisig_outs . clear_cache ( ) ;
m_db_aliases . clear_cache ( ) ;
m_db_addr_to_alias . clear_cache ( ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : clear_altblocks ( )
{
CRITICAL_REGION_LOCAL ( m_alternative_chains_lock ) ;
m_alternative_chains . clear ( ) ;
m_alternative_chains_txs . clear ( ) ;
m_altblocks_keyimages . clear ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : complete_timestamps_vector ( uint64_t start_top_height , std : : vector < uint64_t > & timestamps )
{
if ( timestamps . size ( ) > = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW )
return true ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps . size ( ) ;
CHECK_AND_ASSERT_MES ( start_top_height < m_db_blocks . size ( ) , false , " internal error: passed start_height = " < < start_top_height < < " not less then m_blocks.size()= " < < m_db_blocks . size ( ) ) ;
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0 ;
do
{
timestamps . push_back ( m_db_blocks [ start_top_height ] - > bl . timestamp ) ;
if ( start_top_height = = 0 )
break ;
- - start_top_height ;
} while ( start_top_height ! = stop_offset ) ;
return true ;
}
//------------------------------------------------------------------
std : : string blockchain_storage : : print_alt_chain ( alt_chain_type alt_chain )
{
std : : stringstream ss ;
for ( auto it = alt_chain . begin ( ) ; it ! = alt_chain . end ( ) ; it + + )
{
ss < < " [ " < < ( * it ) - > second . height < < " ] " < < ( * it ) - > first < < " (cumul_dif: " < < ( * it ) - > second . cumulative_diff_adjusted < < " ), parent_id: " < < ( * it ) - > second . bl . prev_id < < ENDL ;
}
return ss . str ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : append_altblock_keyimages_to_big_heap ( const crypto : : hash & block_id , const std : : set < crypto : : key_image > & alt_block_keyimages )
{
for ( auto & ki : alt_block_keyimages )
m_altblocks_keyimages [ ki ] . push_back ( block_id ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_keyimage_from_big_heap ( const crypto : : key_image & ki , const crypto : : hash & id )
{
auto it = m_altblocks_keyimages . find ( ki ) ;
CHECK_AND_ASSERT_MES ( it ! = m_altblocks_keyimages . end ( ) , false , " internal error: keyimage " < < ki
< < " from altblock " < < id < < " not found in m_altblocks_keyimages in purge_keyimage_from_big_heap " ) ;
//TODO: at the moment here is simple liner algo since having the same txs in few altblocks is rare case
std : : list < crypto : : hash > & ki_blocks_ids = it - > second ;
bool found = false ;
for ( auto it_in_blocks = ki_blocks_ids . begin ( ) ; it_in_blocks ! = ki_blocks_ids . end ( ) ; it_in_blocks + + )
{
if ( * it_in_blocks = = id )
{
//found, erase this entry
ki_blocks_ids . erase ( it_in_blocks ) ;
found = true ;
break ;
}
}
CHECK_AND_ASSERT_MES ( found , false , " internal error: keyimage " < < ki
< < " from altblock " < < id < < " not found in m_altblocks_keyimages in purge_keyimage_from_big_heap " ) ;
if ( ! ki_blocks_ids . size ( ) )
m_altblocks_keyimages . erase ( it ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : purge_altblock_keyimages_from_big_heap ( const block & b , const crypto : : hash & id )
{
if ( is_pos_block ( b ) )
{
CHECK_AND_ASSERT_MES ( b . miner_tx . vin . size ( ) > = 2 , false , " paranoid check failed " ) ;
CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( txin_to_key ) , false , " paranoid type check failed " ) ;
purge_keyimage_from_big_heap ( boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) . k_image , id ) ;
}
for ( auto tx_id : b . tx_hashes )
{
std : : shared_ptr < transaction > tx_ptr ;
if ( ! get_transaction_from_pool_or_db ( tx_id , tx_ptr ) )
{
LOG_ERROR ( " failed to get alt block tx " < < tx_id < < " on block detach from alts " ) ;
continue ;
}
transaction & tx = * tx_ptr ;
for ( size_t n = 0 ; n < tx . vin . size ( ) ; + + n )
{
if ( tx . vin [ n ] . type ( ) = = typeid ( txin_to_key ) )
{
purge_keyimage_from_big_heap ( boost : : get < txin_to_key > ( tx . vin [ n ] ) . k_image , id ) ;
}
}
}
return true ;
}
2019-01-13 00:12:30 +03:00
2018-12-27 18:50:45 +03:00
//------------------------------------------------------------------
bool blockchain_storage : : handle_alternative_block ( const block & b , const crypto : : hash & id , block_verification_context & bvc )
{
uint64_t coinbase_height = get_block_height ( b ) ;
if ( m_checkpoints . is_height_passed_zone ( coinbase_height , get_top_block_height ( ) ) )
{
LOG_PRINT_RED_L0 ( " Block with id: " < < id < < " [ " < < coinbase_height < < " ] " < < ENDL < < " for alternative chain, is under checkpoint zone, declined " ) ;
bvc . m_verification_failed = true ;
return false ;
}
TRY_ENTRY ( ) ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
//block is not related with head of main chain
//first of all - look in alternative chains container
auto ptr_main_prev = m_db_blocks_index . find ( b . prev_id ) ;
auto it_prev = m_alternative_chains . find ( b . prev_id ) ;
if ( it_prev ! = m_alternative_chains . end ( ) | | ptr_main_prev ! = m_db_blocks_index . end ( ) )
{
alt_chain_type alt_chain ;
{
//we have new block in alternative chain
2019-01-13 00:12:30 +03:00
//build alternative subchain, front -> mainchain, back -> alternative head
2018-12-27 18:50:45 +03:00
alt_chain_container : : iterator alt_it = it_prev ; //m_alternative_chains.find()
std : : vector < uint64_t > timestamps ;
2019-01-13 00:12:30 +03:00
std : : list < alt_chain_container : : iterator > temp_container ;
2018-12-27 18:50:45 +03:00
while ( alt_it ! = m_alternative_chains . end ( ) )
{
2019-01-13 00:12:30 +03:00
temp_container . push_front ( alt_it ) ;
2018-12-27 18:50:45 +03:00
timestamps . push_back ( alt_it - > second . bl . timestamp ) ;
alt_it = m_alternative_chains . find ( alt_it - > second . bl . prev_id ) ;
}
2019-01-13 00:12:30 +03:00
//TODO: refactoring needed: vector push_front is dramatically ineffective
alt_chain . resize ( temp_container . size ( ) ) ;
auto it_vec = alt_chain . begin ( ) ;
for ( auto it = temp_container . begin ( ) ; it ! = temp_container . end ( ) ; it + + , it_vec + + )
{
* it_vec = * it ;
}
2018-12-27 18:50:45 +03:00
if ( alt_chain . size ( ) )
{
//make sure that it has right connection to main chain
CHECK_AND_ASSERT_MES_CUSTOM ( m_db_blocks . size ( ) > = alt_chain . front ( ) - > second . height , false , bvc . m_verification_failed = true , " main blockchain wrong height: m_db_blocks.size() = " < < m_db_blocks . size ( )
< < " and alt_chain.front()->second.height = " < < alt_chain . front ( ) - > second . height
< < " for block " < < id < < " , prev_id= " < < b . prev_id < < ENDL
< < " alt chain: " < < ENDL < < print_alt_chain ( alt_chain ) < < ENDL
< < " main chain: " < < ENDL < < get_blockchain_string ( m_db_blocks . size ( ) - 10 , CURRENCY_MAX_BLOCK_NUMBER )
) ;
crypto : : hash h = null_hash ;
get_block_hash ( m_db_blocks [ alt_chain . front ( ) - > second . height - 1 ] - > bl , h ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( h = = alt_chain . front ( ) - > second . bl . prev_id , false , bvc . m_verification_failed = true , " alternative chain have wrong connection to main chain " ) ;
complete_timestamps_vector ( alt_chain . front ( ) - > second . height - 1 , timestamps ) ;
}
else
{
CHECK_AND_ASSERT_MES_CUSTOM ( ptr_main_prev ! = m_db_blocks_index . end ( ) , false , bvc . m_verification_failed = true , " internal error: broken imperative condition it_main_prev != m_blocks_index.end() " ) ;
complete_timestamps_vector ( * ptr_main_prev , timestamps ) ;
}
//check timestamp correct
if ( ! check_block_timestamp ( std : : move ( timestamps ) , b ) )
{
LOG_PRINT_RED_L0 ( " Block with id: " < < id
< < ENDL < < " for alternative chain, have invalid timestamp: " < < b . timestamp ) ;
//add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed
bvc . m_verification_failed = true ;
return false ;
}
}
alt_block_extended_info abei = AUTO_VAL_INIT ( abei ) ;
abei . bl = b ;
2019-08-01 18:47:20 +02:00
abei . timestamp = m_core_runtime_config . get_core_time ( ) ;
2018-12-27 18:50:45 +03:00
abei . height = alt_chain . size ( ) ? it_prev - > second . height + 1 : * ptr_main_prev + 1 ;
CHECK_AND_ASSERT_MES_CUSTOM ( coinbase_height = = abei . height , false , bvc . m_verification_failed = true , " block coinbase height doesn't match with altchain height, declined " ) ;
uint64_t connection_height = alt_chain . size ( ) ? alt_chain . front ( ) - > second . height : abei . height ;
CHECK_AND_ASSERT_MES_CUSTOM ( connection_height , false , bvc . m_verification_failed = true , " INTERNAL ERROR: Wrong connection_height==0 in handle_alternative_block " ) ;
if ( ! m_checkpoints . is_in_checkpoint_zone ( abei . height ) )
{
m_is_in_checkpoint_zone = false ;
}
else
{
m_is_in_checkpoint_zone = true ;
if ( ! m_checkpoints . check_block ( abei . height , id ) )
{
LOG_ERROR ( " CHECKPOINT VALIDATION FAILED " ) ;
bvc . m_verification_failed = true ;
return false ;
}
}
bool pos_block = is_pos_block ( abei . bl ) ;
//check if PoS block allowed on this height
CHECK_AND_ASSERT_MES_CUSTOM ( ! ( pos_block & & abei . height < m_core_runtime_config . pos_minimum_heigh ) , false , bvc . m_verification_failed = true , " PoS block is not allowed on this height " ) ;
2019-07-19 18:39:00 +02:00
wide_difficulty_type current_diff = get_next_diff_conditional2 ( pos_block , alt_chain , connection_height , abei ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES_CUSTOM ( current_diff , false , bvc . m_verification_failed = true , " !!!!!!! DIFFICULTY OVERHEAD !!!!!!! " ) ;
crypto : : hash proof_of_work = null_hash ;
uint64_t pos_amount = 0 ;
wide_difficulty_type pos_diff_final = 0 ;
if ( pos_block )
{
//POS
bool res = validate_pos_block ( abei . bl , current_diff , pos_amount , pos_diff_final , abei . stake_hash , id , true , alt_chain , connection_height ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( res , false , bvc . m_verification_failed = true , " Failed to validate_pos_block on alternative block, height = "
< < abei . height
< < " , block id: " < < get_block_hash ( abei . bl ) ) ;
}
else
{
2019-03-25 01:30:20 +01:00
proof_of_work = get_block_longhash ( abei . bl ) ;
2019-01-13 00:12:30 +03:00
2018-12-27 18:50:45 +03:00
if ( ! check_hash ( proof_of_work , current_diff ) )
{
LOG_PRINT_RED_L0 ( " Block with id: " < < id
< < ENDL < < " for alternative chain, have not enough proof of work: " < < proof_of_work
< < ENDL < < " expected difficulty: " < < current_diff ) ;
bvc . m_verification_failed = true ;
return false ;
}
//
}
if ( ! prevalidate_miner_transaction ( b , abei . height , pos_block ) )
{
LOG_PRINT_RED_L0 ( " Block with id: " < < string_tools : : pod_to_hex ( id )
< < " (as alternative) have wrong miner transaction. " ) ;
bvc . m_verification_failed = true ;
return false ;
}
std : : set < crypto : : key_image > alt_block_keyimages ;
uint64_t ki_lookup_total = 0 ;
if ( ! validate_alt_block_txs ( b , id , alt_block_keyimages , abei , alt_chain , connection_height , ki_lookup_total ) )
{
LOG_PRINT_RED_L0 ( " Alternative block " < < id < < " @ " < < abei . height < < " has invalid transactions. Rejected. " ) ;
bvc . m_verification_failed = true ;
return false ;
}
abei . difficulty = current_diff ;
wide_difficulty_type cumulative_diff_delta = 0 ;
abei . cumulative_diff_adjusted = alt_chain . size ( ) ? it_prev - > second . cumulative_diff_adjusted : m_db_blocks [ * ptr_main_prev ] - > cumulative_diff_adjusted ;
if ( pos_block )
cumulative_diff_delta = get_adjusted_cumulative_difficulty_for_next_alt_pos ( alt_chain , abei . height , current_diff , connection_height ) ;
else
cumulative_diff_delta = current_diff ;
size_t sequence_factor = get_current_sequence_factor_for_alt ( alt_chain , pos_block , connection_height ) ;
if ( abei . height > = m_core_runtime_config . pos_minimum_heigh )
cumulative_diff_delta = correct_difficulty_with_sequence_factor ( sequence_factor , cumulative_diff_delta ) ;
2019-08-14 15:35:29 +02:00
if ( abei . height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION & & abei . height < = m_core_runtime_config . hard_fork1_starts_after_height & & pos_block & & sequence_factor > BLOCK_POS_STRICT_SEQUENCE_LIMIT )
2019-06-17 11:15:12 +03:00
{
LOG_PRINT_RED_L0 ( " Alternative block " < < id < < " @ " < < abei . height < < " has too big sequence factor: " < < sequence_factor < < " , rejected " ) ;
bvc . m_verification_failed = true ;
return false ;
}
2018-12-27 18:50:45 +03:00
abei . cumulative_diff_adjusted + = cumulative_diff_delta ;
2019-07-17 21:01:52 +02:00
wide_difficulty_type last_x_cumul_dif_precise_adj = 0 ;
2019-08-08 21:15:09 +02:00
abei . cumulative_diff_precise = get_last_alt_x_block_cumulative_precise_difficulty ( alt_chain , abei . height - 1 , pos_block , last_x_cumul_dif_precise_adj ) ;
2018-12-27 18:50:45 +03:00
abei . cumulative_diff_precise + = current_diff ;
2019-07-17 21:01:52 +02:00
//////////////////////////////////////////////////////////////////////////
wide_difficulty_type diff_precise_adj = correct_difficulty_with_sequence_factor ( sequence_factor , current_diff ) ;
abei . cumulative_diff_precise_adjusted = last_x_cumul_dif_precise_adj + diff_precise_adj ;
//////////////////////////////////////////////////////////////////////////
2018-12-27 18:50:45 +03:00
# ifdef _DEBUG
auto i_dres = m_alternative_chains . find ( id ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( i_dres = = m_alternative_chains . end ( ) , false , bvc . m_verification_failed = true , " insertion of new alternative block " < < id < < " returned as it already exist " ) ;
# endif
auto i_res = m_alternative_chains . insert ( alt_chain_container : : value_type ( id , abei ) ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( i_res . second , false , bvc . m_verification_failed = true , " insertion of new alternative block " < < id < < " returned as it already exist " ) ;
append_altblock_keyimages_to_big_heap ( id , alt_block_keyimages ) ;
add_alt_block_txs_hashs ( i_res . first - > second . bl ) ;
alt_chain . push_back ( i_res . first ) ;
//check if difficulty bigger then in main chain
bvc . height_difference = get_top_block_height ( ) > = abei . height ? get_top_block_height ( ) - abei . height : 0 ;
crypto : : hash proof = null_hash ;
std : : stringstream ss_pow_pos_info ;
if ( pos_block )
{
ss_pow_pos_info < < " PoS: \t " < < abei . stake_hash < < " , stake amount: " < < print_money ( pos_amount ) < < " , final_difficulty: " < < pos_diff_final ;
proof = abei . stake_hash ;
}
else
{
ss_pow_pos_info < < " PoW: \t " < < proof_of_work ;
proof = proof_of_work ;
}
LOG_PRINT_BLUE ( " ----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " < < abei . height < < ( pos_block ? " [PoS] Sq: " : " [PoW] Sq: " ) < < sequence_factor < < " , altchain sz: " < < alt_chain . size ( ) < < " , split h: " < < connection_height
< < ENDL < < " id: \t " < < id
< < ENDL < < " prev \t " < < abei . bl . prev_id
< < ENDL < < ss_pow_pos_info . str ( )
< < ENDL < < " HEIGHT " < < abei . height < < " , difficulty: " < < abei . difficulty < < " , cumul_diff_precise: " < < abei . cumulative_diff_precise < < " , cumul_diff_adj: " < < abei . cumulative_diff_adjusted < < " (current mainchain cumul_diff_adj: " < < m_db_blocks . back ( ) - > cumulative_diff_adjusted < < " , ki lookup total: " < < ki_lookup_total < < " ) "
, LOG_LEVEL_0 ) ;
2019-06-18 01:15:33 +03:00
if ( is_reorganize_required ( * m_db_blocks . back ( ) , alt_chain , proof ) )
2018-12-27 18:50:45 +03:00
{
auto a = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { m_is_reorganize_in_process = false ; } ) ;
CHECK_AND_ASSERT_THROW_MES ( ! m_is_reorganize_in_process , " Detected recursive reorganzie " ) ;
m_is_reorganize_in_process = true ;
//do reorganize!
LOG_PRINT_GREEN ( " ###### REORGANIZE on height: " < < alt_chain . front ( ) - > second . height < < " of " < < m_db_blocks . size ( ) - 1 < < " with cumulative_diff_adjusted " < < m_db_blocks . back ( ) - > cumulative_diff_adjusted
< < ENDL < < " alternative blockchain size: " < < alt_chain . size ( ) < < " with cumulative_diff_adjusted " < < abei . cumulative_diff_adjusted , LOG_LEVEL_0 ) ;
bool r = switch_to_alternative_blockchain ( alt_chain ) ;
if ( r )
bvc . m_added_to_main_chain = true ;
else
bvc . m_verification_failed = true ;
return r ;
}
bvc . added_to_altchain = true ;
2019-08-01 18:47:20 +02:00
//protect ourself from altchains container flood
if ( m_alternative_chains . size ( ) > m_core_runtime_config . max_alt_blocks )
prune_aged_alt_blocks ( ) ;
2018-12-27 18:50:45 +03:00
return true ;
} else
{
//block orphaned
bvc . m_marked_as_orphaned = true ;
2019-07-09 19:22:57 +02:00
if ( m_invalid_blocks . count ( id ) ! = 0 )
{
2019-07-09 22:33:01 +03:00
LOG_PRINT_RED_L0 ( " Block recognized as blacklisted and rejected, id = " < < id < < " , " < < ENDL < < " parent id = " < < b . prev_id < < ENDL < < " height = " < < coinbase_height ) ;
2019-07-09 19:22:57 +02:00
}
else if ( m_invalid_blocks . count ( b . prev_id ) ! = 0 )
{
LOG_PRINT_RED_L0 ( " Block recognized as orphaned (parent " < < b . prev_id < < " is in blacklist) and rejected, id = " < < id < < " , " < < ENDL < < " parent id = " < < b . prev_id < < ENDL < < " height = " < < coinbase_height ) ;
}
else
{
LOG_PRINT_RED_L0 ( " Block recognized as orphaned and rejected, id = " < < id < < " , " < < ENDL < < " parent id = " < < b . prev_id < < ENDL < < " height = " < < coinbase_height ) ;
}
2018-12-27 18:50:45 +03:00
}
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
return true ;
CATCH_ENTRY_CUSTOM ( " blockchain_storage::handle_alternative_block " , bvc . m_verification_failed = true , false ) ;
}
//------------------------------------------------------------------
2019-07-15 20:10:50 +02:00
wide_difficulty_type blockchain_storage : : get_x_difficulty_after_height ( uint64_t height , bool is_pos )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_THROW_MES ( height < m_db_blocks . size ( ) , " Internal error: condition failed: height ( " < < height < < " ) < m_db_blocks.size() " < < m_db_blocks . size ( ) ) ;
wide_difficulty_type diff = 0 ;
for ( uint64_t i = height + 1 ; i ! = m_db_blocks . size ( ) ; i + + )
{
auto bei_ptr = m_db_blocks [ i ] ;
if ( is_pos_block ( bei_ptr - > bl ) = = is_pos )
{
diff = bei_ptr - > difficulty ;
break ;
}
}
if ( diff = = 0 )
{
//never met x type of block, that meanst that difficulty is current
diff = get_cached_next_difficulty ( is_pos ) ;
}
return diff ;
}
//------------------------------------------------------------------
2019-06-18 01:15:33 +03:00
bool blockchain_storage : : is_reorganize_required ( const block_extended_info & main_chain_bei , const alt_chain_type & alt_chain , const crypto : : hash & proof_alt )
2018-12-27 18:50:45 +03:00
{
2019-06-18 01:15:33 +03:00
//alt_chain - back is latest(top), first - connection with main chain
const block_extended_info & alt_chain_bei = alt_chain . back ( ) - > second ;
2019-07-15 20:10:50 +02:00
const block_extended_info & connection_point = alt_chain . front ( ) - > second ;
2018-12-27 18:50:45 +03:00
2019-07-26 18:53:42 +02:00
if ( connection_point . height < = m_core_runtime_config . hard_fork1_starts_after_height )
2019-06-18 01:15:33 +03:00
{
2019-07-15 20:10:50 +02:00
//use pre-hard fork, old-style comparing
2019-06-18 01:15:33 +03:00
if ( main_chain_bei . cumulative_diff_adjusted < alt_chain_bei . cumulative_diff_adjusted )
return true ;
else if ( main_chain_bei . cumulative_diff_adjusted > alt_chain_bei . cumulative_diff_adjusted )
2018-12-27 18:50:45 +03:00
return false ;
2019-06-18 01:15:33 +03:00
else // main_chain_bei.cumulative_diff_adjusted == alt_chain_bei.cumulative_diff_adjusted
{
if ( ! is_pos_block ( main_chain_bei . bl ) )
return false ; // do not reorganize on the same cummul diff if it's a PoW block
//in case of simultaneous PoS blocks are happened on the same height (quite common for PoS)
//we also try to weight them to guarantee consensus in network
if ( std : : memcmp ( & main_chain_bei . stake_hash , & proof_alt , sizeof ( main_chain_bei . stake_hash ) ) > = 0 )
return false ;
LOG_PRINT_L2 ( " [is_reorganize_required]:TRUE, \" by order of memcmp \" main_stake_hash: " < < & main_chain_bei . stake_hash < < " , alt_stake_hash " < < proof_alt ) ;
return true ;
}
}
2019-07-26 18:53:42 +02:00
else if ( alt_chain_bei . height > m_core_runtime_config . hard_fork1_starts_after_height )
2019-06-18 01:15:33 +03:00
{
2019-07-15 20:10:50 +02:00
//new rules, applied after HARD_FORK_1
//to learn this algo please read https://github.com/hyle-team/docs/blob/master/zano/PoS_Analysis_and_improvements_proposal.pdf
wide_difficulty_type difficulty_pos_at_split_point = get_x_difficulty_after_height ( connection_point . height - 1 , true ) ;
wide_difficulty_type difficulty_pow_at_split_point = get_x_difficulty_after_height ( connection_point . height - 1 , false ) ;
difficulties main_cumul_diff = AUTO_VAL_INIT ( main_cumul_diff ) ;
2019-07-19 18:39:00 +02:00
difficulties alt_cumul_diff = AUTO_VAL_INIT ( alt_cumul_diff ) ;
2019-07-17 21:01:52 +02:00
//we use get_last_alt_x_block_cumulative_precise_adj_difficulty for getting both alt chain and main chain diff of given block types
2019-07-15 20:10:50 +02:00
2019-07-17 21:01:52 +02:00
wide_difficulty_type alt_pos_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain , alt_chain_bei . height , true ) ;
wide_difficulty_type alt_pos_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , connection_point . height - 1 , true ) ;
2019-08-09 07:32:12 +03:00
alt_cumul_diff . pos_diff = alt_pos_diff_end - alt_pos_diff_begin ;
2019-07-15 20:10:50 +02:00
2019-07-17 21:01:52 +02:00
wide_difficulty_type alt_pow_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain , alt_chain_bei . height , false ) ;
wide_difficulty_type alt_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , connection_point . height - 1 , false ) ;
2019-07-15 20:10:50 +02:00
alt_cumul_diff . pow_diff = alt_pow_diff_end - alt_pow_diff_begin ;
2019-07-17 21:01:52 +02:00
wide_difficulty_type main_pos_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , m_db_blocks . size ( ) - 1 , true ) ;
wide_difficulty_type main_pos_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , connection_point . height - 1 , true ) ;
2019-07-15 20:10:50 +02:00
main_cumul_diff . pos_diff = main_pos_diff_end - main_pos_diff_begin ;
2019-07-17 21:01:52 +02:00
wide_difficulty_type main_pow_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , m_db_blocks . size ( ) - 1 , false ) ;
wide_difficulty_type main_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty ( alt_chain_type ( ) , connection_point . height - 1 , false ) ;
2019-07-15 20:10:50 +02:00
main_cumul_diff . pow_diff = main_pow_diff_end - main_pow_diff_begin ;
2019-06-18 01:15:33 +03:00
2019-09-23 21:51:25 +02:00
//TODO: measurement of precise cumulative difficult
2019-09-26 22:07:47 +02:00
boost : : multiprecision : : uint1024_t alt = get_a_to_b_relative_cumulative_difficulty ( difficulty_pos_at_split_point , difficulty_pow_at_split_point , alt_cumul_diff , main_cumul_diff ) ;
boost : : multiprecision : : uint1024_t main = get_a_to_b_relative_cumulative_difficulty ( difficulty_pos_at_split_point , difficulty_pow_at_split_point , main_cumul_diff , alt_cumul_diff ) ;
2019-09-23 21:51:25 +02:00
LOG_PRINT_L1 ( " [FORK_CHOICE]: " < < ENDL
< < " difficulty_pow_at_split_point: " < < difficulty_pow_at_split_point < < ENDL
< < " difficulty_pos_at_split_point: " < < difficulty_pos_at_split_point < < ENDL
< < " alt_cumul_diff.pow_diff: " < < alt_cumul_diff . pow_diff < < ENDL
< < " alt_cumul_diff.pos_diff: " < < alt_cumul_diff . pos_diff < < ENDL
< < " main_cumul_diff.pow_diff: " < < main_cumul_diff . pow_diff < < ENDL
< < " main_cumul_diff.pos_diff: " < < main_cumul_diff . pos_diff < < ENDL
< < " alt: " < < alt < < ENDL
< < " main: " < < main < < ENDL
) ;
2019-07-15 20:10:50 +02:00
if ( main < alt )
return true ;
else if ( main > alt )
return false ;
else
{
if ( ! is_pos_block ( main_chain_bei . bl ) )
return false ; // do not reorganize on the same cummul diff if it's a PoW block
//in case of simultaneous PoS blocks are happened on the same height (quite common for PoS)
//we also try to weight them to guarantee consensus in network
if ( std : : memcmp ( & main_chain_bei . stake_hash , & proof_alt , sizeof ( main_chain_bei . stake_hash ) ) > = 0 )
return false ;
2019-09-23 21:51:25 +02:00
LOG_PRINT_L1 ( " [is_reorganize_required]:TRUE, \" by order of memcmp \" main_stake_hash: " < < & main_chain_bei . stake_hash < < " , alt_stake_hash " < < proof_alt ) ;
2019-07-15 20:10:50 +02:00
return true ;
}
2019-06-18 01:15:33 +03:00
}
else
{
ASSERT_MES_AND_THROW ( " Unknown version of block " ) ;
2018-12-27 18:50:45 +03:00
}
}
2019-06-18 01:15:33 +03:00
2018-12-27 18:50:45 +03:00
//------------------------------------------------------------------
bool blockchain_storage : : pre_validate_relayed_block ( block & bl , block_verification_context & bvc , const crypto : : hash & id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! ( bl . prev_id = = get_top_block_id ( ) ) )
{
bvc . m_added_to_main_chain = false ;
bvc . m_verification_failed = false ;
return true ;
}
//check proof of work
bool is_pos_bl = is_pos_block ( bl ) ;
wide_difficulty_type current_diffic = get_next_diff_conditional ( is_pos_bl ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( current_diffic , false , bvc . m_verification_failed = true , " !!!!!!!!! difficulty overhead !!!!!!!!! " ) ;
crypto : : hash proof_hash = AUTO_VAL_INIT ( proof_hash ) ;
if ( is_pos_bl )
{
wide_difficulty_type this_coin_diff = 0 ;
uint64_t amount = 0 ;
bool r = validate_pos_block ( bl , current_diffic , amount , this_coin_diff , proof_hash , id , false ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( r , false , bvc . m_verification_failed = true , " validate_pos_block failed!! " ) ;
bvc . m_added_to_main_chain = true ;
}
else
2019-01-13 00:12:30 +03:00
{
2019-03-25 01:30:20 +01:00
proof_hash = get_block_longhash ( bl ) ; //get_block_longhash(bl);
2018-12-27 18:50:45 +03:00
if ( ! check_hash ( proof_hash , current_diffic ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < ENDL
< < " : " < < proof_hash < < ENDL
< < " unexpected difficulty: " < < current_diffic ) ;
bvc . m_verification_failed = true ;
bvc . m_added_to_main_chain = true ;
return false ;
}
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_blocks ( uint64_t start_offset , size_t count , std : : list < block > & blocks , std : : list < transaction > & txs ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( start_offset > = m_db_blocks . size ( ) )
return false ;
for ( size_t i = start_offset ; i < start_offset + count & & i < m_db_blocks . size ( ) ; i + + )
{
blocks . push_back ( m_db_blocks [ i ] - > bl ) ;
std : : list < crypto : : hash > missed_ids ;
get_transactions ( m_db_blocks [ i ] - > bl . tx_hashes , txs , missed_ids ) ;
CHECK_AND_ASSERT_MES ( ! missed_ids . size ( ) , false , " have missed transactions in own block in main blockchain " ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_tx_rpc_details ( const crypto : : hash & h , tx_rpc_extended_info & tei , uint64_t timestamp , bool is_short ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto tx_ptr = m_db_transactions . get ( h ) ;
if ( ! tx_ptr )
{
tei . keeper_block = - 1 ; // tx is not confirmed yet, probably it's in the pool
return false ;
}
if ( tx_ptr & & ! timestamp )
{
timestamp = get_actual_timestamp ( m_db_blocks [ tx_ptr - > m_keeper_block_height ] - > bl ) ;
}
tei . keeper_block = static_cast < int64_t > ( tx_ptr - > m_keeper_block_height ) ;
fill_tx_rpc_details ( tei , tx_ptr - > tx , & ( * tx_ptr ) , h , timestamp , is_short ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : search_by_id ( const crypto : : hash & id , std : : list < std : : string > & res ) const
{
auto block_ptr = m_db_blocks_index . get ( id ) ;
if ( block_ptr )
{
res . push_back ( " block " ) ;
}
auto tx_ptr = m_db_transactions . get ( id ) ;
if ( tx_ptr )
{
res . push_back ( " tx " ) ;
}
auto ki_ptr = m_db_spent_keys . get ( * reinterpret_cast < const crypto : : key_image * > ( & id ) ) ;
if ( ki_ptr )
{
res . push_back ( " key_image " ) ;
}
auto ms_ptr = m_db_multisig_outs . get ( id ) ;
if ( ms_ptr )
{
res . push_back ( std : : string ( " multisig_id: " ) + epee : : string_tools : : pod_to_hex ( ms_ptr - > tx_id ) + " : " + std : : to_string ( ms_ptr - > out_no ) ) ;
}
if ( m_alternative_chains . end ( ) ! = m_alternative_chains . find ( id ) )
{
res . push_back ( " alt_block " ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_global_index_details ( const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_BY_AMOUNT : : request & req , COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_BY_AMOUNT : : response & resp ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
try
{
auto out_ptr = m_db_outputs . get_subitem ( req . amount , req . i ) ; // get_subitem can rise an out_of_range exception
if ( ! out_ptr )
return false ;
resp . tx_id = out_ptr - > tx_id ;
resp . out_no = out_ptr - > out_no ;
return true ;
}
catch ( std : : out_of_range & )
{
return false ;
}
}
//------------------------------------------------------------------
bool blockchain_storage : : get_multisig_id_details ( const COMMAND_RPC_GET_MULTISIG_INFO : : request & req , COMMAND_RPC_GET_MULTISIG_INFO : : response & resp ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return get_multisig_id_details ( req . ms_id , resp . tx_id , resp . out_no ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_multisig_id_details ( const crypto : : hash & ms_id , crypto : : hash & tx_id , uint64_t & out_no ) const
{
auto out_ptr = m_db_multisig_outs . get ( ms_id ) ;
if ( ! out_ptr )
return false ;
tx_id = out_ptr - > tx_id ;
out_no = out_ptr - > out_no ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_main_block_rpc_details ( uint64_t i , block_rpc_extended_info & bei ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto core_bei_ptr = m_db_blocks [ i ] ;
crypto : : hash id = get_block_hash ( core_bei_ptr - > bl ) ;
bei . is_orphan = false ;
bei . total_fee = 0 ;
bei . total_txs_size = 0 ;
if ( true /*!ignore_transactions*/ )
{
crypto : : hash coinbase_id = get_transaction_hash ( core_bei_ptr - > bl . miner_tx ) ;
//load transactions details
bei . transactions_details . push_back ( tx_rpc_extended_info ( ) ) ;
get_tx_rpc_details ( coinbase_id , bei . transactions_details . back ( ) , get_actual_timestamp ( core_bei_ptr - > bl ) , true ) ;
for ( auto & h : core_bei_ptr - > bl . tx_hashes )
{
bei . transactions_details . push_back ( tx_rpc_extended_info ( ) ) ;
get_tx_rpc_details ( h , bei . transactions_details . back ( ) , get_actual_timestamp ( core_bei_ptr - > bl ) , true ) ;
bei . total_fee + = bei . transactions_details . back ( ) . fee ;
bei . total_txs_size + = bei . transactions_details . back ( ) . blob_size ;
}
}
fill_block_rpc_details ( bei , * core_bei_ptr , id ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_main_block_rpc_details ( const crypto : : hash & id , block_rpc_extended_info & bei ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto iptr = m_db_blocks_index . get ( id ) ;
if ( ! iptr )
return false ;
return get_main_block_rpc_details ( * iptr , bei ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_alt_blocks_rpc_details ( uint64_t start_offset , uint64_t count , std : : vector < block_rpc_extended_info > & blocks ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
if ( start_offset > = m_alternative_chains . size ( ) | | count = = 0 )
return true ; // empty result
if ( start_offset + count > = m_alternative_chains . size ( ) )
count = m_alternative_chains . size ( ) - start_offset ; // correct count if it's too big
// collect iterators to all the alt blocks for speedy sorting
std : : vector < alt_chain_container : : const_iterator > blocks_its ;
blocks_its . reserve ( m_alternative_chains . size ( ) ) ;
for ( alt_chain_container : : const_iterator it = m_alternative_chains . begin ( ) ; it ! = m_alternative_chains . end ( ) ; + + it )
blocks_its . push_back ( it ) ;
// partially sort blocks by height, so only 0...(start_offset+count-1) first blocks are sorted
std : : partial_sort ( blocks_its . begin ( ) , blocks_its . begin ( ) + start_offset + count , blocks_its . end ( ) ,
[ ] ( const alt_chain_container : : const_iterator & lhs , const alt_chain_container : : const_iterator & rhs ) - > bool {
return lhs - > second . height < rhs - > second . height ;
}
) ;
// erase blocks from 0 till start_offset-1
blocks_its . erase ( blocks_its . begin ( ) , blocks_its . begin ( ) + start_offset ) ;
// erase the tail
blocks_its . erase ( blocks_its . begin ( ) + count , blocks_its . end ( ) ) ;
// populate the result
blocks . reserve ( blocks_its . size ( ) ) ;
for ( auto it : blocks_its )
{
blocks . push_back ( block_rpc_extended_info ( ) ) ;
get_alt_block_rpc_details ( it - > second , it - > first , blocks . back ( ) ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_alt_block_rpc_details ( const crypto : : hash & id , block_rpc_extended_info & bei ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
auto it = m_alternative_chains . find ( id ) ;
if ( it = = m_alternative_chains . end ( ) )
return false ;
const block_extended_info & bei_core = it - > second ;
return get_alt_block_rpc_details ( bei_core , id , bei ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_alt_block_rpc_details ( const block_extended_info & bei_core , const crypto : : hash & id , block_rpc_extended_info & bei ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
bei . is_orphan = true ;
crypto : : hash coinbase_id = get_transaction_hash ( bei_core . bl . miner_tx ) ;
//load transactions details
bei . transactions_details . push_back ( tx_rpc_extended_info ( ) ) ;
fill_tx_rpc_details ( bei . transactions_details . back ( ) , bei_core . bl . miner_tx , nullptr , coinbase_id , get_actual_timestamp ( bei_core . bl ) ) ;
bei . total_fee = 0 ;
for ( auto & h : bei_core . bl . tx_hashes )
{
bei . transactions_details . push_back ( tx_rpc_extended_info ( ) ) ;
if ( ! get_tx_rpc_details ( h , bei . transactions_details . back ( ) , get_actual_timestamp ( bei_core . bl ) , true ) )
{
//tx not in blockchain, supposed to be in tx pool
m_tx_pool . get_transaction_details ( h , bei . transactions_details . back ( ) ) ;
}
bei . total_fee + = bei . transactions_details . back ( ) . fee ;
}
fill_block_rpc_details ( bei , bei_core , id ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_main_blocks_rpc_details ( uint64_t start_offset , size_t count , bool ignore_transactions , std : : list < block_rpc_extended_info > & blocks ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( start_offset > = m_db_blocks . size ( ) )
return false ;
for ( size_t i = start_offset ; i < start_offset + count & & i < m_db_blocks . size ( ) ; i + + )
{
blocks . push_back ( block_rpc_extended_info ( ) ) ;
block_rpc_extended_info & bei = blocks . back ( ) ;
get_main_block_rpc_details ( i , bei ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_blocks ( uint64_t start_offset , size_t count , std : : list < block > & blocks ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( start_offset > = m_db_blocks . size ( ) )
return false ;
for ( size_t i = start_offset ; i < start_offset + count & & i < m_db_blocks . size ( ) ; i + + )
blocks . push_back ( m_db_blocks [ i ] - > bl ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : handle_get_objects ( NOTIFY_REQUEST_GET_OBJECTS : : request & arg , NOTIFY_RESPONSE_GET_OBJECTS : : request & rsp ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
rsp . current_blockchain_height = get_current_blockchain_size ( ) ;
std : : list < block > blocks ;
get_blocks ( arg . blocks , blocks , rsp . missed_ids ) ;
BOOST_FOREACH ( const auto & bl , blocks )
{
std : : list < transaction > txs ;
get_transactions ( bl . tx_hashes , txs , rsp . missed_ids ) ;
CHECK_AND_ASSERT_MES ( ! rsp . missed_ids . size ( ) , false , " Host have requested block with missed transactions missed_tx_id.size()= " < < rsp . missed_ids . size ( )
< < ENDL < < " for block id = " < < get_block_hash ( bl ) ) ;
rsp . blocks . push_back ( block_complete_entry ( ) ) ;
block_complete_entry & e = rsp . blocks . back ( ) ;
//pack block
e . block = t_serializable_object_to_blob ( bl ) ;
//pack transactions
BOOST_FOREACH ( transaction & tx , txs )
e . txs . push_back ( t_serializable_object_to_blob ( tx ) ) ;
}
//get another transactions, if need
std : : list < transaction > txs ;
get_transactions ( arg . txs , txs , rsp . missed_ids ) ;
//pack aside transactions
BOOST_FOREACH ( const auto & tx , txs )
rsp . txs . push_back ( t_serializable_object_to_blob ( tx ) ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_transactions_daily_stat ( uint64_t & daily_cnt , uint64_t & daily_volume ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
daily_cnt = daily_volume = 0 ;
for ( size_t i = ( m_db_blocks . size ( ) > CURRENCY_BLOCKS_PER_DAY ? m_db_blocks . size ( ) - CURRENCY_BLOCKS_PER_DAY : 0 ) ; i ! = m_db_blocks . size ( ) ; i + + )
{
auto ptr = m_db_blocks [ i ] ;
for ( auto & h : ptr - > bl . tx_hashes )
{
+ + daily_cnt ;
auto tx_ptr = m_db_transactions . find ( h ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " Wrong transaction hash " < < h < < " in block on height " < < i ) ;
uint64_t am = 0 ;
bool r = get_inputs_money_amount ( tx_ptr - > tx , am ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to get_inputs_money_amount " ) ;
daily_volume + = am ;
}
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : check_keyimages ( const std : : list < crypto : : key_image > & images , std : : list < uint64_t > & images_stat ) const
{
//true - unspent, false - spent
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
for ( auto & ki : images )
{
auto ki_ptr = m_db_spent_keys . get ( ki ) ;
if ( ki_ptr )
images_stat . push_back ( * ki_ptr ) ;
else
images_stat . push_back ( 0 ) ;
}
return true ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_seconds_between_last_n_block ( size_t n ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( m_db_blocks . size ( ) < = n )
return 0 ;
uint64_t top_block_ts = get_actual_timestamp ( m_db_blocks [ m_db_blocks . size ( ) - 1 ] - > bl ) ;
uint64_t n_block_ts = get_actual_timestamp ( m_db_blocks [ m_db_blocks . size ( ) - 1 - n ] - > bl ) ;
return top_block_ts > n_block_ts ? top_block_ts - n_block_ts : 0 ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_current_hashrate ( size_t aprox_count ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( aprox_count = = 0 | | m_db_blocks . size ( ) < = aprox_count )
return 0 ; // incorrect parameters
uint64_t nearest_front_pow_block_i = m_db_blocks . size ( ) - 1 ;
while ( nearest_front_pow_block_i ! = 0 )
{
if ( ! is_pos_block ( m_db_blocks [ nearest_front_pow_block_i ] - > bl ) )
break ;
- - nearest_front_pow_block_i ;
}
uint64_t nearest_back_pow_block_i = m_db_blocks . size ( ) - aprox_count ;
while ( nearest_back_pow_block_i ! = 0 )
{
if ( ! is_pos_block ( m_db_blocks [ nearest_back_pow_block_i ] - > bl ) )
break ;
- - nearest_back_pow_block_i ;
}
std : : shared_ptr < const block_extended_info > front_blk_ptr = m_db_blocks [ nearest_front_pow_block_i ] ;
std : : shared_ptr < const block_extended_info > back_blk_ptr = m_db_blocks [ nearest_back_pow_block_i ] ;
uint64_t front_blk_ts = front_blk_ptr - > bl . timestamp ;
uint64_t back_blk_ts = back_blk_ptr - > bl . timestamp ;
uint64_t ts_delta = front_blk_ts > back_blk_ts ? front_blk_ts - back_blk_ts : DIFFICULTY_POW_TARGET ;
wide_difficulty_type w_hr = ( front_blk_ptr - > cumulative_diff_precise - back_blk_ptr - > cumulative_diff_precise ) / ts_delta ;
return w_hr . convert_to < uint64_t > ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_alternative_blocks ( std : : list < block > & blocks ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
BOOST_FOREACH ( const auto & alt_bl , m_alternative_chains )
{
blocks . push_back ( alt_bl . second . bl ) ;
}
return true ;
}
//------------------------------------------------------------------
size_t blockchain_storage : : get_alternative_blocks_count ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
return m_alternative_chains . size ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : add_out_to_get_random_outs ( COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & result_outs , uint64_t amount , size_t i , uint64_t mix_count , bool use_only_forced_to_mix ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto out_ptr = m_db_outputs . get_subitem ( amount , i ) ;
auto tx_ptr = m_db_transactions . find ( out_ptr - > tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " internal error: transaction with id " < < out_ptr - > tx_id < < ENDL < <
" , used in mounts global index for amount= " < < amount < < " : i= " < < i < < " not found in transactions index " ) ;
CHECK_AND_ASSERT_MES ( tx_ptr - > tx . vout . size ( ) > out_ptr - > out_no , false , " internal error: in global outs index, transaction out index= "
< < out_ptr - > out_no < < " more than transaction outputs = " < < tx_ptr - > tx . vout . size ( ) < < " , for tx id = " < < out_ptr - > tx_id ) ;
const transaction & tx = tx_ptr - > tx ;
CHECK_AND_ASSERT_MES ( tx . vout [ out_ptr - > out_no ] . target . type ( ) = = typeid ( txout_to_key ) , false , " unknown tx out type " ) ;
CHECK_AND_ASSERT_MES ( tx_ptr - > m_spent_flags . size ( ) = = tx . vout . size ( ) , false , " internal error " ) ;
//do not use outputs that obviously spent for mixins
if ( tx_ptr - > m_spent_flags [ out_ptr - > out_no ] )
return false ;
//check if transaction is unlocked
2019-07-22 13:04:01 +02:00
if ( ! is_tx_spendtime_unlocked ( get_tx_unlock_time ( tx , out_ptr - > out_no ) ) )
2018-12-27 18:50:45 +03:00
return false ;
//use appropriate mix_attr out
uint8_t mix_attr = boost : : get < txout_to_key > ( tx . vout [ out_ptr - > out_no ] . target ) . mix_attr ;
if ( mix_attr = = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX )
return false ; //COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS call means that ring signature will have more than one entry.
else if ( use_only_forced_to_mix & & mix_attr = = CURRENCY_TO_KEY_OUT_RELAXED )
return false ; //relaxed not allowed
else if ( mix_attr ! = CURRENCY_TO_KEY_OUT_RELAXED & & mix_attr > mix_count )
return false ; //mix_attr set to specific minimum, and mix_count is less then desired count
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry & oen = * result_outs . outs . insert ( result_outs . outs . end ( ) , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry ( ) ) ;
oen . global_amount_index = i ;
oen . out_key = boost : : get < txout_to_key > ( tx . vout [ out_ptr - > out_no ] . target ) . key ;
return true ;
}
//------------------------------------------------------------------
size_t blockchain_storage : : find_end_of_allowed_index ( uint64_t amount ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint64_t sz = m_db_outputs . get_item_size ( amount ) ;
if ( ! sz )
return 0 ;
uint64_t i = sz ;
do
{
- - i ;
auto out_ptr = m_db_outputs . get_subitem ( amount , i ) ;
auto tx_ptr = m_db_transactions . find ( out_ptr - > tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , 0 , " internal error: failed to find transaction from outputs index with tx_id= " < < out_ptr - > tx_id ) ;
if ( tx_ptr - > m_keeper_block_height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW < = get_current_blockchain_size ( ) )
return i + 1 ;
} while ( i ! = 0 ) ;
return 0 ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_random_outs_for_amounts ( const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request & req , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response & res ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
BOOST_FOREACH ( uint64_t amount , req . amounts )
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & result_outs = * res . outs . insert ( res . outs . end ( ) , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount ( ) ) ;
result_outs . amount = amount ;
uint64_t outs_container_size = m_db_outputs . get_item_size ( amount ) ;
if ( ! outs_container_size )
{
LOG_ERROR ( " COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " < < amount < < " , wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist " ) ;
continue ; //actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
}
//it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
//lets find upper bound of not fresh outs
size_t up_index_limit = find_end_of_allowed_index ( amount ) ;
CHECK_AND_ASSERT_MES ( up_index_limit < = outs_container_size , false , " internal error: find_end_of_allowed_index returned wrong index= " < < up_index_limit < < " , with amount_outs.size = " < < outs_container_size ) ;
if ( up_index_limit > = req . outs_count )
{
std : : set < size_t > used ;
size_t try_count = 0 ;
for ( uint64_t j = 0 ; j ! = req . outs_count & & try_count < up_index_limit ; )
{
size_t i = crypto : : rand < size_t > ( ) % up_index_limit ;
if ( used . count ( i ) )
continue ;
bool added = add_out_to_get_random_outs ( result_outs , amount , i , req . outs_count , req . use_forced_mix_outs ) ;
used . insert ( i ) ;
if ( added )
+ + j ;
+ + try_count ;
}
if ( result_outs . outs . size ( ) < req . outs_count )
{
LOG_PRINT_RED_L0 ( " Not enough inputs for amount " < < amount < < " , needed " < < req . outs_count < < " , added " < < result_outs . outs . size ( ) < < " good outs from " < < up_index_limit < < " unlocked of " < < outs_container_size < < " total " ) ;
}
} else
{
size_t added = 0 ;
for ( size_t i = 0 ; i ! = up_index_limit ; i + + )
added + = add_out_to_get_random_outs ( result_outs , amount , i , req . outs_count , req . use_forced_mix_outs ) ? 1 : 0 ;
LOG_PRINT_RED_L0 ( " Not enough inputs for amount " < < amount < < " , needed " < < req . outs_count < < " , added " < < added < < " good outs from " < < up_index_limit < < " unlocked of " < < outs_container_size < < " total - respond with all good outs " ) ;
}
}
return true ;
}
//------------------------------------------------------------------
2019-04-10 03:00:29 +02:00
boost : : multiprecision : : uint128_t blockchain_storage : : total_coins ( ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! m_db_blocks . size ( ) )
return 0 ;
return m_db_blocks . back ( ) - > already_generated_coins ;
}
//------------------------------------------------------------------
bool blockchain_storage : : is_pos_allowed ( ) const
{
return get_top_block_height ( ) > = m_core_runtime_config . pos_minimum_heigh ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_spent_tx_flags_for_input ( uint64_t amount , const txout_v & o , bool spent )
{
if ( o . type ( ) = = typeid ( ref_by_id ) )
return update_spent_tx_flags_for_input ( boost : : get < ref_by_id > ( o ) . tx_id , boost : : get < ref_by_id > ( o ) . n , spent ) ;
else if ( o . type ( ) = = typeid ( uint64_t ) )
return update_spent_tx_flags_for_input ( amount , boost : : get < uint64_t > ( o ) , spent ) ;
LOG_ERROR ( " Unknown txout_v type " ) ;
return false ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_spent_tx_flags_for_input ( uint64_t amount , uint64_t global_index , bool spent )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint64_t outs_count = m_db_outputs . get_item_size ( amount ) ;
CHECK_AND_ASSERT_MES ( outs_count , false , " Amount " < < amount < < " have not found during update_spent_tx_flags_for_input() " ) ;
CHECK_AND_ASSERT_MES ( global_index < outs_count , false , " Global index " < < global_index < < " for amount " < < amount < < " bigger value than amount's vector size()= " < < outs_count ) ;
auto out_ptr = m_db_outputs . get_subitem ( amount , global_index ) ;
return update_spent_tx_flags_for_input ( out_ptr - > tx_id , out_ptr - > out_no , spent ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_spent_tx_flags_for_input ( const crypto : : hash & tx_id , size_t n , bool spent )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto tx_ptr = m_db_transactions . find ( tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " Can't find transaction id: " < < tx_id ) ;
transaction_chain_entry tce_local = * tx_ptr ;
CHECK_AND_ASSERT_MES ( n < tce_local . m_spent_flags . size ( ) , false , " Wrong input offset: " < < n < < " in transaction id: " < < tx_id ) ;
tce_local . m_spent_flags [ n ] = spent ;
m_db_transactions . set ( tx_id , tce_local ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_spent_tx_flags_for_input ( const crypto : : hash & multisig_id , uint64_t spent_height )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto ms_ptr = m_db_multisig_outs . find ( multisig_id ) ;
CHECK_AND_ASSERT_MES ( ms_ptr , false , " unable to find multisig_id " < < multisig_id ) ;
// update spent height at ms container
ms_output_entry msoe_local = * ms_ptr ;
if ( msoe_local . spent_height ! = 0 & & spent_height ! = 0 )
{
LOG_PRINT_YELLOW ( LOCATION_SS < < " : WARNING: ms out " < < multisig_id < < " was already marked as SPENT at height " < < msoe_local . spent_height < < " , new spent_height: " < < spent_height , LOG_LEVEL_0 ) ;
}
msoe_local . spent_height = spent_height ;
m_db_multisig_outs . set ( multisig_id , msoe_local ) ;
return update_spent_tx_flags_for_input ( ms_ptr - > tx_id , ms_ptr - > out_no , spent_height ! = 0 ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : has_multisig_output ( const crypto : : hash & multisig_id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return static_cast < bool > ( m_db_multisig_outs . find ( multisig_id ) ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : is_multisig_output_spent ( const crypto : : hash & multisig_id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto multisig_ptr = m_db_multisig_outs . find ( multisig_id ) ;
if ( ! multisig_ptr )
return false ; // there's no such output - treat as not spent
const crypto : : hash & source_tx_id = multisig_ptr - > tx_id ;
size_t ms_out_index = multisig_ptr - > out_no ; // index of multisig output in source tx
auto source_tx_ptr = m_db_transactions . find ( source_tx_id ) ;
CHECK_AND_ASSERT_MES ( source_tx_ptr , true , " Internal error: source tx not found for ms out " < < multisig_id < < " , ms out is treated as spent for safety " ) ;
CHECK_AND_ASSERT_MES ( ms_out_index < source_tx_ptr - > m_spent_flags . size ( ) , true , " Internal error: ms out " < < multisig_id < < " has incorrect index " < < ms_out_index < < " in source tx " < < source_tx_id < < " , ms out is treated as spent for safety " ) ;
return source_tx_ptr - > m_spent_flags [ ms_out_index ] ;
}
//------------------------------------------------------------------
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , uint64_t & starter_offset ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! qblock_ids . size ( ) /*|| !req.m_total_height*/ )
{
LOG_ERROR ( " Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()= " < < qblock_ids . size ( ) < < /*", m_height=" << req.m_total_height <<*/ " , dropping connection " ) ;
return false ;
}
//check genesis match
if ( qblock_ids . back ( ) ! = get_block_hash ( m_db_blocks [ 0 ] - > bl ) )
{
LOG_ERROR ( " Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " < < ENDL < < " id: "
< < string_tools : : pod_to_hex ( qblock_ids . back ( ) ) < < " , " < < ENDL < < " expected: " < < get_block_hash ( m_db_blocks [ 0 ] - > bl )
< < " , " < < ENDL < < " dropping connection " ) ;
return false ;
}
/* Figure out what blocks we should request to get state_normal */
for ( auto & bl : qblock_ids )
{
auto block_index_ptr = m_db_blocks_index . find ( bl ) ;
if ( block_index_ptr )
{
//we start to put block ids INCLUDING last known id, just to make other side be sure
starter_offset = * block_index_ptr ;
return true ;
}
}
LOG_ERROR ( " Internal error handling connection, can't find split point " ) ;
return false ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : block_difficulty ( size_t i ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( i < m_db_blocks . size ( ) , false , " wrong block index i = " < < i < < " at blockchain_storage::block_difficulty() " ) ;
return m_db_blocks [ i ] - > difficulty ;
}
//------------------------------------------------------------------
bool blockchain_storage : : forecast_difficulty ( std : : vector < std : : pair < uint64_t , wide_difficulty_type > > & out_height_2_diff_vector , bool pos ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
std : : vector < uint64_t > timestamps ;
std : : vector < wide_difficulty_type > cumulative_difficulties ;
uint64_t blocks_size = m_db_blocks . size ( ) ;
size_t count = 0 ;
uint64_t max_block_height_for_this_type = 0 ;
uint64_t min_block_height_for_this_type = UINT64_MAX ;
wide_difficulty_type last_block_diff_for_this_type = 0 ;
for ( uint64_t cur_ind = blocks_size - 1 ; cur_ind ! = 0 & & count < DIFFICULTY_WINDOW ; cur_ind - - )
{
auto beiptr = m_db_blocks [ cur_ind ] ;
bool is_pos_bl = is_pos_block ( beiptr - > bl ) ;
if ( pos ! = is_pos_bl )
continue ;
if ( max_block_height_for_this_type < beiptr - > height )
max_block_height_for_this_type = beiptr - > height ;
if ( min_block_height_for_this_type > beiptr - > height )
min_block_height_for_this_type = beiptr - > height ;
if ( last_block_diff_for_this_type = = 0 )
last_block_diff_for_this_type = beiptr - > difficulty ;
timestamps . push_back ( beiptr - > bl . timestamp ) ;
cumulative_difficulties . push_back ( beiptr - > cumulative_diff_precise ) ;
+ + count ;
}
if ( count ! = DIFFICULTY_WINDOW )
return false ;
const uint64_t target_seconds = pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET ;
const uint64_t avg_interval = std : : max ( static_cast < uint64_t > ( 1 ) , ( max_block_height_for_this_type - min_block_height_for_this_type ) / count ) ;
uint64_t height = max_block_height_for_this_type ;
out_height_2_diff_vector . clear ( ) ;
out_height_2_diff_vector . push_back ( std : : make_pair ( height , last_block_diff_for_this_type ) ) ; // the first element corresponds to the last block of this type
for ( size_t i = 0 ; i < DIFFICULTY_CUT ; + + i )
{
2019-07-19 18:39:00 +02:00
wide_difficulty_type diff = next_difficulty_1 ( timestamps , cumulative_difficulties , target_seconds ) ;
2018-12-27 18:50:45 +03:00
height + = avg_interval ;
out_height_2_diff_vector . push_back ( std : : make_pair ( height , diff ) ) ;
timestamps . pop_back ( ) ; // keep sorted in descending order
timestamps . insert ( timestamps . begin ( ) , timestamps . front ( ) + target_seconds ) ; // increment time so it won't be affected by sorting in next_difficulty
cumulative_difficulties . pop_back ( ) ;
cumulative_difficulties . insert ( cumulative_difficulties . begin ( ) , 0 ) ; // does not matter
}
return true ;
}
//------------------------------------------------------------------
size_t blockchain_storage : : get_current_sequence_factor ( bool pos ) const
{
size_t n = 0 ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint64_t sz = m_db_blocks . size ( ) ;
if ( ! sz )
return n ;
for ( uint64_t i = sz - 1 ; i ! = 0 ; - - i , n + + )
{
if ( pos ! = is_pos_block ( m_db_blocks [ i ] - > bl ) )
break ;
}
return n ;
}
//------------------------------------------------------------------
size_t blockchain_storage : : get_current_sequence_factor_for_alt ( alt_chain_type & alt_chain , bool pos , uint64_t connection_height ) const
{
size_t n = 0 ;
for ( auto it = alt_chain . rbegin ( ) ; it ! = alt_chain . rend ( ) ; it + + , n + + )
{
if ( pos ! = is_pos_block ( ( * it ) - > second . bl ) )
return n ;
}
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
for ( uint64_t h = connection_height - 1 ; h ! = 0 ; - - h , n + + )
{
if ( pos ! = is_pos_block ( m_db_blocks [ h ] - > bl ) )
{
return n ;
}
}
return n ;
}
//------------------------------------------------------------------
std : : string blockchain_storage : : get_blockchain_string ( uint64_t start_index , uint64_t end_index ) const
{
std : : stringstream ss ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( start_index > = m_db_blocks . size ( ) )
{
LOG_PRINT_L0 ( " Wrong starter index set: " < < start_index < < " , expected max index " < < m_db_blocks . size ( ) - 1 ) ;
return ss . str ( ) ;
}
for ( size_t i = start_index ; i ! = m_db_blocks . size ( ) & & i ! = end_index ; i + + )
{
ss < < ( is_pos_block ( m_db_blocks [ i ] - > bl ) ? " [PoS] " : " [PoW] " ) < < " h: " < < i < < " , timestamp: " < < m_db_blocks [ i ] - > bl . timestamp < < " ( " < < epee : : misc_utils : : get_time_str_v2 ( m_db_blocks [ i ] - > bl . timestamp ) < < " ) "
< < " , cumul_diff_adj: " < < m_db_blocks [ i ] - > cumulative_diff_adjusted
< < " , cumul_diff_pcs: " < < m_db_blocks [ i ] - > cumulative_diff_precise
< < " , cumul_size: " < < m_db_blocks [ i ] - > block_cumulative_size
< < " , id: " < < get_block_hash ( m_db_blocks [ i ] - > bl )
< < " , difficulty: " < < block_difficulty ( i ) < < " , nonce " < < m_db_blocks [ i ] - > bl . nonce < < " , tx_count " < < m_db_blocks [ i ] - > bl . tx_hashes . size ( ) < < ENDL ;
}
return ss . str ( ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : print_blockchain ( uint64_t start_index , uint64_t end_index ) const
{
//LOG_ERROR("NOT IMPLEMENTED YET");
LOG_PRINT_L1 ( " Current blockchain: " < < ENDL < < get_blockchain_string ( start_index , end_index ) ) ;
LOG_PRINT_L0 ( " Blockchain printed with log level 1 " ) ;
}
void blockchain_storage : : print_blockchain_with_tx ( uint64_t start_index , uint64_t end_index ) const
{
boost : : filesystem : : ofstream ss ;
ss . exceptions ( /*std::ifstream::failbit |*/ std : : ifstream : : badbit ) ;
2019-09-25 17:09:38 +03:00
ss . open ( epee : : string_encoding : : utf8_to_wstring ( log_space : : log_singletone : : get_default_log_folder ( ) + " /blockchain_with_tx.txt " ) ,
std : : ios_base : : binary | std : : ios_base : : out | std : : ios_base : : trunc ) ;
2018-12-27 18:50:45 +03:00
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( start_index > = m_db_blocks . size ( ) )
{
LOG_PRINT_L0 ( " Wrong starter index set: " < < start_index < < " , expected max index " < < m_db_blocks . size ( ) - 1 ) ;
return ;
}
for ( size_t i = start_index ; i ! = m_db_blocks . size ( ) & & i ! = end_index ; i + + )
{
ss < < ( is_pos_block ( m_db_blocks [ i ] - > bl ) ? " [PoS] " : " [PoW] " ) < < " h: " < < i < < " , timestamp: " < < m_db_blocks [ i ] - > bl . timestamp < < " ( " < < epee : : misc_utils : : get_time_str_v2 ( m_db_blocks [ i ] - > bl . timestamp ) < < " ) "
< < " , cumul_diff_adj: " < < m_db_blocks [ i ] - > cumulative_diff_adjusted
< < " , cumul_diff_pcs: " < < m_db_blocks [ i ] - > cumulative_diff_precise
< < " , cumul_size: " < < m_db_blocks [ i ] - > block_cumulative_size
< < " , id: " < < get_block_hash ( m_db_blocks [ i ] - > bl )
< < " , difficulty: " < < block_difficulty ( i ) < < " , nonce " < < m_db_blocks [ i ] - > bl . nonce < < " , tx_count " < < m_db_blocks [ i ] - > bl . tx_hashes . size ( ) < < ENDL ;
ss < < " [miner id]: " < < get_transaction_hash ( m_db_blocks [ i ] - > bl . miner_tx ) < < ENDL < < currency : : obj_to_json_str ( m_db_blocks [ i ] - > bl . miner_tx ) < < ENDL ;
for ( size_t j = 0 ; j ! = m_db_blocks [ i ] - > bl . tx_hashes . size ( ) ; j + + )
{
auto tx_it = m_db_transactions . find ( m_db_blocks [ i ] - > bl . tx_hashes [ j ] ) ;
if ( tx_it = = m_db_transactions . end ( ) )
{
LOG_ERROR ( " internal error: tx id " < < m_db_blocks [ i ] - > bl . tx_hashes [ j ] < < " not found in transactions index " ) ;
continue ;
}
ss < < " [id]: " < < m_db_blocks [ i ] - > bl . tx_hashes [ j ] < < ENDL < < currency : : obj_to_json_str ( tx_it - > tx ) < < ENDL ;
}
}
ss . close ( ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : print_blockchain_index ( ) const
{
LOG_ERROR ( " NOT IMPLEMENTED YET " ) ;
// std::stringstream ss;
// CRITICAL_REGION_LOCAL(m_blockchain_lock);
// BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_db_blocks_index)
// ss << "id\t\t" << v.first << " height" << v.second << ENDL << "";
//
// LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str());
}
//------------------------------------------------------------------
void blockchain_storage : : print_db_cache_perfeormance_data ( ) const
{
# define DB_CONTAINER_PERF_DATA_ENTRY(container_name) \
< < # container_name < < " : hit_percent: " < < m_db_blocks . get_performance_data ( ) . hit_percent . get_avg ( ) < < " %, " \
< < " read_cache: " < < container_name . get_performance_data ( ) . read_cache_microsec . get_avg ( ) \
< < " read_db: " < < container_name . get_performance_data ( ) . read_db_microsec . get_avg ( ) \
< < " upd_cache: " < < container_name . get_performance_data ( ) . update_cache_microsec . get_avg ( ) \
< < " write_cache: " < < container_name . get_performance_data ( ) . write_to_cache_microsec . get_avg ( ) \
< < " write_db: " < < container_name . get_performance_data ( ) . write_to_db_microsec . get_avg ( ) \
< < " native_db_set_t: " < < container_name . get_performance_data_native ( ) . backend_set_t_time . get_avg ( ) \
< < " native_db_set_pod: " < < container_name . get_performance_data_native ( ) . backend_set_pod_time . get_avg ( ) \
< < " native_db_seriz: " < < container_name . get_performance_data_native ( ) . set_serialize_t_time . get_avg ( )
LOG_PRINT_L0 ( " DB_PERFORMANCE_DATA: " < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_blocks ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_blocks_index ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_transactions ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_spent_keys ) < < ENDL
//DB_CONTAINER_PERF_DATA_ENTRY(m_db_outputs) << ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_multisig_outs ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_solo_options ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_aliases ) < < ENDL
DB_CONTAINER_PERF_DATA_ENTRY ( m_db_addr_to_alias ) < < ENDL
//DB_CONTAINER_PERF_DATA_ENTRY(m_db_per_block_gindex_incs) << ENDL
//DB_CONTAINER_PERF_DATA_ENTRY(m_tx_fee_median) << ENDL
) ;
}
//------------------------------------------------------------------
2019-08-08 17:20:47 +02:00
void blockchain_storage : : get_last_n_x_blocks ( uint64_t n , bool pos_blocks , std : : list < std : : shared_ptr < const block_extended_info > > & blocks ) const
{
uint64_t count = 0 ;
for ( uint64_t i = m_db_blocks . size ( ) - 1 ; i ! = 0 ; - - i )
{
auto block_ptr = m_db_blocks [ i ] ;
if ( is_pos_block ( block_ptr - > bl ) = = pos_blocks )
{
blocks . push_back ( block_ptr ) ;
+ + count ;
if ( count > = n )
break ;
}
}
}
//------------------------------------------------------------------
void blockchain_storage : : print_last_n_difficulty_numbers ( uint64_t n ) const
{
std : : stringstream ss ;
std : : list < std : : shared_ptr < const block_extended_info > > pos_blocks ;
std : : list < std : : shared_ptr < const block_extended_info > > pow_blocks ;
get_last_n_x_blocks ( n , true , pos_blocks ) ;
get_last_n_x_blocks ( n , false , pow_blocks ) ;
ss < < " PoS blocks difficulty: " < < ENDL ;
for ( auto & bl_ptr : pos_blocks )
{
2019-08-08 17:51:30 +02:00
ss < < bl_ptr - > difficulty < < ENDL ;
2019-08-08 17:20:47 +02:00
}
ss < < " PoW blocks difficulty: " < < ENDL ;
for ( auto & bl_ptr : pow_blocks )
{
2019-08-08 17:51:30 +02:00
ss < < bl_ptr - > difficulty < < ENDL ;
2019-08-08 17:20:47 +02:00
}
2019-08-08 17:33:15 +02:00
LOG_PRINT_L0 ( " LAST BLOCKS: " < < ss . str ( ) ) ;
2019-08-08 17:20:47 +02:00
}
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void blockchain_storage : : print_blockchain_outs_stat ( ) const
{
LOG_ERROR ( " NOT IMPLEMENTED YET " ) ;
// std::stringstream ss;
// CRITICAL_REGION_LOCAL(m_blockchain_lock);
// BOOST_FOREACH(const outputs_container::value_type& v, m_db_outputs)
// {
// const std::vector<std::pair<crypto::hash, size_t> >& vals = v.second;
// if (vals.size())
// {
// ss << "amount: " << print_money(v.first);
// uint64_t total_count = vals.size();
// uint64_t unused_count = 0;
// for (size_t i = 0; i != vals.size(); i++)
// {
// bool used = false;
// auto it_tx = m_db_transactions.find(vals[i].first);
// if (it_tx == m_db_transactions.end())
// {
// LOG_ERROR("Tx with id not found " << vals[i].first);
// }
// else
// {
// if (vals[i].second >= it_tx->second.m_spent_flags.size())
// {
// LOG_ERROR("Tx with id " << vals[i].first << " in global index have wrong entry in global index, offset in tx = " << vals[i].second
// << ", it_tx->second.m_spent_flags.size()=" << it_tx->second.m_spent_flags.size()
// << ", it_tx->second.tx.vin.size()=" << it_tx->second.tx.vin.size());
// }
// used = it_tx->second.m_spent_flags[vals[i].second];
//
// }
// if (!used)
// ++unused_count;
// }
// ss << "\t total: " << total_count << "\t unused: " << unused_count << ENDL;
// }
// }
// LOG_PRINT_L0("OUTS: " << ENDL << ss.str());
}
//------------------------------------------------------------------
void blockchain_storage : : print_blockchain_outs ( const std : : string & file ) const
{
LOG_ERROR ( " NOT IMPLEMENTED YET " ) ;
// std::stringstream ss;
// CRITICAL_REGION_LOCAL(m_blockchain_lock);
// BOOST_FOREACH(const outputs_container::value_type& v, m_db_outputs)
// {
// const std::vector<std::pair<crypto::hash, size_t> >& vals = v.second;
// if(vals.size())
// {
// ss << "amount: " << print_money(v.first) << ENDL;
// for (size_t i = 0; i != vals.size(); i++)
// {
// bool used = false;
// auto it_tx = m_db_transactions.find(vals[i].first);
// if (it_tx == m_db_transactions.end())
// {
// LOG_ERROR("Tx with id not found " << vals[i].first);
// }
// else
// {
// if (vals[i].second >= it_tx->second.m_spent_flags.size())
// {
// LOG_ERROR("Tx with id " << vals[i].first << " in global index have wrong entry in global index, offset in tx = " << vals[i].second
// << ", it_tx->second.m_spent_flags.size()=" << it_tx->second.m_spent_flags.size()
// << ", it_tx->second.tx.vin.size()=" << it_tx->second.tx.vin.size());
// }
// used = it_tx->second.m_spent_flags[vals[i].second];
// }
//
// ss << "\t" << vals[i].first << ": " << vals[i].second << ",used:" << used << ENDL;
// }
// }
// }
// if(file_io_utils::save_string_to_file(file, ss.str()))
// {
// LOG_PRINT_L0("Current outputs index writen to file: " << file);
// }else
// {
// LOG_PRINT_L0("Failed to write current outputs index to file: " << file);
// }
}
//------------------------------------------------------------------
//------------------------------------------------------------------
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , NOTIFY_RESPONSE_CHAIN_ENTRY : : request & resp ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! find_blockchain_supplement ( qblock_ids , resp . start_height ) )
return false ;
resp . total_height = get_current_blockchain_size ( ) ;
size_t count = 0 ;
block_context_info * pprevinfo = nullptr ;
size_t i = 0 ;
for ( i = resp . start_height ; i ! = m_db_blocks . size ( ) & & count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT ; i + + , count + + )
{
resp . m_block_ids . push_back ( block_context_info ( ) ) ;
if ( pprevinfo )
pprevinfo - > h = m_db_blocks [ i ] - > bl . prev_id ;
resp . m_block_ids . back ( ) . cumul_size = m_db_blocks [ i ] - > block_cumulative_size ;
pprevinfo = & resp . m_block_ids . back ( ) ;
}
if ( pprevinfo )
pprevinfo - > h = get_block_hash ( m_db_blocks [ - - i ] - > bl ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , std : : list < std : : pair < block , std : : list < transaction > > > & blocks , uint64_t & total_height , uint64_t & start_height , size_t max_count ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
blocks_direct_container blocks_direct ;
if ( ! find_blockchain_supplement ( qblock_ids , blocks_direct , total_height , start_height , max_count ) )
return false ;
for ( auto & bd : blocks_direct )
{
blocks . push_back ( std : : pair < block , std : : list < transaction > > ( ) ) ;
blocks . back ( ) . first = bd . first - > bl ;
for ( auto & tx_ptr : bd . second )
{
blocks . back ( ) . second . push_back ( tx_ptr - > tx ) ;
}
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , blocks_direct_container & blocks , uint64_t & total_height , uint64_t & start_height , size_t max_count ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( ! find_blockchain_supplement ( qblock_ids , start_height ) )
return false ;
total_height = get_current_blockchain_size ( ) ;
size_t count = 0 ;
for ( size_t i = start_height ; i ! = m_db_blocks . size ( ) & & count < max_count ; i + + , count + + )
{
blocks . resize ( blocks . size ( ) + 1 ) ;
blocks . back ( ) . first = m_db_blocks [ i ] ;
std : : list < crypto : : hash > mis ;
get_transactions_direct ( m_db_blocks [ i ] - > bl . tx_hashes , blocks . back ( ) . second , mis ) ;
2019-05-01 16:03:12 +02:00
CHECK_AND_ASSERT_MES ( ! mis . size ( ) , false , " internal error, block " < < get_block_hash ( m_db_blocks [ i ] - > bl ) < < " [ " < < i < < " ] contains missing transactions: " < < mis ) ;
2018-12-27 18:50:45 +03:00
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : add_block_as_invalid ( const block & bl , const crypto : : hash & h )
{
block_extended_info bei = AUTO_VAL_INIT ( bei ) ;
bei . bl = bl ;
return add_block_as_invalid ( bei , h ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : add_block_as_invalid ( const block_extended_info & bei , const crypto : : hash & h )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_invalid_blocks_lock ) ;
auto i_res = m_invalid_blocks . insert ( std : : map < crypto : : hash , block_extended_info > : : value_type ( h , bei ) ) ;
CHECK_AND_ASSERT_MES ( i_res . second , false , " at insertion invalid by tx returned status existed " ) ;
LOG_PRINT_L0 ( " BLOCK ADDED AS INVALID: " < < h < < ENDL < < " , prev_id= " < < bei . bl . prev_id < < " , m_invalid_blocks count= " < < m_invalid_blocks . size ( ) ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! " ) ;
return true ;
}
//------------------------------------------------------------------
void blockchain_storage : : inspect_blocks_index ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
LOG_PRINT_L0 ( " Started block index inspecting.... " ) ;
m_db_blocks_index . enumerate_items ( [ & ] ( uint64_t count , const crypto : : hash & id , uint64_t index )
{
CHECK_AND_ASSERT_MES ( index < m_db_blocks . size ( ) , true , " invalid index " < < index < < " (m_db_blocks.size()= " < < m_db_blocks . size ( ) < < " ) for id " < < id < < " " ) ;
crypto : : hash calculated_id = get_block_hash ( m_db_blocks [ index ] - > bl ) ;
CHECK_AND_ASSERT_MES ( id = = calculated_id , true , " ID MISSMATCH ON INDEX " < < index < < ENDL
< < " m_db_blocks_index keeps: " < < id < < ENDL
< < " referenced to block with id " < < calculated_id
) ;
return true ;
} ) ;
LOG_PRINT_L0 ( " Block index inspecting finished " ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : have_block ( const crypto : : hash & id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
if ( m_db_blocks_index . find ( id ) )
return true ;
{
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
if ( m_alternative_chains . count ( id ) )
return true ;
}
/*if(m_orphaned_blocks.get<by_id>().count(id))
return true ; */
/*if(m_orphaned_by_tx.count(id))
return true ; */
{
CRITICAL_REGION_LOCAL1 ( m_invalid_blocks_lock ) ;
if ( m_invalid_blocks . count ( id ) )
return true ;
}
return false ;
}
//------------------------------------------------------------------
bool blockchain_storage : : handle_block_to_main_chain ( const block & bl , block_verification_context & bvc )
{
crypto : : hash id = get_block_hash ( bl ) ;
return handle_block_to_main_chain ( bl , id , bvc ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : push_transaction_to_global_outs_index ( const transaction & tx , const crypto : : hash & tx_id , std : : vector < uint64_t > & global_indexes )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
size_t i = 0 ;
BOOST_FOREACH ( const auto & ot , tx . vout )
{
if ( ot . target . type ( ) = = typeid ( txout_to_key ) )
{
m_db_outputs . push_back_item ( ot . amount , global_output_entry : : construct ( tx_id , i ) ) ;
global_indexes . push_back ( m_db_outputs . get_item_size ( ot . amount ) - 1 ) ;
}
else if ( ot . target . type ( ) = = typeid ( txout_multisig ) )
{
crypto : : hash multisig_out_id = get_multisig_out_id ( tx , i ) ;
CHECK_AND_ASSERT_MES ( multisig_out_id ! = null_hash , false , " internal error during handling get_multisig_out_id() with tx id " < < tx_id ) ;
CHECK_AND_ASSERT_MES ( ! m_db_multisig_outs . find ( multisig_out_id ) , false , " Internal error: already have multisig_out_id " < < multisig_out_id < < " in multisig outs index " ) ;
m_db_multisig_outs . set ( multisig_out_id , ms_output_entry : : construct ( tx_id , i ) ) ;
global_indexes . push_back ( 0 ) ; // just stub to make other code easier
}
+ + i ;
}
return true ;
}
//------------------------------------------------------------------
size_t blockchain_storage : : get_total_transactions ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_transactions . size ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_outs ( uint64_t amount , std : : list < crypto : : public_key > & pkeys ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint64_t sz = m_db_outputs . get_item_size ( amount ) ;
if ( ! sz )
return true ;
for ( uint64_t i = 0 ; i ! = sz ; i + + )
{
auto out_entry_ptr = m_db_outputs . get_subitem ( amount , i ) ;
auto tx_ptr = m_db_transactions . find ( out_entry_ptr - > tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " transactions outs global index consistency broken: can't find tx " < < out_entry_ptr - > tx_id < < " in DB, for amount: " < < amount < < " , gindex: " < < i ) ;
CHECK_AND_ASSERT_MES ( tx_ptr - > tx . vout . size ( ) > out_entry_ptr - > out_no , false , " transactions outs global index consistency broken: index in tx_outx == " < < out_entry_ptr - > out_no < < " is greather than tx.vout size == " < < tx_ptr - > tx . vout . size ( ) < < " , for amount: " < < amount < < " , gindex: " < < i ) ;
CHECK_AND_ASSERT_MES ( tx_ptr - > tx . vout [ out_entry_ptr - > out_no ] . target . type ( ) = = typeid ( txout_to_key ) , false , " transactions outs global index consistency broken: out # " < < out_entry_ptr - > out_no < < " in tx " < < out_entry_ptr - > tx_id < < " has wrong type, for amount: " < < amount < < " , gindex: " < < i ) ;
pkeys . push_back ( boost : : get < txout_to_key > ( tx_ptr - > tx . vout [ out_entry_ptr - > out_no ] . target ) . key ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : pop_transaction_from_global_index ( const transaction & tx , const crypto : : hash & tx_id )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
size_t i = tx . vout . size ( ) - 1 ;
BOOST_REVERSE_FOREACH ( const auto & ot , tx . vout )
{
if ( ot . target . type ( ) = = typeid ( txout_to_key ) )
{
uint64_t sz = m_db_outputs . get_item_size ( ot . amount ) ;
CHECK_AND_ASSERT_MES ( sz , false , " transactions outs global index: empty index for amount: " < < ot . amount ) ;
auto back_item = m_db_outputs . get_subitem ( ot . amount , sz - 1 ) ;
CHECK_AND_ASSERT_MES ( back_item - > tx_id = = tx_id , false , " transactions outs global index consistency broken: tx id missmatch " ) ;
CHECK_AND_ASSERT_MES ( back_item - > out_no = = i , false , " transactions outs global index consistency broken: in transaction index missmatch " ) ;
m_db_outputs . pop_back_item ( ot . amount ) ;
//if (!it->second.size())
// m_db_outputs.erase(it);
}
else if ( ot . target . type ( ) = = typeid ( txout_multisig ) )
{
crypto : : hash multisig_out_id = get_multisig_out_id ( tx , i ) ;
CHECK_AND_ASSERT_MES ( multisig_out_id ! = null_hash , false , " internal error during handling get_multisig_out_id() with tx id " < < tx_id ) ;
bool res = m_db_multisig_outs . erase_validate ( multisig_out_id ) ;
CHECK_AND_ASSERT_MES ( res , false , " Internal error: multisig out not found, multisig_out_id " < < multisig_out_id < < " in multisig outs index " ) ;
}
- - i ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : unprocess_blockchain_tx_extra ( const transaction & tx )
{
tx_extra_info ei = AUTO_VAL_INIT ( ei ) ;
bool r = parse_and_validate_tx_extra ( tx , ei ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to validate transaction extra on unprocess_blockchain_tx_extra " ) ;
if ( ei . m_alias . m_alias . size ( ) )
{
r = pop_alias_info ( ei . m_alias ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to pop_alias_info " ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_alias_info ( const std : : string & alias , extra_alias_entry_base & info ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto al_ptr = m_db_aliases . find ( alias ) ;
if ( al_ptr )
{
if ( al_ptr - > size ( ) )
{
info = al_ptr - > back ( ) ;
return true ;
}
}
return false ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_aliases_count ( ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_aliases . size ( ) ;
}
//------------------------------------------------------------------
std : : string blockchain_storage : : get_alias_by_address ( const account_public_address & addr ) const
{
auto alias_ptr = m_db_addr_to_alias . find ( addr ) ;
if ( alias_ptr & & alias_ptr - > size ( ) )
{
return * ( alias_ptr - > begin ( ) ) ;
}
return " " ;
}
//------------------------------------------------------------------
bool blockchain_storage : : pop_alias_info ( const extra_alias_entry & ai )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( ai . m_alias . size ( ) , false , " empty name in pop_alias_info " ) ;
auto alias_history_ptr = m_db_aliases . find ( ai . m_alias ) ;
CHECK_AND_ASSERT_MES ( alias_history_ptr & & alias_history_ptr - > size ( ) , false , " empty name list in pop_alias_info " ) ;
auto addr_to_alias_ptr = m_db_addr_to_alias . find ( alias_history_ptr - > back ( ) . m_address ) ;
if ( addr_to_alias_ptr )
{
//update db
address_to_aliases_container : : t_value_type local_v = * addr_to_alias_ptr ;
auto it_in_set = local_v . find ( ai . m_alias ) ;
CHECK_AND_ASSERT_MES ( it_in_set ! = local_v . end ( ) , false , " it_in_set != it->second.end() validation failed " ) ;
local_v . erase ( it_in_set ) ;
if ( ! local_v . size ( ) )
{
//delete the whole record from db
m_db_addr_to_alias . erase ( alias_history_ptr - > back ( ) . m_address ) ;
}
else
{
//update db
m_db_addr_to_alias . set ( alias_history_ptr - > back ( ) . m_address , local_v ) ;
}
}
else
{
LOG_ERROR ( " In m_addr_to_alias not found " < < get_account_address_as_str ( alias_history_ptr - > back ( ) . m_address ) ) ;
}
aliases_container : : t_value_type local_alias_hist = * alias_history_ptr ;
local_alias_hist . pop_back ( ) ;
if ( local_alias_hist . size ( ) )
m_db_aliases . set ( ai . m_alias , local_alias_hist ) ;
else
m_db_aliases . erase ( ai . m_alias ) ;
if ( local_alias_hist . size ( ) )
{
address_to_aliases_container : : t_value_type local_copy = AUTO_VAL_INIT ( local_copy ) ;
auto set_ptr = m_db_addr_to_alias . get ( local_alias_hist . back ( ) . m_address ) ;
if ( set_ptr )
local_copy = * set_ptr ;
local_copy . insert ( ai . m_alias ) ;
m_db_addr_to_alias . set ( local_alias_hist . back ( ) . m_address , local_copy ) ;
}
2019-04-30 02:30:42 +02:00
LOG_PRINT_MAGENTA ( " [ALIAS_UNREGISTERED]: " < < ai . m_alias < < " : " < < get_account_address_as_str ( ai . m_address ) < < " -> " < < ( ! local_alias_hist . empty ( ) ? get_account_address_as_str ( local_alias_hist . back ( ) . m_address ) : " (available) " ) , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : put_alias_info ( const transaction & tx , extra_alias_entry & ai )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( ai . m_alias . size ( ) , false , " empty name in put_alias_info " ) ;
aliases_container : : t_value_type local_alias_history = AUTO_VAL_INIT ( local_alias_history ) ;
auto alias_history_ptr_ = m_db_aliases . get ( ai . m_alias ) ;
if ( alias_history_ptr_ )
local_alias_history = * alias_history_ptr_ ;
if ( ! local_alias_history . size ( ) )
{
//update alias entry in db
local_alias_history . push_back ( ai ) ;
m_db_aliases . set ( ai . m_alias , local_alias_history ) ;
//update addr-to-alias db entry
address_to_aliases_container : : t_value_type addr_to_alias_local = AUTO_VAL_INIT ( addr_to_alias_local ) ;
auto addr_to_alias_ptr_ = m_db_addr_to_alias . get ( local_alias_history . back ( ) . m_address ) ;
if ( addr_to_alias_ptr_ )
addr_to_alias_local = * addr_to_alias_ptr_ ;
addr_to_alias_local . insert ( ai . m_alias ) ;
m_db_addr_to_alias . set ( local_alias_history . back ( ) . m_address , addr_to_alias_local ) ;
//@@ remove get_tx_fee_median();
LOG_PRINT_MAGENTA ( " [ALIAS_REGISTERED]: " < < ai . m_alias < < " : " < < get_account_address_as_str ( ai . m_address ) < < " , fee median: " < < get_tx_fee_median ( ) , LOG_LEVEL_1 ) ;
rise_core_event ( CORE_EVENT_ADD_ALIAS , alias_info_to_rpc_alias_info ( ai ) ) ;
} else
{
//update procedure
CHECK_AND_ASSERT_MES ( ai . m_sign . size ( ) = = 1 , false , " alias " < < ai . m_alias < < " can't be update, wrong ai.m_sign.size() count: " < < ai . m_sign . size ( ) ) ;
//std::string signed_buff;
//make_tx_extra_alias_entry(signed_buff, ai, true);
std : : string old_address = currency : : get_account_address_as_str ( local_alias_history . back ( ) . m_address ) ;
bool r = crypto : : check_signature ( get_sign_buff_hash_for_alias_update ( ai ) , local_alias_history . back ( ) . m_address . m_spend_public_key , ai . m_sign . back ( ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to check signature, alias update failed. " < < ENDL
< < " alias: " < < ai . m_alias < < ENDL
< < " signed_buff_hash: " < < get_sign_buff_hash_for_alias_update ( ai ) < < ENDL
< < " public key: " < < local_alias_history . back ( ) . m_address . m_spend_public_key < < ENDL
< < " new_address: " < < get_account_address_as_str ( ai . m_address ) < < ENDL
< < " signature: " < < epee : : string_tools : : pod_to_hex ( ai . m_sign ) < < ENDL
< < " alias_history.size() = " < < local_alias_history . size ( ) ) ;
//update adr-to-alias db
auto addr_to_alias_ptr_ = m_db_addr_to_alias . find ( local_alias_history . back ( ) . m_address ) ;
if ( addr_to_alias_ptr_ )
{
address_to_aliases_container : : t_value_type addr_to_alias_local = * addr_to_alias_ptr_ ;
auto it_in_set = addr_to_alias_local . find ( ai . m_alias ) ;
if ( it_in_set = = addr_to_alias_local . end ( ) )
{
LOG_ERROR ( " it_in_set == it->second.end() " ) ;
}
else
{
addr_to_alias_local . erase ( it_in_set ) ;
}
if ( ! addr_to_alias_local . size ( ) )
m_db_addr_to_alias . erase ( local_alias_history . back ( ) . m_address ) ;
else
m_db_addr_to_alias . set ( local_alias_history . back ( ) . m_address , addr_to_alias_local ) ;
}
else
{
LOG_ERROR ( " Wrong m_addr_to_alias state: address not found " < < get_account_address_as_str ( local_alias_history . back ( ) . m_address ) ) ;
}
//update alias db
local_alias_history . push_back ( ai ) ;
m_db_aliases . set ( ai . m_alias , local_alias_history ) ;
//update addr_to_alias db
address_to_aliases_container : : t_value_type addr_to_alias_local2 = AUTO_VAL_INIT ( addr_to_alias_local2 ) ;
auto addr_to_alias_ptr_2 = m_db_addr_to_alias . get ( local_alias_history . back ( ) . m_address ) ;
if ( addr_to_alias_ptr_2 )
addr_to_alias_local2 = * addr_to_alias_ptr_2 ;
addr_to_alias_local2 . insert ( ai . m_alias ) ;
m_db_addr_to_alias . set ( local_alias_history . back ( ) . m_address , addr_to_alias_local2 ) ;
2019-04-30 02:30:42 +02:00
LOG_PRINT_MAGENTA ( " [ALIAS_UPDATED]: " < < ai . m_alias < < " : from: " < < old_address < < " to " < < get_account_address_as_str ( ai . m_address ) , LOG_LEVEL_1 ) ;
2018-12-27 18:50:45 +03:00
rise_core_event ( CORE_EVENT_UPDATE_ALIAS , alias_info_to_rpc_update_alias_info ( ai , old_address ) ) ;
}
return true ;
}
//------------------------------------------------------------------
void blockchain_storage : : set_event_handler ( i_core_event_handler * event_handler ) const
{
if ( event_handler = = nullptr )
{
m_event_handler = & m_event_handler_stub ;
m_services_mgr . set_event_handler ( nullptr ) ;
}
else
{
m_event_handler = event_handler ;
m_services_mgr . set_event_handler ( event_handler ) ;
}
}
//------------------------------------------------------------------
i_core_event_handler * blockchain_storage : : get_event_handler ( ) const
{
return m_event_handler ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : validate_alias_reward ( const transaction & tx , const std : : string & alias ) const
{
//validate alias coast
uint64_t fee_for_alias = get_alias_coast ( alias ) ;
//validate the price had been paid
uint64_t found_alias_reward = get_amount_for_zero_pubkeys ( tx ) ;
//@#@
//work around for net 68's generation
# if CURRENCY_FORMATION_VERSION == 68
if ( alias = = " bhrfrrrtret " & & get_transaction_hash ( tx ) = = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " 760b85546678d2235a1843e18d8a016a2e4d9b8273cc4d7c09bebff1f6fa7eaf " ) )
return true ;
if ( alias = = " test-420 " & & get_transaction_hash ( tx ) = = epee : : string_tools : : parse_tpod_from_hex_string < crypto : : hash > ( " 10f8a2539b2551bd0919bf7e3b1dfbae7553eca63e58cd2264ae60f90030edf8 " ) )
return true ;
# endif
CHECK_AND_ASSERT_MES ( found_alias_reward > = fee_for_alias , false , " registration of alias ' "
< < alias < < " ' goes with a reward of " < < print_money ( found_alias_reward ) < < " which is less than expected: " < < print_money ( fee_for_alias )
< < " (fee median: " < < get_tx_fee_median ( ) < < " ) "
< < " , tx: " < < get_transaction_hash ( tx ) ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : prevalidate_alias_info ( const transaction & tx , extra_alias_entry & eae )
{
bool r = validate_alias_name ( eae . m_alias ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to validate alias name! " ) ;
bool already_have_alias = false ;
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto existing_ptr = m_db_aliases . find ( eae . m_alias ) ;
if ( existing_ptr & & existing_ptr - > size ( ) )
{
already_have_alias = true ;
}
}
//auto alias_history_ptr_ = m_db_aliases.get(eae.m_alias);
if ( ! already_have_alias )
{
bool r = validate_alias_reward ( tx , eae . m_alias ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to validate_alias_reward " ) ;
if ( eae . m_alias . size ( ) < ALIAS_MINIMUM_PUBLIC_SHORT_NAME_ALLOWED )
{
//short alias name, this aliases should be issued only by specific authority
CHECK_AND_ASSERT_MES ( eae . m_sign . size ( ) = = 1 , false , " alias " < < eae . m_alias < < " can't be update, wrong ai.m_sign.size() count: " < < eae . m_sign . size ( ) ) ;
bool r = crypto : : check_signature ( get_sign_buff_hash_for_alias_update ( eae ) , m_core_runtime_config . alias_validation_pubkey , eae . m_sign . back ( ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to check signature, short alias registration failed. " < < ENDL
< < " alias: " < < eae . m_alias < < ENDL
< < " signed_buff_hash: " < < get_sign_buff_hash_for_alias_update ( eae ) < < ENDL
< < " public key: " < < m_core_runtime_config . alias_validation_pubkey < < ENDL
< < " new_address: " < < get_account_address_as_str ( eae . m_address ) < < ENDL
< < " signature: " < < epee : : string_tools : : pod_to_hex ( eae . m_sign ) ) ;
}
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : process_blockchain_tx_extra ( const transaction & tx )
{
//check transaction extra
tx_extra_info ei = AUTO_VAL_INIT ( ei ) ;
bool r = parse_and_validate_tx_extra ( tx , ei ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to validate transaction extra " ) ;
if ( ei . m_alias . m_alias . size ( ) )
{
r = prevalidate_alias_info ( tx , ei . m_alias ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to prevalidate_alias_info " ) ;
r = put_alias_info ( tx , ei . m_alias ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to put_alias_info " ) ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_outs_index_stat ( outs_index_stat & outs_stat ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
outs_stat . amount_0_001 = m_db_outputs . get_item_size ( COIN / 1000 ) ;
outs_stat . amount_0_01 = m_db_outputs . get_item_size ( COIN / 100 ) ;
outs_stat . amount_0_1 = m_db_outputs . get_item_size ( COIN / 10 ) ;
outs_stat . amount_1 = m_db_outputs . get_item_size ( COIN ) ;
outs_stat . amount_10 = m_db_outputs . get_item_size ( COIN * 10 ) ;
outs_stat . amount_100 = m_db_outputs . get_item_size ( COIN * 100 ) ;
outs_stat . amount_1000 = m_db_outputs . get_item_size ( COIN * 1000 ) ;
outs_stat . amount_10000 = m_db_outputs . get_item_size ( COIN * 10000 ) ;
outs_stat . amount_100000 = m_db_outputs . get_item_size ( COIN * 100000 ) ;
outs_stat . amount_1000000 = m_db_outputs . get_item_size ( COIN * 1000000 ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : process_blockchain_tx_attachments ( const transaction & tx , uint64_t h , const crypto : : hash & bl_id , uint64_t timestamp )
{
//check transaction extra
uint64_t count = 0 ;
for ( const auto & at : tx . attachment )
{
if ( at . type ( ) = = typeid ( tx_service_attachment ) )
{
m_services_mgr . handle_entry_push ( boost : : get < tx_service_attachment > ( at ) , count , tx , h , bl_id , timestamp ) ; //handle service
+ + count ;
}
}
return true ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_tx_fee_median ( ) const
{
uint64_t h = m_db_blocks . size ( ) ;
if ( m_current_fee_median_effective_index ! = get_tx_fee_median_effective_index ( h ) )
{
m_current_fee_median = tx_fee_median_for_height ( h ) ;
m_current_fee_median_effective_index = get_tx_fee_median_effective_index ( h ) ;
}
if ( ! m_current_fee_median )
m_current_fee_median = ALIAS_VERY_INITAL_COAST ;
return m_current_fee_median ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_alias_coast ( const std : : string & alias ) const
{
uint64_t median_fee = get_tx_fee_median ( ) ;
//CHECK_AND_ASSERT_MES_NO_RET(median_fee, "can't calculate median");
if ( ! median_fee )
median_fee = ALIAS_VERY_INITAL_COAST ;
return get_alias_coast_from_fee ( alias , median_fee ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : unprocess_blockchain_tx_attachments ( const transaction & tx , uint64_t h , uint64_t timestamp )
{
size_t cnt_serv_attach = get_service_attachments_count_in_tx ( tx ) ;
if ( cnt_serv_attach = = 0 )
return true ;
- - cnt_serv_attach ;
for ( auto it = tx . attachment . rbegin ( ) ; it ! = tx . attachment . rend ( ) ; it + + )
{
auto & at = * it ;
if ( at . type ( ) = = typeid ( tx_service_attachment ) )
{
m_services_mgr . handle_entry_pop ( boost : : get < tx_service_attachment > ( at ) , cnt_serv_attach , tx , h , timestamp ) ;
- - cnt_serv_attach ;
}
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_tx_service_attachmens_in_services ( const tx_service_attachment & a , size_t i , const transaction & tx ) const
{
return m_services_mgr . validate_entry ( a , i , tx ) ;
}
//------------------------------------------------------------------
namespace currency
{
struct add_transaction_input_visitor : public boost : : static_visitor < bool >
{
blockchain_storage & m_bcs ;
blockchain_storage : : key_images_container & m_db_spent_keys ;
const crypto : : hash & m_tx_id ;
const crypto : : hash & m_bl_id ;
const uint64_t m_bl_height ;
add_transaction_input_visitor ( blockchain_storage & bcs , blockchain_storage : : key_images_container & m_db_spent_keys , const crypto : : hash & tx_id , const crypto : : hash & bl_id , const uint64_t bl_height ) :
m_bcs ( bcs ) ,
m_db_spent_keys ( m_db_spent_keys ) ,
m_tx_id ( tx_id ) ,
m_bl_id ( bl_id ) ,
m_bl_height ( bl_height )
{ }
bool operator ( ) ( const txin_to_key & in ) const
{
const crypto : : key_image & ki = in . k_image ;
auto ki_ptr = m_db_spent_keys . get ( ki ) ;
if ( ki_ptr )
{
//double spend detected
LOG_PRINT_RED_L0 ( " tx with id: " < < m_tx_id < < " in block id: " < < m_bl_id < < " have input marked as spent with key image: " < < ki < < " , block declined " ) ;
return false ;
}
m_db_spent_keys . set ( ki , m_bl_height ) ;
if ( in . key_offsets . size ( ) = = 1 )
{
//direct spend detected
if ( ! m_bcs . update_spent_tx_flags_for_input ( in . amount , in . key_offsets [ 0 ] , true ) )
{
//internal error
LOG_PRINT_RED_L0 ( " Failed to update_spent_tx_flags_for_input " ) ;
return false ;
}
}
return true ;
}
bool operator ( ) ( const txin_gen & in ) const { return true ; }
bool operator ( ) ( const txin_multisig & in ) const
{
//mark out as spent
if ( ! m_bcs . update_spent_tx_flags_for_input ( in . multisig_out_id , m_bl_height ) )
{
//internal error
LOG_PRINT_RED_L0 ( " Failed to update_spent_tx_flags_for_input " ) ;
return false ;
}
return true ;
}
} ;
}
bool blockchain_storage : : add_transaction_from_block ( const transaction & tx , const crypto : : hash & tx_id , const crypto : : hash & bl_id , uint64_t bl_height , uint64_t timestamp )
{
bool need_to_profile = ! is_coinbase ( tx ) ;
TIME_MEASURE_START_PD ( tx_append_rl_wait ) ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_append_rl_wait ) ;
TIME_MEASURE_START_PD ( tx_append_is_expired ) ;
CHECK_AND_ASSERT_MES ( ! is_tx_expired ( tx ) , false , " Transaction can't be added to the blockchain since it's already expired. tx expiration time: " < < get_tx_expiration_time ( tx ) < < " , blockchain median time: " < < get_tx_expiration_median ( ) ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_append_is_expired ) ;
2019-08-07 18:41:18 +03:00
CHECK_AND_ASSERT_MES ( validate_tx_for_hardfork_specific_terms ( tx , tx_id , bl_height ) , false , " tx " < < tx_id < < " : hardfork-specific validation failed " ) ;
2018-12-27 18:50:45 +03:00
TIME_MEASURE_START_PD ( tx_process_extra ) ;
bool r = process_blockchain_tx_extra ( tx ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to process_blockchain_tx_extra " ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_process_extra ) ;
TIME_MEASURE_START_PD ( tx_process_attachment ) ;
process_blockchain_tx_attachments ( tx , bl_height , bl_id , timestamp ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_process_attachment ) ;
TIME_MEASURE_START_PD ( tx_process_inputs ) ;
2019-08-07 08:03:44 +03:00
for ( const txin_v & in : tx . vin )
2018-12-27 18:50:45 +03:00
{
if ( ! boost : : apply_visitor ( add_transaction_input_visitor ( * this , m_db_spent_keys , tx_id , bl_id , bl_height ) , in ) )
{
LOG_ERROR ( " critical internal error: add_transaction_input_visitor failed. but key_images should be already checked " ) ;
purge_transaction_keyimages_from_blockchain ( tx , false ) ;
bool r = unprocess_blockchain_tx_extra ( tx ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to unprocess_blockchain_tx_extra " ) ;
unprocess_blockchain_tx_attachments ( tx , bl_height , timestamp ) ;
return false ;
}
}
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_process_inputs ) ;
//check if there is already transaction with this hash
TIME_MEASURE_START_PD ( tx_check_exist ) ;
auto tx_entry_ptr = m_db_transactions . get ( tx_id ) ;
if ( tx_entry_ptr )
{
LOG_ERROR ( " critical internal error: tx with id: " < < tx_id < < " in block id: " < < bl_id < < " already in blockchain " ) ;
purge_transaction_keyimages_from_blockchain ( tx , true ) ;
bool r = unprocess_blockchain_tx_extra ( tx ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to unprocess_blockchain_tx_extra " ) ;
unprocess_blockchain_tx_attachments ( tx , bl_height , timestamp ) ;
return false ;
}
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_check_exist ) ;
TIME_MEASURE_START_PD ( tx_push_global_index ) ;
transaction_chain_entry ch_e ;
ch_e . m_keeper_block_height = bl_height ;
ch_e . m_spent_flags . resize ( tx . vout . size ( ) , false ) ;
ch_e . tx = tx ;
r = push_transaction_to_global_outs_index ( tx , tx_id , ch_e . m_global_output_indexes ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to return push_transaction_to_global_outs_index tx id " < < tx_id ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_push_global_index ) ;
//store everything to db
TIME_MEASURE_START_PD ( tx_store_db ) ;
m_db_transactions . set ( tx_id , ch_e ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_store_db ) ;
TIME_MEASURE_START_PD ( tx_print_log ) ;
LOG_PRINT_L1 ( " Added tx to blockchain: " < < tx_id < < " via block at " < < bl_height < < " id " < < print16 ( bl_id )
< < " , ins: " < < tx . vin . size ( ) < < " , outs: " < < tx . vout . size ( ) < < " , outs sum: " < < print_money_brief ( get_outs_money_amount ( tx ) ) < < " (fee: " < < ( is_coinbase ( tx ) ? " 0[coinbase] " : print_money_brief ( get_tx_fee ( tx ) ) ) < < " ) " ) ;
TIME_MEASURE_FINISH_PD_COND ( need_to_profile , tx_print_log ) ;
//@#@ del me
// LOG_PRINT_L0("APPEND_TX_TIME_INNER: " << m_performance_data.tx_append_rl_wait.get_last_val()
// << " | " << m_performance_data.tx_append_is_expired.get_last_val()
// << " | " << m_performance_data.tx_process_extra.get_last_val()
// << " | " << m_performance_data.tx_process_attachment.get_last_val()
// << " | " << m_performance_data.tx_process_inputs.get_last_val()
// << " | " << m_performance_data.tx_check_exist.get_last_val()
// << " | " << m_performance_data.tx_push_global_index.get_last_val()
// << " | " << m_performance_data.tx_store_db.get_last_val()
// << " | " << m_performance_data.tx_print_log.get_last_val()
// );
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_tx_outputs_gindexs ( const crypto : : hash & tx_id , std : : vector < uint64_t > & indexs ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto tx_ptr = m_db_transactions . find ( tx_id ) ;
if ( ! tx_ptr )
{
LOG_PRINT_RED_L0 ( " warning: get_tx_outputs_gindexs failed to find transaction with id = " < < tx_id ) ;
return false ;
}
CHECK_AND_ASSERT_MES ( tx_ptr - > m_global_output_indexes . size ( ) , false , " internal error: global indexes for transaction " < < tx_id < < " is empty " ) ;
indexs = tx_ptr - > m_global_output_indexes ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : check_tx_inputs ( const transaction & tx , const crypto : : hash & tx_prefix_hash , uint64_t & max_used_block_height , crypto : : hash & max_used_block_id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
2019-07-22 23:54:38 +02:00
bool res = check_tx_inputs ( tx , tx_prefix_hash , max_used_block_height ) ;
2018-12-27 18:50:45 +03:00
if ( ! res ) return false ;
CHECK_AND_ASSERT_MES ( max_used_block_height < m_db_blocks . size ( ) , false , " internal error: max used block index= " < < max_used_block_height < < " is not less then blockchain size = " < < m_db_blocks . size ( ) ) ;
get_block_hash ( m_db_blocks [ max_used_block_height ] - > bl , max_used_block_id ) ;
return true ;
}
//------------------------------------------------------------------
# define PERIOD_DISABLED 0xffffffffffffffffLL
bool blockchain_storage : : rebuild_tx_fee_medians ( )
{
uint64_t sz = m_db_blocks . size ( ) ;
m_db . begin_transaction ( ) ;
LOG_PRINT_L0 ( " Started reinitialization of median fee... " ) ;
math_helper : : once_a_time_seconds < 10 > log_idle ;
epee : : misc_utils : : median_helper < uint64_t , uint64_t > blocks_median ;
for ( uint64_t i = 0 ; i ! = sz ; i + + )
{
log_idle . do_call ( [ & ] ( ) { std : : cout < < " block " < < i < < " of " < < sz < < std : : endl ; return true ; } ) ;
auto bptr = m_db_blocks [ i ] ;
block_extended_info new_bei = * bptr ;
//assign effective median
new_bei . effective_tx_fee_median = blocks_median . get_median ( ) ;
//calculate current median for this particular block
std : : vector < uint64_t > fees ;
for ( auto & h : new_bei . bl . tx_hashes )
{
auto txptr = get_tx_chain_entry ( h ) ;
CHECK_AND_ASSERT_MES ( txptr , false , " failed to find tx id " < < h < < " from block " < < i ) ;
fees . push_back ( get_tx_fee ( txptr - > tx ) ) ;
}
new_bei . this_block_tx_fee_median = epee : : misc_utils : : median ( fees ) ;
m_db_blocks . set ( i , new_bei ) ;
//prepare median helper for next block
if ( new_bei . this_block_tx_fee_median )
blocks_median . push_item ( new_bei . this_block_tx_fee_median , i ) ;
//create callbacks
bool is_pos_allowed_l = i > = m_core_runtime_config . pos_minimum_heigh ;
uint64_t period = is_pos_allowed_l ? ALIAS_COAST_PERIOD : ALIAS_COAST_PERIOD / 2 ;
if ( period > = i + 1 )
continue ;
uint64_t starter_block_index = i + 1 - period ;
uint64_t purge_recent_period = is_pos_allowed_l ? ALIAS_COAST_RECENT_PERIOD : ALIAS_COAST_RECENT_PERIOD / 2 ;
uint64_t purge_recent_block_index = 0 ;
if ( purge_recent_period > = i + 1 )
purge_recent_block_index = PERIOD_DISABLED ;
else
purge_recent_block_index = i + 1 - purge_recent_period ;
auto cb = [ & ] ( uint64_t fee , uint64_t height )
{
if ( height > = starter_block_index )
return true ;
return false ;
} ;
auto cb_final_eraser = [ & ] ( uint64_t fee , uint64_t height )
{
if ( purge_recent_block_index = = PERIOD_DISABLED )
return true ;
if ( height > = purge_recent_block_index )
return true ;
return false ;
} ;
blocks_median . scan_items ( cb , cb_final_eraser ) ;
}
m_db . commit_transaction ( ) ;
LOG_PRINT_L0 ( " Reinitialization of median fee finished! " )
return true ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_tx_fee_median_effective_index ( uint64_t h ) const
{
if ( h < = ALIAS_MEDIAN_RECALC_INTERWAL + 1 )
return 0 ;
h - = 1 ; // for transactions of block that h%ALIAS_MEDIAN_RECALC_INTERWAL==0 we still handle fee median from previous day
return h - ( h % ALIAS_MEDIAN_RECALC_INTERWAL ) ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : tx_fee_median_for_height ( uint64_t h ) const
{
uint64_t effective_index = get_tx_fee_median_effective_index ( h ) ;
CHECK_AND_ASSERT_THROW_MES ( effective_index < m_db_blocks . size ( ) , " internal error: effective_index= " < < effective_index < < " while m_db_blocks.size()= " < < m_db_blocks . size ( ) ) ;
return m_db_blocks [ effective_index ] - > effective_tx_fee_median ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_all_aliases_for_new_median_mode ( )
{
2019-07-05 19:45:57 +02:00
LOG_PRINT_L0 ( " Started reinitialization of median fee... " ) ;
2018-12-27 18:50:45 +03:00
math_helper : : once_a_time_seconds < 10 > log_idle ;
uint64_t sz = m_db_blocks . size ( ) ;
for ( uint64_t i = 0 ; i ! = sz ; i + + )
{
log_idle . do_call ( [ & ] ( ) { std : : cout < < " block " < < i < < " of " < < sz < < std : : endl ; return true ; } ) ;
auto bptr = m_db_blocks [ i ] ;
for ( auto & tx_id : bptr - > bl . tx_hashes )
{
auto tx_ptr = m_db_transactions . find ( tx_id ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " Internal error: tx " < < tx_id < < " from block " < < i < < " not found " ) ;
tx_extra_info tei = AUTO_VAL_INIT ( tei ) ;
bool r = parse_and_validate_tx_extra ( tx_ptr - > tx , tei ) ;
CHECK_AND_ASSERT_MES ( r , false , " Internal error: tx " < < tx_id < < " from block " < < i < < " was failed to parse " ) ;
if ( tei . m_alias . m_alias . size ( ) & & ! tei . m_alias . m_sign . size ( ) )
{
//reward
//validate alias coast
uint64_t median_fee = tx_fee_median_for_height ( i ) ;
uint64_t fee_for_alias = get_alias_coast_from_fee ( tei . m_alias . m_alias , median_fee ) ;
//validate the price had been paid
uint64_t found_alias_reward = get_amount_for_zero_pubkeys ( tx_ptr - > tx ) ;
if ( found_alias_reward < fee_for_alias )
{
LOG_PRINT_RED_L0 ( " [ " < < i < < " ]Found collision on alias: " < < tei . m_alias . m_alias
< < " , expected fee: " < < print_money ( fee_for_alias ) < < " (median: " < < print_money ( median_fee ) < < " ) "
< < " found reward: " < < print_money ( found_alias_reward ) < < " . tx_id: " < < tx_id ) ;
}
}
}
}
LOG_PRINT_L0 ( " Finished. " ) ;
return true ;
}
//------------------------------------------------------------------
2019-07-05 19:45:57 +02:00
bool blockchain_storage : : print_tx_outputs_lookup ( const crypto : : hash & tx_id ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto tx_ptr = m_db_transactions . find ( tx_id ) ;
if ( ! tx_ptr )
{
LOG_PRINT_RED_L0 ( " Tx " < < tx_id < < " not found " ) ;
return true ;
}
//amount -> index -> [{tx_id, rind_count}]
std : : map < uint64_t , std : : map < uint64_t , std : : list < std : : pair < crypto : : hash , uint64_t > > > > usage_stat ;
2019-07-09 19:22:57 +02:00
std : : stringstream strm_tx ;
2019-07-05 19:45:57 +02:00
CHECK_AND_ASSERT_MES ( tx_ptr - > tx . vout . size ( ) = = tx_ptr - > m_global_output_indexes . size ( ) , false , " Internal error: output size missmatch " ) ;
for ( uint64_t i = 0 ; i ! = tx_ptr - > tx . vout . size ( ) ; i + + )
{
2019-07-09 19:22:57 +02:00
strm_tx < < " [ " < < i < < " ]: " < < print_money ( tx_ptr - > tx . vout [ i ] . amount ) < < ENDL ;
2019-07-05 19:45:57 +02:00
if ( tx_ptr - > tx . vout [ i ] . target . type ( ) ! = typeid ( currency : : txout_to_key ) )
continue ;
usage_stat [ tx_ptr - > tx . vout [ i ] . amount ] [ tx_ptr - > m_global_output_indexes [ i ] ] ;
}
LOG_PRINT_L0 ( " Lookup in all transactions.... " ) ;
for ( uint64_t i = 0 ; i ! = m_db_blocks . size ( ) ; i + + )
{
auto block_ptr = m_db_blocks [ i ] ;
for ( auto block_tx_id : block_ptr - > bl . tx_hashes )
{
auto block_tx_ptr = m_db_transactions . find ( block_tx_id ) ;
for ( auto txi_in : block_tx_ptr - > tx . vin )
{
if ( txi_in . type ( ) ! = typeid ( currency : : txin_to_key ) )
continue ;
currency : : txin_to_key & txi_in_tokey = boost : : get < currency : : txin_to_key > ( txi_in ) ;
uint64_t amount = txi_in_tokey . amount ;
auto amount_it = usage_stat . find ( amount ) ;
if ( amount_it = = usage_stat . end ( ) )
continue ;
for ( txout_v & off : txi_in_tokey . key_offsets )
{
if ( off . type ( ) ! = typeid ( uint64_t ) )
continue ;
uint64_t index = boost : : get < uint64_t > ( off ) ;
auto index_it = amount_it - > second . find ( index ) ;
if ( index_it = = amount_it - > second . end ( ) )
continue ;
index_it - > second . push_back ( std : : pair < crypto : : hash , uint64_t > ( block_tx_id , txi_in_tokey . key_offsets . size ( ) ) ) ;
}
}
}
}
std : : stringstream ss ;
for ( auto & amount : usage_stat )
{
for ( auto & index : amount . second )
{
ss < < " [ " < < print_money ( amount . first ) < < " : " < < index . first < < " ] " < < ENDL ;
for ( auto & list_entry : index . second )
{
ss < < " " < < list_entry . first < < " : " < < list_entry . second < < ENDL ;
}
}
}
2019-07-09 19:22:57 +02:00
LOG_PRINT_L0 ( " Results: " < < ENDL < < strm_tx . str ( ) < < ENDL < < ss . str ( ) ) ;
2019-07-05 19:45:57 +02:00
return true ;
}
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
bool blockchain_storage : : have_tx_keyimges_as_spent ( const transaction & tx ) const
{
// check all tx's inputs for being already spent
for ( const txin_v & in : tx . vin )
{
if ( in . type ( ) = = typeid ( txin_to_key ) )
{
if ( have_tx_keyimg_as_spent ( boost : : get < const txin_to_key > ( in ) . k_image ) )
2019-04-25 01:41:49 +02:00
{
2018-12-27 18:50:45 +03:00
return true ;
2019-04-25 01:41:49 +02:00
}
2018-12-27 18:50:45 +03:00
}
else if ( in . type ( ) = = typeid ( txin_multisig ) )
{
if ( is_multisig_output_spent ( boost : : get < const txin_multisig > ( in ) . multisig_out_id ) )
return true ;
}
else if ( in . type ( ) = = typeid ( txin_gen ) )
{
// skip txin_gen
}
else
{
LOG_ERROR ( " Unexpected input type: " < < in . type ( ) . name ( ) ) ;
}
}
return false ;
}
//------------------------------------------------------------------
2019-07-24 00:37:24 +02:00
bool blockchain_storage : : check_tx_inputs ( const transaction & tx , const crypto : : hash & tx_prefix_hash ) const
{
uint64_t stub = 0 ;
return check_tx_inputs ( tx , tx_prefix_hash , stub ) ;
}
//------------------------------------------------------------------
2019-07-22 23:54:38 +02:00
bool blockchain_storage : : check_tx_inputs ( const transaction & tx , const crypto : : hash & tx_prefix_hash , uint64_t & max_used_block_height ) const
2018-12-27 18:50:45 +03:00
{
size_t sig_index = 0 ;
2019-07-22 23:54:38 +02:00
max_used_block_height = 0 ;
2018-12-27 18:50:45 +03:00
std : : vector < crypto : : signature > sig_stub ;
const std : : vector < crypto : : signature > * psig = & sig_stub ;
TIME_MEASURE_START_PD ( tx_check_inputs_loop ) ;
BOOST_FOREACH ( const auto & txin , tx . vin )
{
if ( ! m_is_in_checkpoint_zone )
{
CHECK_AND_ASSERT_MES ( sig_index < tx . signatures . size ( ) , false , " Wrong transaction: missing signature entry for input # " < < sig_index < < " tx: " < < tx_prefix_hash ) ;
psig = & tx . signatures [ sig_index ] ;
}
if ( txin . type ( ) = = typeid ( txin_to_key ) )
{
const txin_to_key & in_to_key = boost : : get < txin_to_key > ( txin ) ;
CHECK_AND_ASSERT_MES ( in_to_key . key_offsets . size ( ) , false , " Empty in_to_key.key_offsets for input # " < < sig_index < < " tx: " < < tx_prefix_hash ) ;
TIME_MEASURE_START_PD ( tx_check_inputs_loop_kimage_check ) ;
if ( have_tx_keyimg_as_spent ( in_to_key . k_image ) )
{
LOG_ERROR ( " Key image was already spent in blockchain: " < < string_tools : : pod_to_hex ( in_to_key . k_image ) < < " for input # " < < sig_index < < " tx: " < < tx_prefix_hash ) ;
return false ;
}
TIME_MEASURE_FINISH_PD ( tx_check_inputs_loop_kimage_check ) ;
2019-07-22 23:54:38 +02:00
uint64_t max_unlock_time = 0 ;
if ( ! check_tx_input ( tx , sig_index , in_to_key , tx_prefix_hash , * psig , max_used_block_height , max_unlock_time ) )
2018-12-27 18:50:45 +03:00
{
LOG_ERROR ( " Failed to validate input # " < < sig_index < < " tx: " < < tx_prefix_hash ) ;
return false ;
}
}
else if ( txin . type ( ) = = typeid ( txin_multisig ) )
{
const txin_multisig & in_ms = boost : : get < txin_multisig > ( txin ) ;
2019-07-22 23:54:38 +02:00
if ( ! check_tx_input ( tx , sig_index , in_ms , tx_prefix_hash , * psig , max_used_block_height ) )
2018-12-27 18:50:45 +03:00
{
LOG_ERROR ( " Failed to validate multisig input # " < < sig_index < < " (ms out id: " < < in_ms . multisig_out_id < < " ) in tx: " < < tx_prefix_hash ) ;
return false ;
}
}
sig_index + + ;
}
TIME_MEASURE_FINISH_PD ( tx_check_inputs_loop ) ;
TIME_MEASURE_START_PD ( tx_check_inputs_attachment_check ) ;
if ( ! m_is_in_checkpoint_zone )
{
CHECK_AND_ASSERT_MES ( tx . signatures . size ( ) = = sig_index , false , " tx signatures count differs from inputs " ) ;
2019-02-20 04:11:33 +00:00
if ( ! ( get_tx_flags ( tx ) & TX_FLAG_SIGNATURE_MODE_SEPARATE ) )
2018-12-27 18:50:45 +03:00
{
bool r = validate_attachment_info ( tx . extra , tx . attachment , false ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to validate attachments in tx " < < tx_prefix_hash < < " : incorrect extra_attachment_info in tx.extra " ) ;
}
}
TIME_MEASURE_FINISH_PD ( tx_check_inputs_attachment_check ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : is_tx_spendtime_unlocked ( uint64_t unlock_time ) const
{
return currency : : is_tx_spendtime_unlocked ( unlock_time , get_current_blockchain_size ( ) , m_core_runtime_config . get_core_time ( ) ) ;
}
//------------------------------------------------------------------
2019-09-05 19:08:10 +03:00
bool blockchain_storage : : check_tx_input ( const transaction & tx , size_t in_index , const txin_to_key & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , uint64_t & max_related_block_height , uint64_t & source_max_unlock_time_for_pos_coinbase ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
//TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop);
std : : vector < crypto : : public_key > output_keys ;
2019-09-05 19:08:10 +03:00
if ( ! get_output_keys_for_input_with_checks ( tx , txin , output_keys , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) )
2018-12-27 18:50:45 +03:00
{
LOG_PRINT_L0 ( " Failed to get output keys for input # " < < in_index < < " (amount = " < < print_money ( txin . amount ) < < " , key_offset.size = " < < txin . key_offsets . size ( ) < < " ) " ) ;
return false ;
}
//TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_ch_in_get_keys_loop);
std : : vector < const crypto : : public_key * > output_keys_ptrs ;
output_keys_ptrs . reserve ( output_keys . size ( ) ) ;
for ( auto & ptr : output_keys )
output_keys_ptrs . push_back ( & ptr ) ;
return check_tokey_input ( tx , in_index , txin , tx_prefix_hash , sig , output_keys_ptrs ) ;
}
//------------------------------------------------------------------
// Checks each referenced output for:
// 1) source tx unlock time validity
// 2) mixin restrictions
// 3) general gindex/ref_by_id corectness
2019-09-05 19:08:10 +03:00
bool blockchain_storage : : get_output_keys_for_input_with_checks ( const transaction & tx , const txin_to_key & txin , std : : vector < crypto : : public_key > & output_keys , uint64_t & max_related_block_height , uint64_t & source_max_unlock_time_for_pos_coinbase ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
struct outputs_visitor
{
std : : vector < crypto : : public_key > & m_results_collector ;
const blockchain_storage & m_bch ;
2019-09-05 19:08:10 +03:00
uint64_t & m_source_max_unlock_time_for_pos_coinbase ;
2018-12-27 18:50:45 +03:00
outputs_visitor ( std : : vector < crypto : : public_key > & results_collector ,
2019-07-22 23:54:38 +02:00
const blockchain_storage & bch ,
2019-09-05 19:08:10 +03:00
uint64_t & source_max_unlock_time_for_pos_coinbase )
: m_results_collector ( results_collector )
, m_bch ( bch )
, m_source_max_unlock_time_for_pos_coinbase ( source_max_unlock_time_for_pos_coinbase )
2018-12-27 18:50:45 +03:00
{ }
2019-07-22 23:54:38 +02:00
bool handle_output ( const transaction & source_tx , const transaction & validated_tx , const tx_out & out , uint64_t out_i )
2018-12-27 18:50:45 +03:00
{
//check tx unlock time
2019-07-22 23:54:38 +02:00
uint64_t source_out_unlock_time = get_tx_unlock_time ( source_tx , out_i ) ;
//let coinbase sources for PoS block to have locked inputs, the outputs supposed to be locked same way, except the reward
2019-08-03 00:22:04 +02:00
if ( is_coinbase ( validated_tx ) & & is_pos_block ( validated_tx ) )
2018-12-27 18:50:45 +03:00
{
2019-09-05 19:08:10 +03:00
CHECK_AND_ASSERT_MES ( should_unlock_value_be_treated_as_block_height ( source_out_unlock_time ) , false , " source output # " < < out_i < < " is locked by time, not by height, which is not allowed for PoS coinbase " ) ;
if ( source_out_unlock_time > m_source_max_unlock_time_for_pos_coinbase )
m_source_max_unlock_time_for_pos_coinbase = source_out_unlock_time ;
2019-07-22 23:54:38 +02:00
}
else
{
if ( ! m_bch . is_tx_spendtime_unlocked ( source_out_unlock_time ) )
{
LOG_PRINT_L0 ( " One of outputs for one of inputs have wrong tx.unlock_time = " < < get_tx_unlock_time ( source_tx , out_i ) ) ;
return false ;
}
2018-12-27 18:50:45 +03:00
}
if ( out . target . type ( ) ! = typeid ( txout_to_key ) )
{
LOG_PRINT_L0 ( " Output have wrong type id, which= " < < out . target . which ( ) ) ;
return false ;
}
crypto : : public_key pk = boost : : get < txout_to_key > ( out . target ) . key ;
m_results_collector . push_back ( pk ) ;
return true ;
}
} ;
2019-09-05 19:08:10 +03:00
outputs_visitor vi ( output_keys , * this , source_max_unlock_time_for_pos_coinbase ) ;
2019-07-22 23:54:38 +02:00
return scan_outputkeys_for_indexes ( tx , txin , vi , max_related_block_height ) ;
2018-12-27 18:50:45 +03:00
}
//------------------------------------------------------------------
// Note: this function can be used for checking to_key inputs against either main chain or alt chain, that's why it has output_keys_ptrs parameter
// Doesn't check spent flags, the caller must check it.
bool blockchain_storage : : check_tokey_input ( const transaction & tx , size_t in_index , const txin_to_key & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , const std : : vector < const crypto : : public_key * > & output_keys_ptrs ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
TIME_MEASURE_START_PD ( tx_check_inputs_loop_ch_in_val_sig ) ;
if ( txin . key_offsets . size ( ) ! = output_keys_ptrs . size ( ) )
{
LOG_PRINT_L0 ( " Output keys for tx with amount = " < < txin . amount < < " and count indexes " < < txin . key_offsets . size ( ) < < " returned wrong keys count " < < output_keys_ptrs . size ( ) ) ;
return false ;
}
if ( m_is_in_checkpoint_zone )
return true ;
if ( get_tx_flags ( tx ) & TX_FLAG_SIGNATURE_MODE_SEPARATE )
{
// check attachments, mentioned directly in this input
bool r = validate_attachment_info ( txin . etc_details , tx . attachment , in_index ! = tx . vin . size ( ) - 1 ) ; // attachment info can be omitted for all inputs, except the last one
CHECK_AND_ASSERT_MES ( r , false , " Failed to validate attachments in tx " < < tx_prefix_hash < < " : incorrect extra_attachment_info in etc_details in input # " < < in_index ) ;
}
else
{
// make sure normal tx does not have extra_attachment_info in etc_details
CHECK_AND_ASSERT_MES ( ! have_type_in_variant_container < extra_attachment_info > ( txin . etc_details ) , false , " Incorrect using of extra_attachment_info in etc_details in input # " < < in_index < < " for tx " < < tx_prefix_hash ) ;
}
// check signatures
size_t expected_signatures_count = output_keys_ptrs . size ( ) ;
bool need_to_check_extra_sign = false ;
if ( get_tx_flags ( tx ) & TX_FLAG_SIGNATURE_MODE_SEPARATE & & in_index = = tx . vin . size ( ) - 1 )
{
expected_signatures_count + + ;
need_to_check_extra_sign = true ;
}
CHECK_AND_ASSERT_MES ( expected_signatures_count = = sig . size ( ) , false , " internal error: tx signatures count= " < < sig . size ( ) < < " mismatch with outputs keys count for inputs= " < < expected_signatures_count ) ;
crypto : : hash tx_hash_for_signature = prepare_prefix_hash_for_sign ( tx , in_index , tx_prefix_hash ) ;
CHECK_AND_ASSERT_MES ( tx_hash_for_signature ! = null_hash , false , " failed to prepare_prefix_hash_for_sign " ) ;
LOG_PRINT_L4 ( " CHECK RING SIGNATURE: tx_prefix_hash " < < tx_prefix_hash
< < " tx_hash_for_signature " < < tx_hash_for_signature
< < " txin.k_image " < < txin . k_image
< < " key_ptr: " < < * output_keys_ptrs [ 0 ]
< < " signature: " < < sig [ 0 ] ) ;
bool r = crypto : : validate_key_image ( txin . k_image ) ;
CHECK_AND_ASSERT_MES ( r , false , " key image for input # " < < in_index < < " is invalid: " < < txin . k_image ) ;
r = crypto : : check_ring_signature ( tx_hash_for_signature , txin . k_image , output_keys_ptrs , sig . data ( ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to check ring signature for input # " < < in_index < < ENDL < < dump_ring_sig_data ( tx_hash_for_signature , txin . k_image , output_keys_ptrs , sig ) ) ;
if ( need_to_check_extra_sign )
{
//here we check extra signature to validate that transaction was finalized by authorized subject
r = crypto : : check_signature ( tx_prefix_hash , get_tx_pub_key_from_extra ( tx ) , sig . back ( ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to check extra signature for last input with TX_FLAG_SIGNATURE_MODE_SEPARATE " ) ;
}
TIME_MEASURE_FINISH_PD ( tx_check_inputs_loop_ch_in_val_sig ) ;
return r ;
}
//------------------------------------------------------------------
// Note: this function doesn't check spent flags by design (to be able to use either for main chain and alt chains).
// The caller MUST check spent flags.
bool blockchain_storage : : check_ms_input ( const transaction & tx , size_t in_index , const txin_multisig & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , const transaction & source_tx , size_t out_n ) const
{
# define LOC_CHK(cond, msg) CHECK_AND_ASSERT_MES(cond, false, "ms input check failed: ms_id: " << txin.multisig_out_id << ", input #" << in_index << " in tx " << tx_prefix_hash << ", refers to ms output #" << out_n << " in source tx " << get_transaction_hash(source_tx) << ENDL << msg)
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
2019-07-22 13:04:01 +02:00
uint64_t unlock_time = get_tx_unlock_time ( source_tx , out_n ) ;
2018-12-27 18:50:45 +03:00
LOC_CHK ( is_tx_spendtime_unlocked ( unlock_time ) , " Source transaction is LOCKED! unlock_time: " < < unlock_time < < " , now is " < < m_core_runtime_config . get_core_time ( ) < < " , blockchain size is " < < get_current_blockchain_size ( ) ) ;
LOC_CHK ( source_tx . vout . size ( ) > out_n , " internal error: out_n== " < < out_n < < " is out-of-bounds of source_tx.vout, size= " < < source_tx . vout . size ( ) ) ;
const tx_out & source_tx_out = source_tx . vout [ out_n ] ;
const txout_multisig & source_ms_out_target = boost : : get < txout_multisig > ( source_tx_out . target ) ;
LOC_CHK ( txin . sigs_count = = source_ms_out_target . minimum_sigs ,
" ms input's sigs_count ( " < < txin . sigs_count < < " ) does not match to ms output's minimum signatures expected ( " < < source_ms_out_target . minimum_sigs < < " ) "
< < " , source_tx_out.amount= " < < print_money ( source_tx_out . amount )
< < " , txin.amount = " < < print_money ( txin . amount ) ) ;
LOC_CHK ( source_tx_out . amount = = txin . amount ,
" amount missmatch "
< < " , source_tx_out.amount= " < < print_money ( source_tx_out . amount )
< < " , txin.amount = " < < print_money ( txin . amount ) ) ;
if ( m_is_in_checkpoint_zone )
return true ;
if ( get_tx_flags ( tx ) & TX_FLAG_SIGNATURE_MODE_SEPARATE )
{
// check attachments, mentioned directly in this input
bool r = validate_attachment_info ( txin . etc_details , tx . attachment , in_index ! = tx . vin . size ( ) - 1 ) ; // attachment info can be omitted for all inputs, except the last one
LOC_CHK ( r , " Failed to validate attachments in tx " < < tx_prefix_hash < < " : incorrect extra_attachment_info in etc_details in input # " < < in_index ) ;
}
else
{
// make sure normal tx does not have extra_attachment_info in etc_details
LOC_CHK ( ! have_type_in_variant_container < extra_attachment_info > ( txin . etc_details ) , " Incorrect using of extra_attachment_info in etc_details in input # " < < in_index < < " for tx " < < tx_prefix_hash ) ;
}
LOC_CHK ( tx . signatures . size ( ) > in_index , " ms input index is out of signatures container bounds, tx.signatures.size() = " < < tx . signatures . size ( ) ) ;
const std : : vector < crypto : : signature > & input_signatures = tx . signatures [ in_index ] ;
size_t expected_signatures_count = txin . sigs_count ;
bool need_to_check_extra_sign = false ;
if ( get_tx_flags ( tx ) & TX_FLAG_SIGNATURE_MODE_SEPARATE & & in_index = = tx . vin . size ( ) - 1 ) // last input in TX_FLAG_SIGNATURE_MODE_SEPARATE must contain one more signature to ensure that tx was completed by an authorized subject
{
expected_signatures_count + + ;
need_to_check_extra_sign = true ;
}
LOC_CHK ( expected_signatures_count = = input_signatures . size ( ) , " Invalid input's signatures count: " < < input_signatures . size ( ) < < " , expected: " < < expected_signatures_count ) ;
crypto : : hash tx_hash_for_signature = prepare_prefix_hash_for_sign ( tx , in_index , tx_prefix_hash ) ;
LOC_CHK ( tx_hash_for_signature ! = null_hash , " prepare_prefix_hash_for_sign failed " ) ;
LOC_CHK ( txin . sigs_count < = source_ms_out_target . keys . size ( ) , " source tx invariant failed: ms output's minimum sigs == ms input's sigs_count ( " < < txin . sigs_count < < " ) is GREATHER than keys.size() = " < < source_ms_out_target . keys . size ( ) ) ; // NOTE: sig_count == minimum_sigs as checked above
size_t out_key_index = 0 ; // index in source_ms_out_target.keys
for ( size_t i = 0 ; i ! = txin . sigs_count ; /* nothing */ )
{
// if we run out of keys for this signature, then it's invalid signature
LOC_CHK ( out_key_index < source_ms_out_target . keys . size ( ) , " invalid signature # " < < i < < " : " < < input_signatures [ i ] ) ;
// check signature #i against ms output key #out_key_index
if ( crypto : : check_signature ( tx_hash_for_signature , source_ms_out_target . keys [ out_key_index ] , input_signatures [ i ] ) )
{
// match: go for the next signature and the next key
i + + ;
out_key_index + + ;
}
else
{
// missmatch: go for the next key for this signature
out_key_index + + ;
}
}
if ( need_to_check_extra_sign )
{
//here we check extra signature to validate that transaction was finilized by authorized subject
bool r = crypto : : check_signature ( tx_prefix_hash , get_tx_pub_key_from_extra ( tx ) , tx . signatures [ in_index ] . back ( ) ) ;
LOC_CHK ( r , " failed to check extra signature for last out with TX_FLAG_SIGNATURE_MODE_SEPARATE " ) ;
}
return true ;
# undef LOC_CHK
}
//------------------------------------------------------------------
2019-07-22 23:54:38 +02:00
bool blockchain_storage : : check_tx_input ( const transaction & tx , size_t in_index , const txin_multisig & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , uint64_t & max_related_block_height ) const
2018-12-27 18:50:45 +03:00
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto multisig_ptr = m_db_multisig_outs . find ( txin . multisig_out_id ) ;
CHECK_AND_ASSERT_MES ( multisig_ptr , false , " Unable to find txin.multisig_out_id= " < < txin . multisig_out_id < < " for ms input # " < < in_index < < " in tx " < < tx_prefix_hash ) ;
const crypto : : hash & source_tx_id = multisig_ptr - > tx_id ; // source tx hash
size_t n = multisig_ptr - > out_no ; // index of multisig output in source tx
# define LOC_CHK(cond, msg) CHECK_AND_ASSERT_MES(cond, false, "ms input check failed: ms_id: " << txin.multisig_out_id << ", input #" << in_index << " in tx " << tx_prefix_hash << ", refers to ms output #" << n << " in source tx " << source_tx_id << ENDL << msg)
LOC_CHK ( multisig_ptr - > spent_height = = 0 , " ms output is already spent on height " < < multisig_ptr - > spent_height ) ;
auto source_tx_ptr = m_db_transactions . find ( source_tx_id ) ;
LOC_CHK ( source_tx_ptr , " Can't find source transaction " ) ;
LOC_CHK ( source_tx_ptr - > tx . vout . size ( ) > n , " ms output index is incorrect, source tx's vout size is " < < source_tx_ptr - > tx . vout . size ( ) ) ;
LOC_CHK ( source_tx_ptr - > tx . vout [ n ] . target . type ( ) = = typeid ( txout_multisig ) , " ms output has wrong type, txout_multisig expected " ) ;
LOC_CHK ( source_tx_ptr - > m_spent_flags . size ( ) > n , " Internal error, m_spent_flags size ( " < < source_tx_ptr - > m_spent_flags . size ( ) < < " ) less then expected, n: " < < n ) ;
LOC_CHK ( source_tx_ptr - > m_spent_flags [ n ] = = false , " Internal error, ms output is already spent " ) ; // should never happen as multisig_ptr->spent_height is checked above
if ( ! check_ms_input ( tx , in_index , txin , tx_prefix_hash , sig , source_tx_ptr - > tx , n ) )
return false ;
2019-07-22 23:54:38 +02:00
max_related_block_height = source_tx_ptr - > m_keeper_block_height ;
2018-12-27 18:50:45 +03:00
return true ;
# undef LOC_CHK
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_adjusted_time ( ) const
{
//TODO: add collecting median time
return m_core_runtime_config . get_core_time ( ) ;
}
//------------------------------------------------------------------
std : : vector < uint64_t > blockchain_storage : : get_last_n_blocks_timestamps ( size_t n ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
std : : vector < uint64_t > timestamps ;
size_t offset = m_db_blocks . size ( ) < = n ? 0 : m_db_blocks . size ( ) - n ;
for ( ; offset ! = m_db_blocks . size ( ) ; + + offset )
timestamps . push_back ( m_db_blocks [ offset ] - > bl . timestamp ) ;
return timestamps ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_last_n_blocks_timestamps_median ( size_t n ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto it = m_timestamps_median_cache . find ( n ) ;
if ( it ! = m_timestamps_median_cache . end ( ) )
return it - > second ;
std : : vector < uint64_t > timestamps = get_last_n_blocks_timestamps ( n ) ;
uint64_t median_res = epee : : misc_utils : : median ( timestamps ) ;
m_timestamps_median_cache [ n ] = median_res ;
return median_res ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_last_timestamps_check_window_median ( ) const
{
return get_last_n_blocks_timestamps_median ( BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ) ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_tx_expiration_median ( ) const
{
return get_last_n_blocks_timestamps_median ( TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : check_block_timestamp_main ( const block & b ) const
{
if ( b . timestamp > get_adjusted_time ( ) + CURRENCY_BLOCK_FUTURE_TIME_LIMIT )
{
LOG_PRINT_L0 ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + " + epee : : misc_utils : : get_time_interval_string ( CURRENCY_BLOCK_FUTURE_TIME_LIMIT ) ) ;
return false ;
}
if ( is_pos_block ( b ) & & b . timestamp > get_adjusted_time ( ) + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT )
{
LOG_PRINT_L0 ( " Timestamp of PoS block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + " + epee : : misc_utils : : get_time_interval_string ( CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT ) + " : " < < get_adjusted_time ( ) < < " ( " < < b . timestamp - get_adjusted_time ( ) < < " ) " ) ;
return false ;
}
std : : vector < uint64_t > timestamps = get_last_n_blocks_timestamps ( BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ) ;
return check_block_timestamp ( std : : move ( timestamps ) , b ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : check_block_timestamp ( std : : vector < uint64_t > timestamps , const block & b ) const
{
if ( timestamps . size ( ) < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW )
return true ;
if ( is_pos_block ( b ) & & b . timestamp > get_adjusted_time ( ) + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT )
{
LOG_PRINT_L0 ( " Timestamp of PoS block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + " + epee : : misc_utils : : get_time_interval_string ( CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT ) + " : " < < get_adjusted_time ( ) < < " ( " < < b . timestamp - get_adjusted_time ( ) < < " ) " ) ;
return false ;
}
uint64_t median_ts = epee : : misc_utils : : median ( timestamps ) ;
if ( b . timestamp < median_ts )
{
LOG_PRINT_L0 ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , less than median of last " < < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW < < " blocks, " < < median_ts ) ;
return false ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : is_tx_expired ( const transaction & tx ) const
{
return currency : : is_tx_expired ( tx , get_tx_expiration_median ( ) ) ;
}
//------------------------------------------------------------------
std : : shared_ptr < const transaction_chain_entry > blockchain_storage : : find_key_image_and_related_tx ( const crypto : : key_image & ki , crypto : : hash & id_result ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
auto ki_index_ptr = m_db_spent_keys . find ( ki ) ;
if ( ! ki_index_ptr )
return std : : shared_ptr < transaction_chain_entry > ( ) ;
auto block_entry = m_db_blocks [ * ki_index_ptr ] ;
if ( ! block_entry )
{
LOG_ERROR ( " Internal error: broken index, key image " < < ki
< < " reffered to height " < < * ki_index_ptr < < " but couldnot find block on this height " ) ;
return std : : shared_ptr < const transaction_chain_entry > ( ) ;
}
//look up coinbase
for ( auto & in : block_entry - > bl . miner_tx . vin )
{
if ( in . type ( ) = = typeid ( txin_to_key ) )
{
if ( boost : : get < txin_to_key > ( in ) . k_image = = ki )
{
id_result = get_transaction_hash ( block_entry - > bl . miner_tx ) ;
return get_tx_chain_entry ( id_result ) ;
}
}
}
//lookup transactions
for ( auto & tx_id : block_entry - > bl . tx_hashes )
{
auto tx_chain_entry = get_tx_chain_entry ( tx_id ) ;
if ( ! tx_chain_entry )
{
LOG_ERROR ( " Internal error: broken index, tx_id " < < tx_id
< < " not found in tx index, block no " < < * ki_index_ptr ) ;
return std : : shared_ptr < const transaction_chain_entry > ( ) ;
}
for ( auto & in : tx_chain_entry - > tx . vin )
{
if ( in . type ( ) = = typeid ( txin_to_key ) )
{
if ( boost : : get < txin_to_key > ( in ) . k_image = = ki )
{
id_result = get_transaction_hash ( tx_chain_entry - > tx ) ;
return tx_chain_entry ;
}
}
}
}
return std : : shared_ptr < const transaction_chain_entry > ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : prune_aged_alt_blocks ( )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CRITICAL_REGION_LOCAL1 ( m_alternative_chains_lock ) ;
uint64_t current_height = get_current_blockchain_size ( ) ;
2019-08-01 18:47:20 +02:00
size_t count_to_delete = 0 ;
if ( m_alternative_chains . size ( ) > m_core_runtime_config . max_alt_blocks )
count_to_delete = m_alternative_chains . size ( ) - m_core_runtime_config . max_alt_blocks ;
std : : map < uint64_t , alt_chain_container : : iterator > alts_to_delete ;
2018-12-27 18:50:45 +03:00
for ( auto it = m_alternative_chains . begin ( ) ; it ! = m_alternative_chains . end ( ) ; )
{
if ( current_height > it - > second . height & & current_height - it - > second . height > CURRENCY_ALT_BLOCK_LIVETIME_COUNT )
{
do_erase_altblock ( it + + ) ;
}
else
{
2019-08-01 18:47:20 +02:00
if ( count_to_delete )
{
if ( ! alts_to_delete . size ( ) )
alts_to_delete [ it - > second . timestamp ] = it ;
else
{
if ( it - > second . timestamp > = alts_to_delete . rbegin ( ) - > first )
alts_to_delete [ it - > second . timestamp ] = it ;
if ( alts_to_delete . size ( ) > count_to_delete )
alts_to_delete . erase ( alts_to_delete . begin ( ) ) ;
}
}
2018-12-27 18:50:45 +03:00
+ + it ;
}
}
2019-08-01 18:47:20 +02:00
//now, if there was count_to_delete we should erase most oldest entries of altblocks
for ( auto & itd : alts_to_delete )
{
m_alternative_chains . erase ( itd . second ) ;
}
2018-12-27 18:50:45 +03:00
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_pos_block ( const block & b , const crypto : : hash & id , bool for_altchain ) const
{
//validate
wide_difficulty_type basic_diff = get_next_diff_conditional ( true ) ;
return validate_pos_block ( b , basic_diff , id , for_altchain ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_pos_block ( const block & b , wide_difficulty_type basic_diff , const crypto : : hash & id , bool for_altchain ) const
{
uint64_t coin_age = 0 ;
wide_difficulty_type final_diff = 0 ;
crypto : : hash proof_hash = null_hash ;
return validate_pos_block ( b , basic_diff , coin_age , final_diff , proof_hash , id , for_altchain ) ;
}
//------------------------------------------------------------------
# define POS_STAKE_TO_DIFF_COEFF 100 // total_coins_in_minting * POS_STAKE_TO_DIFF_COEFF =~ pos_difficulty
void blockchain_storage : : get_pos_mining_estimate ( uint64_t amount_coins ,
uint64_t time ,
uint64_t & estimate_result ,
uint64_t & pos_diff_and_amount_rate ,
std : : vector < uint64_t > & days ) const
{
estimate_result = 0 ;
if ( ! is_pos_allowed ( ) )
return ;
// new algo
// 1. get (CURRENCY_BLOCKS_PER_DAY / 2) PoS blocks (a day in case of PoS/PoW==50/50)
// 2. calculate total minted money C (normalized for 1 day interval) and average difficulty D (based on last (CURRENCY_BLOCKS_PER_DAY / 2) PoS blocks)
// 3. calculate total coins participating in PoS minting as M = D / pos_diff_ratio
// 4. calculate owner's money proportion as P = amount_coins / (M + amount_coins)
// 5. estimate PoS minting income for this day as I = C * P
// 6. amount_coins += I, goto 3
epee : : critical_region_t < decltype ( m_read_lock ) > read_lock_region ( m_read_lock ) ;
size_t estimated_pos_blocks_count_per_day = CURRENCY_BLOCKS_PER_DAY / 2 ; // 50% of all blocks in a perfect world
uint64_t pos_ts_min = UINT64_MAX , pos_ts_max = 0 ;
size_t pos_blocks_count = 0 ;
uint64_t pos_total_minted_money = 0 ;
wide_difficulty_type pos_avg_difficulty = 0 ;
// scan blockchain backward for PoS blocks and collect data
for ( size_t h = m_db_blocks . size ( ) - 1 ; h ! = 0 & & pos_blocks_count < estimated_pos_blocks_count_per_day ; - - h )
{
auto bei = m_db_blocks [ h ] ;
if ( ! is_pos_block ( bei - > bl ) )
continue ;
uint64_t ts = get_actual_timestamp ( bei - > bl ) ;
pos_ts_min = min ( pos_ts_min , ts ) ;
pos_ts_max = max ( pos_ts_max , ts ) ;
pos_total_minted_money + = get_reward_from_miner_tx ( bei - > bl . miner_tx ) ;
pos_avg_difficulty + = bei - > difficulty ;
+ + pos_blocks_count ;
}
if ( pos_blocks_count < estimated_pos_blocks_count_per_day | | pos_ts_max < = pos_ts_min )
return ; // too little pos blocks found or invalid ts
pos_avg_difficulty / = pos_blocks_count ;
uint64_t found_blocks_interval = pos_ts_max - pos_ts_min ; // will be close to 24 * 60 * 60 in case of PoS/PoW == 50/50
uint64_t pos_last_day_total_minted_money = pos_total_minted_money * ( 24 * 60 * 60 ) / found_blocks_interval ; // total minted money normalized for 1 day interval
uint64_t estimated_total_minting_coins = static_cast < uint64_t > ( pos_avg_difficulty / POS_STAKE_TO_DIFF_COEFF ) ;
uint64_t current_amount = amount_coins ;
uint64_t days_count = time / ( 60 * 60 * 24 ) ;
for ( uint64_t d = 0 ; d ! = days_count ; d + + )
{
double user_minting_coins_proportion = static_cast < double > ( current_amount ) / ( estimated_total_minting_coins + current_amount ) ;
current_amount + = pos_last_day_total_minted_money * user_minting_coins_proportion ;
days . push_back ( current_amount ) ;
}
estimate_result = current_amount ;
}
//------------------------------------------------------------------
2019-08-07 18:41:18 +03:00
bool blockchain_storage : : validate_tx_for_hardfork_specific_terms ( const transaction & tx , const crypto : : hash & tx_id , uint64_t block_height ) const
{
if ( block_height < = m_core_runtime_config . hard_fork1_starts_after_height )
{
// before hardfork 1
for ( const auto & el : tx . extra )
{
// etc_tx_details_unlock_time2 is not allowed in txs in blocks prior to hardfork 1
CHECK_AND_ASSERT_MES ( el . type ( ) ! = typeid ( etc_tx_details_unlock_time2 ) , false , " tx " < < tx_id < < " contains etc_tx_details_unlock_time2 which is not allowed on height " < < block_height ) ;
}
return true ;
}
return true ;
}
//------------------------------------------------------------------
2019-09-05 19:08:10 +03:00
bool blockchain_storage : : validate_pos_coinbase_outs_unlock_time ( const transaction & miner_tx , uint64_t staked_amount , uint64_t source_max_unlock_time ) const
2019-07-22 23:54:38 +02:00
{
uint64_t major_unlock_time = get_tx_x_detail < etc_tx_details_unlock_time > ( miner_tx ) ;
if ( major_unlock_time )
{
2019-08-03 00:22:04 +02:00
//if there was etc_tx_details_unlock_time present in tx, then ignore etc_tx_details_unlock_time2
2019-09-05 19:08:10 +03:00
if ( major_unlock_time < source_max_unlock_time )
2019-07-22 23:54:38 +02:00
return false ;
2019-08-03 00:22:04 +02:00
else
return true ;
2019-07-22 23:54:38 +02:00
}
2019-08-03 00:22:04 +02:00
CHECK_AND_ASSERT_MES ( get_block_height ( miner_tx ) > m_core_runtime_config . hard_fork1_starts_after_height , false , " error in block [ " < < get_block_height ( miner_tx ) < < " ] etc_tx_details_unlock_time2 can exist only after hard fork point : " < < m_core_runtime_config . hard_fork1_starts_after_height ) ;
//etc_tx_details_unlock_time2 can be kept only after hard_fork_1 point
2019-07-22 23:54:38 +02:00
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT ( ut2 ) ;
get_type_in_variant_container ( miner_tx . extra , ut2 ) ;
CHECK_AND_ASSERT_MES ( ut2 . unlock_time_array . size ( ) = = miner_tx . vout . size ( ) , false , " ut2.unlock_time_array.size()< " < < ut2 . unlock_time_array . size ( )
< < " > != miner_tx.vout.size()< " < < miner_tx . vout . size ( ) < < " > " ) ;
2019-09-05 19:08:10 +03:00
uint64_t amount_of_coins_in_unlock_in_range = 0 ; // amount of outputs locked for at least the same time
2019-07-22 23:54:38 +02:00
for ( uint64_t i = 0 ; i ! = miner_tx . vout . size ( ) ; i + + )
{
2019-09-05 19:08:10 +03:00
uint64_t unlock_value = ut2 . unlock_time_array [ i ] ;
CHECK_AND_ASSERT_MES ( should_unlock_value_be_treated_as_block_height ( unlock_value ) , false , " output # " < < i < < " is locked by time, not buy height, which is not allowed for PoS coinbase " ) ;
if ( unlock_value > = source_max_unlock_time )
2019-07-22 23:54:38 +02:00
amount_of_coins_in_unlock_in_range + = miner_tx . vout [ i ] . amount ;
}
if ( amount_of_coins_in_unlock_in_range > = staked_amount )
return true ;
LOG_ERROR ( " amount_of_coins_in_unlock_in_range< " < < amount_of_coins_in_unlock_in_range < < " > is less then staked_amount< " < < staked_amount ) ;
return false ;
}
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
bool blockchain_storage : : validate_pos_block ( const block & b ,
wide_difficulty_type basic_diff ,
uint64_t & amount ,
wide_difficulty_type & final_diff ,
crypto : : hash & proof_hash ,
const crypto : : hash & id ,
bool for_altchain ,
const alt_chain_type & alt_chain ,
uint64_t split_height
) const
{
bool is_pos = is_pos_block ( b ) ;
CHECK_AND_ASSERT_MES ( is_pos , false , " is_pos_block() returned false validate_pos_block() " ) ;
//check timestamp
CHECK_AND_ASSERT_MES ( b . timestamp % POS_SCAN_STEP = = 0 , false , " wrong timestamp in PoS block(b.timestamp%POS_SCAN_STEP == 0), b.timestamp = " < < b . timestamp ) ;
//check keyimage
CHECK_AND_ASSERT_MES ( b . miner_tx . vin [ 1 ] . type ( ) = = typeid ( txin_to_key ) , false , " coinstake transaction in the block has the wrong type " ) ;
const txin_to_key & in_to_key = boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) ;
if ( ! for_altchain & & have_tx_keyimg_as_spent ( in_to_key . k_image ) )
{
LOG_PRINT_L0 ( " Key image in coinstake already spent in blockchain: " < < string_tools : : pod_to_hex ( in_to_key . k_image ) ) ;
return false ;
}
//check actual time if it there
uint64_t actual_ts = get_actual_timestamp ( b ) ;
2019-08-01 18:47:20 +02:00
if ( ( actual_ts > b . timestamp & & actual_ts - b . timestamp > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED ) | |
( actual_ts < b . timestamp & & b . timestamp - actual_ts > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED )
2018-12-27 18:50:45 +03:00
)
{
2019-08-01 18:47:20 +02:00
LOG_PRINT_L0 ( " PoS block actual timestamp " < < actual_ts < < " differs from b.timestamp " < < b . timestamp < < " by " < < ( ( int64_t ) actual_ts - ( int64_t ) b . timestamp ) < < " s, it's more than allowed " < < POS_MAX_ACTUAL_TIMESTAMP_TO_MINED < < " s. " ) ;
2018-12-27 18:50:45 +03:00
return false ;
}
//check kernel
stake_kernel sk = AUTO_VAL_INIT ( sk ) ;
stake_modifier_type sm = AUTO_VAL_INIT ( sm ) ;
bool r = build_stake_modifier ( sm , alt_chain , split_height ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to build_stake_modifier " ) ;
amount = 0 ;
r = build_kernel ( b , sk , amount , sm ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to build kernel_stake " ) ;
CHECK_AND_ASSERT_MES ( amount ! = 0 , false , " failed to build kernel_stake, amount == 0 " ) ;
proof_hash = crypto : : cn_fast_hash ( & sk , sizeof ( sk ) ) ;
LOG_PRINT_L2 ( " STAKE KERNEL for bl ID: " < < get_block_hash ( b ) < < ENDL
< < print_stake_kernel_info ( sk )
< < " amount: " < < print_money ( amount ) < < ENDL
< < " kernel_hash: " < < proof_hash ) ;
final_diff = basic_diff / amount ;
if ( ! check_hash ( proof_hash , final_diff ) )
{
LOG_ERROR ( " PoS difficulty check failed for block " < < get_block_hash ( b ) < < " @ HEIGHT " < < get_block_height ( b ) < < " : " < < ENDL
< < " basic_diff: " < < basic_diff < < ENDL
< < " final_diff: " < < final_diff < < ENDL
< < " amount: " < < print_money_brief ( amount ) < < ENDL
< < " kernel_hash: " < < proof_hash < < ENDL
) ;
return false ;
}
//validate signature
uint64_t max_related_block_height = 0 ;
const txin_to_key & coinstake_in = boost : : get < txin_to_key > ( b . miner_tx . vin [ 1 ] ) ;
CHECK_AND_ASSERT_MES ( b . miner_tx . signatures . size ( ) = = 1 , false , " PoS block's miner_tx has incorrect signatures size = " < < b . miner_tx . signatures . size ( ) < < " , block_id = " < < get_block_hash ( b ) ) ;
if ( ! for_altchain )
{
// Do coinstake input validation for main chain only.
// Txs in alternative PoS blocks (including miner_tx) are validated by validate_alt_block_txs()
2019-09-05 19:08:10 +03:00
uint64_t source_max_unlock_time_for_pos_coinbase = 0 ;
r = check_tx_input ( b . miner_tx , 1 , coinstake_in , id , b . miner_tx . signatures [ 0 ] , max_related_block_height , source_max_unlock_time_for_pos_coinbase ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " Failed to validate coinstake input in miner tx, block_id = " < < get_block_hash ( b ) ) ;
2019-07-20 14:36:12 +02:00
if ( get_block_height ( b ) > m_core_runtime_config . hard_fork1_starts_after_height )
{
uint64_t last_pow_h = get_last_x_block_height ( false ) ;
2019-09-05 19:08:10 +03:00
CHECK_AND_ASSERT_MES ( max_related_block_height < = last_pow_h , false , " Failed to validate coinbase in PoS block, condition failed: max_related_block_height( " < < max_related_block_height < < " ) <= last_pow_h( " < < last_pow_h < < " ) " ) ;
2019-07-22 23:54:38 +02:00
//let's check that coinbase amount and unlock time
2019-09-05 19:08:10 +03:00
r = validate_pos_coinbase_outs_unlock_time ( b . miner_tx , coinstake_in . amount , source_max_unlock_time_for_pos_coinbase ) ;
2019-08-07 08:03:44 +03:00
CHECK_AND_ASSERT_MES ( r , false , " Failed to validate_pos_coinbase_outs_unlock_time() in miner tx, block_id = " < < get_block_hash ( b )
2019-09-05 19:08:10 +03:00
< < " source_max_unlock_time_for_pos_coinbase= " < < source_max_unlock_time_for_pos_coinbase ) ;
2019-07-22 23:54:38 +02:00
}
else
{
2019-09-05 19:08:10 +03:00
CHECK_AND_ASSERT_MES ( is_tx_spendtime_unlocked ( source_max_unlock_time_for_pos_coinbase ) , false , " Failed to validate coinbase in PoS block, condition failed: is_tx_spendtime_unlocked(source_max_unlock_time_for_pos_coinbase)( " < < source_max_unlock_time_for_pos_coinbase < < " ) " ) ;
2019-07-20 14:36:12 +02:00
}
2018-12-27 18:50:45 +03:00
}
uint64_t block_height = for_altchain ? split_height + alt_chain . size ( ) : m_db_blocks . size ( ) ;
uint64_t coinstake_age = block_height - max_related_block_height - 1 ;
CHECK_AND_ASSERT_MES ( coinstake_age > = m_core_runtime_config . min_coinstake_age , false ,
" Coinstake age is: " < < coinstake_age < < " is less than minimum expected: " < < m_core_runtime_config . min_coinstake_age ) ;
return true ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_adjusted_cumulative_difficulty_for_next_pos ( wide_difficulty_type next_diff ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
wide_difficulty_type last_pow_diff = 0 ;
wide_difficulty_type last_pos_diff = 0 ;
uint64_t sz = m_db_blocks . size ( ) ;
if ( ! sz )
return next_diff ;
uint64_t i = sz - 1 ;
for ( ; i < sz & & ! ( last_pow_diff & & last_pos_diff ) ; i - - )
{
if ( is_pos_block ( m_db_blocks [ i ] - > bl ) )
{
if ( ! last_pos_diff )
{
last_pos_diff = m_db_blocks [ i ] - > difficulty ;
}
} else
{
if ( ! last_pow_diff )
{
last_pow_diff = m_db_blocks [ i ] - > difficulty ;
}
}
}
if ( ! last_pos_diff )
return next_diff ;
return next_diff * last_pow_diff / last_pos_diff ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_adjusted_cumulative_difficulty_for_next_alt_pos ( alt_chain_type & alt_chain , uint64_t block_height , wide_difficulty_type next_diff , uint64_t connection_height ) const
{
wide_difficulty_type last_pow_diff = 0 ;
wide_difficulty_type last_pos_diff = 0 ;
for ( auto it = alt_chain . rbegin ( ) ; it ! = alt_chain . rend ( ) & & ! ( last_pos_diff & & last_pow_diff ) ; it + + )
{
if ( is_pos_block ( ( * it ) - > second . bl ) )
{
if ( ! last_pos_diff )
{
last_pos_diff = ( * it ) - > second . difficulty ;
}
}
else
{
if ( ! last_pow_diff )
{
last_pow_diff = ( * it ) - > second . difficulty ;
}
}
}
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
for ( uint64_t h = connection_height - 1 ; h ! = 0 & & ! ( last_pos_diff & & last_pow_diff ) ; - - h )
{
if ( is_pos_block ( m_db_blocks [ h ] - > bl ) )
{
if ( ! last_pos_diff )
{
last_pos_diff = m_db_blocks [ h ] - > difficulty ;
}
}
else
{
if ( ! last_pow_diff )
{
last_pow_diff = m_db_blocks [ h ] - > difficulty ;
}
}
}
if ( ! last_pos_diff )
return next_diff ;
return next_diff * last_pow_diff / last_pos_diff ;
}
//------------------------------------------------------------------
uint64_t blockchain_storage : : get_last_x_block_height ( bool pos ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint64_t sz = m_db_blocks . size ( ) ;
if ( ! sz )
return 0 ;
uint64_t i = sz - 1 ;
for ( ; i < sz ; i - - )
{
if ( is_pos_block ( m_db_blocks [ i ] - > bl ) = = pos )
return i ;
}
return 0 ;
}
//------------------------------------------------------------------
2019-07-17 21:01:52 +02:00
wide_difficulty_type blockchain_storage : : get_last_alt_x_block_cumulative_precise_adj_difficulty ( const alt_chain_type & alt_chain , uint64_t block_height , bool pos ) const
{
wide_difficulty_type res = 0 ;
get_last_alt_x_block_cumulative_precise_difficulty ( alt_chain , block_height , pos , res ) ;
return res ;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage : : get_last_alt_x_block_cumulative_precise_difficulty ( const alt_chain_type & alt_chain , uint64_t block_height , bool pos , wide_difficulty_type & cumulative_diff_precise_adj ) const
2018-12-27 18:50:45 +03:00
{
2019-08-08 21:15:09 +02:00
uint64_t main_chain_first_block = block_height ;
2018-12-27 18:50:45 +03:00
for ( auto it = alt_chain . rbegin ( ) ; it ! = alt_chain . rend ( ) ; it + + )
{
if ( is_pos_block ( ( * it ) - > second . bl ) = = pos )
2019-07-17 21:01:52 +02:00
{
cumulative_diff_precise_adj = ( * it ) - > second . cumulative_diff_precise_adjusted ;
2018-12-27 18:50:45 +03:00
return ( * it ) - > second . cumulative_diff_precise ;
2019-07-17 21:01:52 +02:00
}
2018-12-27 18:50:45 +03:00
main_chain_first_block = ( * it ) - > second . height - 1 ;
}
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
CHECK_AND_ASSERT_MES ( main_chain_first_block < m_db_blocks . size ( ) , false , " Intrnal error: main_chain_first_block( " < < main_chain_first_block < < " ) < m_blocks.size() ( " < < m_db_blocks . size ( ) < < " ) " ) ;
for ( uint64_t i = main_chain_first_block ; i ! = 0 ; i - - )
{
if ( is_pos_block ( m_db_blocks [ i ] - > bl ) = = pos )
2019-07-17 21:01:52 +02:00
{
cumulative_diff_precise_adj = m_db_blocks [ i ] - > cumulative_diff_precise_adjusted ;
2018-12-27 18:50:45 +03:00
return m_db_blocks [ i ] - > cumulative_diff_precise ;
2019-07-17 21:01:52 +02:00
}
2018-12-27 18:50:45 +03:00
}
2019-07-17 21:01:52 +02:00
cumulative_diff_precise_adj = 0 ;
2018-12-27 18:50:45 +03:00
return 0 ;
}
//------------------------------------------------------------------
bool blockchain_storage : : handle_block_to_main_chain ( const block & bl , const crypto : : hash & id , block_verification_context & bvc )
{
TIME_MEASURE_START_PD_MS ( block_processing_time_0_ms ) ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
TIME_MEASURE_START_PD ( block_processing_time_1 ) ;
if ( bl . prev_id ! = get_top_block_id ( ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < ENDL
< < " have wrong prev_id: " < < bl . prev_id < < ENDL
< < " expected: " < < get_top_block_id ( ) ) ;
return false ;
}
2019-01-11 23:46:29 +03:00
2018-12-27 18:50:45 +03:00
if ( ! check_block_timestamp_main ( bl ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < ENDL
< < " have invalid timestamp: " < < bl . timestamp ) ;
//add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed
bvc . m_verification_failed = true ;
return false ;
}
if ( m_checkpoints . is_in_checkpoint_zone ( get_current_blockchain_size ( ) ) )
{
m_is_in_checkpoint_zone = true ;
if ( ! m_checkpoints . check_block ( get_current_blockchain_size ( ) , id ) )
{
LOG_ERROR ( " CHECKPOINT VALIDATION FAILED " ) ;
bvc . m_verification_failed = true ;
return false ;
}
}
else
m_is_in_checkpoint_zone = false ;
crypto : : hash proof_hash = null_hash ;
uint64_t pos_coinstake_amount = 0 ;
wide_difficulty_type this_coin_diff = 0 ;
bool is_pos_bl = is_pos_block ( bl ) ;
//check if PoS allowed in this height
CHECK_AND_ASSERT_MES_CUSTOM ( ! ( is_pos_bl & & m_db_blocks . size ( ) < m_core_runtime_config . pos_minimum_heigh ) , false , bvc . m_verification_failed = true , " PoS block not allowed on height " < < m_db_blocks . size ( ) ) ;
//check proof of work
TIME_MEASURE_START_PD ( target_calculating_time_2 ) ;
wide_difficulty_type current_diffic = get_next_diff_conditional ( is_pos_bl ) ;
CHECK_AND_ASSERT_MES_CUSTOM ( current_diffic , false , bvc . m_verification_failed = true , " !!!!!!!!! difficulty overhead !!!!!!!!! " ) ;
TIME_MEASURE_FINISH_PD ( target_calculating_time_2 ) ;
TIME_MEASURE_START_PD ( longhash_calculating_time_3 ) ;
if ( is_pos_bl )
{
bool r = validate_pos_block ( bl , current_diffic , pos_coinstake_amount , this_coin_diff , proof_hash , id , false ) ;
2019-08-05 20:56:15 +02:00
CHECK_AND_ASSERT_MES_CUSTOM ( r , false , bvc . m_verification_failed = true , " validate_pos_block failed!! " ) ;
2018-12-27 18:50:45 +03:00
}
else
{
2019-01-11 23:46:29 +03:00
2019-03-25 01:30:20 +01:00
proof_hash = get_block_longhash ( bl ) ;
2018-12-27 18:50:45 +03:00
if ( ! check_hash ( proof_hash , current_diffic ) )
{
LOG_ERROR ( " Block with id: " < < id < < ENDL
2019-04-14 20:23:49 +02:00
< < " PoW hash: " < < proof_hash < < ENDL
< < " nonce: " < < bl . nonce < < ENDL
2019-04-14 23:41:24 +02:00
< < " header_mining_hash: " < < get_block_header_mining_hash ( bl ) < < ENDL
2019-04-14 20:23:49 +02:00
< < " expected difficulty: " < < current_diffic ) ;
2018-12-27 18:50:45 +03:00
bvc . m_verification_failed = true ;
return false ;
}
}
TIME_MEASURE_FINISH_PD ( longhash_calculating_time_3 ) ;
size_t aliases_count_befor_block = m_db_aliases . size ( ) ;
if ( ! prevalidate_miner_transaction ( bl , m_db_blocks . size ( ) , is_pos_bl ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id
< < " failed to pass prevalidation " ) ;
bvc . m_verification_failed = true ;
return false ;
}
size_t cumulative_block_size = 0 ;
size_t coinbase_blob_size = get_object_blobsize ( bl . miner_tx ) ;
/*
instead of complicated two - phase template construction and adjustment of cumulative size with block reward we
use CURRENCY_COINBASE_BLOB_RESERVED_SIZE as penalty - free coinbase transaction reservation .
*/
if ( coinbase_blob_size > CURRENCY_COINBASE_BLOB_RESERVED_SIZE )
{
cumulative_block_size + = coinbase_blob_size ;
LOG_PRINT_CYAN ( " Big coinbase transaction detected: coinbase_blob_size = " < < coinbase_blob_size , LOG_LEVEL_0 ) ;
}
std : : vector < uint64_t > block_fees ;
block_fees . reserve ( bl . tx_hashes . size ( ) ) ;
//process transactions
TIME_MEASURE_START_PD ( all_txs_insert_time_5 ) ;
if ( ! add_transaction_from_block ( bl . miner_tx , get_transaction_hash ( bl . miner_tx ) , id , get_current_blockchain_size ( ) , get_actual_timestamp ( bl ) ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < " failed to add transaction to blockchain storage " ) ;
bvc . m_verification_failed = true ;
return false ;
}
//preserve extra data to support alt pos blocks validation
//amount = > gindex_increment; a map to store how many txout_to_key outputs with such amount this block has in its transactions(including miner tx)
std : : unordered_map < uint64_t , uint32_t > gindices ;
append_per_block_increments_for_tx ( bl . miner_tx , gindices ) ;
size_t tx_processed_count = 0 ;
uint64_t fee_summary = 0 ;
2019-08-01 18:47:20 +02:00
uint64_t burned_coins = 0 ;
2018-12-27 18:50:45 +03:00
for ( const crypto : : hash & tx_id : bl . tx_hashes )
{
transaction tx ;
size_t blob_size = 0 ;
uint64_t fee = 0 ;
if ( ! m_tx_pool . take_tx ( tx_id , tx , blob_size , fee ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < " has at least one unknown transaction with id: " < < tx_id ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
//add_block_as_invalid(bl, id);
bvc . m_verification_failed = true ;
return false ;
}
append_per_block_increments_for_tx ( tx , gindices ) ;
//If we under checkpoints, ring signatures should be pruned
if ( m_is_in_checkpoint_zone )
{
tx . signatures . clear ( ) ;
tx . attachment . clear ( ) ;
}
TIME_MEASURE_START_PD ( tx_add_one_tx_time ) ;
TIME_MEASURE_START_PD ( tx_check_inputs_time ) ;
if ( ! check_tx_inputs ( tx , tx_id ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < " has at least one transaction (id: " < < tx_id < < " ) with wrong inputs. " ) ;
currency : : tx_verification_context tvc = AUTO_VAL_INIT ( tvc ) ;
bool add_res = m_tx_pool . add_tx ( tx , tvc , true , true ) ;
m_tx_pool . add_transaction_to_black_list ( tx ) ;
CHECK_AND_ASSERT_MES_NO_RET ( add_res , " handle_block_to_main_chain: failed to add transaction back to transaction pool " ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
add_block_as_invalid ( bl , id ) ;
2019-09-07 12:46:25 +03:00
LOG_PRINT_L0 ( " Block with id " < < id < < " added as invalid because of wrong inputs in transactions " ) ;
2018-12-27 18:50:45 +03:00
bvc . m_verification_failed = true ;
return false ;
}
TIME_MEASURE_FINISH_PD ( tx_check_inputs_time ) ;
2019-08-01 18:47:20 +02:00
burned_coins + = get_burned_amount ( tx ) ;
2018-12-27 18:50:45 +03:00
TIME_MEASURE_START_PD ( tx_prapare_append ) ;
uint64_t current_bc_size = get_current_blockchain_size ( ) ;
uint64_t actual_timestamp = get_actual_timestamp ( bl ) ;
TIME_MEASURE_FINISH_PD ( tx_prapare_append ) ;
TIME_MEASURE_START_PD ( tx_append_time ) ;
if ( ! add_transaction_from_block ( tx , tx_id , id , current_bc_size , actual_timestamp ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id < < " failed to add transaction to blockchain storage " ) ;
currency : : tx_verification_context tvc = AUTO_VAL_INIT ( tvc ) ;
bool add_res = m_tx_pool . add_tx ( tx , tvc , true , true ) ;
m_tx_pool . add_transaction_to_black_list ( tx ) ;
CHECK_AND_ASSERT_MES_NO_RET ( add_res , " handle_block_to_main_chain: failed to add transaction back to transaction pool " ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
TIME_MEASURE_FINISH_PD ( tx_append_time ) ;
//LOG_PRINT_L0("APPEND_TX_TIME: " << m_performance_data.tx_append_time.get_last_val());
TIME_MEASURE_FINISH_PD ( tx_add_one_tx_time ) ;
fee_summary + = fee ;
cumulative_block_size + = blob_size ;
+ + tx_processed_count ;
if ( fee )
block_fees . push_back ( fee ) ;
}
TIME_MEASURE_FINISH_PD ( all_txs_insert_time_5 ) ;
TIME_MEASURE_START_PD ( etc_stuff_6 ) ;
//check aliases count
if ( m_db_aliases . size ( ) - aliases_count_befor_block > MAX_ALIAS_PER_BLOCK )
{
LOG_PRINT_L0 ( " Block with id: " < < id
< < " have registered too many aliases " < < m_db_aliases . size ( ) - aliases_count_befor_block < < " , expected no more than " < < MAX_ALIAS_PER_BLOCK ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
uint64_t base_reward = 0 ;
2019-04-10 03:00:29 +02:00
boost : : multiprecision : : uint128_t already_generated_coins = m_db_blocks . size ( ) ? m_db_blocks . back ( ) - > already_generated_coins : 0 ;
2018-12-27 18:50:45 +03:00
if ( ! validate_miner_transaction ( bl , cumulative_block_size , fee_summary , base_reward , already_generated_coins ) )
{
LOG_PRINT_L0 ( " Block with id: " < < id
< < " have wrong miner transaction " ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
//fill block_extended_info
block_extended_info bei = boost : : value_initialized < block_extended_info > ( ) ;
bei . bl = bl ;
bei . height = m_db_blocks . size ( ) ;
bei . block_cumulative_size = cumulative_block_size ;
bei . difficulty = current_diffic ;
if ( is_pos_bl )
bei . stake_hash = proof_hash ;
2019-06-18 01:15:33 +03:00
//////////////////////////////////////////////////////////////////////////
2018-12-27 18:50:45 +03:00
2019-07-17 21:01:52 +02:00
//old style cumulative difficulty collecting
2018-12-27 18:50:45 +03:00
2019-07-17 21:01:52 +02:00
//precise difficulty - difficulty used to calculate next difficulty
uint64_t last_x_h = get_last_x_block_height ( is_pos_bl ) ;
if ( ! last_x_h )
bei . cumulative_diff_precise = current_diffic ;
else
bei . cumulative_diff_precise = m_db_blocks [ last_x_h ] - > cumulative_diff_precise + current_diffic ;
2018-12-27 18:50:45 +03:00
2019-07-17 21:01:52 +02:00
if ( m_db_blocks . size ( ) )
{
bei . cumulative_diff_adjusted = m_db_blocks . back ( ) - > cumulative_diff_adjusted ;
}
2019-06-18 01:15:33 +03:00
2019-07-17 21:01:52 +02:00
//adjusted difficulty - difficulty used to switch blockchain
wide_difficulty_type cumulative_diff_delta = 0 ;
if ( is_pos_bl )
cumulative_diff_delta = get_adjusted_cumulative_difficulty_for_next_pos ( current_diffic ) ;
else
cumulative_diff_delta = current_diffic ;
2019-06-18 01:15:33 +03:00
2019-07-17 21:01:52 +02:00
size_t sequence_factor = get_current_sequence_factor ( is_pos_bl ) ;
if ( bei . height > = m_core_runtime_config . pos_minimum_heigh )
cumulative_diff_delta = correct_difficulty_with_sequence_factor ( sequence_factor , cumulative_diff_delta ) ;
2019-06-18 01:15:33 +03:00
2019-08-07 08:03:44 +03:00
if ( bei . height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION & & is_pos_bl & & sequence_factor > BLOCK_POS_STRICT_SEQUENCE_LIMIT )
2019-07-17 21:01:52 +02:00
{
2019-08-07 08:03:44 +03:00
LOG_PRINT_RED_L0 ( " Block " < < id < < " @ " < < bei . height < < " has too big sequence factor: " < < sequence_factor < < " , rejected " ) ;
2019-07-17 21:01:52 +02:00
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
2019-06-18 01:15:33 +03:00
2019-07-17 21:01:52 +02:00
bei . cumulative_diff_adjusted + = cumulative_diff_delta ;
2019-06-18 01:15:33 +03:00
//////////////////////////////////////////////////////////////////////////
2019-07-17 21:01:52 +02:00
// rebuild cumulative_diff_precise_adjusted for whole period
wide_difficulty_type diff_precise_adj = correct_difficulty_with_sequence_factor ( sequence_factor , current_diffic ) ;
2019-07-26 18:53:42 +02:00
bei . cumulative_diff_precise_adjusted = last_x_h ? m_db_blocks [ last_x_h ] - > cumulative_diff_precise_adjusted + diff_precise_adj : diff_precise_adj ;
2019-06-17 11:15:12 +03:00
2019-07-17 21:01:52 +02:00
//////////////////////////////////////////////////////////////////////////
2018-12-27 18:50:45 +03:00
//etc
2019-08-01 18:47:20 +02:00
if ( already_generated_coins < burned_coins )
{
LOG_ERROR ( " Condition failed: already_generated_coins( " < < already_generated_coins < < " ) >= burned_coins( " < < burned_coins < < " ) " ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
bei . already_generated_coins = already_generated_coins - burned_coins + base_reward ;
2018-12-27 18:50:45 +03:00
auto blocks_index_ptr = m_db_blocks_index . get ( id ) ;
if ( blocks_index_ptr )
{
LOG_ERROR ( " block with id: " < < id < < " already in block indexes " ) ;
purge_block_data_from_blockchain ( bl , tx_processed_count ) ;
bvc . m_verification_failed = true ;
return false ;
}
if ( bei . height % ALIAS_MEDIAN_RECALC_INTERWAL )
{
bei . effective_tx_fee_median = get_tx_fee_median ( ) ;
}
else
{
if ( bei . height = = 0 )
bei . effective_tx_fee_median = 0 ;
else
{
LOG_PRINT_L0 ( " Recalculating median fee... " ) ;
std : : vector < uint64_t > blocks_medians ;
blocks_medians . reserve ( ALIAS_COAST_PERIOD ) ;
for ( uint64_t i = bei . height - 1 ; i ! = 0 & & ALIAS_COAST_PERIOD > = bei . height - i ; i - - )
{
uint64_t i_median = m_db_blocks [ i ] - > this_block_tx_fee_median ;
if ( i_median )
blocks_medians . push_back ( i_median ) ;
}
bei . effective_tx_fee_median = epee : : misc_utils : : median ( blocks_medians ) ;
LOG_PRINT_L0 ( " Median fee recalculated for h = " < < bei . height < < " as " < < print_money ( bei . effective_tx_fee_median ) ) ;
}
}
if ( block_fees . size ( ) )
{
uint64_t block_fee_median = epee : : misc_utils : : median ( block_fees ) ;
bei . this_block_tx_fee_median = block_fee_median ;
}
m_db_blocks_index . set ( id , bei . height ) ;
push_block_to_per_block_increments ( bei . height , gindices ) ;
TIME_MEASURE_FINISH_PD ( etc_stuff_6 ) ;
TIME_MEASURE_START_PD ( insert_time_4 ) ;
m_db_blocks . push_back ( bei ) ;
TIME_MEASURE_FINISH_PD ( insert_time_4 ) ;
TIME_MEASURE_FINISH_PD ( block_processing_time_1 ) ;
TIME_MEASURE_FINISH_PD_MS ( block_processing_time_0_ms ) ;
//print result
stringstream powpos_str_entry , timestamp_str_entry ;
if ( is_pos_bl )
{ // PoS
int64_t actual_ts = get_actual_timestamp ( bei . bl ) ; // signed int is intentionally used here
int64_t ts_diff = actual_ts - m_core_runtime_config . get_core_time ( ) ;
powpos_str_entry < < " PoS: \t " < < proof_hash < < " , stake amount: " < < print_money_brief ( pos_coinstake_amount ) < < " , final_difficulty: " < < this_coin_diff ;
timestamp_str_entry < < " , actual ts: " < < actual_ts < < " (diff: " < < std : : showpos < < ts_diff < < " s) block ts: " < < std : : noshowpos < < bei . bl . timestamp < < " (shift: " < < std : : showpos < < static_cast < int64_t > ( bei . bl . timestamp ) - actual_ts < < " ) " ;
}
else
{ // PoW
int64_t ts_diff = bei . bl . timestamp - static_cast < int64_t > ( m_core_runtime_config . get_core_time ( ) ) ;
powpos_str_entry < < " PoW: \t " < < proof_hash ;
timestamp_str_entry < < " , block ts: " < < bei . bl . timestamp < < " (diff: " < < std : : showpos < < ts_diff < < " s) " ;
}
2019-07-09 19:22:57 +02:00
//explanation of this code will be provided later with public announce
set_lost_tx_unmixable_for_height ( bei . height ) ;
2018-12-27 18:50:45 +03:00
LOG_PRINT_L1 ( " +++++ BLOCK SUCCESSFULLY ADDED " < < ( is_pos_bl ? " [PoS] " : " [PoW] " ) < < " Sq: " < < sequence_factor
< < ENDL < < " id: \t " < < id < < timestamp_str_entry . str ( )
< < ENDL < < powpos_str_entry . str ( )
< < ENDL < < " HEIGHT " < < bei . height < < " , difficulty: " < < current_diffic < < " , cumul_diff_precise: " < < bei . cumulative_diff_precise < < " , cumul_diff_adj: " < < bei . cumulative_diff_adjusted < < " (+ " < < cumulative_diff_delta < < " ) "
< < ENDL < < " block reward: " < < print_money_brief ( base_reward + fee_summary ) < < " ( " < < print_money_brief ( base_reward ) < < " + " < < print_money_brief ( fee_summary )
< < " ) " < < " , coinbase_blob_size: " < < coinbase_blob_size < < " , cumulative size: " < < cumulative_block_size < < " , tx_count: " < < bei . bl . tx_hashes . size ( )
2019-04-18 02:45:43 +02:00
< < " , timing: " < < block_processing_time_0_ms < < " ms "
< < " (micrsec: " < < block_processing_time_1
< < " ( " < < target_calculating_time_2 < < " ( " < < m_performance_data . target_calculating_enum_blocks . get_last_val ( ) < < " / " < < m_performance_data . target_calculating_calc . get_last_val ( ) < < " ) "
2018-12-27 18:50:45 +03:00
< < " / " < < longhash_calculating_time_3
< < " / " < < insert_time_4
< < " / " < < all_txs_insert_time_5
< < " / " < < etc_stuff_6
2019-04-18 02:45:43 +02:00
< < " )) " ) ;
2018-12-27 18:50:45 +03:00
on_block_added ( bei , id ) ;
bvc . m_added_to_main_chain = true ;
return true ;
}
2019-04-18 23:29:38 +02:00
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void blockchain_storage : : on_block_added ( const block_extended_info & bei , const crypto : : hash & id )
{
update_next_comulative_size_limit ( ) ;
m_timestamps_median_cache . clear ( ) ;
m_tx_pool . on_blockchain_inc ( bei . height , id ) ;
2019-04-18 23:29:38 +02:00
update_targetdata_cache_on_block_added ( bei ) ;
2018-12-27 18:50:45 +03:00
TIME_MEASURE_START_PD ( raise_block_core_event ) ;
rise_core_event ( CORE_EVENT_BLOCK_ADDED , void_struct ( ) ) ;
TIME_MEASURE_FINISH_PD ( raise_block_core_event ) ;
2019-04-18 23:29:38 +02:00
2018-12-27 18:50:45 +03:00
}
2019-01-11 23:46:29 +03:00
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void blockchain_storage : : on_block_removed ( const block_extended_info & bei )
{
m_tx_pool . on_blockchain_dec ( m_db_blocks . size ( ) - 1 , get_top_block_id ( ) ) ;
m_timestamps_median_cache . clear ( ) ;
2019-04-18 23:29:38 +02:00
update_targetdata_cache_on_block_removed ( bei ) ;
2018-12-27 18:50:45 +03:00
LOG_PRINT_L2 ( " block at height " < < bei . height < < " was removed from the blockchain " ) ;
}
//------------------------------------------------------------------
2019-04-18 23:29:38 +02:00
void blockchain_storage : : update_targetdata_cache_on_block_added ( const block_extended_info & bei )
{
2019-05-09 13:43:04 +02:00
CRITICAL_REGION_LOCAL ( m_targetdata_cache_lock ) ;
2019-04-18 23:29:38 +02:00
if ( bei . height = = 0 )
return ; //skip genesis
std : : list < std : : pair < wide_difficulty_type , uint64_t > > & targetdata_cache = is_pos_block ( bei . bl ) ? m_pos_targetdata_cache : m_pow_targetdata_cache ;
targetdata_cache . push_back ( std : : pair < wide_difficulty_type , uint64_t > ( bei . cumulative_diff_precise , bei . bl . timestamp ) ) ;
while ( targetdata_cache . size ( ) > TARGETDATA_CACHE_SIZE )
targetdata_cache . pop_front ( ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : update_targetdata_cache_on_block_removed ( const block_extended_info & bei )
{
2019-05-09 13:43:04 +02:00
CRITICAL_REGION_LOCAL ( m_targetdata_cache_lock ) ;
2019-04-18 23:29:38 +02:00
std : : list < std : : pair < wide_difficulty_type , uint64_t > > & targetdata_cache = is_pos_block ( bei . bl ) ? m_pos_targetdata_cache : m_pow_targetdata_cache ;
if ( targetdata_cache . size ( ) )
targetdata_cache . pop_back ( ) ;
if ( targetdata_cache . size ( ) < DIFFICULTY_WINDOW )
targetdata_cache . clear ( ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : load_targetdata_cache ( bool is_pos ) const
{
2019-05-09 13:43:04 +02:00
CRITICAL_REGION_LOCAL ( m_targetdata_cache_lock ) ;
2019-04-18 23:29:38 +02:00
std : : list < std : : pair < wide_difficulty_type , uint64_t > > & targetdata_cache = is_pos ? m_pos_targetdata_cache : m_pow_targetdata_cache ;
targetdata_cache . clear ( ) ;
uint64_t stop_ind = 0 ;
uint64_t blocks_size = m_db_blocks . size ( ) ;
size_t count = 0 ;
for ( uint64_t cur_ind = blocks_size - 1 ; cur_ind ! = stop_ind & & count < DIFFICULTY_WINDOW + 5 ; cur_ind - - )
{
auto beiptr = m_db_blocks [ cur_ind ] ;
bool is_pos_bl = is_pos_block ( beiptr - > bl ) ;
if ( is_pos ! = is_pos_bl )
continue ;
targetdata_cache . push_front ( std : : pair < wide_difficulty_type , uint64_t > ( beiptr - > cumulative_diff_precise , beiptr - > bl . timestamp ) ) ;
+ + count ;
}
}
//------------------------------------------------------------------
2018-12-27 18:50:45 +03:00
void blockchain_storage : : on_abort_transaction ( )
{
if ( m_event_handler ) m_event_handler - > on_clear_events ( ) ;
CHECK_AND_ASSERT_MES_NO_RET ( validate_blockchain_prev_links ( ) , " EPIC FAIL! 4 " ) ;
m_timestamps_median_cache . clear ( ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_next_comulative_size_limit ( )
{
std : : vector < size_t > sz ;
get_last_n_blocks_sizes ( sz , CURRENCY_REWARD_BLOCKS_WINDOW ) ;
uint64_t median = misc_utils : : median ( sz ) ;
if ( median < = CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE )
median = CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE ;
m_db_current_block_cumul_sz_limit = median * 2 ;
return true ;
}
//------------------------------------------------------------------
2019-07-19 18:39:00 +02:00
bool blockchain_storage : : prevalidate_block ( const block & bl )
{
if ( bl . major_version = = BLOCK_MAJOR_VERSION_INITAL & & get_block_height ( bl ) < = m_core_runtime_config . hard_fork1_starts_after_height )
return true ;
if ( bl . major_version ! = CURRENT_BLOCK_MAJOR_VERSION )
{
2019-08-06 14:16:44 +03:00
LOG_ERROR ( " prevalidation failed for block " < < get_block_hash ( bl ) < < " : major block version " < < static_cast < size_t > ( bl . major_version ) < < " is incorrect, " < < CURRENT_BLOCK_MAJOR_VERSION < < " is expected " < < ENDL
< < obj_to_json_str ( bl ) ) ;
2019-07-19 18:39:00 +02:00
return false ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : add_new_block ( const block & bl , block_verification_context & bvc )
2018-12-27 18:50:45 +03:00
{
try
{
m_db . begin_transaction ( ) ;
2019-07-19 18:39:00 +02:00
//block bl = bl_;
2018-12-27 18:50:45 +03:00
crypto : : hash id = get_block_hash ( bl ) ;
CRITICAL_REGION_LOCAL ( m_tx_pool ) ;
//CRITICAL_REGION_LOCAL1(m_read_lock);
if ( have_block ( id ) )
{
LOG_PRINT_L3 ( " block with id = " < < id < < " already exists " ) ;
bvc . m_already_exists = true ;
m_db . commit_transaction ( ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! 1 " ) ;
return false ;
}
2019-07-26 18:53:42 +02:00
if ( ! prevalidate_block ( bl ) )
2019-07-19 18:39:00 +02:00
{
LOG_PRINT_RED_L0 ( " block with id = " < < id < < " failed to prevalidate " ) ;
bvc . m_added_to_main_chain = false ;
bvc . m_verification_failed = true ;
m_db . commit_transaction ( ) ;
return false ;
}
2018-12-27 18:50:45 +03:00
//check that block refers to chain tail
2019-07-19 18:39:00 +02:00
2018-12-27 18:50:45 +03:00
if ( ! ( bl . prev_id = = get_top_block_id ( ) ) )
{
//chain switching or wrong block
bvc . m_added_to_main_chain = false ;
bool r = handle_alternative_block ( bl , id , bvc ) ;
if ( ! r | | bvc . m_verification_failed )
{
m_db . abort_transaction ( ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! 2.2 " ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
return r ;
}
m_db . commit_transaction ( ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! 2 " ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
return r ;
//never relay alternative blocks
}
bool res = handle_block_to_main_chain ( bl , id , bvc ) ;
if ( bvc . m_verification_failed | | ! res )
{
m_db . abort_transaction ( ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
on_abort_transaction ( ) ;
if ( m_event_handler ) m_event_handler - > on_clear_events ( ) ;
return res ;
}
m_db . commit_transaction ( ) ;
CHECK_AND_ASSERT_MES ( validate_blockchain_prev_links ( ) , false , " EPIC FAIL! 3 " ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
if ( m_event_handler ) m_event_handler - > on_complete_events ( ) ;
return res ;
}
catch ( const std : : exception & ex )
{
bvc . m_verification_failed = true ;
bvc . m_added_to_main_chain = false ;
m_db . abort_transaction ( ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
on_abort_transaction ( ) ;
LOG_ERROR ( " UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK: " < < ex . what ( ) ) ;
return false ;
}
catch ( . . . )
{
bvc . m_verification_failed = true ;
bvc . m_added_to_main_chain = false ;
m_db . abort_transaction ( ) ;
m_tx_pool . on_finalize_db_transaction ( ) ;
on_abort_transaction ( ) ;
LOG_ERROR ( " UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK. " ) ;
return false ;
}
}
//------------------------------------------------------------------
bool blockchain_storage : : truncate_blockchain ( uint64_t to_height )
{
m_db . begin_transaction ( ) ;
uint64_t inital_height = get_current_blockchain_size ( ) ;
while ( get_current_blockchain_size ( ) > to_height )
{
pop_block_from_blockchain ( ) ;
}
CRITICAL_REGION_LOCAL ( m_alternative_chains_lock ) ;
m_alternative_chains . clear ( ) ;
m_altblocks_keyimages . clear ( ) ;
m_alternative_chains_txs . clear ( ) ;
LOG_PRINT_MAGENTA ( " Blockchain truncated from " < < inital_height < < " to " < < get_current_blockchain_size ( ) , LOG_LEVEL_0 ) ;
m_db . commit_transaction ( ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : calc_tx_cummulative_blob ( const block & bl ) const
{
uint64_t cummulative_size_pool = 0 ;
uint64_t cummulative_size_calc = 0 ;
uint64_t cummulative_size_serialized = 0 ;
uint64_t i = 0 ;
std : : stringstream ss ;
uint64_t calculated_sz_miner = get_object_blobsize ( bl . miner_tx ) ;
blobdata b = t_serializable_object_to_blob ( bl . miner_tx ) ;
uint64_t serialized_size_miner = b . size ( ) ;
ss < < " [COINBASE]: " < < calculated_sz_miner < < " | " < < serialized_size_miner < < ENDL ;
for ( auto & h : bl . tx_hashes )
{
uint64_t calculated_sz = 0 ;
uint64_t serialized_size = 0 ;
tx_memory_pool : : tx_details td = AUTO_VAL_INIT ( td ) ;
bool in_pool = m_tx_pool . get_transaction ( h , td ) ;
if ( in_pool )
{
calculated_sz = get_object_blobsize ( td . tx ) ;
blobdata b = t_serializable_object_to_blob ( td . tx ) ;
serialized_size = b . size ( ) ;
if ( td . blob_size ! = calculated_sz | | calculated_sz ! = serialized_size )
{
LOG_PRINT_RED ( " BLOB SIZE MISMATCH IN TX: " < < h < < " calculated_sz: " < < calculated_sz
< < " serialized_size: " < < serialized_size
< < " td.blob_size: " < < td . blob_size , LOG_LEVEL_0 ) ;
}
cummulative_size_pool + = td . blob_size ;
}
else
{
auto tx_ptr = m_db_transactions . get ( h ) ;
CHECK_AND_ASSERT_MES ( tx_ptr , false , " tx " < < h < < " not found in blockchain nor tx_pool " ) ;
calculated_sz = get_object_blobsize ( tx_ptr - > tx ) ;
blobdata b = t_serializable_object_to_blob ( tx_ptr - > tx ) ;
serialized_size = b . size ( ) ;
if ( calculated_sz ! = serialized_size )
{
LOG_PRINT_RED ( " BLOB SIZE MISMATCH IN TX: " < < h < < " calculated_sz: " < < calculated_sz
< < " serialized_size: " < < serialized_size , LOG_LEVEL_0 ) ;
}
}
cummulative_size_calc + = calculated_sz ;
cummulative_size_serialized + = serialized_size ;
ss < < " [ " < < i < < " ] " < < h < < " : " < < calculated_sz < < ENDL ;
i + + ;
}
LOG_PRINT_MAGENTA ( " CUMMULATIVE_BLOCK_SIZE_TRACE: " < < get_block_hash ( bl ) < < ENDL
< < " cummulative_size_calc: " < < cummulative_size_calc < < ENDL
< < " cummulative_size_serialized: " < < cummulative_size_serialized < < ENDL
< < " cummulative_size_pool: " < < cummulative_size_pool < < ENDL
< < ss . str ( ) , LOG_LEVEL_0 ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : build_kernel ( const block & bl , stake_kernel & kernel , uint64_t & amount , const stake_modifier_type & stake_modifier ) const
{
CHECK_AND_ASSERT_MES ( bl . miner_tx . vin . size ( ) = = 2 , false , " wrong miner transaction " ) ;
CHECK_AND_ASSERT_MES ( bl . miner_tx . vin [ 0 ] . type ( ) = = typeid ( txin_gen ) , false , " wrong miner transaction " ) ;
CHECK_AND_ASSERT_MES ( bl . miner_tx . vin [ 1 ] . type ( ) = = typeid ( txin_to_key ) , false , " wrong miner transaction " ) ;
const txin_to_key & txin = boost : : get < txin_to_key > ( bl . miner_tx . vin [ 1 ] ) ;
CHECK_AND_ASSERT_MES ( txin . key_offsets . size ( ) , false , " wrong miner transaction " ) ;
amount = txin . amount ;
return build_kernel ( txin . amount , txin . k_image , kernel , stake_modifier , bl . timestamp ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : build_stake_modifier ( stake_modifier_type & sm , const alt_chain_type & alt_chain , uint64_t split_height , crypto : : hash * p_last_block_hash /* = nullptr */ ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
sm = stake_modifier_type ( ) ;
auto pbei_last_pos = get_last_block_of_type ( true , alt_chain , split_height ) ;
auto pbei_last_pow = get_last_block_of_type ( false , alt_chain , split_height ) ;
CHECK_AND_ASSERT_THROW_MES ( pbei_last_pow , " Internal error: pbei_last_pow is null " ) ;
if ( pbei_last_pos )
sm . last_pos_kernel_id = pbei_last_pos - > stake_hash ;
else
{
bool r = string_tools : : parse_tpod_from_hex_string ( POS_STARTER_KERNEL_HASH , sm . last_pos_kernel_id ) ;
2019-09-07 12:46:25 +03:00
CHECK_AND_ASSERT_MES ( r , false , " Failed to parse POS_STARTER_KERNEL_HASH " ) ;
2018-12-27 18:50:45 +03:00
}
sm . last_pow_id = get_block_hash ( pbei_last_pow - > bl ) ;
if ( p_last_block_hash ! = nullptr )
* p_last_block_hash = get_block_hash ( m_db_blocks . back ( ) - > bl ) ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : build_kernel ( uint64_t amount ,
const crypto : : key_image & ki ,
stake_kernel & kernel ,
const stake_modifier_type & stake_modifier ,
uint64_t timestamp ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
kernel = stake_kernel ( ) ;
kernel . kimage = ki ;
kernel . stake_modifier = stake_modifier ;
kernel . block_timestamp = timestamp ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : scan_pos ( const COMMAND_RPC_SCAN_POS : : request & sp , COMMAND_RPC_SCAN_POS : : response & rsp ) const
{
uint64_t timstamp_start = 0 ;
wide_difficulty_type basic_diff = 0 ;
CRITICAL_REGION_BEGIN ( m_read_lock ) ;
timstamp_start = m_db_blocks . back ( ) - > bl . timestamp ;
basic_diff = get_next_diff_conditional ( true ) ;
CRITICAL_REGION_END ( ) ;
stake_modifier_type sm = AUTO_VAL_INIT ( sm ) ;
bool r = build_stake_modifier ( sm ) ;
CHECK_AND_ASSERT_MES ( r , false , " failed to build_stake_modifier " ) ;
for ( size_t i = 0 ; i ! = sp . pos_entries . size ( ) ; i + + )
{
stake_kernel sk = AUTO_VAL_INIT ( sk ) ;
build_kernel ( sp . pos_entries [ i ] . amount , sp . pos_entries [ i ] . keyimage , sk , sm , 0 ) ;
for ( uint64_t ts = timstamp_start ; ts < timstamp_start + POS_SCAN_WINDOW ; ts + + )
{
sk . block_timestamp = ts ;
crypto : : hash kernel_hash = crypto : : cn_fast_hash ( & sk , sizeof ( sk ) ) ;
wide_difficulty_type this_coin_diff = basic_diff / sp . pos_entries [ i ] . amount ;
if ( ! check_hash ( kernel_hash , this_coin_diff ) )
continue ;
else
{
//found kernel
LOG_PRINT_GREEN ( " Found kernel: amount= " < < print_money ( sp . pos_entries [ i ] . amount ) < < " , key_image " < < sp . pos_entries [ i ] . keyimage , LOG_LEVEL_0 ) ;
rsp . index = i ;
rsp . block_timestamp = ts ;
rsp . status = CORE_RPC_STATUS_OK ;
return true ;
}
}
}
rsp . status = CORE_RPC_STATUS_NOT_FOUND ;
return false ;
}
//------------------------------------------------------------------
void blockchain_storage : : set_core_runtime_config ( const core_runtime_config & pc ) const
{
m_core_runtime_config = pc ;
m_services_mgr . set_core_runtime_config ( pc ) ;
}
//------------------------------------------------------------------
const core_runtime_config & blockchain_storage : : get_core_runtime_config ( ) const
{
return m_core_runtime_config ;
}
//------------------------------------------------------------------
std : : shared_ptr < const transaction_chain_entry > blockchain_storage : : get_tx_chain_entry ( const crypto : : hash & tx_hash ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
return m_db_transactions . find ( tx_hash ) ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_tx_chain_entry ( const crypto : : hash & tx_hash , transaction_chain_entry & entry ) const
{
auto ch_entry_ptr = get_tx_chain_entry ( tx_hash ) ;
if ( ! ch_entry_ptr )
return false ;
entry = * ch_entry_ptr ;
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_blockchain_prev_links ( size_t last_n_blocks_to_check /* = 10 */ ) const
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
TRY_ENTRY ( )
if ( m_db_blocks . size ( ) < 2 | | last_n_blocks_to_check < 1 )
return true ;
bool ok = true ;
for ( size_t attempt_counter = 1 ; attempt_counter < = 2 ; + + attempt_counter )
{
ok = true ;
for ( size_t height = m_db_blocks . size ( ) - 1 , blocks_to_check = last_n_blocks_to_check ; height ! = 0 & & blocks_to_check ! = 0 ; - - height , - - blocks_to_check )
{
auto bei_ptr = m_db_blocks [ height ] ;
auto & bei = * bei_ptr ;
if ( bei . height ! = height )
{
LOG_ERROR ( " bei.height = " < < bei . height < < " , expected: " < < height ) ;
ok = false ;
}
auto prev_bei_ptr = m_db_blocks [ height - 1 ] ;
auto & prev_bei = * prev_bei_ptr ;
crypto : : hash prev_id = get_block_hash ( prev_bei . bl ) ;
if ( bei . bl . prev_id ! = prev_id )
{
LOG_ERROR ( " EPIC FAIL: Block " < < get_block_hash ( bei . bl ) < < " @ " < < height < < " has prev_id == " < < bei . bl . prev_id < <
" while block @ " < < height - 1 < < " has id: " < < prev_id < < ENDL < <
" Block @ " < < height < < " : " < < ENDL < < currency : : obj_to_json_str ( bei . bl ) < < ENDL < <
" Block @ " < < height - 1 < < " : " < < ENDL < < currency : : obj_to_json_str ( prev_bei . bl ) ) ;
m_performance_data . epic_failure_happend = true ;
ok = false ;
}
}
if ( ok & & attempt_counter = = 1 )
{
break ;
}
else if ( ! ok & & attempt_counter = = 1 )
{
LOG_PRINT_YELLOW ( " ************* EPIC FAIL workaround attempt: try to reset DB cache and re-check ************* " , LOG_LEVEL_0 ) ;
reset_db_cache ( ) ;
continue ;
}
else if ( ! ok & & attempt_counter = = 2 )
{
LOG_PRINT_RED ( " ************* EPIC FAIL workaround attempt failed ************* " , LOG_LEVEL_0 ) ;
break ;
}
else if ( ok & & attempt_counter = = 2 )
{
LOG_PRINT_GREEN ( " ************* EPIC FAIL workaround attempt succeded! ************* " , LOG_LEVEL_0 ) ;
break ;
}
else
{
LOG_ERROR ( " should never get here " ) ;
return false ;
}
LOG_ERROR ( " should never get here also " ) ;
return false ;
}
return ok ;
CATCH_ENTRY2 ( false ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : push_block_to_per_block_increments ( uint64_t height_ , std : : unordered_map < uint64_t , uint32_t > & gindices )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint32_t height = static_cast < uint32_t > ( height_ ) ;
block_gindex_increments bgi = AUTO_VAL_INIT ( bgi ) ;
bgi . increments . reserve ( gindices . size ( ) ) ;
for ( auto & v : gindices )
bgi . increments . push_back ( gindex_increment : : construct ( v . first , v . second ) ) ;
m_db_per_block_gindex_incs . set ( height , bgi ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : pop_block_from_per_block_increments ( uint64_t height_ )
{
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
uint32_t height = static_cast < uint32_t > ( height_ ) ;
m_db_per_block_gindex_incs . erase ( height ) ;
}
//------------------------------------------------------------------
void blockchain_storage : : calculate_local_gindex_lookup_table_for_height ( uint64_t height , std : : map < uint64_t , uint64_t > & gindexes ) const
{
gindexes . clear ( ) ;
CHECK_AND_ASSERT_THROW_MES ( m_db_per_block_gindex_incs . size ( ) = = m_db_blocks . size ( ) , " invariant failure: m_db_per_block_gindex_incs.size() == " < < m_db_per_block_gindex_incs . size ( ) < < " , m_db_blocks.size() == " < < m_db_blocks . size ( ) ) ;
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
// Calculate total number of outputs for each amount in the main chain from given height
size_t top_block_height = static_cast < size_t > ( m_db_blocks . size ( ) ) - 1 ;
uint64_t h = height ;
while ( h < = top_block_height )
{
auto p = m_db_per_block_gindex_incs . get ( h ) ;
CHECK_AND_ASSERT_THROW_MES ( p , " null p for height " < < h ) ;
for ( auto & el : p - > increments )
gindexes [ el . amount ] + = el . increment ;
+ + h ;
}
// Translate total number of outputs for each amount into global output indexes these amounts had at given height.
// (those amounts which are not present in the main chain after given height have the same gindexes at given height and at the most recent block)
for ( auto it = gindexes . begin ( ) ; it ! = gindexes . end ( ) ; + + it )
{
uint64_t amount = it - > first ;
uint64_t gindexes_in_mainchain = m_db_outputs . get_item_size ( amount ) ;
CHECK_AND_ASSERT_THROW_MES ( gindexes_in_mainchain > = it - > second , " inconsistent gindex increments for amount " < < amount < < " : gindexes_in_mainchain == " < < gindexes_in_mainchain < < " , gindex increment for height " < < height < < " is " < < it - > second ) ;
it - > second = gindexes_in_mainchain - it - > second ;
}
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_alt_block_input ( const transaction & input_tx , std : : set < crypto : : key_image > & collected_keyimages , const crypto : : hash & bl_id , const crypto : : hash & input_tx_hash , size_t input_index ,
const std : : vector < crypto : : signature > & input_sigs , uint64_t split_height , const alt_chain_type & alt_chain , const std : : set < crypto : : hash > & alt_chain_block_ids , uint64_t & ki_lookuptime ,
uint64_t * p_max_related_block_height /* = nullptr */ ) const
{
// Main and alt chain outline:
//
// A- A- A- B- B- B- B- <-- main chain
// |
// \- C- C- C- <-- alt chain
// ^
// |
// split height
//
// List of possible cases
//
// | src tx | another | this tx |
// # | output | tx input | input | meaning
// ------------ bad cases ------------------
// b1 A A C already spent in main chain
// b2 A C C already spent in alt chain
// b3 C C C already spent in alt chain
// b4 B B/- C output not found (in case there's no valid outs in altchain C)
// ------------ good cases ------------------
// g1 A - C normal spending output from main chain A
// g2 A B C normal spending output from main chain A (although it is spent in main chain B as well)
// g3 C - C normal spending output from alt chain C
// g4 B,C - C src tx added to both main chain B and alt chain C
// g5 B,C B C src tx added to both main chain B and alt chain C, also spent in B
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
bool r = false ;
if ( p_max_related_block_height ! = nullptr )
* p_max_related_block_height = 0 ;
CHECK_AND_ASSERT_MES ( input_index < input_tx . vin . size ( ) & & input_tx . vin [ input_index ] . type ( ) = = typeid ( txin_to_key ) , false , " invalid input index: " < < input_index ) ;
const txin_to_key & input = boost : : get < txin_to_key > ( input_tx . vin [ input_index ] ) ;
// check case b1: key_image spent status in main chain, should be either non-spent or has spent height >= split_height
auto p = m_db_spent_keys . get ( input . k_image ) ;
CHECK_AND_ASSERT_MES ( p = = nullptr | | * p > = split_height , false , " key image " < < input . k_image < < " has been already spent in main chain at height " < < * p < < " , split height: " < < split_height ) ;
TIME_MEASURE_START ( ki_lookup_time ) ;
//check key_image in altchain
//check among this alt block already collected key images first
if ( collected_keyimages . find ( input . k_image ) ! = collected_keyimages . end ( ) )
{
// cases b2, b3
LOG_ERROR ( " key image " < < input . k_image < < " already spent in this alt block " ) ;
return false ;
}
auto ki_it = m_altblocks_keyimages . find ( input . k_image ) ;
if ( ki_it ! = m_altblocks_keyimages . end ( ) )
{
//have some entry for this key image. Check if this key image belongs to this alt chain
const std : : list < crypto : : hash > & key_image_block_ids = ki_it - > second ;
for ( auto & h : key_image_block_ids )
{
if ( alt_chain_block_ids . find ( h ) ! = alt_chain_block_ids . end ( ) )
{
// cases b2, b3
LOG_ERROR ( " key image " < < input . k_image < < " already spent in altchain " ) ;
return false ;
}
}
}
//update altchain with key image
collected_keyimages . insert ( input . k_image ) ;
TIME_MEASURE_FINISH ( ki_lookup_time ) ;
ki_lookuptime = ki_lookup_time ;
std : : vector < txout_v > abs_key_offsets = relative_output_offsets_to_absolute ( input . key_offsets ) ;
CHECK_AND_ASSERT_MES ( abs_key_offsets . size ( ) > 0 & & abs_key_offsets . size ( ) = = input . key_offsets . size ( ) , false , " internal error: abs_key_offsets.size()== " < < abs_key_offsets . size ( ) < < " , input.key_offsets.size()== " < < input . key_offsets . size ( ) ) ;
// eventually we should found all public keys for all outputs this input refers to, for checking ring signature
std : : vector < crypto : : public_key > pub_keys ( abs_key_offsets . size ( ) , null_pkey ) ;
//
// let's try to resolve references and populate pub keys container for check_tokey_input()
//
uint64_t global_outs_for_amount = 0 ;
//figure out if this amount touched alt_chain amount's index and if it is, get
bool amount_touched_altchain = false ;
//auto abg_it = abei.gindex_lookup_table.find(input.amount);
//if (abg_it == abei.gindex_lookup_table.end())
if ( ! alt_chain . empty ( ) )
{
auto abg_it = alt_chain . back ( ) - > second . gindex_lookup_table . find ( input . amount ) ;
if ( abg_it ! = alt_chain . back ( ) - > second . gindex_lookup_table . end ( ) )
{
amount_touched_altchain = true ;
//Notice: since transactions is not allowed to refer to each other in one block, then we can consider that index in
//tx input would be always less then top for previous block, so just take it
global_outs_for_amount = abg_it - > second ;
}
else
{
//quite easy,
global_outs_for_amount = m_db_outputs . get_item_size ( input . amount ) ;
}
}
else
{
//quite easy,
global_outs_for_amount = m_db_outputs . get_item_size ( input . amount ) ;
}
CHECK_AND_ASSERT_MES ( pub_keys . size ( ) = = abs_key_offsets . size ( ) , false , " pub_keys.size()== " < < pub_keys . size ( ) < < " != abs_key_offsets.size()== " < < abs_key_offsets . size ( ) ) ; // just a little bit of paranoia
std : : vector < const crypto : : public_key * > pub_key_pointers ;
for ( size_t pk_n = 0 ; pk_n < pub_keys . size ( ) ; + + pk_n )
{
crypto : : public_key & pk = pub_keys [ pk_n ] ;
crypto : : hash tx_id = null_hash ;
uint64_t out_n = UINT64_MAX ;
auto & off = abs_key_offsets [ pk_n ] ;
if ( off . type ( ) = = typeid ( uint64_t ) )
{
uint64_t offset_gindex = boost : : get < uint64_t > ( off ) ;
CHECK_AND_ASSERT_MES ( offset_gindex < global_outs_for_amount , false ,
" invalid global output index " < < offset_gindex < < " for amount= " < < input . amount < <
" , max is " < < global_outs_for_amount < <
" , referred to by offset # " < < pk_n < <
" , amount_touched_altchain = " < < amount_touched_altchain ) ;
if ( amount_touched_altchain )
{
bool found_the_key = false ;
for ( auto alt_it = alt_chain . rbegin ( ) ; alt_it ! = alt_chain . rend ( ) ; alt_it + + )
{
auto it_aag = ( * alt_it ) - > second . gindex_lookup_table . find ( input . amount ) ;
if ( it_aag = = ( * alt_it ) - > second . gindex_lookup_table . end ( ) )
{
CHECK_AND_ASSERT_MES ( alt_it ! = alt_chain . rbegin ( ) , false , " internal error: was marked as amount_touched_altchain but unable to find on first entry " ) ;
//item supposed to be in main chain
break ;
}
if ( offset_gindex > = it_aag - > second )
{
//GOT IT!!
//TODO: At the moment we ignore check of mix_attr again mixing to simplify alt chain check, but in future consider it for stronger validation
uint64_t local_offset = offset_gindex - it_aag - > second ;
auto & alt_keys = ( * alt_it ) - > second . outputs_pub_keys ;
CHECK_AND_ASSERT_MES ( local_offset < alt_keys [ input . amount ] . size ( ) , false , " Internal error: local_offset= " < < local_offset < < " while alt_keys[ " < < input . amount < < " ].size()= " < < alt_keys . size ( ) ) ;
pk = alt_keys [ input . amount ] [ local_offset ] ;
pub_key_pointers . push_back ( & pk ) ;
found_the_key = true ;
break ;
}
}
if ( found_the_key )
break ;
//otherwise lookup in main chain index
}
auto p = m_db_outputs . get_subitem ( input . amount , offset_gindex ) ;
CHECK_AND_ASSERT_MES ( p ! = nullptr , false , " global output was not found, amount: " < < input . amount < < " , gindex: " < < offset_gindex < < " , referred to by offset # " < < pk_n ) ;
tx_id = p - > tx_id ;
out_n = p - > out_no ;
}
else if ( off . type ( ) = = typeid ( ref_by_id ) )
{
auto & rbi = boost : : get < ref_by_id > ( off ) ;
tx_id = rbi . tx_id ;
out_n = rbi . n ;
}
auto p = m_db_transactions . get ( tx_id ) ;
CHECK_AND_ASSERT_MES ( p ! = nullptr & & out_n < p - > tx . vout . size ( ) , false , " can't find output # " < < out_n < < " for tx " < < tx_id < < " referred by offset # " < < pk_n ) ;
auto & t = p - > tx . vout [ out_n ] . target ;
CHECK_AND_ASSERT_MES ( t . type ( ) = = typeid ( txout_to_key ) , false , " txin_to_key input offset # " < < pk_n < < " refers to incorrect output type " < < t . type ( ) . name ( ) ) ;
auto & out_tk = boost : : get < txout_to_key > ( t ) ;
pk = out_tk . key ;
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter ( out_tk . mix_attr , abs_key_offsets . size ( ) - 1 ) ;
CHECK_AND_ASSERT_MES ( mixattr_ok , false , " input offset # " < < pk_n < < " violates mixin restrictions: mix_attr = " < < static_cast < uint32_t > ( out_tk . mix_attr ) < < " , input's key_offsets.size = " < < abs_key_offsets . size ( ) ) ;
// case b4 (make sure source tx in the main chain is preceding split point, otherwise this referece is invalid)
CHECK_AND_ASSERT_MES ( p - > m_keeper_block_height < split_height , false , " input offset # " < < pk_n < < " refers to main chain tx " < < tx_id < < " at height " < < p - > m_keeper_block_height < < " while split height is " < < split_height ) ;
if ( p_max_related_block_height ! = nullptr & & * p_max_related_block_height < p - > m_keeper_block_height )
* p_max_related_block_height = p - > m_keeper_block_height ;
// TODO: consider checking p->tx for unlock time validity as it's checked in get_output_keys_for_input_with_checks()
// make sure it was actually found
2019-06-04 21:46:56 +03:00
// let's disable this check due to missing equal check in main chain validation code
//TODO: implement more strict validation with next hard fork
//CHECK_AND_ASSERT_MES(pk != null_pkey, false, "Can't determine output public key for offset " << pk_n << " in related tx: " << tx_id << ", out_n = " << out_n);
2018-12-27 18:50:45 +03:00
pub_key_pointers . push_back ( & pk ) ;
}
// do input checks (attachment_info, ring signature and extra signature, etc.)
r = check_tokey_input ( input_tx , input_index , input , input_tx_hash , input_sigs , pub_key_pointers ) ;
CHECK_AND_ASSERT_MES ( r , false , " to_key input validation failed " ) ;
// TODO: consider checking input_tx for valid extra attachment info as it's checked in check_tx_inputs()
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : validate_alt_block_ms_input ( const transaction & input_tx , const crypto : : hash & input_tx_hash , size_t input_index , const std : : vector < crypto : : signature > & input_sigs , uint64_t split_height , const alt_chain_type & alt_chain ) const
{
// Main and alt chain outline:
//
// A- A- A- B- B- B- B- <-- main chain
// |
// \- C- C- C- <-- alt chain
// ^
// |
// split height
//
// List of possible cases
//
// | src tx | another | this tx |
// # | output | tx input | input | meaning
// ------------ bad cases ------------------
// b1 A A C already spent in main chain
// b2 A C C already spent in alt chain
// b3 C C C already spent in alt chain
// b4 B B/- C output not found (in case there's no valid outs in altchain C)
// ------------ good cases ------------------
// g1 A - C normal spending output from main chain A
// g2 A B C normal spending output from main chain A (although it is spent in main chain B as well)
// g3 C - C normal spending output from alt chain C
// g4 B,C - C src tx added to both main chain B and alt chain C
// g5 B,C B C src tx added to both main chain B and alt chain C, also spent in B
CRITICAL_REGION_LOCAL ( m_read_lock ) ;
bool r = false ;
CHECK_AND_ASSERT_MES ( input_index < input_tx . vin . size ( ) & & input_tx . vin [ input_index ] . type ( ) = = typeid ( txin_multisig ) , false , " invalid ms input index: " < < input_index < < " or type " ) ;
const txin_multisig & input = boost : : get < txin_multisig > ( input_tx . vin [ input_index ] ) ;
// check corresponding ms out in the main chain
auto p = m_db_multisig_outs . get ( input . multisig_out_id ) ;
if ( p ! = nullptr )
{
// check case b1 (note: need to distinguish from case g2)
CHECK_AND_ASSERT_MES ( p - > spent_height = = 0 | | p - > spent_height > = split_height , false , " ms output was already spent in main chain at height " < < p - > spent_height < < " while split_height is " < < split_height ) ;
const crypto : : hash & source_tx_id = p - > tx_id ;
auto p_source_tx = m_db_transactions . get ( source_tx_id ) ;
CHECK_AND_ASSERT_MES ( p_source_tx ! = nullptr , false , " source tx for ms output " < < source_tx_id < < " was not found, ms out id: " < < input . multisig_out_id ) ;
if ( p_source_tx - > m_keeper_block_height < split_height )
{
// cases g1, g2
return check_ms_input ( input_tx , input_index , input , input_tx_hash , input_sigs , p_source_tx - > tx , p - > out_no ) ;
}
// p_source_tx is above split_height in main chain B, so it can't be a source for this input
// do nohting here and proceed to alt chain scan for possible cases b4, g4, g5
}
else
{
// ms out was not found in DB
// do nothing here and proceed to alt chain scan for possible cases b2, b3, g3, g4, g5
}
// walk the alt chain backward (from the the last added alt block towards split point -- it important as we stop scanning when find correct output, need to make sure it was not already spent)
for ( alt_chain_type : : const_reverse_iterator it = alt_chain . rbegin ( ) ; it ! = alt_chain . rend ( ) ; + + it )
{
const block_extended_info & bei = ( * it ) - > second ;
const block & b = bei . bl ;
bool output_found = false ;
auto tx_altchain_checker = [ & ] ( const transaction & tx , const crypto : : hash & tx_id ) - > bool
{
// check ms out being already spent in current alt chain
for ( auto & in : tx . vin )
{
if ( in . type ( ) = = typeid ( txin_multisig ) )
{
// check cases b2, b3
CHECK_AND_ASSERT_MES ( input . multisig_out_id ! = boost : : get < txin_multisig > ( in ) . multisig_out_id , false , " ms out " < < input . multisig_out_id < < " has been already spent in altchain by tx " < < tx_id < < " in block " < < get_block_hash ( b ) < < " height " < < bei . height ) ;
}
}
for ( size_t out_n = 0 ; out_n < tx . vout . size ( ) ; + + out_n )
{
const tx_out & out = tx . vout [ out_n ] ;
if ( out . target . type ( ) = = typeid ( txout_multisig ) )
{
const crypto : : hash & ms_out_id = get_multisig_out_id ( tx , out_n ) ;
if ( ms_out_id = = input . multisig_out_id )
{
// cases g3, g4, g5
output_found = true ;
return check_ms_input ( input_tx , input_index , input , input_tx_hash , input_sigs , tx , out_n ) ;
}
}
}
return true ;
} ;
// for each alt block look into miner_tx and txs
CHECK_AND_ASSERT_MES ( tx_altchain_checker ( b . miner_tx , get_transaction_hash ( b . miner_tx ) ) , false , " tx_altchain_checker failed for miner tx " ) ;
if ( output_found )
return true ; // g3, g4, g5
for ( auto & tx_id : b . tx_hashes )
{
std : : shared_ptr < transaction > tx_ptr ;
r = get_transaction_from_pool_or_db ( tx_id , tx_ptr , split_height ) ;
CHECK_AND_ASSERT_MES ( r , false , " can't get transaction " < < tx_id < < " for alt block " < < get_block_hash ( b ) < < " height " < < bei . height < < " , split height is " < < split_height ) ;
transaction & tx = * tx_ptr ;
CHECK_AND_ASSERT_MES ( tx_altchain_checker ( tx , tx_id ) , false , " tx_altchain_checker failed for tx " < < tx_id ) ;
if ( output_found )
return true ; // g3, g4, g5
}
}
// case b4
LOG_ERROR ( " ms outout " < < input . multisig_out_id < < " was not found neither in main chain, nor in alt chain " ) ;
return false ;
}
//------------------------------------------------------------------
bool blockchain_storage : : get_transaction_from_pool_or_db ( const crypto : : hash & tx_id , std : : shared_ptr < transaction > & tx_ptr , uint64_t min_allowed_block_height /* = 0 */ ) const
{
tx_ptr . reset ( new transaction ( ) ) ;
if ( ! m_tx_pool . get_transaction ( tx_id , * tx_ptr ) ) // first try to get from the pool
{
auto p = m_db_transactions . get ( tx_id ) ; // if not found in the pool -- get from the DB
CHECK_AND_ASSERT_MES ( p ! = nullptr , false , " can't get tx " < < tx_id < < " neither from the pool, nor from db_transactions " ) ;
CHECK_AND_ASSERT_MES ( p - > m_keeper_block_height > = min_allowed_block_height , false , " tx " < < tx_id < < " found in the main chain at height " < < p - > m_keeper_block_height < < " while required min allowed height is " < < min_allowed_block_height ) ;
* tx_ptr = p - > tx ;
}
return true ;
}
//------------------------------------------------------------------
bool blockchain_storage : : update_alt_out_indexes_for_tx_in_block ( const transaction & tx , alt_block_extended_info & abei ) const
{
//add tx outputs to gindex_lookup_table
for ( auto o : tx . vout )
{
if ( o . target . type ( ) = = typeid ( txout_to_key ) )
{
//LOG_PRINT_MAGENTA("ALT_OUT KEY ON H[" << abei.height << "] AMOUNT: " << o.amount, LOG_LEVEL_0);
// first, look at local gindexes tables
if ( abei . gindex_lookup_table . find ( o . amount ) = = abei . gindex_lookup_table . end ( ) )
{
// amount was not found in altchain gindexes container, start indexing from current main chain gindex
abei . gindex_lookup_table [ o . amount ] = m_db_outputs . get_item_size ( o . amount ) ;
//LOG_PRINT_MAGENTA("FIRST TOUCH: size=" << abei.gindex_lookup_table[o.amount], LOG_LEVEL_0);
}
abei . outputs_pub_keys [ o . amount ] . push_back ( boost : : get < txout_to_key > ( o . target ) . key ) ;
//TODO: At the moment we ignore check of mix_attr again mixing to simplify alt chain check, but in future consider it for stronger validation
}
}
return true ;
}
bool blockchain_storage : : validate_alt_block_txs ( const block & b , const crypto : : hash & id , std : : set < crypto : : key_image > & collected_keyimages , alt_block_extended_info & abei , const alt_chain_type & alt_chain , uint64_t split_height , uint64_t & ki_lookup_time_total ) const
{
uint64_t height = abei . height ;
bool r = false ;
std : : set < crypto : : hash > alt_chain_block_ids ;
alt_chain_block_ids . insert ( id ) ;
// prepare data structure for output global indexes tracking within current alt chain
if ( alt_chain . size ( ) )
{
//TODO: in this two lines we scarify memory to earn speed for algo, but need to be careful with RAM consuming on long switches
abei . gindex_lookup_table = alt_chain . back ( ) - > second . gindex_lookup_table ;
//adjust indices for next alt block entry according to emount of pubkeys in txs of prev block
auto & prev_alt_keys = alt_chain . back ( ) - > second . outputs_pub_keys ;
for ( auto it = prev_alt_keys . begin ( ) ; it ! = prev_alt_keys . end ( ) ; it + + )
{
auto it_amont_in_abs_ind = abei . gindex_lookup_table . find ( it - > first ) ;
CHECK_AND_ASSERT_MES ( it_amont_in_abs_ind ! = abei . gindex_lookup_table . end ( ) , false , " internal error: not found amount " < < it - > first < < " in gindex_lookup_table " ) ;
//increase index starter for amount of outputs in prev block
it_amont_in_abs_ind - > second + = it - > second . size ( ) ;
}
//generate set of alt block ids
for ( auto & ch : alt_chain )
{
alt_chain_block_ids . insert ( get_block_hash ( ch - > second . bl ) ) ;
}
}
else
{
// initialize alt chain entry with initial gindexes
calculate_local_gindex_lookup_table_for_height ( split_height , abei . gindex_lookup_table ) ;
}
if ( is_pos_block ( b ) )
{
// check PoS block miner tx in a special way
CHECK_AND_ASSERT_MES ( b . miner_tx . signatures . size ( ) = = 1 & & b . miner_tx . vin . size ( ) = = 2 , false , " invalid PoS block's miner_tx, signatures size = " < < b . miner_tx . signatures . size ( ) < < " , miner_tx.vin.size() = " < < b . miner_tx . vin . size ( ) ) ;
uint64_t max_related_block_height = 0 ;
uint64_t ki_lookup = 0 ;
r = validate_alt_block_input ( b . miner_tx , collected_keyimages , id , get_block_hash ( b ) , 1 , b . miner_tx . signatures [ 0 ] , split_height , alt_chain , alt_chain_block_ids , ki_lookup , & max_related_block_height ) ;
CHECK_AND_ASSERT_MES ( r , false , " miner tx " < < get_transaction_hash ( b . miner_tx ) < < " : validation failed " ) ;
ki_lookup_time_total + = ki_lookup ;
// check stake age
uint64_t coinstake_age = height - max_related_block_height - 1 ;
CHECK_AND_ASSERT_MES ( coinstake_age > = m_core_runtime_config . min_coinstake_age , false ,
" miner tx's coinstake age is " < < coinstake_age < < " , that is less than minimum required " < < m_core_runtime_config . min_coinstake_age < < " ; max_related_block_height == " < < max_related_block_height ) ;
}
update_alt_out_indexes_for_tx_in_block ( b . miner_tx , abei ) ;
2019-08-07 18:41:18 +03:00
CHECK_AND_ASSERT_MES ( validate_tx_for_hardfork_specific_terms ( b . miner_tx , null_hash , height ) , false , " miner tx hardfork-specific validation failed " ) ;
2018-12-27 18:50:45 +03:00
for ( auto tx_id : b . tx_hashes )
{
std : : shared_ptr < transaction > tx_ptr ;
CHECK_AND_ASSERT_MES ( get_transaction_from_pool_or_db ( tx_id , tx_ptr , split_height ) , false , " failed to get alt block tx " < < tx_id < < " with split_height == " < < split_height ) ;
transaction & tx = * tx_ptr ;
CHECK_AND_ASSERT_MES ( tx . signatures . size ( ) = = tx . vin . size ( ) , false , " invalid tx: tx.signatures.size() == " < < tx . signatures . size ( ) < < " , tx.vin.size() == " < < tx . vin . size ( ) ) ;
for ( size_t n = 0 ; n < tx . vin . size ( ) ; + + n )
{
if ( tx . vin [ n ] . type ( ) = = typeid ( txin_to_key ) )
{
uint64_t ki_lookup = 0 ;
r = validate_alt_block_input ( tx , collected_keyimages , id , tx_id , n , tx . signatures [ n ] , split_height , alt_chain , alt_chain_block_ids , ki_lookup ) ;
CHECK_AND_ASSERT_MES ( r , false , " tx " < < tx_id < < " , input # " < < n < < " : validation failed " ) ;
ki_lookup_time_total + = ki_lookup ;
}
else if ( tx . vin [ n ] . type ( ) = = typeid ( txin_multisig ) )
{
r = validate_alt_block_ms_input ( tx , tx_id , n , tx . signatures [ n ] , split_height , alt_chain ) ;
CHECK_AND_ASSERT_MES ( r , false , " tx " < < tx_id < < " , input # " < < n < < " (multisig): validation failed " ) ;
}
else if ( tx . vin [ n ] . type ( ) = = typeid ( txin_gen ) )
{
// genesis can't be in tx_hashes
CHECK_AND_ASSERT_MES ( false , false , " input # " < < n < < " has unexpected type ( " < < tx . vin [ n ] . type ( ) . name ( ) < < " , genesis can't be in tx_hashes), tx " < < tx_id ) ;
}
else
{
CHECK_AND_ASSERT_MES ( false , false , " input # " < < n < < " has unexpected type ( " < < tx . vin [ n ] . type ( ) . name ( ) < < " ), tx " < < tx_id ) ;
}
}
2019-08-07 18:41:18 +03:00
CHECK_AND_ASSERT_MES ( validate_tx_for_hardfork_specific_terms ( tx , tx_id , height ) , false , " tx " < < tx_id < < " : hardfork-specific validation failed " ) ;
2018-12-27 18:50:45 +03:00
// Updating abei (and not updating alt_chain) during this cycle is safe because txs in the same block can't reference one another,
// so only valid references are either to previous alt blocks (accessed via alt_chain) or to main chain blocks.
update_alt_out_indexes_for_tx_in_block ( tx , abei ) ;
}
return true ;
}