2015-09-18 11:55:31 +00:00
// Copyright (c) 2011-2015 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
# include "Blockchain.h"
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
# include <algorithm>
# include <cstdio>
2015-05-27 12:08:46 +00:00
# include <boost/foreach.hpp>
2015-07-15 12:23:00 +00:00
# include "Common/Math.h"
2015-05-27 12:08:46 +00:00
# include "Common/ShuffleGenerator.h"
2015-07-30 15:22:07 +00:00
# include "Common/StdInputStream.h"
# include "Common/StdOutputStream.h"
# include "Rpc/CoreRpcServerCommandsDefinitions.h"
# include "Serialization/BinarySerializationTools.h"
# include "CryptoNoteTools.h"
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
using namespace Logging ;
2015-07-30 15:22:07 +00:00
using namespace Common ;
2014-06-20 15:56:33 +00:00
namespace {
2015-07-15 12:23:00 +00:00
2015-05-27 12:08:46 +00:00
std : : string appendPath ( const std : : string & path , const std : : string & fileName ) {
std : : string result = path ;
if ( ! result . empty ( ) ) {
result + = ' / ' ;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
result + = fileName ;
return result ;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace std {
2015-07-30 15:22:07 +00:00
bool operator < ( const Crypto : : Hash & hash1 , const Crypto : : Hash & hash2 ) {
return memcmp ( & hash1 , & hash2 , Crypto : : HASH_SIZE ) < 0 ;
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool operator < ( const Crypto : : KeyImage & keyImage1 , const Crypto : : KeyImage & keyImage2 ) {
2015-05-27 12:08:46 +00:00
return memcmp ( & keyImage1 , & keyImage2 , 32 ) < 0 ;
}
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
# define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1
2015-07-30 15:22:07 +00:00
# define CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER 1
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace CryptoNote {
class BlockCacheSerializer ;
2015-07-30 15:22:07 +00:00
class BlockchainIndicesSerializer ;
}
namespace CryptoNote {
template < typename K , typename V , typename Hash >
bool serialize ( google : : sparse_hash_map < K , V , Hash > & value , Common : : StringView name , CryptoNote : : ISerializer & serializer ) {
return serializeMap ( value , name , serializer , [ & value ] ( size_t size ) { value . resize ( size ) ; } ) ;
}
template < typename K , typename Hash >
bool serialize ( google : : sparse_hash_set < K , Hash > & value , Common : : StringView name , CryptoNote : : ISerializer & serializer ) {
size_t size = value . size ( ) ;
if ( ! serializer . beginArray ( size , name ) ) {
return false ;
}
if ( serializer . type ( ) = = ISerializer : : OUTPUT ) {
for ( auto & key : value ) {
serializer ( const_cast < K & > ( key ) , " " ) ;
}
} else {
value . resize ( size ) ;
while ( size - - ) {
K key ;
serializer ( key , " " ) ;
value . insert ( key ) ;
}
}
serializer . endArray ( ) ;
return true ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
// custom serialization to speedup cache loading
bool serialize ( std : : vector < std : : pair < Blockchain : : TransactionIndex , uint16_t > > & value , Common : : StringView name , CryptoNote : : ISerializer & s ) {
const size_t elementSize = sizeof ( std : : pair < Blockchain : : TransactionIndex , uint16_t > ) ;
size_t size = value . size ( ) * elementSize ;
if ( ! s . beginArray ( size , name ) ) {
return false ;
}
if ( s . type ( ) = = CryptoNote : : ISerializer : : INPUT ) {
if ( size % elementSize ! = 0 ) {
throw std : : runtime_error ( " Invalid vector size " ) ;
}
value . resize ( size / elementSize ) ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
if ( size ) {
s . binary ( value . data ( ) , size , " " ) ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
s . endArray ( ) ;
return true ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
void serialize ( Blockchain : : TransactionIndex & value , ISerializer & s ) {
s ( value . block , " block " ) ;
s ( value . transaction , " tx " ) ;
2014-08-13 10:51:37 +00:00
}
2015-05-27 12:08:46 +00:00
class BlockCacheSerializer {
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
public :
2015-07-30 15:22:07 +00:00
BlockCacheSerializer ( Blockchain & bs , const Crypto : : Hash lastBlockHash , ILogger & logger ) :
2015-05-27 12:08:46 +00:00
m_bs ( bs ) , m_lastBlockHash ( lastBlockHash ) , m_loaded ( false ) , logger ( logger , " BlockCacheSerializer " ) {
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
void load ( const std : : string & filename ) {
try {
std : : ifstream stdStream ( filename , std : : ios : : binary ) ;
if ( ! stdStream ) {
return ;
}
StdInputStream stream ( stdStream ) ;
BinaryInputStreamSerializer s ( stream ) ;
CryptoNote : : serialize ( * this , s ) ;
} catch ( std : : exception & e ) {
logger ( WARNING ) < < " loading failed: " < < e . what ( ) ;
}
}
bool save ( const std : : string & filename ) {
try {
std : : ofstream file ( filename , std : : ios : : binary ) ;
if ( ! file ) {
return false ;
}
StdOutputStream stream ( file ) ;
BinaryOutputStreamSerializer s ( stream ) ;
CryptoNote : : serialize ( * this , s ) ;
} catch ( std : : exception & ) {
return false ;
}
return true ;
}
void serialize ( ISerializer & s ) {
auto start = std : : chrono : : steady_clock : : now ( ) ;
uint8_t version = CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER ;
s ( version , " version " ) ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
// ignore old versions, do rebuild
if ( version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER )
return ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
std : : string operation ;
2015-07-30 15:22:07 +00:00
if ( s . type ( ) = = ISerializer : : INPUT ) {
2015-05-27 12:08:46 +00:00
operation = " - loading " ;
2015-07-30 15:22:07 +00:00
Crypto : : Hash blockHash ;
s ( blockHash , " last_block " ) ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
if ( blockHash ! = m_lastBlockHash ) {
2014-08-13 10:51:37 +00:00
return ;
}
2015-05-27 12:08:46 +00:00
} else {
operation = " - saving " ;
2015-07-30 15:22:07 +00:00
s ( m_lastBlockHash , " last_block " ) ;
2015-05-27 12:08:46 +00:00
}
logger ( INFO ) < < operation < < " block index... " ;
2015-07-30 15:22:07 +00:00
s ( m_bs . m_blockIndex , " block_index " ) ;
2014-08-25 14:35:07 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < operation < < " transaction map... " ;
2015-07-30 15:22:07 +00:00
s ( m_bs . m_transactionMap , " transactions " ) ;
2014-08-25 14:35:07 +00:00
2015-07-30 15:22:07 +00:00
logger ( INFO ) < < operation < < " spent keys... " ;
s ( m_bs . m_spent_keys , " spent_keys " ) ;
2014-08-25 14:35:07 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < operation < < " outputs... " ;
2015-07-30 15:22:07 +00:00
s ( m_bs . m_outputs , " outputs " ) ;
2014-08-25 14:35:07 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO ) < < operation < < " multi-signature outputs... " ;
2015-07-30 15:22:07 +00:00
s ( m_bs . m_multisignatureOutputs , " multisig_outputs " ) ;
auto dur = std : : chrono : : steady_clock : : now ( ) - start ;
logger ( INFO ) < < " Serialization time: " < < std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( dur ) . count ( ) < < " ms " ;
m_loaded = true ;
}
bool loaded ( ) const {
return m_loaded ;
}
private :
LoggerRef logger ;
bool m_loaded ;
Blockchain & m_bs ;
Crypto : : Hash m_lastBlockHash ;
} ;
class BlockchainIndicesSerializer {
public :
BlockchainIndicesSerializer ( Blockchain & bs , const Crypto : : Hash lastBlockHash , ILogger & logger ) :
m_bs ( bs ) , m_lastBlockHash ( lastBlockHash ) , m_loaded ( false ) , logger ( logger , " BlockchainIndicesSerializer " ) {
}
void serialize ( ISerializer & s ) {
uint8_t version = CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER ;
KV_MEMBER ( version ) ;
// ignore old versions, do rebuild
if ( version ! = CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER )
return ;
std : : string operation ;
if ( s . type ( ) = = ISerializer : : INPUT ) {
operation = " - loading " ;
Crypto : : Hash blockHash ;
s ( blockHash , " blockHash " ) ;
if ( blockHash ! = m_lastBlockHash ) {
return ;
}
} else {
operation = " - saving " ;
s ( m_lastBlockHash , " blockHash " ) ;
}
logger ( INFO ) < < operation < < " paymentID index... " ;
s ( m_bs . m_paymentIdIndex , " paymentIdIndex " ) ;
logger ( INFO ) < < operation < < " timestamp index... " ;
s ( m_bs . m_timestampIndex , " timestampIndex " ) ;
logger ( INFO ) < < operation < < " generated transactions index... " ;
s ( m_bs . m_generatedTransactionsIndex , " generatedTransactionsIndex " ) ;
m_loaded = true ;
}
template < class Archive > void serialize ( Archive & ar , unsigned int version ) {
// ignore old versions, do rebuild
if ( version < CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER )
return ;
std : : string operation ;
if ( Archive : : is_loading : : value ) {
operation = " - loading " ;
Crypto : : Hash blockHash ;
ar & blockHash ;
if ( blockHash ! = m_lastBlockHash ) {
return ;
}
} else {
operation = " - saving " ;
ar & m_lastBlockHash ;
}
logger ( INFO ) < < operation < < " paymentID index... " ;
ar & m_bs . m_paymentIdIndex ;
logger ( INFO ) < < operation < < " timestamp index... " ;
ar & m_bs . m_timestampIndex ;
logger ( INFO ) < < operation < < " generated transactions index... " ;
ar & m_bs . m_generatedTransactionsIndex ;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
m_loaded = true ;
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
bool loaded ( ) const {
return m_loaded ;
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
private :
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
LoggerRef logger ;
bool m_loaded ;
2015-07-30 15:22:07 +00:00
Blockchain & m_bs ;
Crypto : : Hash m_lastBlockHash ;
2015-05-27 12:08:46 +00:00
} ;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
Blockchain : : Blockchain ( const Currency & currency , tx_memory_pool & tx_pool , ILogger & logger ) :
logger ( logger , " Blockchain " ) ,
2015-05-27 12:08:46 +00:00
m_currency ( currency ) ,
m_tx_pool ( tx_pool ) ,
m_current_block_cumul_sz_limit ( 0 ) ,
m_is_in_checkpoint_zone ( false ) ,
m_is_blockchain_storing ( false ) ,
m_checkpoints ( logger ) {
2014-08-13 10:51:37 +00:00
m_outputs . set_deleted_key ( 0 ) ;
2015-07-30 15:22:07 +00:00
Crypto : : KeyImage nullImage = boost : : value_initialized < decltype ( nullImage ) > ( ) ;
2014-08-13 10:51:37 +00:00
m_spent_keys . set_deleted_key ( nullImage ) ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : addObserver ( IBlockchainStorageObserver * observer ) {
2015-04-06 16:13:07 +00:00
return m_observerManager . add ( observer ) ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : removeObserver ( IBlockchainStorageObserver * observer ) {
2015-04-06 16:13:07 +00:00
return m_observerManager . remove ( observer ) ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : checkTransactionInputs ( const CryptoNote : : Transaction & tx , BlockInfo & maxUsedBlock ) {
return checkTransactionInputs ( tx , maxUsedBlock . height , maxUsedBlock . id ) ;
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : checkTransactionInputs ( const CryptoNote : : Transaction & tx , BlockInfo & maxUsedBlock , BlockInfo & lastFailed ) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
if ( ! lastFailed . empty ( ) & & getCurrentBlockchainHeight ( ) > lastFailed . height & & getBlockIdByHeight ( lastFailed . height ) = = lastFailed . id ) {
2014-08-13 10:51:37 +00:00
return false ; //we already sure that this tx is broken for this height
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
if ( ! checkTransactionInputs ( tx , maxUsedBlock . height , maxUsedBlock . id , & tail ) ) {
2014-08-13 10:51:37 +00:00
lastFailed = tail ;
return false ;
}
2015-05-27 12:08:46 +00:00
} else {
2015-07-30 15:22:07 +00:00
if ( maxUsedBlock . height > = getCurrentBlockchainHeight ( ) ) {
2014-08-13 10:51:37 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
if ( getBlockIdByHeight ( maxUsedBlock . height ) ! = maxUsedBlock . id ) {
2014-08-13 10:51:37 +00:00
//if we already failed on this height and id, skip actual ring signature check
2015-07-30 15:22:07 +00:00
if ( lastFailed . id = = getBlockIdByHeight ( lastFailed . height ) ) {
2014-08-13 10:51:37 +00:00
return false ;
}
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
2015-07-30 15:22:07 +00:00
if ( ! checkTransactionInputs ( tx , maxUsedBlock . height , maxUsedBlock . id , & tail ) ) {
2014-08-13 10:51:37 +00:00
lastFailed = tail ;
return false ;
}
}
}
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : haveSpentKeyImages ( const CryptoNote : : Transaction & tx ) {
return this - > haveTransactionKeyImagesAsSpent ( tx ) ;
2014-08-13 10:51:37 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
/**
* \ pre m_blockchain_lock is locked
*/
bool Blockchain : : checkTransactionSize ( size_t blobSize ) {
if ( blobSize > getCurrentCumulativeBlocksizeLimit ( ) - m_currency . minerTxBlobReservedSize ( ) ) {
logger ( ERROR ) < < " transaction is too big " < < blobSize < < " , maximum allowed size is " < <
( getCurrentCumulativeBlocksizeLimit ( ) - m_currency . minerTxBlobReservedSize ( ) ) ;
return false ;
}
return true ;
}
bool Blockchain : : haveTransaction ( const Crypto : : Hash & id ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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
2015-07-30 15:22:07 +00:00
bool Blockchain : : have_tx_keyimg_as_spent ( const Crypto : : KeyImage & key_im ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-03-03 22:07:58 +00:00
return m_spent_keys . find ( key_im ) ! = m_spent_keys . end ( ) ;
}
2015-07-30 15:22:07 +00:00
uint32_t Blockchain : : getCurrentBlockchainHeight ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
return static_cast < uint32_t > ( m_blocks . size ( ) ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : init ( const std : : string & config_folder , bool load_existing ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
if ( ! config_folder . empty ( ) & & ! Tools : : create_directories_if_necessary ( config_folder ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Failed to create data directory: " < < m_config_folder ;
2014-06-21 14:09:13 +00:00
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
2015-05-27 12:08:46 +00:00
if ( load_existing & & ! m_blocks . empty ( ) ) {
logger ( INFO , BRIGHT_WHITE ) < < " Loading blockchain... " ;
BlockCacheSerializer loader ( * this , get_block_hash ( m_blocks . back ( ) . bl ) , logger . getLogger ( ) ) ;
2015-07-30 15:22:07 +00:00
loader . load ( appendPath ( config_folder , m_currency . blocksCacheFileName ( ) ) ) ;
2015-05-27 12:08:46 +00:00
if ( ! loader . loaded ( ) ) {
logger ( WARNING , BRIGHT_YELLOW ) < < " No actual blockchain cache found, rebuilding internal structures... " ;
2015-07-30 15:22:07 +00:00
rebuildCache ( ) ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
loadBlockchainIndices ( ) ;
2014-06-25 17:21:42 +00:00
} else {
m_blocks . clear ( ) ;
2014-06-20 15:56:33 +00:00
}
if ( m_blocks . empty ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE )
< < " Blockchain not loaded, generating genesis block. " ;
2015-07-30 15:22:07 +00:00
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
pushBlock ( m_currency . genesisBlock ( ) , bvc ) ;
2015-05-27 12:08:46 +00:00
if ( bvc . m_verifivation_failed ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to add genesis block to blockchain " ;
return false ;
}
2014-08-13 10:51:37 +00:00
} else {
2015-07-30 15:22:07 +00:00
Crypto : : Hash firstBlockHash = get_block_hash ( m_blocks [ 0 ] . bl ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( firstBlockHash = = m_currency . genesisBlockHash ( ) ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to init: genesis block mismatch. "
" Probably you set --testnet flag with data "
" dir with non-test blockchain or another "
" network. " ;
return false ;
}
2014-08-13 10:51:37 +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
}
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN )
< < " Blockchain initialized. last block: " < < m_blocks . size ( ) - 1 < < " , "
< < Common : : timeIntervalToString ( timestamp_diff )
2015-07-30 15:22:07 +00:00
< < " time ago, current difficulty: " < < getDifficultyForNextBlock ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-03-20 11:46:11 +00:00
2015-07-30 15:22:07 +00:00
void Blockchain : : rebuildCache ( ) {
std : : chrono : : steady_clock : : time_point timePoint = std : : chrono : : steady_clock : : now ( ) ;
m_blockIndex . clear ( ) ;
m_transactionMap . clear ( ) ;
m_spent_keys . clear ( ) ;
m_outputs . clear ( ) ;
m_multisignatureOutputs . clear ( ) ;
for ( uint32_t b = 0 ; b < m_blocks . size ( ) ; + + b ) {
if ( b % 1000 = = 0 ) {
logger ( INFO , BRIGHT_WHITE ) < < " Height " < < b < < " of " < < m_blocks . size ( ) ;
}
const BlockEntry & block = m_blocks [ b ] ;
Crypto : : Hash blockHash = get_block_hash ( block . bl ) ;
m_blockIndex . push ( blockHash ) ;
for ( uint16_t t = 0 ; t < block . transactions . size ( ) ; + + t ) {
const TransactionEntry & transaction = block . transactions [ t ] ;
Crypto : : Hash transactionHash = getObjectHash ( transaction . tx ) ;
TransactionIndex transactionIndex = { b , t } ;
m_transactionMap . insert ( std : : make_pair ( transactionHash , transactionIndex ) ) ;
// process inputs
for ( auto & i : transaction . tx . inputs ) {
if ( i . type ( ) = = typeid ( KeyInput ) ) {
m_spent_keys . insert ( : : boost : : get < KeyInput > ( i ) . keyImage ) ;
} else if ( i . type ( ) = = typeid ( MultisignatureInput ) ) {
auto out = : : boost : : get < MultisignatureInput > ( i ) ;
m_multisignatureOutputs [ out . amount ] [ out . outputIndex ] . isUsed = true ;
}
}
// process outputs
for ( uint16_t o = 0 ; o < transaction . tx . outputs . size ( ) ; + + o ) {
const auto & out = transaction . tx . outputs [ o ] ;
if ( out . target . type ( ) = = typeid ( KeyOutput ) ) {
m_outputs [ out . amount ] . push_back ( std : : make_pair < > ( transactionIndex , o ) ) ;
} else if ( out . target . type ( ) = = typeid ( MultisignatureOutput ) ) {
MultisignatureOutputUsage usage = { transactionIndex , o , false } ;
m_multisignatureOutputs [ out . amount ] . push_back ( usage ) ;
}
}
}
}
std : : chrono : : duration < double > duration = std : : chrono : : steady_clock : : now ( ) - timePoint ;
logger ( INFO , BRIGHT_WHITE ) < < " Rebuilding internal structures took: " < < duration . count ( ) ;
}
bool Blockchain : : storeCache ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " Saving blockchain... " ;
2015-07-30 15:22:07 +00:00
BlockCacheSerializer ser ( * this , getTailId ( ) , logger . getLogger ( ) ) ;
if ( ! ser . save ( appendPath ( m_config_folder , m_currency . blocksCacheFileName ( ) ) ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " Failed to save blockchain cache " ;
2014-08-13 10:51:37 +00:00
return false ;
2014-03-03 22:07:58 +00:00
}
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : deinit ( ) {
2014-08-13 10:51:37 +00:00
storeCache ( ) ;
2015-07-30 15:22:07 +00:00
storeBlockchainIndices ( ) ;
assert ( m_messageQueueList . empty ( ) ) ;
2014-08-13 10:51:37 +00:00
return true ;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : resetAndSetGenesisBlock ( const Block & b ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-03-03 22:07:58 +00:00
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 ( ) ;
2015-07-30 15:22:07 +00:00
m_paymentIdIndex . clear ( ) ;
m_timestampIndex . clear ( ) ;
m_generatedTransactionsIndex . clear ( ) ;
m_orthanBlocksIndex . clear ( ) ;
2014-03-03 22:07:58 +00:00
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
2015-07-30 15:22:07 +00:00
addNewBlock ( b , bvc ) ;
2014-03-03 22:07:58 +00:00
return bvc . m_added_to_main_chain & & ! bvc . m_verifivation_failed ;
}
2015-07-30 15:22:07 +00:00
Crypto : : Hash Blockchain : : getTailId ( uint32_t & height ) {
assert ( ! m_blocks . empty ( ) ) ;
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
height = getCurrentBlockchainHeight ( ) - 1 ;
return getTailId ( ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
Crypto : : Hash Blockchain : : getTailId ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
return m_blocks . empty ( ) ? NULL_HASH : m_blockIndex . getTailId ( ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > Blockchain : : buildSparseChain ( ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
assert ( m_blockIndex . size ( ) ! = 0 ) ;
return doBuildSparseChain ( m_blockIndex . getTailId ( ) ) ;
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > Blockchain : : buildSparseChain ( const Crypto : : Hash & startBlockId ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
assert ( haveBlock ( startBlockId ) ) ;
return doBuildSparseChain ( startBlockId ) ;
}
2015-04-06 16:13:07 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > Blockchain : : doBuildSparseChain ( const Crypto : : Hash & startBlockId ) const {
assert ( m_blockIndex . size ( ) ! = 0 ) ;
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > sparseChain ;
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
if ( m_blockIndex . hasBlock ( startBlockId ) ) {
sparseChain = m_blockIndex . buildSparseChain ( startBlockId ) ;
} else {
assert ( m_alternative_chains . count ( startBlockId ) > 0 ) ;
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > alternativeChain ;
Crypto : : Hash blockchainAncestor ;
for ( auto it = m_alternative_chains . find ( startBlockId ) ; it ! = m_alternative_chains . end ( ) ; it = m_alternative_chains . find ( blockchainAncestor ) ) {
alternativeChain . emplace_back ( it - > first ) ;
blockchainAncestor = it - > second . bl . previousBlockHash ;
}
2015-04-06 16:13:07 +00:00
2015-07-30 15:22:07 +00:00
for ( size_t i = 1 ; i < = alternativeChain . size ( ) ; i * = 2 ) {
sparseChain . emplace_back ( alternativeChain [ i - 1 ] ) ;
}
2015-04-06 16:13:07 +00:00
2015-07-30 15:22:07 +00:00
assert ( ! sparseChain . empty ( ) ) ;
assert ( m_blockIndex . hasBlock ( blockchainAncestor ) ) ;
std : : vector < Crypto : : Hash > sparseMainChain = m_blockIndex . buildSparseChain ( blockchainAncestor ) ;
sparseChain . reserve ( sparseChain . size ( ) + sparseMainChain . size ( ) ) ;
std : : copy ( sparseMainChain . begin ( ) , sparseMainChain . end ( ) , std : : back_inserter ( sparseChain ) ) ;
}
return sparseChain ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
Crypto : : Hash Blockchain : : getBlockIdByHeight ( uint32_t height ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
assert ( height < m_blockIndex . size ( ) ) ;
2014-08-13 10:51:37 +00:00
return m_blockIndex . getBlockId ( height ) ;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlockByHash ( const Crypto : : Hash & blockHash , Block & b ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
uint32_t height = 0 ;
2014-08-13 10:51:37 +00:00
if ( m_blockIndex . getBlockHeight ( blockHash , height ) ) {
b = m_blocks [ height ] . bl ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-07-30 15:22:07 +00:00
logger ( WARNING ) < < blockHash ;
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 ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlockHeight ( const Crypto : : Hash & blockId , uint32_t & blockHeight ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lock ( m_blockchain_lock ) ;
return m_blockIndex . getBlockHeight ( blockId , blockHeight ) ;
}
difficulty_type Blockchain : : getDifficultyForNextBlock ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
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
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 ) ;
}
2015-07-30 15:22:07 +00:00
uint64_t Blockchain : : getCoinsInCirculation ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
if ( m_blocks . empty ( ) ) {
return 0 ;
} else {
return m_blocks . back ( ) . already_generated_coins ;
}
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : rollback_blockchain_switching ( std : : list < Block > & original_chain , size_t rollback_height ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
// remove failed subchain
for ( size_t i = m_blocks . size ( ) - 1 ; i > = rollback_height ; i - - ) {
2014-06-20 15:56:33 +00:00
popBlock ( get_block_hash ( m_blocks . back ( ) . bl ) ) ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
// return back original chain
2015-07-30 15:22:07 +00:00
for ( auto & bl : original_chain ) {
2015-05-27 12:08:46 +00:00
block_verification_context bvc =
boost : : value_initialized < block_verification_context > ( ) ;
2014-06-20 15:56:33 +00:00
bool r = pushBlock ( bl , bvc ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( r & & bvc . m_added_to_main_chain ) ) {
logger ( ERROR , BRIGHT_RED ) < < " PANIC!!! failed to add (again) block while "
" chain switching during the rollback! " ;
return false ;
}
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " Rollback success. " ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : switch_to_alternative_blockchain ( std : : list < blocks_ext_by_hash : : iterator > & alt_chain , bool discard_disconnected_chain ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
if ( ! ( alt_chain . size ( ) ) ) {
logger ( ERROR , BRIGHT_RED ) < < " switch_to_alternative_blockchain: empty chain passed " ;
return false ;
}
2014-03-03 22:07:58 +00:00
size_t split_height = alt_chain . front ( ) - > second . height ;
2015-05-27 12:08:46 +00:00
if ( ! ( m_blocks . size ( ) > split_height ) ) {
logger ( ERROR , BRIGHT_RED ) < < " switch_to_alternative_blockchain: blockchain size is lower than split height " ;
return false ;
}
2014-03-03 22:07:58 +00:00
//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 ) ) ;
2015-05-27 12:08:46 +00:00
//if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to remove block on chain switching"; return false; }
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 ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " Failed to switch to alternative blockchain " ;
2014-03-03 22:07:58 +00:00
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));
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " The block was inserted as invalid while connecting new alternative chain, block_id: " < < get_block_hash ( ch_ent - > second . bl ) ;
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex . remove ( ch_ent - > second . bl ) ;
2014-03-03 22:07:58 +00:00
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);
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex . remove ( ( * alt_ch_to_orph_iter ) - > second . bl ) ;
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 > ( ) ;
2015-07-30 15:22:07 +00:00
bool r = handle_alternative_block ( old_ch_ent , get_block_hash ( old_ch_ent ) , bvc , false ) ;
2014-06-20 15:56:33 +00:00
if ( ! r ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < ( " Failed to push ex-main chain blocks to alternative chain " ) ;
2014-05-15 16:40:40 +00:00
rollback_blockchain_switching ( disconnected_chain , split_height ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
}
}
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > blocksFromCommonRoot ;
blocksFromCommonRoot . reserve ( alt_chain . size ( ) + 1 ) ;
blocksFromCommonRoot . push_back ( alt_chain . front ( ) - > second . bl . previousBlockHash ) ;
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 ) {
2015-07-30 15:22:07 +00:00
blocksFromCommonRoot . push_back ( get_block_hash ( ch_ent - > second . bl ) ) ;
m_orthanBlocksIndex . remove ( ch_ent - > second . bl ) ;
2014-03-03 22:07:58 +00:00
m_alternative_chains . erase ( ch_ent ) ;
}
2015-07-30 15:22:07 +00:00
sendMessage ( BlockchainMessage ( std : : move ( ChainSwitchMessage ( std : : move ( blocksFromCommonRoot ) ) ) ) ) ;
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN ) < < " REORGANIZE SUCCESS! on height: " < < split_height < < " , new blockchain size: " < < m_blocks . size ( ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
difficulty_type Blockchain : : 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 ( ) ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-03-03 22:07:58 +00:00
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 ) ;
}
2015-05-27 12:08:46 +00:00
if ( ! ( ( alt_chain . size ( ) + timestamps . size ( ) ) < = m_currency . difficultyBlocksCount ( ) ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Internal error, alt_chain.size()[ " < < alt_chain . size ( ) < < " ] + timestamps.size()[ " < < timestamps . size ( ) < <
" ] NOT <= m_currency.difficultyBlocksCount()[ " < < m_currency . difficultyBlocksCount ( ) < < ' ] ' ; return false ;
}
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : prevalidate_miner_transaction ( const Block & b , uint32_t height ) {
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
if ( ! ( b . baseTransaction . inputs . size ( ) = = 1 ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED )
< < " coinbase transaction in the block has no inputs " ;
return false ;
}
2015-07-30 15:22:07 +00:00
if ( ! ( b . baseTransaction . inputs [ 0 ] . type ( ) = = typeid ( BaseInput ) ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED )
< < " coinbase transaction in the block has the wrong type " ;
return false ;
}
2015-07-30 15:22:07 +00:00
if ( boost : : get < BaseInput > ( b . baseTransaction . inputs [ 0 ] ) . blockIndex ! = height ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < < " The miner transaction in block has invalid height: " < <
2015-07-30 15:22:07 +00:00
boost : : get < BaseInput > ( b . baseTransaction . inputs [ 0 ] ) . blockIndex < < " , expected: " < < height ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
if ( ! ( b . baseTransaction . unlockTime = = height + m_currency . minedMoneyUnlockWindow ( ) ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED )
< < " coinbase transaction transaction have wrong unlock time= "
2015-07-30 15:22:07 +00:00
< < b . baseTransaction . unlockTime < < " , expected "
2015-05-27 12:08:46 +00:00
< < height + m_currency . minedMoneyUnlockWindow ( ) ;
return false ;
}
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
if ( ! check_outs_overflow ( b . baseTransaction ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < < " miner transaction have money overflow in block " < < get_block_hash ( b ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : validate_miner_transaction ( const Block & b , uint32_t height , size_t cumulativeBlockSize ,
2015-05-27 12:08:46 +00:00
uint64_t alreadyGeneratedCoins , uint64_t fee ,
uint64_t & reward , int64_t & emissionChange ) {
2014-08-13 10:51:37 +00:00
uint64_t minerReward = 0 ;
2015-07-30 15:22:07 +00:00
for ( auto & o : b . baseTransaction . outputs ) {
2014-08-13 10:51:37 +00:00
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 ( ) ) ;
2015-07-15 12:23:00 +00:00
size_t blocksSizeMedian = Common : : medianValue ( lastBlocksSizes ) ;
2014-08-13 10:51:37 +00:00
2015-09-18 11:55:31 +00:00
if ( ! m_currency . getBlockReward ( blocksSizeMedian , cumulativeBlockSize , alreadyGeneratedCoins , fee , reward , emissionChange ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " block size " < < cumulativeBlockSize < < " is bigger than allowed for this blockchain " ;
2014-03-03 22:07:58 +00:00
return false ;
}
2015-05-27 12:08:46 +00:00
2014-08-13 10:51:37 +00:00
if ( minerReward > reward ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " 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 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " 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
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBackwardBlocksSize ( size_t from_height , std : : vector < size_t > & sz , size_t count ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
if ( ! ( from_height < m_blocks . size ( ) ) ) {
logger ( ERROR , BRIGHT_RED )
< < " Internal error: get_backward_blocks_sizes called with from_height= "
< < from_height < < " , blockchain height = " < < m_blocks . size ( ) ;
return false ;
}
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : get_last_n_blocks_sizes ( std : : vector < size_t > & sz , size_t count ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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
}
2015-07-30 15:22:07 +00:00
return getBackwardBlocksSize ( m_blocks . size ( ) - 1 , sz , count ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
uint64_t Blockchain : : getCurrentCumulativeBlocksizeLimit ( ) {
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : 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 ;
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
size_t need_elements = m_currency . timestampCheckWindow ( ) - timestamps . size ( ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( start_top_height < m_blocks . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: passed start_height = " < < start_top_height < < " not less then m_blocks.size()= " < < m_blocks . size ( ) ; return false ; }
2014-06-20 15:56:33 +00:00
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0 ;
2015-05-27 12:08:46 +00:00
do {
2014-03-03 22:07:58 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : handle_alternative_block ( const Block & b , const Crypto : : Hash & id , block_verification_context & bvc , bool sendNewAlternativeBlockMessage ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
auto block_height = get_block_height ( b ) ;
2014-06-20 15:56:33 +00:00
if ( block_height = = 0 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Block with id: " < < Common : : podToHex ( 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
2015-07-30 15:22:07 +00:00
if ( ! m_checkpoints . is_alternative_block_allowed ( getCurrentBlockchainHeight ( ) , block_height ) ) {
2015-05-27 12:08:46 +00:00
logger ( TRACE ) < < " Block with id: " < < id < < std : : endl < <
2014-08-25 14:35:07 +00:00
" can't be accepted for alternative chain, block height: " < < block_height < < std : : endl < <
2015-07-30 15:22:07 +00:00
" blockchain height: " < < getCurrentBlockchainHeight ( ) ;
2014-05-15 16:40:40 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2014-08-13 10:51:37 +00:00
size_t cumulativeSize ;
if ( ! getBlockCumulativeSize ( b , cumulativeSize ) ) {
2015-05-27 12:08:46 +00:00
logger ( TRACE ) < < " Block with id: " < < id < < " has at least one unknown transaction. Cumulative size is calculated imprecisely " ;
2014-08-13 10:51:37 +00:00
}
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
2015-07-30 15:22:07 +00:00
uint32_t mainPrevHeight = 0 ;
const bool mainPrev = m_blockIndex . getBlockHeight ( b . previousBlockHash , mainPrevHeight ) ;
const auto it_prev = m_alternative_chains . find ( b . previousBlockHash ) ;
2014-08-13 10:51:37 +00:00
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 ) ;
2015-07-30 15:22:07 +00:00
alt_it = m_alternative_chains . find ( alt_it - > second . bl . previousBlockHash ) ;
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
2015-05-27 12:08:46 +00:00
if ( ! ( m_blocks . size ( ) > alt_chain . front ( ) - > second . height ) ) { logger ( ERROR , BRIGHT_RED ) < < " main blockchain wrong height " ; return false ; }
2015-07-30 15:22:07 +00:00
Crypto : : Hash h = NULL_HASH ;
2014-03-03 22:07:58 +00:00
get_block_hash ( m_blocks [ alt_chain . front ( ) - > second . height - 1 ] . bl , h ) ;
2015-07-30 15:22:07 +00:00
if ( ! ( h = = alt_chain . front ( ) - > second . bl . previousBlockHash ) ) { logger ( ERROR , BRIGHT_RED ) < < " alternative chain have wrong connection to main chain " ; return false ; }
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 {
2015-05-27 12:08:46 +00:00
if ( ! ( mainPrev ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: broken imperative condition it_main_prev != m_blocks_index.end() " ; return false ; }
2014-08-13 10:51:37 +00:00
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < <
" Block with id: " < < id
< < ENDL < < " for alternative chain, have invalid timestamp: " < < b . timestamp ;
2014-03-03 22:07:58 +00:00
//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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" CHECKPOINT VALIDATION FAILED " ;
2014-05-15 16:40:40 +00:00
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 ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( current_diff ) ) { logger ( ERROR , BRIGHT_RED ) < < " !!!!!!! DIFFICULTY OVERHEAD !!!!!!! " ; return false ; }
2015-07-30 15:22:07 +00:00
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < <
" Block with id: " < < id
2014-05-15 16:40:40 +00:00
< < ENDL < < " for alternative chain, have not enough proof of work: " < < proof_of_work
2015-05-27 12:08:46 +00:00
< < ENDL < < " expected difficulty: " < < current_diff ;
2014-05-15 16:40:40 +00:00
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < <
" Block with id: " < < Common : : podToHex ( 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 ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( i_dres = = m_alternative_chains . end ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " insertion of new alternative block returned as it already exist " ; return false ; }
2014-03-03 22:07:58 +00:00
# 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 ) ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( i_res . second ) ) { logger ( ERROR , BRIGHT_RED ) < < " insertion of new alternative block returned as it already exist " ; return false ; }
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex . add ( bei . bl ) ;
2014-03-03 22:07:58 +00:00
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!
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN ) < <
" ###### REORGANIZE on height: " < < alt_chain . front ( ) - > second . height < < " of " < < m_blocks . size ( ) - 1 < <
" , checkpoint is found in alternative chain on height " < < bei . height ;
2014-05-15 16:40:40 +00:00
bool r = switch_to_alternative_blockchain ( alt_chain , true ) ;
2015-07-15 12:23:00 +00:00
if ( r ) {
bvc . m_added_to_main_chain = true ;
bvc . m_switched_to_alt_chain = true ;
} else {
bvc . m_verifivation_failed = true ;
}
2014-05-15 16:40:40 +00:00
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!
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_GREEN ) < <
" ###### REORGANIZE on height: " < < alt_chain . front ( ) - > second . height < < " of " < < m_blocks . size ( ) - 1 < < " with cum_difficulty " < < m_blocks . back ( ) . cumulative_difficulty
< < ENDL < < " alternative blockchain size: " < < alt_chain . size ( ) < < " with cum_difficulty " < < bei . cumulative_difficulty ;
2014-05-15 16:40:40 +00:00
bool r = switch_to_alternative_blockchain ( alt_chain , false ) ;
2015-07-15 12:23:00 +00:00
if ( r ) {
bvc . m_added_to_main_chain = true ;
bvc . m_switched_to_alt_chain = true ;
} else {
bvc . m_verifivation_failed = true ;
}
2014-03-03 22:07:58 +00:00
return r ;
2014-06-20 15:56:33 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_BLUE ) < <
" ----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " < < bei . height
2014-05-15 16:40:40 +00:00
< < ENDL < < " id: \t " < < id
< < ENDL < < " PoW: \t " < < proof_of_work
2015-05-27 12:08:46 +00:00
< < ENDL < < " difficulty: \t " < < current_diff ;
2015-07-30 15:22:07 +00:00
if ( sendNewAlternativeBlockMessage ) {
sendMessage ( BlockchainMessage ( std : : move ( NewAlternativeBlockMessage ( id ) ) ) ) ;
}
2014-05-15 16:40:40 +00:00
return true ;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
} else {
2014-03-03 22:07:58 +00:00
//block orphaned
bvc . m_marked_as_orphaned = true ;
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_RED ) < <
" Block recognized as orphaned and rejected, id = " < < id ;
2014-03-03 22:07:58 +00:00
}
return true ;
2015-07-30 15:22:07 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlocks ( uint32_t start_offset , uint32_t count , std : : list < Block > & blocks , std : : list < Transaction > & txs ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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 ;
2015-05-27 12:08:46 +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 ) ;
2015-07-30 15:22:07 +00:00
std : : list < Crypto : : Hash > missed_ids ;
getTransactions ( m_blocks [ i ] . bl . transactionHashes , txs , missed_ids ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( ! missed_ids . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " have missed transactions in own block in main blockchain " ; return false ; }
2014-03-03 22:07:58 +00:00
}
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlocks ( uint32_t start_offset , uint32_t count , std : : list < Block > & blocks ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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
2015-07-30 15:22:07 +00:00
for ( uint32_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
2015-07-30 15:22:07 +00:00
bool Blockchain : : handleGetObjects ( NOTIFY_REQUEST_GET_OBJECTS : : request & arg , NOTIFY_RESPONSE_GET_OBJECTS : : request & rsp ) { //Deprecated. Should be removed with CryptoNoteProtocolHandler.
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
rsp . current_blockchain_height = getCurrentBlockchainHeight ( ) ;
2014-08-13 10:51:37 +00:00
std : : list < Block > blocks ;
2015-07-30 15:22:07 +00:00
getBlocks ( arg . blocks , blocks , rsp . missed_ids ) ;
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
for ( const auto & bl : blocks ) {
2015-07-30 15:22:07 +00:00
std : : list < Crypto : : Hash > missed_tx_id ;
2014-08-13 10:51:37 +00:00
std : : list < Transaction > txs ;
2015-07-30 15:22:07 +00:00
getTransactions ( bl . transactionHashes , txs , rsp . missed_ids ) ;
if ( ! ( ! missed_tx_id . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " Internal error: have missed missed_tx_id.size()= " < < missed_tx_id . size ( ) < < ENDL < < " for block id = " < < get_block_hash ( bl ) ; return false ; } //WTF???
2014-06-20 15:56:33 +00:00
rsp . blocks . push_back ( block_complete_entry ( ) ) ;
block_complete_entry & e = rsp . blocks . back ( ) ;
//pack block
2015-07-30 15:22:07 +00:00
e . block = asString ( toBinaryArray ( bl ) ) ;
2014-06-20 15:56:33 +00:00
//pack transactions
2014-08-13 10:51:37 +00:00
for ( Transaction & tx : txs ) {
2015-07-30 15:22:07 +00:00
e . txs . push_back ( asString ( toBinaryArray ( tx ) ) ) ;
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
//get another transactions, if need
2014-08-13 10:51:37 +00:00
std : : list < Transaction > txs ;
2015-07-30 15:22:07 +00:00
getTransactions ( arg . txs , txs , rsp . missed_ids ) ;
2014-03-03 22:07:58 +00:00
//pack aside transactions
2014-06-20 15:56:33 +00:00
for ( const auto & tx : txs ) {
2015-07-30 15:22:07 +00:00
rsp . txs . push_back ( asString ( toBinaryArray ( tx ) ) ) ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getAlternativeBlocks ( std : : list < Block > & blocks ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
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
2015-07-30 15:22:07 +00:00
uint32_t Blockchain : : getAlternativeBlocksCount ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
return static_cast < uint32_t > ( m_alternative_chains . size ( ) ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : 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 ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
const Transaction & tx = transactionByIndex ( amount_outs [ i ] . first ) . tx ;
2015-07-30 15:22:07 +00:00
if ( ! ( tx . outputs . size ( ) > amount_outs [ i ] . second ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < < " internal error: in global outs index, transaction out index= "
2015-07-30 15:22:07 +00:00
< < amount_outs [ i ] . second < < " more than transaction outputs = " < < tx . outputs . size ( ) < < " , for tx id = " < < getObjectHash ( tx ) ; return false ;
2015-05-27 12:08:46 +00:00
}
2015-07-30 15:22:07 +00:00
if ( ! ( tx . outputs [ amount_outs [ i ] . second ] . target . type ( ) = = typeid ( KeyOutput ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " unknown tx out type " ; return false ; }
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 ( ) ) ;
2015-07-30 15:22:07 +00:00
oen . global_amount_index = static_cast < uint32_t > ( i ) ;
oen . out_key = boost : : get < KeyOutput > ( tx . outputs [ amount_outs [ i ] . second ] . target ) . key ;
2014-03-03 22:07:58 +00:00
return true ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
size_t Blockchain : : find_end_of_allowed_index ( const std : : vector < std : : pair < TransactionIndex , uint16_t > > & amount_outs ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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 ;
2015-07-30 15:22:07 +00:00
if ( amount_outs [ i ] . first . block + m_currency . minedMoneyUnlockWindow ( ) < = getCurrentBlockchainHeight ( ) ) {
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : getRandomOutsByAmount ( const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request & req , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response & res ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-04-06 16:13:07 +00:00
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 ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" 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 " ;
2014-03-03 22:07:58 +00:00
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 ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( up_index_limit < = amount_outs . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: find_end_of_allowed_index returned wrong index= " < < up_index_limit < < " , with amount_outs.size = " < < amount_outs . size ( ) ; return false ; }
2015-04-06 16:13:07 +00:00
if ( up_index_limit > 0 ) {
2015-07-30 15:22:07 +00:00
ShuffleGenerator < size_t , Crypto : : random_engine < size_t > > generator ( up_index_limit ) ;
2015-04-06 16:13:07 +00:00
for ( uint64_t j = 0 ; j < up_index_limit & & result_outs . outs . size ( ) < req . outs_count ; + + j ) {
add_out_to_get_random_outs ( amount_outs , result_outs , amount , generator ( ) ) ;
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
2015-07-30 15:22:07 +00:00
uint32_t Blockchain : : findBlockchainSupplement ( const std : : vector < Crypto : : Hash > & qblock_ids ) {
assert ( ! qblock_ids . empty ( ) ) ;
assert ( qblock_ids . back ( ) = = m_blockIndex . getBlockId ( 0 ) ) ;
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
uint32_t blockIndex ;
// assert above guarantees that method returns true
m_blockIndex . findSupplement ( qblock_ids , blockIndex ) ;
return blockIndex ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
uint64_t Blockchain : : blockDifficulty ( size_t i ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
if ( ! ( i < m_blocks . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " wrong block index i = " < < i < < " at Blockchain::block_difficulty() " ; return false ; }
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
2015-07-30 15:22:07 +00:00
void Blockchain : : print_blockchain ( uint64_t start_index , uint64_t end_index ) {
2014-03-03 22:07:58 +00:00
std : : stringstream ss ;
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
if ( start_index > = m_blocks . size ( ) ) {
logger ( INFO , BRIGHT_WHITE ) < <
" Wrong starter index set: " < < start_index < < " , expected max index " < < m_blocks . size ( ) - 1 ;
2014-03-03 22:07:58 +00:00
return ;
}
2015-05-27 12:08:46 +00:00
for ( size_t i = start_index ; i ! = m_blocks . size ( ) & & i ! = end_index ; i + + ) {
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 )
2015-07-30 15:22:07 +00:00
< < " \n difficulty \t \t " < < blockDifficulty ( i ) < < " , nonce " < < m_blocks [ i ] . bl . nonce < < " , tx_count " < < m_blocks [ i ] . bl . transactionHashes . size ( ) < < ENDL ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Current blockchain: " < < ENDL < < ss . str ( ) ;
logger ( INFO , BRIGHT_WHITE ) < <
" Blockchain printed with log level 1 " ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
void Blockchain : : print_blockchain_index ( ) {
2014-03-03 22:07:58 +00:00
std : : stringstream ss ;
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > blockIds = m_blockIndex . getBlockIds ( 0 , std : : numeric_limits < uint32_t > : : max ( ) ) ;
logger ( INFO , BRIGHT_WHITE ) < < " Current blockchain index: " ;
2014-08-13 10:51:37 +00:00
size_t height = 0 ;
for ( auto i = blockIds . begin ( ) ; i ! = blockIds . end ( ) ; + + i , + + height ) {
2015-07-30 15:22:07 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " 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
2015-07-30 15:22:07 +00:00
void Blockchain : : print_blockchain_outs ( const std : : string & file ) {
2014-03-03 22:07:58 +00:00
std : : stringstream ss ;
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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 + + ) {
2015-07-30 15:22:07 +00:00
ss < < " \t " < < getObjectHash ( transactionByIndex ( vals [ i ] . first ) . tx ) < < " : " < < vals [ i ] . second < < ENDL ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
if ( Common : : saveStringToFile ( file , ss . str ( ) ) ) {
logger ( INFO , BRIGHT_WHITE ) < <
" Current outputs index writen to file: " < < file ;
2014-06-20 15:56:33 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger ( WARNING , BRIGHT_YELLOW ) < <
" Failed to write current outputs index to file: " < < file ;
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > Blockchain : : findBlockchainSupplement ( const std : : vector < Crypto : : Hash > & remoteBlockIds , size_t maxCount ,
uint32_t & totalBlockCount , uint32_t & startBlockIndex ) {
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
assert ( ! remoteBlockIds . empty ( ) ) ;
assert ( remoteBlockIds . back ( ) = = m_blockIndex . getBlockId ( 0 ) ) ;
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
totalBlockCount = getCurrentBlockchainHeight ( ) ;
startBlockIndex = findBlockchainSupplement ( remoteBlockIds ) ;
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
return m_blockIndex . getBlockIds ( startBlockIndex , static_cast < uint32_t > ( maxCount ) ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : haveBlock ( const Crypto : : Hash & id ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( 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 ;
}
2015-07-30 15:22:07 +00:00
size_t Blockchain : : getTotalTransactions ( ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
return m_transactionMap . size ( ) ;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getTransactionOutputGlobalIndexes ( const Crypto : : Hash & tx_id , std : : vector < uint32_t > & indexs ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-06-20 15:56:33 +00:00
auto it = m_transactionMap . find ( tx_id ) ;
if ( it = = m_transactionMap . end ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( WARNING , YELLOW ) < < " warning: get_tx_outputs_gindexs failed to find transaction with id = " < < tx_id ;
2014-03-03 22:07:58 +00:00
return false ;
}
2014-08-13 10:51:37 +00:00
const TransactionEntry & tx = transactionByIndex ( it - > second ) ;
2015-05-27 12:08:46 +00:00
if ( ! ( tx . m_global_output_indexes . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: global indexes for transaction " < < tx_id < < " is empty " ; return false ; }
2014-06-20 15:56:33 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : get_out_by_msig_gindex ( uint64_t amount , uint64_t gindex , MultisignatureOutput & out ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
auto it = m_multisignatureOutputs . find ( amount ) ;
if ( it = = m_multisignatureOutputs . end ( ) ) {
return false ;
}
if ( it - > second . size ( ) < = gindex ) {
return false ;
}
auto msigUsage = it - > second [ gindex ] ;
auto & targetOut = transactionByIndex ( msigUsage . transactionIndex ) . tx . outputs [ msigUsage . outputIndex ] . target ;
if ( targetOut . type ( ) ! = typeid ( MultisignatureOutput ) ) {
return false ;
}
out = boost : : get < MultisignatureOutput > ( targetOut ) ;
return true ;
}
bool Blockchain : : checkTransactionInputs ( const Transaction & tx , uint32_t & max_used_block_height , Crypto : : Hash & max_used_block_id , BlockInfo * tail ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-08-13 10:51:37 +00:00
if ( tail )
2015-07-30 15:22:07 +00:00
tail - > id = getTailId ( tail - > height ) ;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
bool res = checkTransactionInputs ( tx , & max_used_block_height ) ;
2014-06-20 15:56:33 +00:00
if ( ! res ) return false ;
2015-05-27 12:08:46 +00:00
if ( ! ( max_used_block_height < m_blocks . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: max used block index= " < < max_used_block_height < < " is not less then blockchain size = " < < m_blocks . size ( ) ; return false ; }
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : haveTransactionKeyImagesAsSpent ( const Transaction & tx ) {
for ( const auto & in : tx . inputs ) {
if ( in . type ( ) = = typeid ( KeyInput ) ) {
if ( have_tx_keyimg_as_spent ( boost : : get < KeyInput > ( in ) . keyImage ) ) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : checkTransactionInputs ( const Transaction & tx , uint32_t * pmax_used_block_height ) {
Crypto : : Hash tx_prefix_hash = getObjectHash ( * static_cast < const TransactionPrefix * > ( & tx ) ) ;
return checkTransactionInputs ( tx , tx_prefix_hash , pmax_used_block_height ) ;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : checkTransactionInputs ( const Transaction & tx , const Crypto : : Hash & tx_prefix_hash , uint32_t * pmax_used_block_height ) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
Crypto : : Hash transactionHash = getObjectHash ( tx ) ;
for ( const auto & txin : tx . inputs ) {
2014-08-13 10:51:37 +00:00
assert ( inputIndex < tx . signatures . size ( ) ) ;
2015-07-30 15:22:07 +00:00
if ( txin . type ( ) = = typeid ( KeyInput ) ) {
const KeyInput & in_to_key = boost : : get < KeyInput > ( txin ) ;
if ( ! ( ! in_to_key . outputIndexes . empty ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " empty in_to_key.outputIndexes in transaction with id " < < getObjectHash ( tx ) ; return false ; }
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Key image already spent in blockchain: " < < Common : : podToHex ( in_to_key . keyImage ) ;
2014-08-13 10:51:37 +00:00
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Failed to check ring signature for tx " < < transactionHash ;
2014-08-13 10:51:37 +00:00
return false ;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
+ + inputIndex ;
2015-07-30 15:22:07 +00:00
} else if ( txin . type ( ) = = typeid ( MultisignatureInput ) ) {
if ( ! validateInput ( : : boost : : get < MultisignatureInput > ( txin ) , transactionHash , tx_prefix_hash , tx . signatures [ inputIndex ] ) ) {
2014-08-13 10:51:37 +00:00
return false ;
}
+ + inputIndex ;
} else {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" 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
2015-07-30 15:22:07 +00:00
bool Blockchain : : 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
2015-07-30 15:22:07 +00:00
if ( getCurrentBlockchainHeight ( ) - 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
2015-07-30 15:22:07 +00:00
bool Blockchain : : check_tx_input ( const KeyInput & txin , const Crypto : : Hash & tx_prefix_hash , const std : : vector < Crypto : : Signature > & sig , uint32_t * pmax_related_block_height ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
struct outputs_visitor {
2015-07-30 15:22:07 +00:00
std : : vector < const Crypto : : PublicKey * > & m_results_collector ;
Blockchain & m_bch ;
2015-05-27 12:08:46 +00:00
LoggerRef logger ;
2015-07-30 15:22:07 +00:00
outputs_visitor ( std : : vector < const Crypto : : PublicKey * > & results_collector , Blockchain & bch , ILogger & logger ) : m_results_collector ( results_collector ) , m_bch ( bch ) , logger ( logger , " outputs_visitor " ) {
2015-05-27 12:08:46 +00:00
}
2015-07-15 12:23:00 +00:00
bool handle_output ( const Transaction & tx , const TransactionOutput & out , size_t transactionOutputIndex ) {
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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" One of outputs for one of inputs have wrong tx.unlockTime = " < < tx . unlockTime ;
2014-03-03 22:07:58 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
if ( out . target . type ( ) ! = typeid ( KeyOutput ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Output have wrong type id, which= " < < out . target . which ( ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
m_results_collector . push_back ( & boost : : get < KeyOutput > ( out . target ) . key ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
} ;
//check ring signature
2015-07-30 15:22:07 +00:00
std : : vector < const Crypto : : PublicKey * > output_keys ;
2015-05-27 12:08:46 +00:00
outputs_visitor vi ( output_keys , * this , logger . getLogger ( ) ) ;
2015-07-30 15:22:07 +00:00
if ( ! scanOutputKeysForIndexes ( txin , vi , pmax_related_block_height ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Failed to get output keys for tx with amount = " < < m_currency . formatAmount ( txin . amount ) < <
2015-07-30 15:22:07 +00:00
" and count indexes " < < txin . outputIndexes . size ( ) ;
2014-03-03 22:07:58 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
if ( txin . outputIndexes . size ( ) ! = output_keys . size ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
2015-07-30 15:22:07 +00:00
" Output keys for tx with amount = " < < txin . amount < < " and count indexes " < < txin . outputIndexes . 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
2015-05-27 12:08:46 +00:00
if ( ! ( sig . size ( ) = = output_keys . size ( ) ) ) { logger ( ERROR , BRIGHT_RED ) < < " internal error: tx signatures count= " < < sig . size ( ) < < " mismatch with outputs keys count for inputs= " < < output_keys . size ( ) ; return false ; }
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
}
2015-07-30 15:22:07 +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
2015-07-30 15:22:07 +00:00
uint64_t Blockchain : : 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
2015-07-30 15:22:07 +00:00
bool Blockchain : : check_block_timestamp_main ( const Block & b ) {
2014-08-13 10:51:37 +00:00
if ( b . timestamp > get_adjusted_time ( ) + m_currency . blockFutureTimeLimit ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + 2 hours " ;
2014-03-03 22:07:58 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain : : check_block_timestamp ( std : : vector < uint64_t > timestamps , const Block & b ) {
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-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2015-07-15 12:23:00 +00:00
uint64_t median_ts = Common : : medianValue ( timestamps ) ;
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
if ( b . timestamp < median_ts ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < <
" , less than median of last " < < m_currency . timestampCheckWindow ( ) < < " blocks, " < < median_ts ;
2014-08-13 10:51:37 +00:00
return false ;
}
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : checkCumulativeBlockSize ( const Crypto : : Hash & blockId , size_t cumulativeBlockSize , uint64_t height ) {
2014-08-13 10:51:37 +00:00
size_t maxBlockCumulativeSize = m_currency . maxBlockCumulativeSize ( height ) ;
if ( cumulativeBlockSize > maxBlockCumulativeSize ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" 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.
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlockCumulativeSize ( const Block & block , size_t & cumulativeSize ) {
2014-08-13 10:51:37 +00:00
std : : vector < Transaction > blockTxs ;
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > missedTxs ;
getTransactions ( block . transactionHashes , blockTxs , missedTxs , true ) ;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
cumulativeSize = getObjectBinarySize ( block . baseTransaction ) ;
2014-08-13 10:51:37 +00:00
for ( const Transaction & tx : blockTxs ) {
2015-07-30 15:22:07 +00:00
cumulativeSize + = getObjectBinarySize ( tx ) ;
2014-08-13 10:51:37 +00:00
}
return missedTxs . empty ( ) ;
}
2014-08-14 15:41:44 +00:00
// Precondition: m_blockchain_lock is locked.
2015-07-30 15:22:07 +00:00
bool Blockchain : : update_next_comulative_size_limit ( ) {
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
2015-07-15 12:23:00 +00:00
uint64_t median = Common : : medianValue ( sz ) ;
2015-09-18 11:55:31 +00:00
if ( median < = m_currency . blockGrantedFullRewardZone ( ) ) {
median = m_currency . blockGrantedFullRewardZone ( ) ;
2014-08-14 15:41:44 +00:00
}
2014-06-20 15:56:33 +00:00
m_current_block_cumul_sz_limit = median * 2 ;
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : addNewBlock ( 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_ ;
2015-07-30 15:22:07 +00:00
Crypto : : Hash id ;
2014-08-13 10:51:37 +00:00
if ( ! get_block_hash ( bl , id ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Failed to get block hash, possible block has invalid format " ;
2014-08-13 10:51:37 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2015-04-06 16:13:07 +00:00
bool add_result ;
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
{ //to avoid deadlock lets lock tx_pool for whole add/reorganize process
std : : lock_guard < decltype ( m_tx_pool ) > poolLock ( m_tx_pool ) ;
std : : lock_guard < decltype ( m_blockchain_lock ) > bcLock ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
if ( haveBlock ( id ) ) {
2015-05-27 12:08:46 +00:00
logger ( TRACE ) < < " block with id = " < < id < < " already exists " ;
bvc . m_already_exists = true ;
return false ;
}
//check that block refers to chain tail
2015-07-30 15:22:07 +00:00
if ( ! ( bl . previousBlockHash = = getTailId ( ) ) ) {
2015-05-27 12:08:46 +00:00
//chain switching or wrong block
bvc . m_added_to_main_chain = false ;
add_result = handle_alternative_block ( bl , id , bvc ) ;
} else {
add_result = pushBlock ( bl , bvc ) ;
2015-07-30 15:22:07 +00:00
if ( add_result ) {
sendMessage ( BlockchainMessage ( std : : move ( NewBlockMessage ( id ) ) ) ) ;
}
2015-05-27 12:08:46 +00:00
}
2015-04-06 16:13:07 +00:00
}
if ( add_result & & bvc . m_added_to_main_chain ) {
m_observerManager . notify ( & IBlockchainStorageObserver : : blockchainUpdated ) ;
2014-06-20 15:56:33 +00:00
}
2015-04-06 16:13:07 +00:00
return add_result ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
const Blockchain : : TransactionEntry & Blockchain : : transactionByIndex ( TransactionIndex index ) {
2014-06-20 15:56:33 +00:00
return m_blocks [ index . block ] . transactions [ index . transaction ] ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : pushBlock ( const Block & blockData , block_verification_context & bvc ) {
std : : vector < Transaction > transactions ;
if ( ! loadTransactions ( blockData , transactions ) ) {
bvc . m_verifivation_failed = true ;
return false ;
}
if ( ! pushBlock ( blockData , transactions , bvc ) ) {
saveTransactions ( transactions ) ;
return false ;
}
return true ;
}
bool Blockchain : : pushBlock ( const Block & blockData , const std : : vector < Transaction > & transactions , block_verification_context & bvc ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
auto blockProcessingStart = std : : chrono : : steady_clock : : now ( ) ;
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
Crypto : : Hash blockHash = get_block_hash ( blockData ) ;
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( m_blockIndex . hasBlock ( blockHash ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Block " < < blockHash < < " already exists in blockchain. " ;
2014-06-20 15:56:33 +00:00
bvc . m_verifivation_failed = true ;
2014-03-03 22:07:58 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
if ( blockData . previousBlockHash ! = getTailId ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
2015-07-30 15:22:07 +00:00
" Block " < < blockHash < < " has wrong previousBlockHash: " < < blockData . previousBlockHash < < " , expected: " < < getTailId ( ) ;
2014-06-20 15:56:33 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
if ( ! check_block_timestamp_main ( blockData ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" Block " < < blockHash < < " has invalid timestamp: " < < blockData . timestamp ;
2014-03-03 22:07:58 +00:00
bvc . m_verifivation_failed = true ;
return false ;
}
2015-05-27 12:08:46 +00:00
auto targetTimeStart = std : : chrono : : steady_clock : : now ( ) ;
2015-07-30 15:22:07 +00:00
difficulty_type currentDifficulty = getDifficultyForNextBlock ( ) ;
2015-05-27 12:08:46 +00:00
auto target_calculating_time = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : steady_clock : : now ( ) - targetTimeStart ) . count ( ) ;
2015-07-30 15:22:07 +00:00
if ( ! ( currentDifficulty ) ) {
logger ( ERROR , BRIGHT_RED ) < < " !!!!!!!!! difficulty overhead !!!!!!!!! " ;
return false ;
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
2015-05-27 12:08:46 +00:00
auto longhashTimeStart = std : : chrono : : steady_clock : : now ( ) ;
2015-07-30 15:22:07 +00:00
Crypto : : Hash proof_of_work = NULL_HASH ;
if ( m_checkpoints . is_in_checkpoint_zone ( getCurrentBlockchainHeight ( ) ) ) {
if ( ! m_checkpoints . check_block ( getCurrentBlockchainHeight ( ) , blockHash ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" 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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" 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
2015-05-27 12:08:46 +00:00
auto longhash_calculating_time = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : steady_clock : : now ( ) - longhashTimeStart ) . count ( ) ;
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
if ( ! prevalidate_miner_transaction ( blockData , static_cast < uint32_t > ( m_blocks . size ( ) ) ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" 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
2015-07-30 15:22:07 +00:00
Crypto : : Hash minerTransactionHash = getObjectHash ( blockData . baseTransaction ) ;
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 ) ;
2015-07-30 15:22:07 +00:00
block . transactions [ 0 ] . tx = blockData . baseTransaction ;
TransactionIndex transactionIndex = { static_cast < uint32_t > ( m_blocks . size ( ) ) , static_cast < uint16_t > ( 0 ) } ;
2014-06-20 15:56:33 +00:00
pushTransaction ( block , minerTransactionHash , transactionIndex ) ;
2015-07-30 15:22:07 +00:00
size_t coinbase_blob_size = getObjectBinarySize ( blockData . baseTransaction ) ;
2014-03-03 22:07:58 +00:00
size_t cumulative_block_size = coinbase_blob_size ;
uint64_t fee_summary = 0 ;
2015-07-30 15:22:07 +00:00
for ( size_t i = 0 ; i < transactions . size ( ) ; + + i ) {
const Crypto : : Hash & tx_id = blockData . transactionHashes [ i ] ;
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 ;
2015-07-30 15:22:07 +00:00
block . transactions . back ( ) . tx = transactions [ i ] ;
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
blob_size = toBinaryArray ( block . transactions . back ( ) . tx ) . size ( ) ;
fee = getInputAmount ( block . transactions . back ( ) . tx ) - getOutputAmount ( block . transactions . back ( ) . tx ) ;
if ( ! checkTransactionInputs ( block . transactions . back ( ) . tx ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < <
" 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
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 ;
2015-07-30 15:22:07 +00:00
if ( ! validate_miner_transaction ( blockData , static_cast < uint32_t > ( m_blocks . size ( ) ) , cumulative_block_size , already_generated_coins , fee_summary , reward , emissionChange ) ) {
2015-05-27 12:08:46 +00:00
logger ( INFO , BRIGHT_WHITE ) < < " 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 ) ;
2015-05-27 12:08:46 +00:00
auto block_processing_time = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : steady_clock : : now ( ) - blockProcessingStart ) . count ( ) ;
logger ( DEBUGGING ) < <
" +++++ 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
2015-05-27 12:08:46 +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
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 ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : pushBlock ( BlockEntry & block ) {
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 ) ;
2015-07-30 15:22:07 +00:00
m_timestampIndex . add ( block . bl . timestamp , blockHash ) ;
m_generatedTransactionsIndex . add ( block . bl ) ;
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
void Blockchain : : popBlock ( const Crypto : : Hash & blockHash ) {
2014-06-20 15:56:33 +00:00
if ( m_blocks . empty ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Attempt to pop block from empty blockchain. " ;
2014-06-20 15:56:33 +00:00
return ;
}
2015-07-30 15:22:07 +00:00
std : : vector < Transaction > transactions ( m_blocks . back ( ) . transactions . size ( ) - 1 ) ;
for ( size_t i = 0 ; i < m_blocks . back ( ) . transactions . size ( ) - 1 ; + + i ) {
transactions [ i ] = m_blocks . back ( ) . transactions [ 1 + i ] . tx ;
}
saveTransactions ( transactions ) ;
popTransactions ( m_blocks . back ( ) , getObjectHash ( m_blocks . back ( ) . bl . baseTransaction ) ) ;
m_timestampIndex . remove ( m_blocks . back ( ) . bl . timestamp , blockHash ) ;
m_generatedTransactionsIndex . remove ( m_blocks . back ( ) . bl ) ;
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 ( ) ) ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : 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 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" 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 ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Double spending transaction was pushed to blockchain. " ;
2014-08-13 10:51:37 +00:00
m_transactionMap . erase ( transactionHash ) ;
return false ;
}
2015-07-30 15:22:07 +00:00
for ( size_t i = 0 ; i < transaction . tx . inputs . size ( ) ; + + i ) {
if ( transaction . tx . inputs [ i ] . type ( ) = = typeid ( KeyInput ) ) {
auto result = m_spent_keys . insert ( : : boost : : get < KeyInput > ( transaction . tx . inputs [ i ] ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
if ( ! result . second ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Double spending transaction was pushed to blockchain. " ;
2014-06-20 15:56:33 +00:00
for ( size_t j = 0 ; j < i ; + + j ) {
2015-07-30 15:22:07 +00:00
m_spent_keys . erase ( : : boost : : get < KeyInput > ( transaction . tx . inputs [ i - 1 - j ] ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
}
m_transactionMap . erase ( transactionHash ) ;
return false ;
}
}
}
2015-07-30 15:22:07 +00:00
for ( const auto & inv : transaction . tx . inputs ) {
if ( inv . type ( ) = = typeid ( MultisignatureInput ) ) {
const MultisignatureInput & in = : : boost : : get < MultisignatureInput > ( inv ) ;
2014-08-13 10:51:37 +00:00
auto & amountOutputs = m_multisignatureOutputs [ in . amount ] ;
amountOutputs [ in . outputIndex ] . isUsed = true ;
}
}
2015-07-30 15:22:07 +00:00
transaction . m_global_output_indexes . resize ( transaction . tx . outputs . size ( ) ) ;
for ( uint16_t output = 0 ; output < transaction . tx . outputs . size ( ) ; + + output ) {
if ( transaction . tx . outputs [ output ] . target . type ( ) = = typeid ( KeyOutput ) ) {
auto & amountOutputs = m_outputs [ transaction . tx . outputs [ output ] . amount ] ;
2015-05-27 12:08:46 +00:00
transaction . m_global_output_indexes [ output ] = static_cast < uint32_t > ( amountOutputs . size ( ) ) ;
2014-08-13 10:51:37 +00:00
amountOutputs . push_back ( std : : make_pair < > ( transactionIndex , output ) ) ;
2015-07-30 15:22:07 +00:00
} else if ( transaction . tx . outputs [ output ] . target . type ( ) = = typeid ( MultisignatureOutput ) ) {
auto & amountOutputs = m_multisignatureOutputs [ transaction . tx . outputs [ output ] . amount ] ;
2015-05-27 12:08:46 +00:00
transaction . m_global_output_indexes [ output ] = static_cast < uint32_t > ( amountOutputs . size ( ) ) ;
2015-07-30 15:22:07 +00:00
MultisignatureOutputUsage outputUsage = { transactionIndex , output , false } ;
2014-08-13 10:51:37 +00:00
amountOutputs . push_back ( outputUsage ) ;
}
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
m_paymentIdIndex . add ( transaction . tx ) ;
2014-06-20 15:56:33 +00:00
return true ;
}
2015-07-30 15:22:07 +00:00
void Blockchain : : popTransaction ( const Transaction & transaction , const Crypto : : Hash & transactionHash ) {
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = m_transactionMap . at ( transactionHash ) ;
2015-07-30 15:22:07 +00:00
for ( size_t outputIndex = 0 ; outputIndex < transaction . outputs . size ( ) ; + + outputIndex ) {
const TransactionOutput & output = transaction . outputs [ transaction . outputs . size ( ) - 1 - outputIndex ] ;
if ( output . target . type ( ) = = typeid ( KeyOutput ) ) {
2014-08-13 10:51:37 +00:00
auto amountOutputs = m_outputs . find ( output . amount ) ;
if ( amountOutputs = = m_outputs . end ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - cannot find specific amount in outputs map. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if ( amountOutputs - > second . empty ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - output array for specific amount is empty. " ;
2014-08-13 10:51:37 +00:00
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 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - invalid transaction index. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
if ( amountOutputs - > second . back ( ) . second ! = transaction . outputs . size ( ) - 1 - outputIndex ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - invalid output index. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
amountOutputs - > second . pop_back ( ) ;
if ( amountOutputs - > second . empty ( ) ) {
m_outputs . erase ( amountOutputs ) ;
}
2015-07-30 15:22:07 +00:00
} else if ( output . target . type ( ) = = typeid ( MultisignatureOutput ) ) {
2014-08-13 10:51:37 +00:00
auto amountOutputs = m_multisignatureOutputs . find ( output . amount ) ;
if ( amountOutputs = = m_multisignatureOutputs . end ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - cannot find specific amount in outputs map. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
if ( amountOutputs - > second . empty ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - output array for specific amount is empty. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
if ( amountOutputs - > second . back ( ) . isUsed ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - attempting to remove used output. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
if ( amountOutputs - > second . back ( ) . transactionIndex . block ! = transactionIndex . block | | amountOutputs - > second . back ( ) . transactionIndex . transaction ! = transactionIndex . transaction ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - invalid transaction index. " ;
2014-08-13 10:51:37 +00:00
continue ;
}
2015-07-30 15:22:07 +00:00
if ( amountOutputs - > second . back ( ) . outputIndex ! = transaction . outputs . size ( ) - 1 - outputIndex ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - invalid output index. " ;
2014-08-13 10:51:37 +00:00
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
}
}
2015-07-30 15:22:07 +00:00
for ( auto & input : transaction . inputs ) {
if ( input . type ( ) = = typeid ( KeyInput ) ) {
size_t count = m_spent_keys . erase ( : : boost : : get < KeyInput > ( input ) . keyImage ) ;
2014-06-20 15:56:33 +00:00
if ( count ! = 1 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - cannot find spent key. " ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
} else if ( input . type ( ) = = typeid ( MultisignatureInput ) ) {
const MultisignatureInput & in = : : boost : : get < MultisignatureInput > ( input ) ;
2014-08-13 10:51:37 +00:00
auto & amountOutputs = m_multisignatureOutputs [ in . amount ] ;
if ( ! amountOutputs [ in . outputIndex ] . isUsed ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - multisignature output not marked as used. " ;
2014-08-13 10:51:37 +00:00
}
amountOutputs [ in . outputIndex ] . isUsed = false ;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
m_paymentIdIndex . remove ( transaction ) ;
2014-06-20 15:56:33 +00:00
size_t count = m_transactionMap . erase ( transactionHash ) ;
if ( count ! = 1 ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR , BRIGHT_RED ) < <
" Blockchain consistency broken - cannot find transaction by hash. " ;
2014-06-20 15:56:33 +00:00
}
}
2015-07-30 15:22:07 +00:00
void Blockchain : : 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 ) {
2015-07-30 15:22:07 +00:00
popTransaction ( block . transactions [ block . transactions . size ( ) - 1 - i ] . tx , block . bl . transactionHashes [ block . transactions . size ( ) - 2 - i ] ) ;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
popTransaction ( block . bl . baseTransaction , minerTransactionHash ) ;
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : validateInput ( const MultisignatureInput & input , const Crypto : : Hash & transactionHash , const Crypto : : Hash & transactionPrefixHash , const std : : vector < Crypto : : Signature > & transactionSignatures ) {
assert ( input . signatureCount = = transactionSignatures . size ( ) ) ;
2014-08-13 10:51:37 +00:00
MultisignatureOutputsContainer : : const_iterator amountOutputs = m_multisignatureOutputs . find ( input . amount ) ;
if ( amountOutputs = = m_multisignatureOutputs . end ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains multisignature input with invalid amount. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
if ( input . outputIndex > = amountOutputs - > second . size ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains multisignature input with invalid outputIndex. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
const MultisignatureOutputUsage & outputIndex = amountOutputs - > second [ input . outputIndex ] ;
if ( outputIndex . isUsed ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains double spending multisignature input. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
const Transaction & outputTransaction = m_blocks [ outputIndex . transactionIndex . block ] . transactions [ outputIndex . transactionIndex . transaction ] . tx ;
if ( ! is_tx_spendtime_unlocked ( outputTransaction . unlockTime ) ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains multisignature input which points to a locked transaction. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
assert ( outputTransaction . outputs [ outputIndex . outputIndex ] . amount = = input . amount ) ;
assert ( outputTransaction . outputs [ outputIndex . outputIndex ] . target . type ( ) = = typeid ( MultisignatureOutput ) ) ;
const MultisignatureOutput & output = : : boost : : get < MultisignatureOutput > ( outputTransaction . outputs [ outputIndex . outputIndex ] . target ) ;
if ( input . signatureCount ! = output . requiredSignatureCount ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains multisignature input with invalid signature count. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
size_t inputSignatureIndex = 0 ;
size_t outputKeyIndex = 0 ;
while ( inputSignatureIndex < input . signatureCount ) {
2014-08-13 10:51:37 +00:00
if ( outputKeyIndex = = output . keys . size ( ) ) {
2015-05-27 12:08:46 +00:00
logger ( DEBUGGING ) < <
" Transaction << " < < transactionHash < < " contains multisignature input with invalid signatures. " ;
2014-08-13 10:51:37 +00:00
return false ;
}
2015-07-30 15:22:07 +00:00
if ( Crypto : : check_signature ( transactionPrefixHash , output . keys [ outputKeyIndex ] , transactionSignatures [ inputSignatureIndex ] ) ) {
2014-08-13 10:51:37 +00:00
+ + inputSignatureIndex ;
}
+ + outputKeyIndex ;
}
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getLowerBound ( uint64_t timestamp , uint64_t startOffset , uint32_t & height ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
assert ( startOffset < m_blocks . size ( ) ) ;
2014-08-13 10:51:37 +00:00
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 ;
}
2015-07-30 15:22:07 +00:00
height = static_cast < uint32_t > ( std : : distance ( m_blocks . begin ( ) , bound ) ) ;
2014-08-13 10:51:37 +00:00
return true ;
}
2015-07-30 15:22:07 +00:00
std : : vector < Crypto : : Hash > Blockchain : : getBlockIds ( uint32_t startHeight , uint32_t maxCount ) {
2015-05-27 12:08:46 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
2015-07-30 15:22:07 +00:00
return m_blockIndex . getBlockIds ( startHeight , maxCount ) ;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlockContainingTransaction ( const Crypto : : Hash & txId , Crypto : : Hash & blockId , uint32_t & blockHeight ) {
2015-07-15 12:23:00 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
auto it = m_transactionMap . find ( txId ) ;
2015-07-30 15:22:07 +00:00
if ( it = = m_transactionMap . end ( ) ) {
2015-07-15 12:23:00 +00:00
return false ;
} else {
blockHeight = m_blocks [ it - > second . block ] . height ;
2015-07-30 15:22:07 +00:00
blockId = getBlockIdByHeight ( blockHeight ) ;
2015-07-15 12:23:00 +00:00
return true ;
}
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getAlreadyGeneratedCoins ( const Crypto : : Hash & hash , uint64_t & generatedCoins ) {
2015-07-15 12:23:00 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
// try to find block in main chain
2015-07-30 15:22:07 +00:00
uint32_t height = 0 ;
2015-07-15 12:23:00 +00:00
if ( m_blockIndex . getBlockHeight ( hash , height ) ) {
generatedCoins = m_blocks [ height ] . already_generated_coins ;
return true ;
}
// try to find block in alternative chain
auto blockByHashIterator = m_alternative_chains . find ( hash ) ;
if ( blockByHashIterator ! = m_alternative_chains . end ( ) ) {
generatedCoins = blockByHashIterator - > second . already_generated_coins ;
return true ;
}
logger ( DEBUGGING ) < < " Can't find block with hash " < < hash < < " to get already generated coins. " ;
return false ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getBlockSize ( const Crypto : : Hash & hash , size_t & size ) {
2015-07-15 12:23:00 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
// try to find block in main chain
2015-07-30 15:22:07 +00:00
uint32_t height = 0 ;
2015-07-15 12:23:00 +00:00
if ( m_blockIndex . getBlockHeight ( hash , height ) ) {
size = m_blocks [ height ] . block_cumulative_size ;
return true ;
}
// try to find block in alternative chain
auto blockByHashIterator = m_alternative_chains . find ( hash ) ;
if ( blockByHashIterator ! = m_alternative_chains . end ( ) ) {
size = blockByHashIterator - > second . block_cumulative_size ;
return true ;
}
logger ( DEBUGGING ) < < " Can't find block with hash " < < hash < < " to get block size. " ;
return false ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : getMultisigOutputReference ( const MultisignatureInput & txInMultisig , std : : pair < Crypto : : Hash , size_t > & outputReference ) {
2015-07-15 12:23:00 +00:00
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
MultisignatureOutputsContainer : : const_iterator amountIter = m_multisignatureOutputs . find ( txInMultisig . amount ) ;
if ( amountIter = = m_multisignatureOutputs . end ( ) ) {
logger ( DEBUGGING ) < < " Transaction contains multisignature input with invalid amount. " ;
return false ;
}
if ( amountIter - > second . size ( ) < = txInMultisig . outputIndex ) {
logger ( DEBUGGING ) < < " Transaction contains multisignature input with invalid outputIndex. " ;
return false ;
}
const MultisignatureOutputUsage & outputIndex = amountIter - > second [ txInMultisig . outputIndex ] ;
const Transaction & outputTransaction = m_blocks [ outputIndex . transactionIndex . block ] . transactions [ outputIndex . transactionIndex . transaction ] . tx ;
2015-07-30 15:22:07 +00:00
outputReference . first = getObjectHash ( outputTransaction ) ;
2015-07-15 12:23:00 +00:00
outputReference . second = outputIndex . outputIndex ;
return true ;
}
2015-07-30 15:22:07 +00:00
bool Blockchain : : storeBlockchainIndices ( ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
logger ( INFO , BRIGHT_WHITE ) < < " Saving blockchain indices... " ;
BlockchainIndicesSerializer ser ( * this , getTailId ( ) , logger . getLogger ( ) ) ;
if ( ! storeToBinaryFile ( ser , appendPath ( m_config_folder , m_currency . blockchinIndicesFileName ( ) ) ) ) {
logger ( ERROR , BRIGHT_RED ) < < " Failed to save blockchain indices " ;
return false ;
}
return true ;
}
bool Blockchain : : loadBlockchainIndices ( ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
logger ( INFO , BRIGHT_WHITE ) < < " Loading blockchain indices for BlockchainExplorer... " ;
BlockchainIndicesSerializer loader ( * this , get_block_hash ( m_blocks . back ( ) . bl ) , logger . getLogger ( ) ) ;
loadFromBinaryFile ( loader , appendPath ( m_config_folder , m_currency . blockchinIndicesFileName ( ) ) ) ;
if ( ! loader . loaded ( ) ) {
logger ( WARNING , BRIGHT_YELLOW ) < < " No actual blockchain indices for BlockchainExplorer found, rebuilding... " ;
std : : chrono : : steady_clock : : time_point timePoint = std : : chrono : : steady_clock : : now ( ) ;
m_paymentIdIndex . clear ( ) ;
m_timestampIndex . clear ( ) ;
m_generatedTransactionsIndex . clear ( ) ;
for ( uint32_t b = 0 ; b < m_blocks . size ( ) ; + + b ) {
if ( b % 1000 = = 0 ) {
logger ( INFO , BRIGHT_WHITE ) < < " Height " < < b < < " of " < < m_blocks . size ( ) ;
}
const BlockEntry & block = m_blocks [ b ] ;
m_timestampIndex . add ( block . bl . timestamp , get_block_hash ( block . bl ) ) ;
m_generatedTransactionsIndex . add ( block . bl ) ;
for ( uint16_t t = 0 ; t < block . transactions . size ( ) ; + + t ) {
const TransactionEntry & transaction = block . transactions [ t ] ;
m_paymentIdIndex . add ( transaction . tx ) ;
}
}
std : : chrono : : duration < double > duration = std : : chrono : : steady_clock : : now ( ) - timePoint ;
logger ( INFO , BRIGHT_WHITE ) < < " Rebuilding blockchain indices took: " < < duration . count ( ) ;
}
return true ;
}
bool Blockchain : : getGeneratedTransactionsNumber ( uint32_t height , uint64_t & generatedTransactions ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
return m_generatedTransactionsIndex . find ( height , generatedTransactions ) ;
}
bool Blockchain : : getOrphanBlockIdsByHeight ( uint32_t height , std : : vector < Crypto : : Hash > & blockHashes ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
return m_orthanBlocksIndex . find ( height , blockHashes ) ;
}
bool Blockchain : : getBlockIdsByTimestamp ( uint64_t timestampBegin , uint64_t timestampEnd , uint32_t blocksNumberLimit , std : : vector < Crypto : : Hash > & hashes , uint32_t & blocksNumberWithinTimestamps ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
return m_timestampIndex . find ( timestampBegin , timestampEnd , blocksNumberLimit , hashes , blocksNumberWithinTimestamps ) ;
}
bool Blockchain : : getTransactionIdsByPaymentId ( const Crypto : : Hash & paymentId , std : : vector < Crypto : : Hash > & transactionHashes ) {
std : : lock_guard < decltype ( m_blockchain_lock ) > lk ( m_blockchain_lock ) ;
return m_paymentIdIndex . find ( paymentId , transactionHashes ) ;
}
bool Blockchain : : loadTransactions ( const Block & block , std : : vector < Transaction > & transactions ) {
transactions . resize ( block . transactionHashes . size ( ) ) ;
size_t transactionSize ;
uint64_t fee ;
for ( size_t i = 0 ; i < block . transactionHashes . size ( ) ; + + i ) {
if ( ! m_tx_pool . take_tx ( block . transactionHashes [ i ] , transactions [ i ] , transactionSize , fee ) ) {
tx_verification_context context ;
for ( size_t j = 0 ; j < i ; + + j ) {
if ( ! m_tx_pool . add_tx ( transactions [ i - 1 - j ] , context , true ) ) {
throw std : : runtime_error ( " Blockchain::loadTransactions, failed to add transaction to pool " ) ;
}
}
return false ;
}
}
return true ;
}
void Blockchain : : saveTransactions ( const std : : vector < Transaction > & transactions ) {
tx_verification_context context ;
for ( size_t i = 0 ; i < transactions . size ( ) ; + + i ) {
if ( ! m_tx_pool . add_tx ( transactions [ transactions . size ( ) - 1 - i ] , context , true ) ) {
throw std : : runtime_error ( " Blockchain::saveTransactions, failed to add transaction to pool " ) ;
}
}
}
bool Blockchain : : addMessageQueue ( MessageQueue < BlockchainMessage > & messageQueue ) {
return m_messageQueueList . insert ( messageQueue ) ;
}
bool Blockchain : : removeMessageQueue ( MessageQueue < BlockchainMessage > & messageQueue ) {
return m_messageQueueList . remove ( messageQueue ) ;
}
void Blockchain : : sendMessage ( const BlockchainMessage & message ) {
for ( IntrusiveLinkedList < MessageQueue < BlockchainMessage > > : : iterator iter = m_messageQueueList . begin ( ) ; iter ! = m_messageQueueList . end ( ) ; + + iter ) {
iter - > push ( message ) ;
}
}
bool Blockchain : : isBlockInMainChain ( const Crypto : : Hash & blockId ) {
return m_blockIndex . hasBlock ( blockId ) ;
}
2015-05-27 12:08:46 +00:00
}