2014-08-13 10:38:35 +00:00
// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
# include <include_base_utils.h>
# include "blockchain_storage.h"
2014-03-03 22:07:58 +00:00
# include <algorithm>
# include <cstdio>
2014-08-13 10:51:37 +00:00
2014-03-03 22:07:58 +00:00
# include <boost/archive/binary_oarchive.hpp>
# include <boost/archive/binary_iarchive.hpp>
2014-08-13 10:51:37 +00:00
// epee
# include "file_io_utils.h"
# include "misc_language.h"
# include "profile_tools.h"
# include "time_helper.h"
# include "common/boost_serialization_helper.h"
2014-03-03 22:07:58 +00:00
# include "cryptonote_format_utils.h"
# include "cryptonote_boost_serialization.h"
2014-08-13 10:51:37 +00:00
# include "rpc/core_rpc_server_commands_defs.h"
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
//namespace {
// std::string hashHex(const crypto::hash& hash) {
// std::string result;
// for (size_t i = 0; i < crypto::HASH_SIZE; ++i) {
// result += "0123456789ABCDEF"[static_cast<uint8_t>(hash.data[i]) >> 4];
// result += "0123456789ABCDEF"[static_cast<uint8_t>(hash.data[i]) & 15];
// }
//
// return result;
// }
//}
namespace {
std : : string appendPath ( const std : : string & path , const std : : string & fileName ) {
std : : string result = path ;
if ( ! result . empty ( ) ) {
result + = ' / ' ;
}
result + = fileName ;
return result ;
}
}
namespace std {
bool operator < ( const crypto : : hash & hash1 , const crypto : : hash & hash2 ) {
return memcmp ( & hash1 , & hash2 , crypto : : HASH_SIZE ) < 0 ;
}
bool operator < ( const crypto : : key_image & keyImage1 , const crypto : : key_image & keyImage2 ) {
return memcmp ( & keyImage1 , & keyImage2 , 32 ) < 0 ;
}
}
2014-03-03 22:07:58 +00:00
using namespace cryptonote ;
DISABLE_VS_WARNINGS ( 4267 )
2014-06-20 15:56:33 +00:00
namespace cryptonote {
struct transaction_chain_entry {
2014-08-13 10:51:37 +00:00
Transaction tx ;
2014-06-20 15:56:33 +00:00
uint64_t m_keeper_block_height ;
size_t m_blob_size ;
std : : vector < uint64_t > m_global_output_indexes ;
template < class archive_t > void serialize ( archive_t & ar , unsigned int version ) ;
} ;
struct block_extended_info {
2014-08-13 10:51:37 +00:00
Block bl ;
2014-06-20 15:56:33 +00:00
uint64_t height ;
size_t block_cumulative_size ;
difficulty_type cumulative_difficulty ;
uint64_t already_generated_coins ;
template < class archive_t > void serialize ( archive_t & ar , unsigned int version ) ;
} ;
template < class archive_t > void transaction_chain_entry : : serialize ( archive_t & ar , unsigned int version ) {
ar & tx ;
ar & m_keeper_block_height ;
ar & m_blob_size ;
ar & m_global_output_indexes ;
}
template < class archive_t > void block_extended_info : : serialize ( archive_t & ar , unsigned int version ) {
ar & bl ;
ar & height ;
ar & cumulative_difficulty ;
ar & block_cumulative_size ;
ar & already_generated_coins ;
}
}
2014-08-13 10:51:37 +00:00
template < class Archive > void cryptonote : : blockchain_storage : : TransactionEntry : : serialize ( Archive & archive , unsigned int version ) {
2014-06-20 15:56:33 +00:00
archive & tx ;
}
2014-08-13 10:51:37 +00:00
template < class Archive > void cryptonote : : blockchain_storage : : BlockEntry : : serialize ( Archive & archive , unsigned int version ) {
2014-06-20 15:56:33 +00:00
archive & bl ;
archive & height ;
archive & block_cumulative_size ;
archive & cumulative_difficulty ;
archive & already_generated_coins ;
archive & transactions ;
}
template < class Archive > void cryptonote : : blockchain_storage : : TransactionIndex : : serialize ( Archive & archive , unsigned int version ) {
archive & block ;
archive & transaction ;
}
2014-08-13 10:51:37 +00:00
template < class Archive > void cryptonote : : blockchain_storage : : MultisignatureOutputUsage : : serialize ( Archive & archive , unsigned int version ) {
archive & transactionIndex ;
archive & outputIndex ;
archive & isUsed ;
}
2014-06-20 15:56:33 +00:00
namespace cryptonote {
# define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13
template < class archive_t > void blockchain_storage : : serialize ( archive_t & ar , const unsigned int version ) {
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
if ( version < 12 ) {
LOG_PRINT_L0 ( " Detected blockchain of unsupported version, migration is not possible. " ) ;
return ;
}
LOG_PRINT_L0 ( " Blockchain of previous version detected, migrating. This may take several minutes, please be patient... " ) ;
std : : vector < block_extended_info > blocks ;
ar & blocks ;
{
std : : unordered_map < crypto : : hash , size_t > blocks_index ;
ar & blocks_index ;
}
std : : unordered_map < crypto : : hash , transaction_chain_entry > transactions ;
ar & transactions ;
{
std : : unordered_set < crypto : : key_image > spent_keys ;
ar & spent_keys ;
}
{
std : : unordered_map < crypto : : hash , block_extended_info > alternative_chains ;
ar & alternative_chains ;
}
{
std : : map < uint64_t , std : : vector < std : : pair < crypto : : hash , size_t > > > outputs ;
ar & outputs ;
}
{
std : : unordered_map < crypto : : hash , block_extended_info > invalid_blocks ;
ar & invalid_blocks ;
}
size_t current_block_cumul_sz_limit ;
ar & current_block_cumul_sz_limit ;
LOG_PRINT_L0 ( " Old blockchain storage: " < < ENDL < <
" blocks: " < < blocks . size ( ) < < ENDL < <
" transactions: " < < transactions . size ( ) < < ENDL < <
" current_block_cumul_sz_limit: " < < current_block_cumul_sz_limit ) ;
2014-08-13 10:51:37 +00:00
BlockEntry block ;
TransactionEntry transaction ;
2014-06-20 15:56:33 +00:00
for ( uint32_t b = 0 ; b < blocks . size ( ) ; + + b ) {
block . bl = blocks [ b ] . bl ;
block . height = b ;
block . block_cumulative_size = blocks [ b ] . block_cumulative_size ;
block . cumulative_difficulty = blocks [ b ] . cumulative_difficulty ;
block . already_generated_coins = blocks [ b ] . already_generated_coins ;
2014-08-13 10:51:37 +00:00
block . transactions . resize ( 1 + blocks [ b ] . bl . txHashes . size ( ) ) ;
block . transactions [ 0 ] . tx = blocks [ b ] . bl . minerTx ;
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = { b , 0 } ;
2014-08-13 10:51:37 +00:00
pushTransaction ( block , get_transaction_hash ( blocks [ b ] . bl . minerTx ) , transactionIndex ) ;
for ( uint32_t t = 0 ; t < blocks [ b ] . bl . txHashes . size ( ) ; + + t ) {
block . transactions [ 1 + t ] . tx = transactions [ blocks [ b ] . bl . txHashes [ t ] ] . tx ;
2014-06-20 15:56:33 +00:00
transactionIndex . transaction = 1 + t ;
2014-08-13 10:51:37 +00:00
pushTransaction ( block , blocks [ b ] . bl . txHashes [ t ] , transactionIndex ) ;
2014-06-20 15:56:33 +00:00
}
pushBlock ( block ) ;
}
update_next_comulative_size_limit ( ) ;
if ( m_current_block_cumul_sz_limit ! = current_block_cumul_sz_limit ) {
LOG_ERROR ( " Migration was unsuccessful. " ) ;
}
}
}
BOOST_CLASS_VERSION ( cryptonote : : blockchain_storage , CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER )
2014-08-13 10:51:37 +00:00
namespace cryptonote
{
# define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1
class BlockCacheSerializer {
public :
BlockCacheSerializer ( blockchain_storage & bs , const crypto : : hash lastBlockHash ) :
m_bs ( bs ) , m_lastBlockHash ( lastBlockHash ) , m_loaded ( false ) { }
template < class Archive > void serialize ( Archive & ar , unsigned int version ) {
// ignore old versions, do rebuild
if ( version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER )
return ;
2014-08-25 14:35:07 +00:00
std : : string operation ;
2014-08-13 10:51:37 +00:00
if ( Archive : : is_loading : : value ) {
2014-08-25 14:35:07 +00:00
operation = " - loading " ;
2014-08-13 10:51:37 +00:00
crypto : : hash blockHash ;
ar & blockHash ;
if ( blockHash ! = m_lastBlockHash ) {
return ;
}
} else {
2014-08-25 14:35:07 +00:00
operation = " - saving " ;
2014-08-13 10:51:37 +00:00
ar & m_lastBlockHash ;
}
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( operation < < " block index... " ) ;
2014-08-13 10:51:37 +00:00
ar & m_bs . m_blockIndex ;
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( operation < < " transaction map... " ) ;
2014-08-13 10:51:37 +00:00
ar & m_bs . m_transactionMap ;
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( operation < < " spend keys... " ) ;
2014-08-13 10:51:37 +00:00
ar & m_bs . m_spent_keys ;
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( operation < < " outputs... " ) ;
2014-08-13 10:51:37 +00:00
ar & m_bs . m_outputs ;
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( operation < < " multi-signature outputs... " ) ;
2014-08-13 10:51:37 +00:00
ar & m_bs . m_multisignatureOutputs ;
m_loaded = true ;
}
bool loaded ( ) const {
return m_loaded ;
}
private :
bool m_loaded ;
blockchain_storage & m_bs ;
crypto : : hash m_lastBlockHash ;
} ;
}
BOOST_CLASS_VERSION ( cryptonote : : BlockCacheSerializer , CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER )
blockchain_storage : : blockchain_storage ( const Currency & currency , tx_memory_pool & tx_pool ) :
m_currency ( currency ) ,
m_tx_pool ( tx_pool ) ,
2014-08-14 15:41:44 +00:00
m_current_block_cumul_sz_limit ( 0 ) ,
2014-08-13 10:51:37 +00:00
m_is_in_checkpoint_zone ( false ) ,
m_is_blockchain_storing ( false ) ,
m_upgradeDetector ( currency , m_blocks , BLOCK_MAJOR_VERSION_2 ) {
m_outputs . set_deleted_key ( 0 ) ;
crypto : : key_image nullImage = AUTO_VAL_INIT ( nullImage ) ;
m_spent_keys . set_deleted_key ( nullImage ) ;
}
bool blockchain_storage : : checkTransactionInputs ( const cryptonote : : Transaction & tx , BlockInfo & maxUsedBlock ) {
return check_tx_inputs ( tx , maxUsedBlock . height , maxUsedBlock . id ) ;
}
bool blockchain_storage : : checkTransactionInputs ( const cryptonote : : Transaction & tx , BlockInfo & maxUsedBlock , BlockInfo & lastFailed ) {
BlockInfo tail ;
//not the best implementation at this time, sorry :(
//check is ring_signature already checked ?
if ( maxUsedBlock . empty ( ) ) {
//not checked, lets try to check
if ( ! lastFailed . empty ( ) & & get_current_blockchain_height ( ) > lastFailed . height & & get_block_id_by_height ( lastFailed . height ) = = lastFailed . id ) {
return false ; //we already sure that this tx is broken for this height
}
if ( ! check_tx_inputs ( tx , maxUsedBlock . height , maxUsedBlock . id , & tail ) ) {
lastFailed = tail ;
return false ;
}
}
else {
if ( maxUsedBlock . height > = get_current_blockchain_height ( ) ) {
return false ;
}
if ( get_block_id_by_height ( maxUsedBlock . height ) ! = maxUsedBlock . id ) {
//if we already failed on this height and id, skip actual ring signature check
if ( lastFailed . id = = get_block_id_by_height ( lastFailed . height ) ) {
return false ;
}
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
if ( ! check_tx_inputs ( tx , maxUsedBlock . height , maxUsedBlock . id , & tail ) ) {
lastFailed = tail ;
return false ;
}
}
}
return true ;
}
bool blockchain_storage : : haveSpentKeyImages ( const cryptonote : : Transaction & tx ) {
return this - > have_tx_keyimges_as_spent ( tx ) ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : have_tx ( const crypto : : hash & id ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
return m_transactionMap . find ( id ) ! = m_transactionMap . end ( ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : have_tx_keyimg_as_spent ( const crypto : : key_image & key_im ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
return m_spent_keys . find ( key_im ) ! = m_spent_keys . end ( ) ;
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage : : get_current_blockchain_height ( ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
return m_blocks . size ( ) ;
}
2014-06-20 15:56:33 +00:00
2014-06-25 17:21:42 +00:00
bool blockchain_storage : : init ( const std : : string & config_folder , bool load_existing ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-25 17:21:42 +00:00
if ( ! config_folder . empty ( ) & & ! tools : : create_directories_if_necessary ( config_folder ) ) {
2014-06-21 14:09:13 +00:00
LOG_ERROR ( " Failed to create data directory: " < < m_config_folder ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
m_config_folder = config_folder ;
2014-06-25 17:21:42 +00:00
2014-08-13 10:51:37 +00:00
if ( ! m_blocks . open ( appendPath ( config_folder , m_currency . blocksFileName ( ) ) , appendPath ( config_folder , m_currency . blockIndexesFileName ( ) ) , 1024 ) ) {
2014-06-20 15:56:33 +00:00
return false ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-06-25 17:21:42 +00:00
if ( load_existing ) {
LOG_PRINT_L0 ( " Loading blockchain... " ) ;
if ( m_blocks . empty ( ) ) {
2014-08-13 10:51:37 +00:00
const std : : string filename = appendPath ( m_config_folder , cryptonote : : parameters : : CRYPTONOTE_BLOCKCHAINDATA_FILENAME ) ;
2014-06-25 17:21:42 +00:00
if ( ! tools : : unserialize_obj_from_file ( * this , filename ) ) {
LOG_PRINT_L0 ( " Can't load blockchain storage from file. " ) ;
}
} else {
2014-08-13 10:51:37 +00:00
BlockCacheSerializer loader ( * this , get_block_hash ( m_blocks . back ( ) . bl ) ) ;
tools : : unserialize_obj_from_file ( loader , appendPath ( config_folder , m_currency . blocksCacheFileName ( ) ) ) ;
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( ! loader . loaded ( ) ) {
2014-06-25 17:21:42 +00:00
LOG_PRINT_L0 ( " No actual blockchain cache found, rebuilding internal structures... " ) ;
std : : chrono : : steady_clock : : time_point timePoint = std : : chrono : : steady_clock : : now ( ) ;
2014-08-13 10:51:37 +00:00
m_blockIndex . clear ( ) ;
m_transactionMap . clear ( ) ;
m_spent_keys . clear ( ) ;
m_outputs . clear ( ) ;
m_multisignatureOutputs . clear ( ) ;
2014-06-25 17:21:42 +00:00
for ( uint32_t b = 0 ; b < m_blocks . size ( ) ; + + b ) {
2014-08-25 14:35:07 +00:00
if ( b % 1000 = = 0 ) {
std : : cout < < " Height " < < b < < " of " < < m_blocks . size ( ) < < ' \r ' ;
}
2014-08-13 10:51:37 +00:00
const BlockEntry & block = m_blocks [ b ] ;
2014-06-25 17:21:42 +00:00
crypto : : hash blockHash = get_block_hash ( block . bl ) ;
2014-08-13 10:51:37 +00:00
m_blockIndex . push ( blockHash ) ;
2014-06-25 17:21:42 +00:00
for ( uint16_t t = 0 ; t < block . transactions . size ( ) ; + + t ) {
2014-08-13 10:51:37 +00:00
const TransactionEntry & transaction = block . transactions [ t ] ;
2014-06-25 17:21:42 +00:00
crypto : : hash transactionHash = get_transaction_hash ( transaction . tx ) ;
TransactionIndex transactionIndex = { b , t } ;
m_transactionMap . insert ( std : : make_pair ( transactionHash , transactionIndex ) ) ;
for ( auto & i : transaction . tx . vin ) {
2014-08-13 10:51:37 +00:00
if ( i . type ( ) = = typeid ( TransactionInputToKey ) ) {
m_spent_keys . insert ( : : boost : : get < TransactionInputToKey > ( i ) . keyImage ) ;
2014-06-25 17:21:42 +00:00
}
2014-06-20 15:56:33 +00:00
}
2014-06-25 17:21:42 +00:00
for ( uint16_t o = 0 ; o < transaction . tx . vout . size ( ) ; + + o ) {
m_outputs [ transaction . tx . vout [ o ] . amount ] . push_back ( std : : make_pair < > ( transactionIndex , o ) ) ;
}
2014-06-20 15:56:33 +00:00
}
}
2014-06-25 17:21:42 +00:00
std : : chrono : : duration < double > duration = std : : chrono : : steady_clock : : now ( ) - timePoint ;
LOG_PRINT_L0 ( " Rebuilding internal structures took: " < < duration . count ( ) ) ;
}
2014-06-20 15:56:33 +00:00
}
2014-06-25 17:21:42 +00:00
} else {
m_blocks . clear ( ) ;
2014-06-20 15:56:33 +00:00
}
if ( m_blocks . empty ( ) ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Blockchain not loaded, generating genesis block. " ) ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
2014-08-13 10:51:37 +00:00
add_new_block ( m_currency . genesisBlock ( ) , bvc ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( ! bvc . m_verifivation_failed , false , " Failed to add genesis block to blockchain " ) ;
2014-08-13 10:51:37 +00:00
} else {
crypto : : hash firstBlockHash = get_block_hash ( m_blocks [ 0 ] . bl ) ;
CHECK_AND_ASSERT_MES ( firstBlockHash = = m_currency . genesisBlockHash ( ) , false ,
" Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network. " ) ;
}
if ( ! m_upgradeDetector . init ( ) ) {
LOG_ERROR ( " Failed to initialize upgrade detector " ) ;
return false ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-14 15:41:44 +00:00
update_next_comulative_size_limit ( ) ;
2014-03-03 22:07:58 +00:00
uint64_t timestamp_diff = time ( NULL ) - m_blocks . back ( ) . bl . timestamp ;
2014-08-13 10:51:37 +00:00
if ( ! m_blocks . back ( ) . bl . timestamp ) {
2014-03-03 22:07:58 +00:00
timestamp_diff = time ( NULL ) - 1341378000 ;
2014-08-13 10:51:37 +00:00
}
2014-06-20 15:56:33 +00:00
LOG_PRINT_GREEN ( " Blockchain initialized. last block: " < < m_blocks . size ( ) - 1 < < " , " < < epee : : misc_utils : : get_time_interval_string ( timestamp_diff ) < < " time ago, current difficulty: " < < get_difficulty_for_next_block ( ) , LOG_LEVEL_0 ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-03-20 11:46:11 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : storeCache ( ) {
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
2014-08-25 14:35:07 +00:00
LOG_PRINT_L0 ( " Saving blockchain... " ) ;
2014-08-13 10:51:37 +00:00
BlockCacheSerializer ser ( * this , get_tail_id ( ) ) ;
if ( ! tools : : serialize_obj_to_file ( ser , appendPath ( m_config_folder , m_currency . blocksCacheFileName ( ) ) ) ) {
LOG_ERROR ( " Failed to save blockchain cache " ) ;
return false ;
2014-03-03 22:07:58 +00:00
}
return true ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : deinit ( ) {
2014-08-13 10:51:37 +00:00
storeCache ( ) ;
return true ;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : reset_and_set_genesis_block ( const Block & b ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
m_blocks . clear ( ) ;
2014-08-13 10:51:37 +00:00
m_blockIndex . clear ( ) ;
2014-06-20 15:56:33 +00:00
m_transactionMap . clear ( ) ;
m_spent_keys . clear ( ) ;
2014-03-03 22:07:58 +00:00
m_alternative_chains . clear ( ) ;
m_outputs . clear ( ) ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
add_new_block ( b , bvc ) ;
return bvc . m_added_to_main_chain & & ! bvc . m_verifivation_failed ;
}
2014-06-20 15:56:33 +00:00
crypto : : hash blockchain_storage : : get_tail_id ( uint64_t & height ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
height = get_current_blockchain_height ( ) - 1 ;
2014-03-03 22:07:58 +00:00
return get_tail_id ( ) ;
}
2014-06-20 15:56:33 +00:00
crypto : : hash blockchain_storage : : get_tail_id ( ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
return m_blockIndex . getTailId ( ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : get_short_chain_history ( std : : list < crypto : : hash > & ids ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
return m_blockIndex . getShortChainHistory ( ids ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
crypto : : hash blockchain_storage : : get_block_id_by_height ( uint64_t height ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
return m_blockIndex . getBlockId ( height ) ;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : get_block_by_hash ( const crypto : : hash & blockHash , Block & b ) {
2014-06-20 15:56:33 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
uint64_t height = 0 ;
if ( m_blockIndex . getBlockHeight ( blockHash , height ) ) {
b = m_blocks [ height ] . bl ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
auto blockByHashIterator = m_alternative_chains . find ( blockHash ) ;
if ( blockByHashIterator ! = m_alternative_chains . end ( ) ) {
b = blockByHashIterator - > second . bl ;
2014-03-03 22:07:58 +00:00
return true ;
}
return false ;
}
2014-06-20 15:56:33 +00:00
difficulty_type blockchain_storage : : get_difficulty_for_next_block ( ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
std : : vector < uint64_t > timestamps ;
std : : vector < difficulty_type > commulative_difficulties ;
2014-08-13 10:51:37 +00:00
size_t offset = m_blocks . size ( ) - std : : min ( m_blocks . size ( ) , static_cast < uint64_t > ( m_currency . difficultyBlocksCount ( ) ) ) ;
2014-06-20 15:56:33 +00:00
if ( offset = = 0 ) {
+ + offset ;
}
for ( ; offset < m_blocks . size ( ) ; offset + + ) {
2014-03-03 22:07:58 +00:00
timestamps . push_back ( m_blocks [ offset ] . bl . timestamp ) ;
commulative_difficulties . push_back ( m_blocks [ offset ] . cumulative_difficulty ) ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_currency . nextDifficulty ( timestamps , commulative_difficulties ) ;
}
uint64_t blockchain_storage : : getCoinsInCirculation ( ) {
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
if ( m_blocks . empty ( ) ) {
return 0 ;
} else {
return m_blocks . back ( ) . already_generated_coins ;
}
}
uint8_t blockchain_storage : : get_block_major_version_for_height ( uint64_t height ) const {
return height > m_upgradeDetector . upgradeHeight ( ) ? m_upgradeDetector . targetVersion ( ) : BLOCK_MAJOR_VERSION_1 ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : rollback_blockchain_switching ( std : : list < Block > & original_chain , size_t rollback_height ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
//remove failed subchain
2014-06-20 15:56:33 +00:00
for ( size_t i = m_blocks . size ( ) - 1 ; i > = rollback_height ; i - - )
2014-03-03 22:07:58 +00:00
{
2014-06-20 15:56:33 +00:00
popBlock ( get_block_hash ( m_blocks . back ( ) . bl ) ) ;
//bool r = pop_block_from_blockchain();
//CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!");
2014-03-03 22:07:58 +00:00
}
//return back original chain
BOOST_FOREACH ( auto & bl , original_chain )
{
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
2014-06-20 15:56:33 +00:00
bool r = pushBlock ( bl , bvc ) ;
2014-03-03 22:07:58 +00:00
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 ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : switch_to_alternative_blockchain ( std : : list < blocks_ext_by_hash : : iterator > & alt_chain , bool discard_disconnected_chain ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
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_blocks . size ( ) > split_height , false , " switch_to_alternative_blockchain: blockchain size is lower than split height " ) ;
//disconnecting old chain
2014-08-13 10:51:37 +00:00
std : : list < Block > disconnected_chain ;
2014-06-20 15:56:33 +00:00
for ( size_t i = m_blocks . size ( ) - 1 ; i > = split_height ; i - - ) {
2014-08-13 10:51:37 +00:00
Block b = m_blocks [ i ] . bl ;
2014-06-20 15:56:33 +00:00
popBlock ( get_block_hash ( b ) ) ;
//CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching");
2014-03-03 22:07:58 +00:00
disconnected_chain . push_front ( b ) ;
}
//connecting new alternative chain
2014-06-20 15:56:33 +00:00
for ( auto alt_ch_iter = alt_chain . begin ( ) ; alt_ch_iter ! = alt_chain . end ( ) ; alt_ch_iter + + ) {
2014-03-03 22:07:58 +00:00
auto ch_ent = * alt_ch_iter ;
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
2014-06-20 15:56:33 +00:00
bool r = pushBlock ( ch_ent - > second . bl , bvc ) ;
if ( ! r | | ! bvc . m_added_to_main_chain ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Failed to switch to alternative blockchain " ) ;
rollback_blockchain_switching ( disconnected_chain , split_height ) ;
2014-06-20 15:56:33 +00:00
//add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " The block was inserted as invalid while connecting new alternative chain, block_id: " < < get_block_hash ( ch_ent - > second . bl ) ) ;
m_alternative_chains . erase ( ch_ent ) ;
2014-06-20 15:56:33 +00:00
for ( auto alt_ch_to_orph_iter = + + alt_ch_iter ; alt_ch_to_orph_iter ! = alt_chain . end ( ) ; alt_ch_to_orph_iter + + ) {
2014-03-03 22:07:58 +00:00
//block_verification_context bvc = boost::value_initialized<block_verification_context>();
2014-06-20 15:56:33 +00:00
//add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
2014-03-03 22:07:58 +00:00
m_alternative_chains . erase ( * alt_ch_to_orph_iter ) ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false ;
}
}
2014-06-20 15:56:33 +00:00
if ( ! discard_disconnected_chain ) {
2014-05-15 16:40:40 +00:00
//pushing old chain as alternative chain
2014-06-20 15:56:33 +00:00
for ( auto & old_ch_ent : disconnected_chain ) {
2014-05-15 16:40:40 +00:00
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 ) ;
2014-06-20 15:56:33 +00:00
if ( ! r ) {
2014-05-15 16:40:40 +00:00
LOG_ERROR ( " Failed to push ex-main chain blocks to alternative chain " ) ;
rollback_blockchain_switching ( disconnected_chain , split_height ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
}
}
//removing all_chain entries from alternative chain
2014-06-20 15:56:33 +00:00
for ( auto ch_ent : alt_chain ) {
2014-03-03 22:07:58 +00:00
m_alternative_chains . erase ( ch_ent ) ;
}
LOG_PRINT_GREEN ( " REORGANIZE SUCCESS! on height: " < < split_height < < " , new blockchain size: " < < m_blocks . size ( ) , LOG_LEVEL_0 ) ;
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
difficulty_type blockchain_storage : : get_next_difficulty_for_alternative_chain ( const std : : list < blocks_ext_by_hash : : iterator > & alt_chain , BlockEntry & bei ) {
2014-03-03 22:07:58 +00:00
std : : vector < uint64_t > timestamps ;
std : : vector < difficulty_type > commulative_difficulties ;
2014-08-13 10:51:37 +00:00
if ( alt_chain . size ( ) < m_currency . difficultyBlocksCount ( ) ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
size_t main_chain_stop_offset = alt_chain . size ( ) ? alt_chain . front ( ) - > second . height : bei . height ;
2014-08-13 10:51:37 +00:00
size_t main_chain_count = m_currency . difficultyBlocksCount ( ) - std : : min ( m_currency . difficultyBlocksCount ( ) , alt_chain . size ( ) ) ;
2014-03-03 22:07:58 +00:00
main_chain_count = std : : min ( main_chain_count , main_chain_stop_offset ) ;
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count ;
2014-06-20 15:56:33 +00:00
if ( ! main_chain_start_offset )
2014-03-03 22:07:58 +00:00
+ + main_chain_start_offset ; //skip genesis block
2014-06-20 15:56:33 +00:00
for ( ; main_chain_start_offset < main_chain_stop_offset ; + + main_chain_start_offset ) {
2014-03-03 22:07:58 +00:00
timestamps . push_back ( m_blocks [ main_chain_start_offset ] . bl . timestamp ) ;
commulative_difficulties . push_back ( m_blocks [ main_chain_start_offset ] . cumulative_difficulty ) ;
}
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( ( alt_chain . size ( ) + timestamps . size ( ) ) < = m_currency . difficultyBlocksCount ( ) , false ,
" Internal error, alt_chain.size()[ " < < alt_chain . size ( ) < < " ] + timestamps.size()[ " < < timestamps . size ( ) < <
" ] NOT <= m_currency.difficultyBlocksCount()[ " < < m_currency . difficultyBlocksCount ( ) < < ' ] ' ) ;
2014-06-20 15:56:33 +00:00
for ( auto it : alt_chain ) {
2014-03-03 22:07:58 +00:00
timestamps . push_back ( it - > second . bl . timestamp ) ;
commulative_difficulties . push_back ( it - > second . cumulative_difficulty ) ;
}
2014-06-20 15:56:33 +00:00
} else {
2014-08-13 10:51:37 +00:00
timestamps . resize ( std : : min ( alt_chain . size ( ) , m_currency . difficultyBlocksCount ( ) ) ) ;
commulative_difficulties . resize ( std : : min ( alt_chain . size ( ) , m_currency . difficultyBlocksCount ( ) ) ) ;
2014-03-03 22:07:58 +00:00
size_t count = 0 ;
2014-06-20 15:56:33 +00:00
size_t max_i = timestamps . size ( ) - 1 ;
BOOST_REVERSE_FOREACH ( auto it , alt_chain ) {
2014-03-03 22:07:58 +00:00
timestamps [ max_i - count ] = it - > second . bl . timestamp ;
commulative_difficulties [ max_i - count ] = it - > second . cumulative_difficulty ;
count + + ;
2014-08-13 10:51:37 +00:00
if ( count > = m_currency . difficultyBlocksCount ( ) ) {
2014-03-03 22:07:58 +00:00
break ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_currency . nextDifficulty ( timestamps , commulative_difficulties ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : prevalidate_miner_transaction ( const Block & b , uint64_t height ) {
CHECK_AND_ASSERT_MES ( b . minerTx . vin . size ( ) = = 1 , false , " coinbase transaction in the block has no inputs " ) ;
CHECK_AND_ASSERT_MES ( b . minerTx . vin [ 0 ] . type ( ) = = typeid ( TransactionInputGenerate ) , false ,
" coinbase transaction in the block has the wrong type " ) ;
if ( boost : : get < TransactionInputGenerate > ( b . minerTx . vin [ 0 ] ) . height ! = height ) {
LOG_PRINT_RED_L0 ( " The miner transaction in block has invalid height: " < <
boost : : get < TransactionInputGenerate > ( b . minerTx . vin [ 0 ] ) . height < < " , expected: " < < height ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( b . minerTx . unlockTime = = height + m_currency . minedMoneyUnlockWindow ( ) ,
2014-06-20 15:56:33 +00:00
false ,
2014-08-13 10:51:37 +00:00
" coinbase transaction transaction have wrong unlock time= " < < b . minerTx . unlockTime < < " , expected " < < height + m_currency . minedMoneyUnlockWindow ( ) ) ;
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if ( ! check_outs_overflow ( b . minerTx ) ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_RED_L0 ( " miner transaction have money overflow in block " < < get_block_hash ( b ) ) ;
return false ;
}
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : validate_miner_transaction ( const Block & b , uint64_t height , size_t cumulativeBlockSize ,
uint64_t alreadyGeneratedCoins , uint64_t fee ,
uint64_t & reward , int64_t & emissionChange ) {
uint64_t minerReward = 0 ;
for ( auto & o : b . minerTx . vout ) {
minerReward + = o . amount ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
std : : vector < size_t > lastBlocksSizes ;
get_last_n_blocks_sizes ( lastBlocksSizes , m_currency . rewardBlocksWindow ( ) ) ;
size_t blocksSizeMedian = epee : : misc_utils : : median ( lastBlocksSizes ) ;
bool penalizeFee = get_block_major_version_for_height ( height ) > BLOCK_MAJOR_VERSION_1 ;
if ( ! m_currency . getBlockReward ( blocksSizeMedian , cumulativeBlockSize , alreadyGeneratedCoins , fee , penalizeFee , reward , emissionChange ) ) {
LOG_PRINT_L0 ( " block size " < < cumulativeBlockSize < < " is bigger than allowed for this blockchain " ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( minerReward > reward ) {
LOG_ERROR ( " Coinbase transaction spend too much money: " < < m_currency . formatAmount ( minerReward ) < <
" , block reward is " < < m_currency . formatAmount ( reward ) ) ;
2014-03-03 22:07:58 +00:00
return false ;
2014-08-13 10:51:37 +00:00
} else if ( minerReward < reward ) {
LOG_ERROR ( " Coinbase transaction doesn't use full amount of block reward: spent " < <
m_currency . formatAmount ( minerReward ) < < " , block reward is " < < m_currency . formatAmount ( reward ) ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : get_backward_blocks_sizes ( size_t from_height , std : : vector < size_t > & sz , size_t count ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
CHECK_AND_ASSERT_MES ( from_height < m_blocks . size ( ) , false , " Internal error: get_backward_blocks_sizes called with from_height= " < < from_height < < " , blockchain height = " < < m_blocks . size ( ) ) ;
2014-06-20 15:56:33 +00:00
size_t start_offset = ( from_height + 1 ) - std : : min ( ( from_height + 1 ) , count ) ;
for ( size_t i = start_offset ; i ! = from_height + 1 ; i + + ) {
2014-03-03 22:07:58 +00:00
sz . push_back ( m_blocks [ i ] . block_cumulative_size ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : get_last_n_blocks_sizes ( std : : vector < size_t > & sz , size_t count ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( ! m_blocks . size ( ) ) {
2014-03-03 22:07:58 +00:00
return true ;
2014-06-20 15:56:33 +00:00
}
return get_backward_blocks_sizes ( m_blocks . size ( ) - 1 , sz , count ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage : : get_current_comulative_blocksize_limit ( ) {
2014-04-02 16:00:17 +00:00
return m_current_block_cumul_sz_limit ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : create_block_template ( Block & b , const AccountPublicAddress & miner_address , difficulty_type & diffic , uint64_t & height , const blobdata & ex_nonce ) {
2014-04-02 16:00:17 +00:00
size_t median_size ;
uint64_t already_generated_coins ;
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_BEGIN ( m_blockchain_lock ) ;
height = m_blocks . size ( ) ;
diffic = get_difficulty_for_next_block ( ) ;
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( diffic , false , " difficulty overhead. " ) ;
b = boost : : value_initialized < Block > ( ) ;
b . majorVersion = get_block_major_version_for_height ( height ) ;
if ( BLOCK_MAJOR_VERSION_1 = = b . majorVersion ) {
b . minorVersion = BLOCK_MINOR_VERSION_1 ;
} else if ( BLOCK_MAJOR_VERSION_2 = = b . majorVersion ) {
b . minorVersion = BLOCK_MINOR_VERSION_0 ;
b . parentBlock . majorVersion = BLOCK_MAJOR_VERSION_1 ;
b . parentBlock . majorVersion = BLOCK_MINOR_VERSION_0 ;
b . parentBlock . numberOfTransactions = 1 ;
tx_extra_merge_mining_tag mm_tag = AUTO_VAL_INIT ( mm_tag ) ;
bool r = append_mm_tag_to_extra ( b . parentBlock . minerTx . extra , mm_tag ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to append merge mining tag to extra of the parent block miner transaction " ) ;
}
b . prevId = get_tail_id ( ) ;
b . timestamp = time ( NULL ) ;
2014-03-03 22:07:58 +00:00
2014-04-19 20:53:40 +00:00
median_size = m_current_block_cumul_sz_limit / 2 ;
2014-04-02 16:00:17 +00:00
already_generated_coins = m_blocks . back ( ) . already_generated_coins ;
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_END ( ) ;
2014-04-02 16:00:17 +00:00
size_t txs_size ;
uint64_t fee ;
2014-08-13 10:51:37 +00:00
if ( ! m_tx_pool . fill_block_template ( b , median_size , m_currency . maxBlockCumulativeSize ( height ) , already_generated_coins ,
txs_size , fee ) ) {
2014-04-02 16:00:17 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-04-29 16:26:45 +00:00
# if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0 ;
uint64_t real_fee = 0 ;
CRITICAL_REGION_BEGIN ( m_tx_pool . m_transactions_lock ) ;
2014-08-13 10:51:37 +00:00
for ( crypto : : hash & cur_hash : b . txHashes ) {
2014-04-29 16:26:45 +00:00
auto cur_res = m_tx_pool . m_transactions . find ( cur_hash ) ;
if ( cur_res = = m_tx_pool . m_transactions . end ( ) ) {
LOG_ERROR ( " Creating block template: error: transaction not found " ) ;
continue ;
}
tx_memory_pool : : tx_details & cur_tx = cur_res - > second ;
real_txs_size + = cur_tx . blob_size ;
real_fee + = cur_tx . fee ;
if ( cur_tx . blob_size ! = get_object_blobsize ( cur_tx . tx ) ) {
LOG_ERROR ( " Creating block template: error: invalid transaction size " ) ;
}
uint64_t inputs_amount ;
if ( ! get_inputs_money_amount ( cur_tx . tx , inputs_amount ) ) {
LOG_ERROR ( " Creating block template: error: cannot get inputs amount " ) ;
} else if ( cur_tx . fee ! = inputs_amount - get_outs_money_amount ( cur_tx . tx ) ) {
LOG_ERROR ( " Creating block template: error: invalid fee " ) ;
}
}
if ( txs_size ! = real_txs_size ) {
LOG_ERROR ( " Creating block template: error: wrongly calculated transaction size " ) ;
}
if ( fee ! = real_fee ) {
LOG_ERROR ( " Creating block template: error: wrongly calculated fee " ) ;
}
CRITICAL_REGION_END ( ) ;
LOG_PRINT_L1 ( " Creating block template: height " < < height < <
" , median size " < < median_size < <
" , already generated coins " < < already_generated_coins < <
" , transaction size " < < txs_size < <
" , fee " < < fee ) ;
# endif
2014-03-03 22:07:58 +00:00
/*
two - phase miner transaction generation : we don ' t know exact block size until we prepare block , but we don ' t know reward until we know
block size , so first miner transaction generated with fake amount of money , and with phase we know think we know expected block size
2014-06-20 15:56:33 +00:00
*/
2014-03-03 22:07:58 +00:00
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
2014-08-13 10:51:37 +00:00
bool penalizeFee = b . majorVersion > BLOCK_MAJOR_VERSION_1 ;
bool r = m_currency . constructMinerTx ( height , median_size , already_generated_coins , txs_size , fee , miner_address , b . minerTx , ex_nonce , 11 , penalizeFee ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( r , false , " Failed to construc miner tx, first chance " ) ;
2014-08-13 10:51:37 +00:00
size_t cumulative_size = txs_size + get_object_blobsize ( b . minerTx ) ;
2014-04-29 16:26:45 +00:00
# if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
2014-08-13 10:51:37 +00:00
LOG_PRINT_L1 ( " Creating block template: miner tx size " < < get_object_blobsize ( b . minerTx ) < <
2014-04-29 16:26:45 +00:00
" , cumulative size " < < cumulative_size ) ;
# endif
2014-04-02 16:00:17 +00:00
for ( size_t try_count = 0 ; try_count ! = 10 ; + + try_count ) {
2014-08-13 10:51:37 +00:00
r = m_currency . constructMinerTx ( height , median_size , already_generated_coins , cumulative_size , fee , miner_address , b . minerTx , ex_nonce , 11 , penalizeFee ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( r , false , " Failed to construc miner tx, second chance " ) ;
2014-08-13 10:51:37 +00:00
size_t coinbase_blob_size = get_object_blobsize ( b . minerTx ) ;
2014-04-02 16:00:17 +00:00
if ( coinbase_blob_size > cumulative_size - txs_size ) {
cumulative_size = txs_size + coinbase_blob_size ;
2014-04-29 16:26:45 +00:00
# if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1 ( " Creating block template: miner tx size " < < coinbase_blob_size < <
" , cumulative size " < < cumulative_size < < " is greater then before " ) ;
# endif
2014-03-03 22:07:58 +00:00
continue ;
2014-04-02 16:00:17 +00:00
}
if ( coinbase_blob_size < cumulative_size - txs_size ) {
size_t delta = cumulative_size - txs_size - coinbase_blob_size ;
2014-04-29 16:26:45 +00:00
# if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1 ( " Creating block template: miner tx size " < < coinbase_blob_size < <
" , cumulative size " < < txs_size + coinbase_blob_size < <
" is less then before, adding " < < delta < < " zero bytes " ) ;
# endif
2014-08-13 10:51:37 +00:00
b . minerTx . extra . insert ( b . minerTx . extra . end ( ) , delta , 0 ) ;
2014-04-02 16:00:17 +00:00
//here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
2014-08-13 10:51:37 +00:00
if ( cumulative_size ! = txs_size + get_object_blobsize ( b . minerTx ) ) {
CHECK_AND_ASSERT_MES ( cumulative_size + 1 = = txs_size + get_object_blobsize ( b . minerTx ) , false , " unexpected case: cumulative_size= " < < cumulative_size < < " + 1 is not equal txs_cumulative_size= " < < txs_size < < " + get_object_blobsize(b.minerTx)= " < < get_object_blobsize ( b . minerTx ) ) ;
b . minerTx . extra . resize ( b . minerTx . extra . size ( ) - 1 ) ;
if ( cumulative_size ! = txs_size + get_object_blobsize ( b . minerTx ) ) {
2014-04-02 16:00:17 +00:00
//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
2014-06-20 15:56:33 +00:00
LOG_PRINT_RED ( " Miner tx creation have no luck with delta_extra size = " < < delta < < " and " < < delta - 1 , LOG_LEVEL_2 ) ;
2014-04-02 16:00:17 +00:00
cumulative_size + = delta - 1 ;
continue ;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
LOG_PRINT_GREEN ( " Setting extra for block: " < < b . minerTx . extra . size ( ) < < " , try_count= " < < try_count , LOG_LEVEL_1 ) ;
2014-03-03 22:07:58 +00:00
}
}
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( cumulative_size = = txs_size + get_object_blobsize ( b . minerTx ) , false , " unexpected case: cumulative_size= " < < cumulative_size < < " is not equal txs_cumulative_size= " < < txs_size < < " + get_object_blobsize(b.minerTx)= " < < get_object_blobsize ( b . minerTx ) ) ;
2014-04-29 16:26:45 +00:00
# if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1 ( " Creating block template: miner tx size " < < coinbase_blob_size < <
" , cumulative size " < < cumulative_size < < " is now good " ) ;
# endif
2014-04-02 16:00:17 +00:00
return true ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-04-02 16:00:17 +00:00
LOG_ERROR ( " Failed to create_block_template with " < < 10 < < " tries " ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-04-02 16:00:17 +00:00
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : complete_timestamps_vector ( uint64_t start_top_height , std : : vector < uint64_t > & timestamps ) {
2014-08-13 10:51:37 +00:00
if ( timestamps . size ( ) > = m_currency . timestampCheckWindow ( ) )
2014-03-03 22:07:58 +00:00
return true ;
2014-04-02 16:00:17 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
size_t need_elements = m_currency . timestampCheckWindow ( ) - timestamps . size ( ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( start_top_height < m_blocks . size ( ) , false , " internal error: passed start_height = " < < start_top_height < < " not less then m_blocks.size()= " < < m_blocks . size ( ) ) ;
2014-06-20 15:56:33 +00:00
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0 ;
2014-03-03 22:07:58 +00:00
do
{
timestamps . push_back ( m_blocks [ start_top_height ] . bl . timestamp ) ;
2014-06-20 15:56:33 +00:00
if ( start_top_height = = 0 )
2014-03-03 22:07:58 +00:00
break ;
- - start_top_height ;
2014-06-20 15:56:33 +00:00
} while ( start_top_height ! = stop_offset ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : handle_alternative_block ( const Block & b , const crypto : : hash & id , block_verification_context & bvc ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-05-15 16:40:40 +00:00
uint64_t block_height = get_block_height ( b ) ;
2014-06-20 15:56:33 +00:00
if ( block_height = = 0 ) {
LOG_ERROR ( " Block with id: " < < epee : : string_tools : : pod_to_hex ( id ) < < " (as alternative) have wrong miner transaction " ) ;
2014-05-15 16:40:40 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-06-20 15:56:33 +00:00
if ( ! m_checkpoints . is_alternative_block_allowed ( get_current_blockchain_height ( ) , block_height ) ) {
2014-08-25 14:35:07 +00:00
LOG_PRINT_L2 ( " Block with id: " < < id < < std : : endl < <
" can't be accepted for alternative chain, block height: " < < block_height < < std : : endl < <
" blockchain height: " < < get_current_blockchain_height ( ) ) ;
2014-05-15 16:40:40 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-08-13 10:51:37 +00:00
if ( ! checkBlockVersion ( b , id ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
if ( ! checkParentBlockSize ( b , id ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
size_t cumulativeSize ;
if ( ! getBlockCumulativeSize ( b , cumulativeSize ) ) {
LOG_PRINT_L2 ( " Block with id: " < < id < < " has at least one unknown transaction. Cumulative size is calculated imprecisely " ) ;
}
if ( ! checkCumulativeBlockSize ( id , cumulativeSize , block_height ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
2014-03-03 22:07:58 +00:00
//block is not related with head of main chain
//first of all - look in alternative chains container
2014-08-13 10:51:37 +00:00
uint64_t mainPrevHeight = 0 ;
const bool mainPrev = m_blockIndex . getBlockHeight ( b . prevId , mainPrevHeight ) ;
const auto it_prev = m_alternative_chains . find ( b . prevId ) ;
if ( it_prev ! = m_alternative_chains . end ( ) | | mainPrev ) {
2014-03-03 22:07:58 +00:00
//we have new block in alternative chain
//build alternative subchain, front -> mainchain, back -> alternative head
blocks_ext_by_hash : : iterator alt_it = it_prev ; //m_alternative_chains.find()
std : : list < blocks_ext_by_hash : : iterator > alt_chain ;
std : : vector < uint64_t > timestamps ;
2014-06-20 15:56:33 +00:00
while ( alt_it ! = m_alternative_chains . end ( ) ) {
2014-03-03 22:07:58 +00:00
alt_chain . push_front ( alt_it ) ;
timestamps . push_back ( alt_it - > second . bl . timestamp ) ;
2014-08-13 10:51:37 +00:00
alt_it = m_alternative_chains . find ( alt_it - > second . bl . prevId ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
if ( alt_chain . size ( ) ) {
2014-03-03 22:07:58 +00:00
//make sure that it has right connection to main chain
CHECK_AND_ASSERT_MES ( m_blocks . size ( ) > alt_chain . front ( ) - > second . height , false , " main blockchain wrong height " ) ;
crypto : : hash h = null_hash ;
get_block_hash ( m_blocks [ alt_chain . front ( ) - > second . height - 1 ] . bl , h ) ;
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( h = = alt_chain . front ( ) - > second . bl . prevId , false , " alternative chain have wrong connection to main chain " ) ;
2014-03-03 22:07:58 +00:00
complete_timestamps_vector ( alt_chain . front ( ) - > second . height - 1 , timestamps ) ;
2014-06-20 15:56:33 +00:00
} else {
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( mainPrev , false , " internal error: broken imperative condition it_main_prev != m_blocks_index.end() " ) ;
complete_timestamps_vector ( mainPrevHeight , timestamps ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
//check timestamp correct
2014-06-20 15:56:33 +00:00
if ( ! check_block_timestamp ( timestamps , b ) ) {
2014-03-03 22:07:58 +00:00
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_verifivation_failed = true ;
return false ;
}
2014-08-13 10:51:37 +00:00
BlockEntry bei = boost : : value_initialized < BlockEntry > ( ) ;
2014-03-03 22:07:58 +00:00
bei . bl = b ;
2014-08-13 10:51:37 +00:00
bei . height = static_cast < uint32_t > ( alt_chain . size ( ) ? it_prev - > second . height + 1 : mainPrevHeight + 1 ) ;
2014-05-15 16:40:40 +00:00
bool is_a_checkpoint ;
2014-06-20 15:56:33 +00:00
if ( ! m_checkpoints . check_block ( bei . height , id , is_a_checkpoint ) ) {
2014-05-15 16:40:40 +00:00
LOG_ERROR ( " CHECKPOINT VALIDATION FAILED " ) ;
bvc . m_verifivation_failed = true ;
return false ;
}
// Always check PoW for alternative blocks
m_is_in_checkpoint_zone = false ;
2014-03-03 22:07:58 +00:00
difficulty_type current_diff = get_next_difficulty_for_alternative_chain ( alt_chain , bei ) ;
CHECK_AND_ASSERT_MES ( current_diff , false , " !!!!!!! DIFFICULTY OVERHEAD !!!!!!! " ) ;
crypto : : hash proof_of_work = null_hash ;
2014-08-13 10:51:37 +00:00
if ( ! m_currency . checkProofOfWork ( m_cn_context , bei . bl , current_diff , proof_of_work ) ) {
2014-05-15 16:40:40 +00:00
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_verifivation_failed = true ;
return false ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
if ( ! prevalidate_miner_transaction ( b , bei . height ) ) {
LOG_PRINT_RED_L0 ( " Block with id: " < < epee : : string_tools : : pod_to_hex ( id ) < < " (as alternative) have wrong miner transaction. " ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-08-13 10:51:37 +00:00
bei . cumulative_difficulty = alt_chain . size ( ) ? it_prev - > second . cumulative_difficulty : m_blocks [ mainPrevHeight ] . cumulative_difficulty ;
2014-03-03 22:07:58 +00:00
bei . cumulative_difficulty + = current_diff ;
# ifdef _DEBUG
auto i_dres = m_alternative_chains . find ( id ) ;
CHECK_AND_ASSERT_MES ( i_dres = = m_alternative_chains . end ( ) , false , " insertion of new alternative block returned as it already exist " ) ;
# endif
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
auto i_res = m_alternative_chains . insert ( blocks_ext_by_hash : : value_type ( id , bei ) ) ;
CHECK_AND_ASSERT_MES ( i_res . second , false , " insertion of new alternative block returned as it already exist " ) ;
alt_chain . push_back ( i_res . first ) ;
2014-05-15 16:40:40 +00:00
2014-06-20 15:56:33 +00:00
if ( is_a_checkpoint ) {
2014-03-03 22:07:58 +00:00
//do reorganize!
2014-05-15 16:40:40 +00:00
LOG_PRINT_GREEN ( " ###### REORGANIZE on height: " < < alt_chain . front ( ) - > second . height < < " of " < < m_blocks . size ( ) - 1 < <
" , checkpoint is found in alternative chain on height " < < bei . height , LOG_LEVEL_0 ) ;
bool r = switch_to_alternative_blockchain ( alt_chain , true ) ;
2014-06-20 15:56:33 +00:00
if ( r ) bvc . m_added_to_main_chain = true ;
2014-05-15 16:40:40 +00:00
else bvc . m_verifivation_failed = true ;
return r ;
2014-06-20 15:56:33 +00:00
} else if ( m_blocks . back ( ) . cumulative_difficulty < bei . cumulative_difficulty ) //check if difficulty bigger then in main chain
2014-05-15 16:40:40 +00:00
{
//do reorganize!
LOG_PRINT_GREEN ( " ###### REORGANIZE on height: " < < alt_chain . front ( ) - > second . height < < " of " < < m_blocks . size ( ) - 1 < < " with cum_difficulty " < < m_blocks . back ( ) . cumulative_difficulty
2014-03-03 22:07:58 +00:00
< < ENDL < < " alternative blockchain size: " < < alt_chain . size ( ) < < " with cum_difficulty " < < bei . cumulative_difficulty , LOG_LEVEL_0 ) ;
2014-05-15 16:40:40 +00:00
bool r = switch_to_alternative_blockchain ( alt_chain , false ) ;
2014-06-20 15:56:33 +00:00
if ( r ) bvc . m_added_to_main_chain = true ;
2014-03-03 22:07:58 +00:00
else bvc . m_verifivation_failed = true ;
return r ;
2014-06-20 15:56:33 +00:00
} else {
2014-05-15 16:40:40 +00:00
LOG_PRINT_BLUE ( " ----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " < < bei . height
< < ENDL < < " id: \t " < < id
< < ENDL < < " PoW: \t " < < proof_of_work
< < ENDL < < " difficulty: \t " < < current_diff , LOG_LEVEL_0 ) ;
return true ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
} else {
2014-03-03 22:07:58 +00:00
//block orphaned
bvc . m_marked_as_orphaned = true ;
LOG_PRINT_RED_L0 ( " Block recognized as orphaned and rejected, id = " < < id ) ;
}
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : get_blocks ( uint64_t start_offset , size_t count , std : : list < Block > & blocks , std : : list < Transaction > & txs ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( start_offset > = m_blocks . size ( ) )
2014-03-03 22:07:58 +00:00
return false ;
2014-06-20 15:56:33 +00:00
for ( size_t i = start_offset ; i < start_offset + count & & i < m_blocks . size ( ) ; i + + )
2014-03-03 22:07:58 +00:00
{
blocks . push_back ( m_blocks [ i ] . bl ) ;
std : : list < crypto : : hash > missed_ids ;
2014-08-13 10:51:37 +00:00
get_transactions ( m_blocks [ i ] . bl . txHashes , txs , missed_ids ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( ! missed_ids . size ( ) , false , " have missed transactions in own block in main blockchain " ) ;
}
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : get_blocks ( uint64_t start_offset , size_t count , std : : list < Block > & blocks ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( start_offset > = m_blocks . size ( ) ) {
2014-03-03 22:07:58 +00:00
return false ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
for ( size_t i = start_offset ; i < start_offset + count & & i < m_blocks . size ( ) ; i + + ) {
2014-03-03 22:07:58 +00:00
blocks . push_back ( m_blocks [ i ] . bl ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : handle_get_objects ( NOTIFY_REQUEST_GET_OBJECTS : : request & arg , NOTIFY_RESPONSE_GET_OBJECTS : : request & rsp ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
rsp . current_blockchain_height = get_current_blockchain_height ( ) ;
2014-08-13 10:51:37 +00:00
std : : list < Block > blocks ;
2014-03-03 22:07:58 +00:00
get_blocks ( arg . blocks , blocks , rsp . missed_ids ) ;
2014-06-20 15:56:33 +00:00
for ( const auto & bl : blocks ) {
2014-03-03 22:07:58 +00:00
std : : list < crypto : : hash > missed_tx_id ;
2014-08-13 10:51:37 +00:00
std : : list < Transaction > txs ;
get_transactions ( bl . txHashes , txs , rsp . missed_ids ) ;
2014-06-20 15:56:33 +00:00
CHECK_AND_ASSERT_MES ( ! missed_tx_id . size ( ) , false , " Internal error: have missed missed_tx_id.size()= " < < missed_tx_id . 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
2014-08-13 10:51:37 +00:00
for ( Transaction & tx : txs ) {
2014-06-20 15:56:33 +00:00
e . txs . push_back ( t_serializable_object_to_blob ( tx ) ) ;
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
//get another transactions, if need
2014-08-13 10:51:37 +00:00
std : : list < Transaction > txs ;
2014-03-03 22:07:58 +00:00
get_transactions ( arg . txs , txs , rsp . missed_ids ) ;
//pack aside transactions
2014-06-20 15:56:33 +00:00
for ( const auto & tx : txs ) {
2014-03-03 22:07:58 +00:00
rsp . txs . push_back ( t_serializable_object_to_blob ( tx ) ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true ;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : get_alternative_blocks ( std : : list < Block > & blocks ) {
2014-06-20 15:56:33 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
for ( auto & alt_bl : m_alternative_chains ) {
2014-03-03 22:07:58 +00:00
blocks . push_back ( alt_bl . second . bl ) ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage : : get_alternative_blocks_count ( ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
return m_alternative_chains . size ( ) ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : add_out_to_get_random_outs ( std : : vector < std : : pair < TransactionIndex , uint16_t > > & amount_outs , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & result_outs , uint64_t amount , size_t i ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
const Transaction & tx = transactionByIndex ( amount_outs [ i ] . first ) . tx ;
2014-06-20 15:56:33 +00:00
CHECK_AND_ASSERT_MES ( tx . vout . size ( ) > amount_outs [ i ] . second , false , " internal error: in global outs index, transaction out index= "
< < amount_outs [ i ] . second < < " more than transaction outputs = " < < tx . vout . size ( ) < < " , for tx id = " < < get_transaction_hash ( tx ) ) ;
2014-08-13 10:51:37 +00:00
CHECK_AND_ASSERT_MES ( tx . vout [ amount_outs [ i ] . second ] . target . type ( ) = = typeid ( TransactionOutputToKey ) , false , " unknown tx out type " ) ;
2014-03-03 22:07:58 +00:00
//check if transaction is unlocked
2014-08-13 10:51:37 +00:00
if ( ! is_tx_spendtime_unlocked ( tx . unlockTime ) )
2014-03-03 22:07:58 +00:00
return false ;
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 ;
2014-08-13 10:51:37 +00:00
oen . out_key = boost : : get < TransactionOutputToKey > ( tx . vout [ amount_outs [ i ] . second ] . target ) . key ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage : : find_end_of_allowed_index ( const std : : vector < std : : pair < TransactionIndex , uint16_t > > & amount_outs ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( amount_outs . empty ( ) ) {
2014-03-03 22:07:58 +00:00
return 0 ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
size_t i = amount_outs . size ( ) ;
2014-06-20 15:56:33 +00:00
do {
2014-03-03 22:07:58 +00:00
- - i ;
2014-08-13 10:51:37 +00:00
if ( amount_outs [ i ] . first . block + m_currency . minedMoneyUnlockWindow ( ) < = get_current_blockchain_height ( ) ) {
2014-06-20 15:56:33 +00:00
return i + 1 ;
}
2014-03-03 22:07:58 +00:00
} while ( i ! = 0 ) ;
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return 0 ;
}
2014-06-20 15:56:33 +00:00
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 ) {
2014-03-03 22:07:58 +00:00
srand ( static_cast < unsigned int > ( time ( NULL ) ) ) ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
for ( uint64_t amount : req . amounts ) {
2014-03-03 22:07:58 +00:00
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 ;
auto it = m_outputs . find ( amount ) ;
2014-06-20 15:56:33 +00:00
if ( it = = m_outputs . end ( ) ) {
2014-03-03 22:07:58 +00:00
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
}
2014-06-20 15:56:33 +00:00
std : : vector < std : : pair < TransactionIndex , uint16_t > > & amount_outs = it - > second ;
2014-03-03 22:07:58 +00:00
//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_outs ) ;
CHECK_AND_ASSERT_MES ( up_index_limit < = amount_outs . size ( ) , false , " internal error: find_end_of_allowed_index returned wrong index= " < < up_index_limit < < " , with amount_outs.size = " < < amount_outs . size ( ) ) ;
2014-06-20 15:56:33 +00:00
if ( amount_outs . size ( ) > req . outs_count ) {
2014-03-03 22:07:58 +00:00
std : : set < size_t > used ;
size_t try_count = 0 ;
2014-06-20 15:56:33 +00:00
for ( uint64_t j = 0 ; j ! = req . outs_count & & try_count < up_index_limit ; ) {
size_t i = rand ( ) % up_index_limit ;
if ( used . count ( i ) )
2014-03-03 22:07:58 +00:00
continue ;
bool added = add_out_to_get_random_outs ( amount_outs , result_outs , amount , i ) ;
used . insert ( i ) ;
2014-06-20 15:56:33 +00:00
if ( added )
2014-03-03 22:07:58 +00:00
+ + j ;
+ + try_count ;
}
2014-06-20 15:56:33 +00:00
} else {
for ( size_t i = 0 ; i ! = up_index_limit ; i + + ) {
2014-03-03 22:07:58 +00:00
add_out_to_get_random_outs ( amount_outs , result_outs , amount , i ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
}
return true ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , uint64_t & starter_offset )
{
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( ! qblock_ids . size ( ) /*|| !req.m_total_height*/ )
2014-03-03 22:07:58 +00:00
{
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
2014-06-20 15:56:33 +00:00
if ( qblock_ids . back ( ) ! = get_block_hash ( m_blocks [ 0 ] . bl ) )
2014-03-03 22:07:58 +00:00
{
LOG_ERROR ( " Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " < < ENDL < < " id: "
< < qblock_ids . back ( ) < < " , " < < ENDL < < " expected: " < < get_block_hash ( m_blocks [ 0 ] . bl )
< < " , " < < ENDL < < " dropping connection " ) ;
return false ;
}
/* Figure out what blocks we should request to get state_normal */
2014-08-13 10:51:37 +00:00
if ( m_blockIndex . findSupplement ( qblock_ids , starter_offset ) ) {
return true ;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
//this should NEVER happen, but, dose of paranoia in such cases is not too bad
LOG_ERROR ( " Internal error handling connection, can't find split point " ) ;
return false ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
uint64_t blockchain_storage : : block_difficulty ( size_t i )
{
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
CHECK_AND_ASSERT_MES ( i < m_blocks . size ( ) , false , " wrong block index i = " < < i < < " at blockchain_storage::block_difficulty() " ) ;
2014-06-20 15:56:33 +00:00
if ( i = = 0 )
2014-03-03 22:07:58 +00:00
return m_blocks [ i ] . cumulative_difficulty ;
2014-06-20 15:56:33 +00:00
return m_blocks [ i ] . cumulative_difficulty - m_blocks [ i - 1 ] . cumulative_difficulty ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
void blockchain_storage : : print_blockchain ( uint64_t start_index , uint64_t end_index )
{
std : : stringstream ss ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( start_index > = m_blocks . size ( ) )
2014-03-03 22:07:58 +00:00
{
2014-06-20 15:56:33 +00:00
LOG_PRINT_L0 ( " Wrong starter index set: " < < start_index < < " , expected max index " < < m_blocks . size ( ) - 1 ) ;
2014-03-03 22:07:58 +00:00
return ;
}
2014-06-20 15:56:33 +00:00
for ( size_t i = start_index ; i ! = m_blocks . size ( ) & & i ! = end_index ; i + + )
2014-03-03 22:07:58 +00:00
{
2014-03-20 11:46:11 +00:00
ss < < " height " < < i < < " , timestamp " < < m_blocks [ i ] . bl . timestamp < < " , cumul_dif " < < m_blocks [ i ] . cumulative_difficulty < < " , cumul_size " < < m_blocks [ i ] . block_cumulative_size
2014-06-20 15:56:33 +00:00
< < " \n id \t \t " < < get_block_hash ( m_blocks [ i ] . bl )
2014-08-13 10:51:37 +00:00
< < " \n difficulty \t \t " < < block_difficulty ( i ) < < " , nonce " < < m_blocks [ i ] . bl . nonce < < " , tx_count " < < m_blocks [ i ] . bl . txHashes . size ( ) < < ENDL ;
2014-03-03 22:07:58 +00:00
}
2014-04-02 16:00:17 +00:00
LOG_PRINT_L1 ( " Current blockchain: " < < ENDL < < ss . str ( ) ) ;
LOG_PRINT_L0 ( " Blockchain printed with log level 1 " ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
void blockchain_storage : : print_blockchain_index ( ) {
2014-03-03 22:07:58 +00:00
std : : stringstream ss ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
std : : list < crypto : : hash > blockIds ;
m_blockIndex . getBlockIds ( 0 , std : : numeric_limits < size_t > : : max ( ) , blockIds ) ;
LOG_PRINT_L0 ( " Current blockchain index: " < < ENDL ) ;
size_t height = 0 ;
for ( auto i = blockIds . begin ( ) ; i ! = blockIds . end ( ) ; + + i , + + height ) {
LOG_PRINT_L0 ( " id \t \t " < < * i < < " height " < < height ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
void blockchain_storage : : print_blockchain_outs ( const std : : string & file ) {
2014-03-03 22:07:58 +00:00
std : : stringstream ss ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
for ( const outputs_container : : value_type & v : m_outputs ) {
const std : : vector < std : : pair < TransactionIndex , uint16_t > > & vals = v . second ;
if ( ! vals . empty ( ) ) {
ss < < " amount: " < < v . first < < ENDL ;
for ( size_t i = 0 ; i ! = vals . size ( ) ; i + + ) {
ss < < " \t " < < get_transaction_hash ( transactionByIndex ( vals [ i ] . first ) . tx ) < < " : " < < vals [ i ] . second < < ENDL ;
}
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
if ( epee : : file_io_utils : : save_string_to_file ( file , ss . str ( ) ) ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Current outputs index writen to file: " < < file ) ;
2014-06-20 15:56:33 +00:00
} else {
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Failed to write current outputs index to file: " < < file ) ;
}
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , NOTIFY_RESPONSE_CHAIN_ENTRY : : request & resp ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( ! find_blockchain_supplement ( qblock_ids , resp . start_height ) )
2014-03-03 22:07:58 +00:00
return false ;
resp . total_height = get_current_blockchain_height ( ) ;
size_t count = 0 ;
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_blockIndex . getBlockIds ( resp . start_height , BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT , resp . m_block_ids ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
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 ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
if ( ! find_blockchain_supplement ( qblock_ids , start_height ) ) {
2014-03-03 22:07:58 +00:00
return false ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
total_height = get_current_blockchain_height ( ) ;
size_t count = 0 ;
2014-06-20 15:56:33 +00:00
for ( size_t i = start_height ; i ! = m_blocks . size ( ) & & count < max_count ; i + + , count + + ) {
blocks . resize ( blocks . size ( ) + 1 ) ;
2014-03-03 22:07:58 +00:00
blocks . back ( ) . first = m_blocks [ i ] . bl ;
std : : list < crypto : : hash > mis ;
2014-08-13 10:51:37 +00:00
get_transactions ( m_blocks [ i ] . bl . txHashes , blocks . back ( ) . second , mis ) ;
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( ! mis . size ( ) , false , " internal error, transaction from block not found " ) ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
bool blockchain_storage : : have_block ( const crypto : : hash & id )
{
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
if ( m_blockIndex . hasBlock ( id ) )
2014-03-03 22:07:58 +00:00
return true ;
2014-06-20 15:56:33 +00:00
if ( m_alternative_chains . count ( id ) )
2014-03-03 22:07:58 +00:00
return true ;
return false ;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage : : get_total_transactions ( ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
return m_transactionMap . size ( ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : get_tx_outputs_gindexs ( const crypto : : hash & tx_id , std : : vector < uint64_t > & indexs ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
auto it = m_transactionMap . find ( tx_id ) ;
if ( it = = m_transactionMap . end ( ) ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_RED_L0 ( " warning: get_tx_outputs_gindexs failed to find transaction with id = " < < tx_id ) ;
return false ;
}
2014-08-13 10:51:37 +00:00
const TransactionEntry & tx = transactionByIndex ( it - > second ) ;
2014-06-20 15:56:33 +00:00
CHECK_AND_ASSERT_MES ( tx . m_global_output_indexes . size ( ) , false , " internal error: global indexes for transaction " < < tx_id < < " is empty " ) ;
indexs . resize ( tx . m_global_output_indexes . size ( ) ) ;
for ( size_t i = 0 ; i < tx . m_global_output_indexes . size ( ) ; + + i ) {
indexs [ i ] = tx . m_global_output_indexes [ i ] ;
}
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_tx_inputs ( const Transaction & tx , uint64_t & max_used_block_height , crypto : : hash & max_used_block_id , BlockInfo * tail ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
if ( tail )
tail - > id = get_tail_id ( tail - > height ) ;
2014-03-03 22:07:58 +00:00
bool res = check_tx_inputs ( tx , & max_used_block_height ) ;
2014-06-20 15:56:33 +00:00
if ( ! res ) return false ;
CHECK_AND_ASSERT_MES ( max_used_block_height < m_blocks . size ( ) , false , " internal error: max used block index= " < < max_used_block_height < < " is not less then blockchain size = " < < m_blocks . size ( ) ) ;
2014-03-03 22:07:58 +00:00
get_block_hash ( m_blocks [ max_used_block_height ] . bl , max_used_block_id ) ;
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : have_tx_keyimges_as_spent ( const Transaction & tx ) {
for ( const auto & in : tx . vin ) {
if ( in . type ( ) = = typeid ( TransactionInputToKey ) ) {
if ( have_tx_keyimg_as_spent ( boost : : get < TransactionInputToKey > ( in ) . keyImage ) ) {
return true ;
}
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_tx_inputs ( const Transaction & tx , uint64_t * pmax_used_block_height ) {
2014-03-03 22:07:58 +00:00
crypto : : hash tx_prefix_hash = get_transaction_prefix_hash ( tx ) ;
return check_tx_inputs ( tx , tx_prefix_hash , pmax_used_block_height ) ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_tx_inputs ( const Transaction & tx , const crypto : : hash & tx_prefix_hash , uint64_t * pmax_used_block_height ) {
size_t inputIndex = 0 ;
2014-06-20 15:56:33 +00:00
if ( pmax_used_block_height ) {
2014-03-03 22:07:58 +00:00
* pmax_used_block_height = 0 ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
crypto : : hash transactionHash = get_transaction_hash ( tx ) ;
2014-06-20 15:56:33 +00:00
for ( const auto & txin : tx . vin ) {
2014-08-13 10:51:37 +00:00
assert ( inputIndex < tx . signatures . size ( ) ) ;
if ( txin . type ( ) = = typeid ( TransactionInputToKey ) ) {
const TransactionInputToKey & in_to_key = boost : : get < TransactionInputToKey > ( txin ) ;
CHECK_AND_ASSERT_MES ( ! in_to_key . keyOffsets . empty ( ) , false , " empty in_to_key.keyOffsets in transaction with id " < < get_transaction_hash ( tx ) ) ;
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if ( have_tx_keyimg_as_spent ( in_to_key . keyImage ) ) {
LOG_PRINT_L1 ( " Key image already spent in blockchain: " < < epee : : string_tools : : pod_to_hex ( in_to_key . keyImage ) ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if ( ! check_tx_input ( in_to_key , tx_prefix_hash , tx . signatures [ inputIndex ] , pmax_used_block_height ) ) {
LOG_PRINT_L0 ( " Failed to check ring signature for tx " < < transactionHash ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
+ + inputIndex ;
} else if ( txin . type ( ) = = typeid ( TransactionInputMultisignature ) ) {
if ( ! validateInput ( : : boost : : get < TransactionInputMultisignature > ( txin ) , transactionHash , tx_prefix_hash , tx . signatures [ inputIndex ] ) ) {
return false ;
}
+ + inputIndex ;
} else {
LOG_PRINT_L0 ( " Transaction << " < < transactionHash < < " contains input of unsupported type. " ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
}
return true ;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : is_tx_spendtime_unlocked ( uint64_t unlock_time ) {
2014-08-13 10:51:37 +00:00
if ( unlock_time < m_currency . maxBlockHeight ( ) ) {
2014-03-03 22:07:58 +00:00
//interpret as block index
2014-08-13 10:51:37 +00:00
if ( get_current_blockchain_height ( ) - 1 + m_currency . lockedTxAllowedDeltaBlocks ( ) > = unlock_time )
2014-03-03 22:07:58 +00:00
return true ;
else
return false ;
2014-06-20 15:56:33 +00:00
} else {
2014-03-03 22:07:58 +00:00
//interpret as time
uint64_t current_time = static_cast < uint64_t > ( time ( NULL ) ) ;
2014-08-13 10:51:37 +00:00
if ( current_time + m_currency . lockedTxAllowedDeltaSeconds ( ) > = unlock_time )
2014-03-03 22:07:58 +00:00
return true ;
else
return false ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_tx_input ( const TransactionInputToKey & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , uint64_t * pmax_related_block_height ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
struct outputs_visitor
{
std : : vector < const crypto : : public_key * > & m_results_collector ;
blockchain_storage & m_bch ;
2014-06-20 15:56:33 +00:00
outputs_visitor ( std : : vector < const crypto : : public_key * > & results_collector , blockchain_storage & bch ) : m_results_collector ( results_collector ) , m_bch ( bch )
2014-03-03 22:07:58 +00:00
{ }
2014-08-13 10:51:37 +00:00
bool handle_output ( const Transaction & tx , const TransactionOutput & out ) {
2014-03-03 22:07:58 +00:00
//check tx unlock time
2014-08-13 10:51:37 +00:00
if ( ! m_bch . is_tx_spendtime_unlocked ( tx . unlockTime ) ) {
LOG_PRINT_L0 ( " One of outputs for one of inputs have wrong tx.unlockTime = " < < tx . unlockTime ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
if ( out . target . type ( ) ! = typeid ( TransactionOutputToKey ) )
2014-03-03 22:07:58 +00:00
{
LOG_PRINT_L0 ( " Output have wrong type id, which= " < < out . target . which ( ) ) ;
return false ;
}
2014-08-13 10:51:37 +00:00
m_results_collector . push_back ( & boost : : get < TransactionOutputToKey > ( out . target ) . key ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
} ;
//check ring signature
std : : vector < const crypto : : public_key * > output_keys ;
outputs_visitor vi ( output_keys , * this ) ;
2014-06-20 15:56:33 +00:00
if ( ! scan_outputkeys_for_indexes ( txin , vi , pmax_related_block_height ) ) {
2014-08-13 10:51:37 +00:00
LOG_PRINT_L0 ( " Failed to get output keys for tx with amount = " < < m_currency . formatAmount ( txin . amount ) < <
" and count indexes " < < txin . keyOffsets . size ( ) ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
if ( txin . keyOffsets . size ( ) ! = output_keys . size ( ) ) {
LOG_PRINT_L0 ( " Output keys for tx with amount = " < < txin . amount < < " and count indexes " < < txin . keyOffsets . size ( ) < < " returned wrong keys count " < < output_keys . size ( ) ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
CHECK_AND_ASSERT_MES ( sig . size ( ) = = output_keys . size ( ) , false , " internal error: tx signatures count= " < < sig . size ( ) < < " mismatch with outputs keys count for inputs= " < < output_keys . size ( ) ) ;
2014-06-20 15:56:33 +00:00
if ( m_is_in_checkpoint_zone ) {
2014-03-03 22:07:58 +00:00
return true ;
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
return crypto : : check_ring_signature ( tx_prefix_hash , txin . keyImage , output_keys , sig . data ( ) ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage : : get_adjusted_time ( ) {
2014-03-03 22:07:58 +00:00
//TODO: add collecting median time
return time ( NULL ) ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_block_timestamp_main ( const Block & b ) {
if ( b . timestamp > get_adjusted_time ( ) + m_currency . blockFutureTimeLimit ( ) ) {
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + 2 hours " ) ;
return false ;
}
std : : vector < uint64_t > timestamps ;
2014-08-13 10:51:37 +00:00
size_t offset = m_blocks . size ( ) < = m_currency . timestampCheckWindow ( ) ? 0 : m_blocks . size ( ) - m_currency . timestampCheckWindow ( ) ;
2014-06-20 15:56:33 +00:00
for ( ; offset ! = m_blocks . size ( ) ; + + offset ) {
2014-03-03 22:07:58 +00:00
timestamps . push_back ( m_blocks [ offset ] . bl . timestamp ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return check_block_timestamp ( std : : move ( timestamps ) , b ) ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : check_block_timestamp ( std : : vector < uint64_t > timestamps , const Block & b ) {
if ( timestamps . size ( ) < m_currency . timestampCheckWindow ( ) ) {
2014-03-03 22:07:58 +00:00
return true ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
uint64_t median_ts = epee : : misc_utils : : median ( timestamps ) ;
2014-06-20 15:56:33 +00:00
if ( b . timestamp < median_ts ) {
2014-08-13 10:51:37 +00:00
LOG_PRINT_L0 ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < <
" , less than median of last " < < m_currency . timestampCheckWindow ( ) < < " blocks, " < < median_ts ) ;
return false ;
}
return true ;
}
bool blockchain_storage : : checkBlockVersion ( const Block & b , const crypto : : hash & blockHash ) {
uint64_t height = get_block_height ( b ) ;
const uint8_t expectedBlockVersion = get_block_major_version_for_height ( height ) ;
if ( b . majorVersion ! = expectedBlockVersion ) {
2014-08-25 14:35:07 +00:00
LOG_PRINT_L2 ( " Block " < < blockHash < < " has wrong major version: " < < static_cast < int > ( b . majorVersion ) < <
2014-08-13 10:51:37 +00:00
" , at height " < < height < < " expected version is " < < static_cast < int > ( expectedBlockVersion ) ) ;
return false ;
}
return true ;
}
bool blockchain_storage : : checkParentBlockSize ( const Block & b , const crypto : : hash & blockHash ) {
if ( BLOCK_MAJOR_VERSION_2 = = b . majorVersion ) {
auto serializer = makeParentBlockSerializer ( b , false , false ) ;
size_t parentBlockSize ;
if ( ! get_object_blobsize ( serializer , parentBlockSize ) ) {
LOG_ERROR ( " Block " < < blockHash < < " : failed to determine parent block size " ) ;
return false ;
}
if ( parentBlockSize > 2 * 1024 ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " contains too big parent block: " < < parentBlockSize < <
" bytes, expected no more than " < < 2 * 1024 < < " bytes " ) ;
return false ;
}
}
return true ;
}
bool blockchain_storage : : checkCumulativeBlockSize ( const crypto : : hash & blockId , size_t cumulativeBlockSize , uint64_t height ) {
size_t maxBlockCumulativeSize = m_currency . maxBlockCumulativeSize ( height ) ;
if ( cumulativeBlockSize > maxBlockCumulativeSize ) {
LOG_PRINT_L0 ( " Block " < < blockId < < " is too big: " < < cumulativeBlockSize < < " bytes, " < <
" exptected no more than " < < maxBlockCumulativeSize < < " bytes " ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
return true ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
// Returns true, if cumulativeSize is calculated precisely, else returns false.
bool blockchain_storage : : getBlockCumulativeSize ( const Block & block , size_t & cumulativeSize ) {
std : : vector < Transaction > blockTxs ;
std : : vector < crypto : : hash > missedTxs ;
get_transactions ( block . txHashes , blockTxs , missedTxs , true ) ;
cumulativeSize = get_object_blobsize ( block . minerTx ) ;
for ( const Transaction & tx : blockTxs ) {
cumulativeSize + = get_object_blobsize ( tx ) ;
}
return missedTxs . empty ( ) ;
}
2014-08-14 15:41:44 +00:00
// Precondition: m_blockchain_lock is locked.
2014-06-20 15:56:33 +00:00
bool blockchain_storage : : update_next_comulative_size_limit ( ) {
2014-08-14 15:41:44 +00:00
uint8_t nextBlockMajorVersion = get_block_major_version_for_height ( m_blocks . size ( ) ) ;
size_t nextBlockGrantedFullRewardZone = nextBlockMajorVersion = = BLOCK_MAJOR_VERSION_1 ?
parameters : : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 :
m_currency . blockGrantedFullRewardZone ( ) ;
2014-06-20 15:56:33 +00:00
std : : vector < size_t > sz ;
2014-08-13 10:51:37 +00:00
get_last_n_blocks_sizes ( sz , m_currency . rewardBlocksWindow ( ) ) ;
2014-06-20 15:56:33 +00:00
uint64_t median = epee : : misc_utils : : median ( sz ) ;
2014-08-14 15:41:44 +00:00
if ( median < = nextBlockGrantedFullRewardZone ) {
median = nextBlockGrantedFullRewardZone ;
}
2014-06-20 15:56:33 +00:00
m_current_block_cumul_sz_limit = median * 2 ;
return true ;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : add_new_block ( const Block & bl_ , block_verification_context & bvc ) {
2014-06-20 15:56:33 +00:00
//copy block here to let modify block.target
2014-08-13 10:51:37 +00:00
Block bl = bl_ ;
crypto : : hash id ;
if ( ! get_block_hash ( bl , id ) ) {
LOG_ERROR ( " Failed to get block hash, possible block has invalid format " ) ;
bvc . m_verifivation_failed = true ;
return false ;
}
2014-06-20 15:56:33 +00:00
CRITICAL_REGION_LOCAL ( m_tx_pool ) ; //to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1 ( m_blockchain_lock ) ;
if ( have_block ( id ) ) {
LOG_PRINT_L3 ( " block with id = " < < id < < " already exists " ) ;
bvc . m_already_exists = true ;
return false ;
}
//check that block refers to chain tail
2014-08-13 10:51:37 +00:00
if ( ! ( bl . prevId = = get_tail_id ( ) ) ) {
2014-06-20 15:56:33 +00:00
//chain switching or wrong block
bvc . m_added_to_main_chain = false ;
return handle_alternative_block ( bl , id , bvc ) ;
//never relay alternative blocks
}
return pushBlock ( bl , bvc ) ;
}
2014-08-13 10:51:37 +00:00
const blockchain_storage : : TransactionEntry & blockchain_storage : : transactionByIndex ( TransactionIndex index ) {
2014-06-20 15:56:33 +00:00
return m_blocks [ index . block ] . transactions [ index . transaction ] ;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : pushBlock ( const Block & blockData , block_verification_context & bvc ) {
2014-03-03 22:07:58 +00:00
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
TIME_MEASURE_START ( block_processing_time ) ;
crypto : : hash blockHash = get_block_hash ( blockData ) ;
2014-08-13 10:51:37 +00:00
if ( m_blockIndex . hasBlock ( blockHash ) ) {
2014-06-20 15:56:33 +00:00
LOG_ERROR ( " Block " < < blockHash < < " already exists in blockchain. " ) ;
bvc . m_verifivation_failed = true ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
if ( ! checkBlockVersion ( blockData , blockHash ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
if ( ! checkParentBlockSize ( blockData , blockHash ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
if ( blockData . prevId ! = get_tail_id ( ) ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " has wrong prevId: " < < blockData . prevId < < " , expected: " < < get_tail_id ( ) ) ;
2014-06-20 15:56:33 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
if ( ! check_block_timestamp_main ( blockData ) ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " has invalid timestamp: " < < blockData . timestamp ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
TIME_MEASURE_START ( target_calculating_time ) ;
2014-06-20 15:56:33 +00:00
difficulty_type currentDifficulty = get_difficulty_for_next_block ( ) ;
2014-03-03 22:07:58 +00:00
TIME_MEASURE_FINISH ( target_calculating_time ) ;
2014-06-20 15:56:33 +00:00
CHECK_AND_ASSERT_MES ( currentDifficulty , false , " !!!!!!!!! difficulty overhead !!!!!!!!! " ) ;
2014-03-03 22:07:58 +00:00
TIME_MEASURE_START ( longhash_calculating_time ) ;
crypto : : hash proof_of_work = null_hash ;
2014-06-20 15:56:33 +00:00
if ( m_checkpoints . is_in_checkpoint_zone ( get_current_blockchain_height ( ) ) ) {
if ( ! m_checkpoints . check_block ( get_current_blockchain_height ( ) , blockHash ) ) {
LOG_ERROR ( " CHECKPOINT VALIDATION FAILED " ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-06-20 15:56:33 +00:00
} else {
2014-08-13 10:51:37 +00:00
if ( ! m_currency . checkProofOfWork ( m_cn_context , blockData , currentDifficulty , proof_of_work ) ) {
2014-06-20 15:56:33 +00:00
LOG_PRINT_L0 ( " Block " < < blockHash < < " , has too weak proof of work: " < < proof_of_work < < " , expected difficulty: " < < currentDifficulty ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
TIME_MEASURE_FINISH ( longhash_calculating_time ) ;
2014-06-20 15:56:33 +00:00
if ( ! prevalidate_miner_transaction ( blockData , m_blocks . size ( ) ) ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " failed to pass prevalidation " ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
crypto : : hash minerTransactionHash = get_transaction_hash ( blockData . minerTx ) ;
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
BlockEntry block ;
2014-06-20 15:56:33 +00:00
block . bl = blockData ;
block . transactions . resize ( 1 ) ;
2014-08-13 10:51:37 +00:00
block . transactions [ 0 ] . tx = blockData . minerTx ;
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = { static_cast < uint32_t > ( m_blocks . size ( ) ) , static_cast < uint16_t > ( 0 ) } ;
pushTransaction ( block , minerTransactionHash , transactionIndex ) ;
2014-08-13 10:51:37 +00:00
size_t coinbase_blob_size = get_object_blobsize ( blockData . minerTx ) ;
2014-03-03 22:07:58 +00:00
size_t cumulative_block_size = coinbase_blob_size ;
uint64_t fee_summary = 0 ;
2014-08-13 10:51:37 +00:00
for ( const crypto : : hash & tx_id : blockData . txHashes ) {
2014-06-20 15:56:33 +00:00
block . transactions . resize ( block . transactions . size ( ) + 1 ) ;
2014-03-03 22:07:58 +00:00
size_t blob_size = 0 ;
uint64_t fee = 0 ;
2014-06-20 15:56:33 +00:00
if ( ! m_tx_pool . take_tx ( tx_id , block . transactions . back ( ) . tx , blob_size , fee ) ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " has at least one unknown transaction: " < < tx_id ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
2014-06-20 15:56:33 +00:00
tx_verification_context tvc = : : AUTO_VAL_INIT ( tvc ) ;
block . transactions . pop_back ( ) ;
popTransactions ( block , minerTransactionHash ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
if ( ! check_tx_inputs ( block . transactions . back ( ) . tx ) ) {
LOG_PRINT_L0 ( " Block " < < blockHash < < " has at least one transaction with wrong inputs: " < < tx_id ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
2014-06-20 15:56:33 +00:00
tx_verification_context tvc = : : AUTO_VAL_INIT ( tvc ) ;
if ( ! m_tx_pool . add_tx ( block . transactions . back ( ) . tx , tvc , true ) ) {
LOG_ERROR ( " Cannot move transaction from blockchain to transaction pool. " ) ;
}
block . transactions . pop_back ( ) ;
popTransactions ( block , minerTransactionHash ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
+ + transactionIndex . transaction ;
pushTransaction ( block , tx_id , transactionIndex ) ;
2014-03-03 22:07:58 +00:00
cumulative_block_size + = blob_size ;
2014-06-20 15:56:33 +00:00
fee_summary + = fee ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( ! checkCumulativeBlockSize ( blockHash , cumulative_block_size , m_blocks . size ( ) ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
int64_t emissionChange = 0 ;
uint64_t reward = 0 ;
uint64_t already_generated_coins = m_blocks . empty ( ) ? 0 : m_blocks . back ( ) . already_generated_coins ;
if ( ! validate_miner_transaction ( blockData , m_blocks . size ( ) , cumulative_block_size , already_generated_coins , fee_summary , reward , emissionChange ) ) {
2014-06-20 15:56:33 +00:00
LOG_PRINT_L0 ( " Block " < < blockHash < < " has invalid miner transaction " ) ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
2014-06-20 15:56:33 +00:00
popTransactions ( block , minerTransactionHash ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
block . height = static_cast < uint32_t > ( m_blocks . size ( ) ) ;
block . block_cumulative_size = cumulative_block_size ;
block . cumulative_difficulty = currentDifficulty ;
2014-08-13 10:51:37 +00:00
block . already_generated_coins = already_generated_coins + emissionChange ;
2014-06-20 15:56:33 +00:00
if ( m_blocks . size ( ) > 0 ) {
block . cumulative_difficulty + = m_blocks . back ( ) . cumulative_difficulty ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
pushBlock ( block ) ;
2014-03-03 22:07:58 +00:00
TIME_MEASURE_FINISH ( block_processing_time ) ;
2014-06-20 15:56:33 +00:00
LOG_PRINT_L1 ( " +++++ BLOCK SUCCESSFULLY ADDED " < < ENDL < < " id: \t " < < blockHash
2014-03-03 22:07:58 +00:00
< < ENDL < < " PoW: \t " < < proof_of_work
2014-06-20 15:56:33 +00:00
< < ENDL < < " HEIGHT " < < block . height < < " , difficulty: \t " < < currentDifficulty
2014-08-13 10:51:37 +00:00
< < ENDL < < " block reward: " < < m_currency . formatAmount ( reward ) < < " , fee = " < < m_currency . formatAmount ( fee_summary )
< < " , coinbase_blob_size: " < < coinbase_blob_size < < " , cumulative size: " < < cumulative_block_size
2014-06-20 15:56:33 +00:00
< < " , " < < block_processing_time < < " ( " < < target_calculating_time < < " / " < < longhash_calculating_time < < " )ms " ) ;
2014-03-03 22:07:58 +00:00
bvc . m_added_to_main_chain = true ;
2014-08-13 10:51:37 +00:00
m_upgradeDetector . blockPushed ( ) ;
2014-08-14 15:41:44 +00:00
update_next_comulative_size_limit ( ) ;
2014-08-13 10:51:37 +00:00
2014-03-03 22:07:58 +00:00
return true ;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : pushBlock ( BlockEntry & block ) {
2014-06-20 15:56:33 +00:00
crypto : : hash blockHash = get_block_hash ( block . bl ) ;
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
m_blocks . push_back ( block ) ;
2014-08-13 10:51:37 +00:00
m_blockIndex . push ( blockHash ) ;
assert ( m_blockIndex . size ( ) = = m_blocks . size ( ) ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
void blockchain_storage : : popBlock ( const crypto : : hash & blockHash ) {
if ( m_blocks . empty ( ) ) {
LOG_ERROR ( " Attempt to pop block from empty blockchain. " ) ;
return ;
}
2014-08-13 10:51:37 +00:00
popTransactions ( m_blocks . back ( ) , get_transaction_hash ( m_blocks . back ( ) . bl . minerTx ) ) ;
2014-06-20 15:56:33 +00:00
m_blocks . pop_back ( ) ;
2014-08-13 10:51:37 +00:00
m_blockIndex . pop ( ) ;
assert ( m_blockIndex . size ( ) = = m_blocks . size ( ) ) ;
m_upgradeDetector . blockPopped ( ) ;
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage : : pushTransaction ( BlockEntry & block , const crypto : : hash & transactionHash , TransactionIndex transactionIndex ) {
2014-06-20 15:56:33 +00:00
auto result = m_transactionMap . insert ( std : : make_pair ( transactionHash , transactionIndex ) ) ;
if ( ! result . second ) {
LOG_ERROR ( " Duplicate transaction was pushed to blockchain. " ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
TransactionEntry & transaction = block . transactions [ transactionIndex . transaction ] ;
if ( ! checkMultisignatureInputsDiff ( transaction . tx ) ) {
LOG_ERROR ( " Double spending transaction was pushed to blockchain. " ) ;
m_transactionMap . erase ( transactionHash ) ;
return false ;
}
2014-06-20 15:56:33 +00:00
for ( size_t i = 0 ; i < transaction . tx . vin . size ( ) ; + + i ) {
2014-08-13 10:51:37 +00:00
if ( transaction . tx . vin [ i ] . type ( ) = = typeid ( TransactionInputToKey ) ) {
auto result = m_spent_keys . insert ( : : boost : : get < TransactionInputToKey > ( transaction . tx . vin [ i ] ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
if ( ! result . second ) {
LOG_ERROR ( " Double spending transaction was pushed to blockchain. " ) ;
for ( size_t j = 0 ; j < i ; + + j ) {
2014-08-13 10:51:37 +00:00
m_spent_keys . erase ( : : boost : : get < TransactionInputToKey > ( transaction . tx . vin [ i - 1 - j ] ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
}
m_transactionMap . erase ( transactionHash ) ;
return false ;
}
}
}
2014-08-13 10:51:37 +00:00
for ( const auto & inv : transaction . tx . vin ) {
if ( inv . type ( ) = = typeid ( TransactionInputMultisignature ) ) {
const TransactionInputMultisignature & in = : : boost : : get < TransactionInputMultisignature > ( inv ) ;
auto & amountOutputs = m_multisignatureOutputs [ in . amount ] ;
amountOutputs [ in . outputIndex ] . isUsed = true ;
}
}
2014-06-20 15:56:33 +00:00
transaction . m_global_output_indexes . resize ( transaction . tx . vout . size ( ) ) ;
for ( uint16_t output = 0 ; output < transaction . tx . vout . size ( ) ; + + output ) {
2014-08-13 10:51:37 +00:00
if ( transaction . tx . vout [ output ] . target . type ( ) = = typeid ( TransactionOutputToKey ) ) {
auto & amountOutputs = m_outputs [ transaction . tx . vout [ output ] . amount ] ;
transaction . m_global_output_indexes [ output ] = amountOutputs . size ( ) ;
amountOutputs . push_back ( std : : make_pair < > ( transactionIndex , output ) ) ;
} else if ( transaction . tx . vout [ output ] . target . type ( ) = = typeid ( TransactionOutputMultisignature ) ) {
auto & amountOutputs = m_multisignatureOutputs [ transaction . tx . vout [ output ] . amount ] ;
MultisignatureOutputUsage outputUsage = { transactionIndex , output , false } ;
amountOutputs . push_back ( outputUsage ) ;
}
2014-06-20 15:56:33 +00:00
}
return true ;
}
2014-08-13 10:51:37 +00:00
void blockchain_storage : : popTransaction ( const Transaction & transaction , const crypto : : hash & transactionHash ) {
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = m_transactionMap . at ( transactionHash ) ;
2014-08-13 10:51:37 +00:00
for ( size_t outputIndex = 0 ; outputIndex < transaction . vout . size ( ) ; + + outputIndex ) {
const TransactionOutput & output = transaction . vout [ transaction . vout . size ( ) - 1 - outputIndex ] ;
if ( output . target . type ( ) = = typeid ( TransactionOutputToKey ) ) {
auto amountOutputs = m_outputs . find ( output . amount ) ;
if ( amountOutputs = = m_outputs . end ( ) ) {
LOG_ERROR ( " Blockchain consistency broken - cannot find specific amount in outputs map. " ) ;
continue ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( amountOutputs - > second . empty ( ) ) {
LOG_ERROR ( " Blockchain consistency broken - output array for specific amount is empty. " ) ;
continue ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( amountOutputs - > second . back ( ) . first . block ! = transactionIndex . block | | amountOutputs - > second . back ( ) . first . transaction ! = transactionIndex . transaction ) {
LOG_ERROR ( " Blockchain consistency broken - invalid transaction index. " ) ;
continue ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( amountOutputs - > second . back ( ) . second ! = transaction . vout . size ( ) - 1 - outputIndex ) {
LOG_ERROR ( " Blockchain consistency broken - invalid output index. " ) ;
continue ;
}
amountOutputs - > second . pop_back ( ) ;
if ( amountOutputs - > second . empty ( ) ) {
m_outputs . erase ( amountOutputs ) ;
}
} else if ( output . target . type ( ) = = typeid ( TransactionOutputMultisignature ) ) {
auto amountOutputs = m_multisignatureOutputs . find ( output . amount ) ;
if ( amountOutputs = = m_multisignatureOutputs . end ( ) ) {
LOG_ERROR ( " Blockchain consistency broken - cannot find specific amount in outputs map. " ) ;
continue ;
}
if ( amountOutputs - > second . empty ( ) ) {
LOG_ERROR ( " Blockchain consistency broken - output array for specific amount is empty. " ) ;
continue ;
}
if ( amountOutputs - > second . back ( ) . isUsed ) {
LOG_ERROR ( " Blockchain consistency broken - attempting to remove used output. " ) ;
continue ;
}
if ( amountOutputs - > second . back ( ) . transactionIndex . block ! = transactionIndex . block | | amountOutputs - > second . back ( ) . transactionIndex . transaction ! = transactionIndex . transaction ) {
LOG_ERROR ( " Blockchain consistency broken - invalid transaction index. " ) ;
continue ;
}
if ( amountOutputs - > second . back ( ) . outputIndex ! = transaction . vout . size ( ) - 1 - outputIndex ) {
LOG_ERROR ( " Blockchain consistency broken - invalid output index. " ) ;
continue ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
amountOutputs - > second . pop_back ( ) ;
if ( amountOutputs - > second . empty ( ) ) {
m_multisignatureOutputs . erase ( amountOutputs ) ;
}
2014-06-20 15:56:33 +00:00
}
}
for ( auto & input : transaction . vin ) {
2014-08-13 10:51:37 +00:00
if ( input . type ( ) = = typeid ( TransactionInputToKey ) ) {
size_t count = m_spent_keys . erase ( : : boost : : get < TransactionInputToKey > ( input ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
if ( count ! = 1 ) {
LOG_ERROR ( " Blockchain consistency broken - cannot find spent key. " ) ;
}
2014-08-13 10:51:37 +00:00
} else if ( input . type ( ) = = typeid ( TransactionInputMultisignature ) ) {
const TransactionInputMultisignature & in = : : boost : : get < TransactionInputMultisignature > ( input ) ;
auto & amountOutputs = m_multisignatureOutputs [ in . amount ] ;
if ( ! amountOutputs [ in . outputIndex ] . isUsed ) {
LOG_ERROR ( " Blockchain consistency broken - multisignature output not marked as used. " ) ;
}
amountOutputs [ in . outputIndex ] . isUsed = false ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
size_t count = m_transactionMap . erase ( transactionHash ) ;
if ( count ! = 1 ) {
LOG_ERROR ( " Blockchain consistency broken - cannot find transaction by hash. " ) ;
}
}
2014-08-13 10:51:37 +00:00
void blockchain_storage : : popTransactions ( const BlockEntry & block , const crypto : : hash & minerTransactionHash ) {
2014-06-20 15:56:33 +00:00
for ( size_t i = 0 ; i < block . transactions . size ( ) - 1 ; + + i ) {
2014-08-13 10:51:37 +00:00
popTransaction ( block . transactions [ block . transactions . size ( ) - 1 - i ] . tx , block . bl . txHashes [ block . transactions . size ( ) - 2 - i ] ) ;
2014-06-20 15:56:33 +00:00
tx_verification_context tvc = : : AUTO_VAL_INIT ( tvc ) ;
if ( ! m_tx_pool . add_tx ( block . transactions [ block . transactions . size ( ) - 1 - i ] . tx , tvc , true ) ) {
LOG_ERROR ( " Cannot move transaction from blockchain to transaction pool. " ) ;
}
}
2014-08-13 10:51:37 +00:00
popTransaction ( block . bl . minerTx , minerTransactionHash ) ;
}
bool blockchain_storage : : validateInput ( const TransactionInputMultisignature & input , const crypto : : hash & transactionHash , const crypto : : hash & transactionPrefixHash , const std : : vector < crypto : : signature > & transactionSignatures ) {
assert ( input . signatures = = transactionSignatures . size ( ) ) ;
MultisignatureOutputsContainer : : const_iterator amountOutputs = m_multisignatureOutputs . find ( input . amount ) ;
if ( amountOutputs = = m_multisignatureOutputs . end ( ) ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains multisignature input with invalid amount. " ) ;
return false ;
}
if ( input . outputIndex > = amountOutputs - > second . size ( ) ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains multisignature input with invalid outputIndex. " ) ;
return false ;
}
const MultisignatureOutputUsage & outputIndex = amountOutputs - > second [ input . outputIndex ] ;
if ( outputIndex . isUsed ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains double spending multisignature input. " ) ;
return false ;
}
const Transaction & outputTransaction = m_blocks [ outputIndex . transactionIndex . block ] . transactions [ outputIndex . transactionIndex . transaction ] . tx ;
if ( ! is_tx_spendtime_unlocked ( outputTransaction . unlockTime ) ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains multisignature input which points to a locked transaction. " ) ;
return false ;
}
assert ( outputTransaction . vout [ outputIndex . outputIndex ] . amount = = input . amount ) ;
assert ( outputTransaction . vout [ outputIndex . outputIndex ] . target . type ( ) = = typeid ( TransactionOutputMultisignature ) ) ;
const TransactionOutputMultisignature & output = : : boost : : get < TransactionOutputMultisignature > ( outputTransaction . vout [ outputIndex . outputIndex ] . target ) ;
if ( input . signatures ! = output . requiredSignatures ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains multisignature input with invalid signature count. " ) ;
return false ;
}
std : : size_t inputSignatureIndex = 0 ;
std : : size_t outputKeyIndex = 0 ;
while ( inputSignatureIndex < input . signatures ) {
if ( outputKeyIndex = = output . keys . size ( ) ) {
LOG_PRINT_L1 ( " Transaction << " < < transactionHash < < " contains multisignature input with invalid signatures. " ) ;
return false ;
}
if ( crypto : : check_signature ( transactionPrefixHash , output . keys [ outputKeyIndex ] , transactionSignatures [ inputSignatureIndex ] ) ) {
+ + inputSignatureIndex ;
}
+ + outputKeyIndex ;
}
return true ;
}
bool blockchain_storage : : getLowerBound ( uint64_t timestamp , uint64_t startOffset , uint64_t & height ) {
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
if ( startOffset > = m_blocks . size ( ) ) {
return false ;
}
auto bound = std : : lower_bound ( m_blocks . begin ( ) + startOffset , m_blocks . end ( ) , timestamp - m_currency . blockFutureTimeLimit ( ) ,
[ ] ( const BlockEntry & b , uint64_t timestamp ) { return b . bl . timestamp < timestamp ; } ) ;
if ( bound = = m_blocks . end ( ) ) {
return false ;
}
height = std : : distance ( m_blocks . begin ( ) , bound ) ;
return true ;
}
bool blockchain_storage : : getBlockIds ( uint64_t startHeight , size_t maxCount , std : : list < crypto : : hash > & items ) {
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
return m_blockIndex . getBlockIds ( startHeight , maxCount , items ) ;
2014-06-20 15:56:33 +00:00
}