@ -35,6 +35,7 @@
# include <libdevcore/StructuredLogger.h>
# include <libdevcore/FileSystem.h>
# include <libethcore/Exceptions.h>
# include <libethcore/EthashAux.h>
# include <libethcore/ProofOfWork.h>
# include <libethcore/BlockInfo.h>
# include <libethcore/Params.h>
@ -42,13 +43,14 @@
# include "GenesisInfo.h"
# include "State.h"
# include "Defaults.h"
using namespace std ;
using namespace dev ;
using namespace dev : : eth ;
namespace js = json_spirit ;
# define ETH_CATCH 1
# define ETH_TIMED_IMPORTS 0
# define ETH_TIMED_IMPORTS 1
# ifdef _WIN32
const char * BlockChainDebug : : name ( ) { return EthBlue " 8 " EthWhite " <> " ; }
@ -307,7 +309,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{
// _bq.tick(*this);
vector < pair < BlockInfo , bytes > > blocks ;
VerifiedBlocks blocks ;
_bq . drain ( blocks , _max ) ;
h256s fresh ;
@ -320,7 +322,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r ;
DEV_TIMED_ABOVE ( Block import , 500 )
r = import ( block . first , block . s econ d , _stateDB , ImportRequirements : : Default & ~ ImportRequirements : : ValidNonce & ~ ImportRequirements : : CheckUncles ) ;
r = import ( block . veri fied, _stateDB , ImportRequirements : : Default & ~ ImportRequirements : : ValidNonce & ~ ImportRequirements : : CheckUncles ) ;
fresh + = r . first ;
dead + = r . second ;
}
@ -329,14 +331,23 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
cwarn < < " ODD: Import queue contains block with unknown parent. " < < LogTag : : Error < < boost : : current_exception_diagnostic_information ( ) ;
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks . push_back ( block . first . hash ( ) ) ;
badBlocks . push_back ( block . verified . info . hash ( ) ) ;
}
catch ( dev : : eth : : FutureTime )
{
cwarn < < " ODD: Import queue contains a block with future time. " < < LogTag : : Error < < boost : : current_exception_diagnostic_information ( ) ;
// NOTE: don't reimport since the queue should guarantee everything in the past.
// Can't continue - chain bad.
badBlocks . push_back ( block . verified . info . hash ( ) ) ;
}
catch ( Exception const & _e )
catch ( Exception & ex )
{
cnote < < " Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks! " < < LogTag : : Error < < diagnostic_information ( _e ) ;
cnote < < " Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks! " < < LogTag : : Error < < diagnostic_information ( ex ) ;
if ( m_onBad )
m_onBad ( ex ) ;
// NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad.
badBlocks . push_back ( block . first . hash ( ) ) ;
badBlocks . push_back ( block . verified . info . hash ( ) ) ;
}
}
return make_tuple ( fresh , dead , _bq . doneDrain ( badBlocks ) ) ;
@ -346,7 +357,7 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{
try
{
return make_pair ( ImportResult : : Success , import ( _block , _stateDB , _ir ) ) ;
return make_pair ( ImportResult : : Success , import ( verifyBlock ( _block , m_onBad , _ir ) , _stateDB , _ir ) ) ;
}
catch ( UnknownParent & )
{
@ -360,8 +371,10 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{
return make_pair ( ImportResult : : FutureTime , make_pair ( h256s ( ) , h256s ( ) ) ) ;
}
catch ( . . . )
catch ( Exception & ex )
{
if ( m_onBad )
m_onBad ( ex ) ;
return make_pair ( ImportResult : : Malformed , make_pair ( h256s ( ) , h256s ( ) ) ) ;
}
}
@ -369,28 +382,28 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
ImportRoute BlockChain : : import ( bytes const & _block , OverlayDB const & _db , ImportRequirements : : value _ir )
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi ;
VerifiedBlockRef block ;
# if ETH_CATCH
try
# endif
{
bi . populate ( & _block ) ;
bi . verifyInternals ( & _block ) ;
block = verifyBlock ( _block , m_onBad ) ;
}
# if ETH_CATCH
catch ( Exception const & _ e)
catch ( Exception & ex )
{
clog ( BlockChainNote ) < < " Malformed block: " < < diagnostic_information ( _e ) ;
_e < < errinfo_comment ( " Malformed block " ) ;
clog ( BlockChainNote ) < < " Malformed block: " < < diagnostic_information ( ex ) ;
ex < < errinfo_now ( time ( 0 ) ) ;
ex < < errinfo_block ( _block ) ;
throw ;
}
# endif
return import ( bi , _b lock , _db , _ir ) ;
return import ( block , _db , _ir ) ;
}
ImportRoute BlockChain : : import ( BlockInfo const & _bi , bytes const & _block , OverlayDB const & _db , ImportRequirements : : value _ir )
ImportRoute BlockChain : : import ( VerifiedBlockRef const & _block , OverlayDB const & _db , ImportRequirements : : value _ir )
{
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
@ -405,28 +418,28 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
# endif
// Check block doesn't already exist first!
if ( isKnown ( _bi . hash ( ) ) & & ( _ir & ImportRequirements : : DontHave ) )
if ( isKnown ( _block . info . hash ( ) ) & & ( _ir & ImportRequirements : : DontHave ) )
{
clog ( BlockChainNote ) < < _bi . hash ( ) < < " : Not new. " ;
clog ( BlockChainNote ) < < _block . info . hash ( ) < < " : Not new. " ;
BOOST_THROW_EXCEPTION ( AlreadyHaveBlock ( ) ) ;
}
// Work out its number as the parent's number + 1
if ( ! isKnown ( _bi . parentHash ) )
if ( ! isKnown ( _block . info . parentHash ) )
{
clog ( BlockChainNote ) < < _bi . hash ( ) < < " : Unknown parent " < < _bi . parentHash ;
clog ( BlockChainNote ) < < _block . info . hash ( ) < < " : Unknown parent " < < _block . info . parentHash ;
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
BOOST_THROW_EXCEPTION ( UnknownParent ( ) ) ;
}
auto pd = details ( _bi . parentHash ) ;
auto pd = details ( _block . info . parentHash ) ;
if ( ! pd )
{
auto pdata = pd . rlp ( ) ;
clog ( BlockChainDebug ) < < " Details is returning false despite block known: " < < RLP ( pdata ) ;
auto parentBlock = block ( _bi . parentHash ) ;
clog ( BlockChainDebug ) < < " isKnown: " < < isKnown ( _bi . parentHash ) ;
clog ( BlockChainDebug ) < < " last/number: " < < m_lastBlockNumber < < m_lastBlockHash < < _bi . number ;
auto parentBlock = block ( _block . info . parentHash ) ;
clog ( BlockChainDebug ) < < " isKnown: " < < isKnown ( _block . info . parentHash ) ;
clog ( BlockChainDebug ) < < " last/number: " < < m_lastBlockNumber < < m_lastBlockHash < < _block . info . number ;
clog ( BlockChainDebug ) < < " Block: " < < BlockInfo ( parentBlock ) ;
clog ( BlockChainDebug ) < < " RLP: " < < RLP ( parentBlock ) ;
clog ( BlockChainDebug ) < < " DATABASE CORRUPTION: CRITICAL FAILURE " ;
@ -434,14 +447,14 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
}
// Check it's not crazy
if ( _bi . timestamp > ( u256 ) time ( 0 ) )
if ( _block . info . timestamp > ( u256 ) time ( 0 ) )
{
clog ( BlockChainChat ) < < _bi . hash ( ) < < " : Future time " < < _bi . timestamp < < " (now at " < < time ( 0 ) < < " ) " ;
clog ( BlockChainChat ) < < _block . info . hash ( ) < < " : Future time " < < _block . info . timestamp < < " (now at " < < time ( 0 ) < < " ) " ;
// Block has a timestamp in the future. This is no good.
BOOST_THROW_EXCEPTION ( FutureTime ( ) ) ;
}
clog ( BlockChainChat ) < < " Attempting import of " < < _bi . hash ( ) < < " ... " ;
clog ( BlockChainChat ) < < " Attempting import of " < < _block . info . hash ( ) < < " ... " ;
# if ETH_TIMED_IMPORTS
preliminaryChecks = t . elapsed ( ) ;
@ -461,7 +474,7 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// Check transactions are valid and that they result in a state equivalent to our state_root.
// Get total difficulty increase and update state, checking it.
State s ( _db ) ;
auto tdIncrease = s . enactOn ( & _block , _bi , * this , _ir ) ;
auto tdIncrease = s . enactOn ( _block , * this , _ir ) ;
BlockLogBlooms blb ;
BlockReceipts br ;
@ -470,14 +483,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
blb . blooms . push_back ( s . receipt ( i ) . bloom ( ) ) ;
br . receipts . push_back ( s . receipt ( i ) ) ;
}
try {
s . cleanup ( true ) ;
}
catch ( BadRoot )
{
cwarn < < " BadRoot error. Retrying import later. " ;
BOOST_THROW_EXCEPTION ( FutureTime ( ) ) ;
}
s . cleanup ( true ) ;
td = pd . totalDifficulty + tdIncrease ;
@ -497,22 +504,22 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// together with an "ensureCachedWithUpdatableLock(l)" method.
// This is safe in practice since the caches don't get flushed nearly often enough to be
// done here.
details ( _bi . parentHash ) ;
details ( _block . info . parentHash ) ;
DEV_WRITE_GUARDED ( x_details )
m_details [ _bi . parentHash ] . children . push_back ( _bi . hash ( ) ) ;
m_details [ _block . info . parentHash ] . children . push_back ( _block . info . hash ( ) ) ;
# if ETH_TIMED_IMPORTS || !ETH_TRUE
collation = t . elapsed ( ) ;
t . restart ( ) ;
# endif
blocksBatch . Put ( toSlice ( _bi . hash ( ) ) , ( ldb : : Slice ) ref ( _ block) ) ;
blocksBatch . Put ( toSlice ( _block . info . hash ( ) ) , ldb : : Slice ( _block . block ) ) ;
DEV_READ_GUARDED ( x_details )
extrasBatch . Put ( toSlice ( _bi . parentHash , ExtraDetails ) , ( ldb : : Slice ) dev : : ref ( m_details [ _bi . parentHash ] . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _block . info . parentHash , ExtraDetails ) , ( ldb : : Slice ) dev : : ref ( m_details [ _block . info . parentHash ] . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _bi . hash ( ) , ExtraDetails ) , ( ldb : : Slice ) dev : : ref ( BlockDetails ( ( unsigned ) pd . number + 1 , td , _bi . parentHash , { } ) . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _bi . hash ( ) , ExtraLogBlooms ) , ( ldb : : Slice ) dev : : ref ( blb . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _bi . hash ( ) , ExtraReceipts ) , ( ldb : : Slice ) dev : : ref ( br . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _block . info . hash ( ) , ExtraDetails ) , ( ldb : : Slice ) dev : : ref ( BlockDetails ( ( unsigned ) pd . number + 1 , td , _block . info . parentHash , { } ) . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _block . info . hash ( ) , ExtraLogBlooms ) , ( ldb : : Slice ) dev : : ref ( blb . rlp ( ) ) ) ;
extrasBatch . Put ( toSlice ( _block . info . hash ( ) , ExtraReceipts ) , ( ldb : : Slice ) dev : : ref ( br . rlp ( ) ) ) ;
# if ETH_TIMED_IMPORTS || !ETH_TRUE
writing = t . elapsed ( ) ;
@ -520,30 +527,25 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
# endif
}
# if ETH_CATCH
catch ( InvalidNonce cons t& _ e)
catch ( BadRoo t& ex )
{
clog ( BlockChainNote ) < < " Malformed block: " < < diagnostic_information ( _e ) ;
_e < < errinfo_comment ( " Malformed block " ) ;
throw ;
cwarn < < " BadRoot error. Retrying import later. " ;
BOOST_THROW_EXCEPTION ( FutureTime ( ) ) ;
}
catch ( Exception const & _ e)
catch ( Exception & ex )
{
clog ( BlockChainWarn ) < < " Malformed block: " < < diagnostic_information ( _e ) ;
_e < < errinfo_comment ( " Malformed block " ) ;
clog ( BlockChainWarn ) < < " Block: " < < _bi . hash ( ) ;
clog ( BlockChainWarn ) < < _bi ;
clog ( BlockChainWarn ) < < " Block parent: " < < _bi . parentHash ;
clog ( BlockChainWarn ) < < BlockInfo ( block ( _bi . parentHash ) ) ;
ex < < errinfo_now ( time ( 0 ) ) ;
ex < < errinfo_block ( _block . block . toBytes ( ) ) ;
throw ;
}
# endif
StructuredLogger : : chainReceivedNewBlock (
_bi . headerHash ( WithoutNonce ) . abridged ( ) ,
_bi . nonce . abridged ( ) ,
_block . info . headerHash ( WithoutNonce ) . abridged ( ) ,
_block . info . nonce . abridged ( ) ,
currentHash ( ) . abridged ( ) ,
" " , // TODO: remote id ??
_bi . parentHash . abridged ( )
_block . info . parentHash . abridged ( )
) ;
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
@ -556,8 +558,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// don't include bi.hash() in treeRoute, since it's not yet in details DB...
// just tack it on afterwards.
unsigned commonIndex ;
tie ( route , common , commonIndex ) = treeRoute ( last , _bi . parentHash ) ;
route . push_back ( _bi . hash ( ) ) ;
tie ( route , common , commonIndex ) = treeRoute ( last , _block . info . parentHash ) ;
route . push_back ( _block . info . hash ( ) ) ;
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if ( common ! = last )
@ -569,8 +571,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
for ( auto i = route . rbegin ( ) ; i ! = route . rend ( ) & & * i ! = common ; + + i )
{
BlockInfo tbi ;
if ( * i = = _bi . hash ( ) )
tbi = _bi ;
if ( * i = = _block . info . hash ( ) )
tbi = _block . info ;
else
tbi = BlockInfo ( block ( * i ) ) ;
@ -597,7 +599,7 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
h256s newTransactionAddresses ;
{
bytes blockBytes ;
RLP blockRLP ( * i = = _bi . hash ( ) ? _block : ( blockBytes = block ( * i ) ) ) ;
RLP blockRLP ( * i = = _block . info . hash ( ) ? _block . block : & ( blockBytes = block ( * i ) ) ) ;
TransactionAddress ta ;
ta . blockHash = tbi . hash ( ) ;
for ( ta . index = 0 ; ta . index < blockRLP [ 1 ] . itemCount ( ) ; + + ta . index )
@ -613,17 +615,17 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
// FINALLY! change our best hash.
{
newLastBlockHash = _bi . hash ( ) ;
newLastBlockNumber = ( unsigned ) _bi . number ;
newLastBlockHash = _block . info . hash ( ) ;
newLastBlockNumber = ( unsigned ) _block . info . number ;
}
clog ( BlockChainNote ) < < " Imported and best " < < td < < " (# " < < _bi . number < < " ). Has " < < ( details ( _bi . parentHash ) . children . size ( ) - 1 ) < < " siblings. Route: " < < route ;
clog ( BlockChainNote ) < < " Imported and best " < < td < < " (# " < < _block . info . number < < " ). Has " < < ( details ( _block . info . parentHash ) . children . size ( ) - 1 ) < < " siblings. Route: " < < route ;
StructuredLogger : : chainNewHead (
_bi . headerHash ( WithoutNonce ) . abridged ( ) ,
_bi . nonce . abridged ( ) ,
_block . info . headerHash ( WithoutNonce ) . abridged ( ) ,
_block . info . nonce . abridged ( ) ,
currentHash ( ) . abridged ( ) ,
_bi . parentHash . abridged ( )
_block . info . parentHash . abridged ( )
) ;
}
else
@ -634,24 +636,26 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
m_blocksDB - > Write ( m_writeOptions , & blocksBatch ) ;
m_extrasDB - > Write ( m_writeOptions , & extrasBatch ) ;
if ( isKnown ( _bi . hash ( ) ) & & ! details ( _bi . hash ( ) ) )
# if ETH_PARANOIA || !ETH_TRUE
if ( isKnown ( _block . info . hash ( ) ) & & ! details ( _block . info . hash ( ) ) )
{
clog ( BlockChainDebug ) < < " Known block just inserted has no details. " ;
clog ( BlockChainDebug ) < < " Block: " < < _bi ;
clog ( BlockChainDebug ) < < " Block: " < < _block . info ;
clog ( BlockChainDebug ) < < " DATABASE CORRUPTION: CRITICAL FAILURE " ;
exit ( - 1 ) ;
}
try {
State canary ( _db , * this , _bi . hash ( ) , ImportRequirements : : DontHave ) ;
State canary ( _db , * this , _block . info . hash ( ) , ImportRequirements : : DontHave ) ;
}
catch ( . . . )
{
clog ( BlockChainDebug ) < < " Failed to initialise State object form imported block. " ;
clog ( BlockChainDebug ) < < " Block: " < < _bi ;
clog ( BlockChainDebug ) < < " Block: " < < _block . info ;
clog ( BlockChainDebug ) < < " DATABASE CORRUPTION: CRITICAL FAILURE " ;
exit ( - 1 ) ;
}
# endif
if ( m_lastBlockHash ! = newLastBlockHash )
DEV_WRITE_GUARDED ( x_lastBlockHash )
@ -667,12 +671,16 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
# if ETH_TIMED_IMPORTS
checkBest = t . elapsed ( ) ;
cnote < < " Import took: " < < total . elapsed ( ) ;
cnote < < " preliminaryChecks: " < < preliminaryChecks ;
cnote < < " enactment: " < < enactment ;
cnote < < " collation: " < < collation ;
cnote < < " writing: " < < writing ;
cnote < < " checkBest: " < < checkBest ;
if ( total . elapsed ( ) > 1.0 )
{
cnote < < " SLOW IMPORT: " < < _block . info . hash ( ) ;
cnote < < " Import took: " < < total . elapsed ( ) ;
cnote < < " preliminaryChecks: " < < preliminaryChecks ;
cnote < < " enactment: " < < enactment ;
cnote < < " collation: " < < collation ;
cnote < < " writing: " < < writing ;
cnote < < " checkBest: " < < checkBest ;
}
# endif
if ( ! route . empty ( ) )
@ -1054,3 +1062,63 @@ bytes BlockChain::block(h256 const& _hash) const
return m_blocks [ _hash ] ;
}
VerifiedBlockRef BlockChain : : verifyBlock ( bytes const & _block , function < void ( Exception & ) > const & _onBad , ImportRequirements : : value _ir )
{
VerifiedBlockRef res ;
try
{
Strictness strictNess = Strictness : : CheckEverything ;
if ( _ir & ~ ImportRequirements : : ValidNonce )
strictNess = Strictness : : IgnoreNonce ;
res . info . populate ( _block , strictNess ) ;
res . info . verifyInternals ( & _block ) ;
}
catch ( Exception & ex )
{
ex < < errinfo_now ( time ( 0 ) ) ;
ex < < errinfo_block ( _block ) ;
if ( _onBad )
_onBad ( ex ) ;
throw ;
}
RLP r ( _block ) ;
unsigned i = 0 ;
for ( auto const & uncle : r [ 2 ] )
{
try
{
BlockInfo ( ) . populateFromHeader ( RLP ( uncle . data ( ) ) , CheckEverything ) ;
}
catch ( Exception & ex )
{
ex < < errinfo_uncleIndex ( i ) ;
ex < < errinfo_now ( time ( 0 ) ) ;
ex < < errinfo_block ( _block ) ;
if ( _onBad )
_onBad ( ex ) ;
throw ;
}
+ + i ;
}
i = 0 ;
for ( auto const & tr : r [ 1 ] )
{
try
{
res . transactions . push_back ( Transaction ( tr . data ( ) , CheckTransaction : : Everything ) ) ;
}
catch ( Exception & ex )
{
ex < < errinfo_transactionIndex ( i ) ;
ex < < errinfo_block ( _block ) ;
throw ;
}
+ + i ;
}
res . block = bytesConstRef ( & _block ) ;
return move ( res ) ;
}