@ -36,9 +36,10 @@ using namespace p2p;
EthereumPeer : : EthereumPeer ( Session * _s , HostCapabilityFace * _h , unsigned _i ) :
EthereumPeer : : EthereumPeer ( Session * _s , HostCapabilityFace * _h , unsigned _i ) :
Capability ( _s , _h , _i ) ,
Capability ( _s , _h , _i ) ,
m_sub ( host ( ) - > m_man )
m_sub ( host ( ) - > m_man ) ,
m_hashSub ( host ( ) - > m_hashMan )
{
{
transition ( Asking : : State ) ;
requestState ( ) ;
}
}
EthereumPeer : : ~ EthereumPeer ( )
EthereumPeer : : ~ EthereumPeer ( )
@ -50,7 +51,7 @@ EthereumPeer::~EthereumPeer()
void EthereumPeer : : abortSync ( )
void EthereumPeer : : abortSync ( )
{
{
if ( isSyncing ( ) )
if ( isSyncing ( ) )
transition ( Asking : : Nothing , true ) ;
setIdle ( ) ;
}
}
EthereumHost * EthereumPeer : : host ( ) const
EthereumHost * EthereumPeer : : host ( ) const
@ -74,141 +75,87 @@ string toString(Asking _a)
return " ? " ;
return " ? " ;
}
}
void EthereumPeer : : transition ( Asking _a , bool _force , bool _needHelp )
{
clog ( NetMessageSummary ) < < " Transition! " < < : : toString ( _a ) < < " from " < < : : toString ( m_asking ) < < " , " < < ( isSyncing ( ) ? " syncing " : " holding " ) < < ( needsSyncing ( ) ? " & needed " : " " ) ;
if ( m_asking = = Asking : : State & & _a ! = Asking : : State )
void EthereumPeer : : setIdle ( )
m_requireTransactions = true ;
{
if ( m_asking = = Asking : : Blocks )
{
clog ( NetNote ) < < " Finishing blocks fetch... " ;
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
m_sub . doneFetch ( ) ;
m_hashSub . doneFetch ( ) ;
RLPStream s ;
setAsking ( Asking : : Nothing ) ;
}
else if ( m_asking = = Asking : : Hashes )
{
clog ( NetNote ) < < " Finishing hashes fetch... " ;
if ( _a = = Asking : : State )
setAsking ( Asking : : Nothing ) ;
}
else if ( m_asking = = Asking : : State )
{
{
if ( m_asking = = Asking : : Nothing )
setAsking ( Asking : : Nothing ) ;
}
}
void EthereumPeer : : requestState ( )
{
{
setAsking ( Asking : : State , false ) ;
if ( m_asking ! = Asking : : Nothing )
clog ( NetWarn ) < < " Bad state: requesting state should be the first action " ;
setAsking ( Asking : : State ) ;
RLPStream s ;
prep ( s , StatusPacket , 5 )
prep ( s , StatusPacket , 5 )
< < host ( ) - > protocolVersion ( )
< < host ( ) - > protocolVersion ( ) - 1
< < host ( ) - > networkId ( )
< < host ( ) - > networkId ( )
< < host ( ) - > m_chain . details ( ) . totalDifficulty
< < host ( ) - > m_chain . details ( ) . totalDifficulty
< < host ( ) - > m_chain . currentHash ( )
< < host ( ) - > m_chain . currentHash ( )
< < host ( ) - > m_chain . genesisHash ( ) ;
< < host ( ) - > m_chain . genesisHash ( ) ;
sealAndSend ( s ) ;
sealAndSend ( s ) ;
return ;
}
}
}
else if ( _a = = Asking : : Hashes )
{
if ( m_asking = = Asking : : State | | m_asking = = Asking : : Nothing )
{
if ( isSyncing ( ) )
clog ( NetWarn ) < < " Bad state: not asking for Hashes, yet syncing! " ;
m_syncingLatestHash = m_latestHash ;
void EthereumPeer : : requestHashes ( )
m_syncingTotalDifficulty = m_totalDifficulty ;
resetNeedsSyncing ( ) ;
setAsking ( _a , true ) ;
prep ( s , GetBlockHashesPacket , 2 ) < < m_syncingLatestHash < < c_maxHashesAsk ;
m_syncingNeededBlocks = h256s ( 1 , m_syncingLatestHash ) ;
sealAndSend ( s ) ;
return ;
}
else if ( m_asking = = Asking : : Hashes )
{
{
if ( ! isSyncing ( ) )
assert ( m_asking ! = Asking : : Blocks ) ;
clog ( NetWarn ) < < " Bad state: asking for Hashes yet not syncing! " ;
m_syncHashNumber = m_hashSub . nextFetch ( c_maxBlocksAsk ) ;
setAsking ( Asking : : Hashes ) ;
setAsking ( _a , true ) ;
RLPStream s ;
prep ( s , GetBlockHashesPacket , 2 ) < < m_syncingLastReceived Hash < < c_maxHashesAsk ;
prep ( s , GetBlockHashesPacket , 2 ) < < m_syncHashNumber < < c_maxHashesAsk ;
sealAndSend ( s ) ;
sealAndSend ( s ) ;
return ;
}
}
}
else if ( _a = = Asking : : Blocks )
{
if ( m_asking = = Asking : : Hashes )
{
if ( ! isSyncing ( ) )
clog ( NetWarn ) < < " Bad state: asking for Hashes yet not syncing! " ;
if ( shouldGrabBlocks ( ) )
{
clog ( NetNote ) < < " Difficulty of hashchain HIGHER. Grabbing " < < m_syncingNeededBlocks . size ( ) < < " blocks [latest now " < < m_syncingLatestHash < < " , was " < < host ( ) - > m_latestBlockSent < < " ] " ;
host ( ) - > m_man . resetToChain ( m_syncingNeededBlocks ) ;
void EthereumPeer : : requestHashes ( h256 const & _lastHash )
// host()->m_latestBlockSent = m_syncingLatestHash;
}
else
{
{
clog ( NetNote ) < < " Difficulty of hashchain not HIGHER. Ignoring. " ;
assert ( m_asking ! = Asking : : Blocks ) ;
m_syncingLatestHash = h256 ( ) ;
setAsking ( Asking : : Hashes ) ;
setAsking ( Asking : : Nothing , false ) ;
RLPStream s ;
return ;
prep ( s , GetBlockHashesPacket , 2 ) < < _lastHash < < c_maxHashesAsk ;
}
sealAndSend ( s ) ;
}
}
// run through into...
if ( m_asking = = Asking : : Nothing | | m_asking = = Asking : : Hashes | | m_asking = = Asking : : Blocks )
void EthereumPeer : : requestBlocks ( )
{
{
// Looks like it's the best yet for total difficulty. Set to download.
// Looks like it's the best yet for total difficulty. Set to download.
setAsking ( Asking : : Blocks , isSyncing ( ) , _needHelp ) ; // will kick off other peers to help if available.
setAsking ( Asking : : Blocks ) ; // will kick off other peers to help if available.
auto blocks = m_sub . nextFetch ( c_maxBlocksAsk ) ;
auto blocks = m_sub . nextFetch ( c_maxBlocksAsk ) ;
if ( blocks . size ( ) )
if ( blocks . size ( ) )
{
{
RLPStream s ;
prep ( s , GetBlocksPacket , blocks . size ( ) ) ;
prep ( s , GetBlocksPacket , blocks . size ( ) ) ;
for ( auto const & i : blocks )
for ( auto const & i : blocks )
s < < i ;
s < < i ;
sealAndSend ( s ) ;
sealAndSend ( s ) ;
}
}
else
else
transition ( Asking : : Nothing ) ;
setIdle ( ) ;
return ;
return ;
}
}
}
else if ( _a = = Asking : : Nothing )
{
if ( m_asking = = Asking : : Blocks )
{
clog ( NetNote ) < < " Finishing blocks fetch... " ;
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
if ( isSyncing ( ) )
host ( ) - > noteDoneBlocks ( this , _force ) ;
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
m_sub . doneFetch ( ) ;
setAsking ( Asking : : Nothing , false ) ;
}
else if ( m_asking = = Asking : : Hashes )
{
clog ( NetNote ) < < " Finishing hashes fetch... " ;
setAsking ( Asking : : Nothing , false ) ;
}
else if ( m_asking = = Asking : : State )
{
setAsking ( Asking : : Nothing , false ) ;
// Just got the state - should check to see if we can be of help downloading the chain if any.
// Otherwise, should put ourselves up for sync.
setNeedsSyncing ( m_latestHash , m_totalDifficulty ) ;
}
// Otherwise it's fine. We don't care if it's Nothing->Nothing.
return ;
}
clog ( NetWarn ) < < " Invalid state transition: " < < : : toString ( _a ) < < " from " < < : : toString ( m_asking ) < < " , " < < ( isSyncing ( ) ? " syncing " : " holding " ) < < ( needsSyncing ( ) ? " & needed " : " " ) ;
}
void EthereumPeer : : setAsking ( Asking _a , bool _isSyncing , bool _needHelp )
void EthereumPeer : : setAsking ( Asking _a )
{
{
bool changedAsking = ( m_asking ! = _a ) ;
m_asking = _a ;
m_asking = _a ;
if ( _isSyncing ! = ( host ( ) - > m_syncer = = this ) | | ( _isSyncing & & changedAsking ) )
if ( ! isSyncing ( ) )
host ( ) - > changeSyncer ( _isSyncing ? this : nullptr , _needHelp ) ;
if ( ! _isSyncing )
{
{
m_syncingLatestHash = h256 ( ) ;
m_syncingLatestHash = h256 ( ) ;
m_syncingTotalDifficulty = 0 ;
m_syncingTotalDifficulty = 0 ;
@ -241,57 +188,7 @@ void EthereumPeer::tick()
bool EthereumPeer : : isSyncing ( ) const
bool EthereumPeer : : isSyncing ( ) const
{
{
return host ( ) - > m_syncer = = this ;
return m_asking ! = Asking : : Nothing ;
}
bool EthereumPeer : : shouldGrabBlocks ( ) const
{
auto td = m_syncingTotalDifficulty ;
auto lh = m_syncingLatestHash ;
auto ctd = host ( ) - > m_chain . details ( ) . totalDifficulty ;
if ( m_syncingNeededBlocks . empty ( ) )
return false ;
clog ( NetNote ) < < " Should grab blocks? " < < td < < " vs " < < ctd < < " ; " < < m_syncingNeededBlocks . size ( ) < < " blocks, ends " < < m_syncingNeededBlocks . back ( ) ;
if ( td < ctd | | ( td = = ctd & & host ( ) - > m_chain . currentHash ( ) = = lh ) )
return false ;
return true ;
}
void EthereumPeer : : attemptSync ( )
{
if ( m_asking ! = Asking : : Nothing )
{
clog ( NetAllDetail ) < < " Can't synced with this peer - outstanding asks. " ;
return ;
}
// if already done this, then ignore.
if ( ! needsSyncing ( ) )
{
clog ( NetAllDetail ) < < " Already synced with this peer. " ;
return ;
}
h256 c = host ( ) - > m_chain . currentHash ( ) ;
unsigned n = host ( ) - > m_chain . number ( ) ;
u256 td = host ( ) - > m_chain . details ( ) . totalDifficulty ;
clog ( NetAllDetail ) < < " Attempt chain-grab? Latest: " < < c < < " , number: " < < n < < " , TD: " < < td < < " versus " < < m_totalDifficulty ;
if ( td > = m_totalDifficulty )
{
clog ( NetAllDetail ) < < " No. Our chain is better. " ;
resetNeedsSyncing ( ) ;
transition ( Asking : : Nothing ) ;
}
else
{
clog ( NetAllDetail ) < < " Yes. Their chain is better. " ;
transition ( Asking : : Hashes ) ;
}
}
}
bool EthereumPeer : : interpret ( unsigned _id , RLP const & _r )
bool EthereumPeer : : interpret ( unsigned _id , RLP const & _r )
@ -303,27 +200,16 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
case StatusPacket :
case StatusPacket :
{
{
m_protocolVersion = _r [ 0 ] . toInt < unsigned > ( ) ;
m_protocolVersion = _r [ 0 ] . toInt < unsigned > ( ) ;
if ( ! ! session ( ) - > cap < EthereumPeer > ( EthereumHost : : staticVersion ( ) ) )
m_protocolVersion = host ( ) - > protocolVersion ( ) ;
m_networkId = _r [ 1 ] . toInt < u256 > ( ) ;
m_networkId = _r [ 1 ] . toInt < u256 > ( ) ;
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
m_totalDifficulty = _r [ 2 ] . toInt < u256 > ( ) ;
m_totalDifficulty = _r [ 2 ] . toInt < u256 > ( ) ;
m_latestHash = _r [ 3 ] . toHash < h256 > ( ) ;
m_latestHash = _r [ 3 ] . toHash < h256 > ( ) ;
auto genesisHash = _r [ 4 ] . toHash < h256 > ( ) ;
m_genesisHash = _r [ 4 ] . toHash < h256 > ( ) ;
clog ( NetMessageSummary ) < < " Status: " < < m_protocolVersion < < " / " < < m_networkId < < " / " < < m_genesisHash < < " , TD: " < < m_totalDifficulty < < " = " < < m_latestHash ;
clog ( NetMessageSummary ) < < " Status: " < < m_protocolVersion < < " / " < < m_networkId < < " / " < < genesisHash < < " , TD: " < < m_totalDifficulty < < " = " < < m_latestHash ;
host ( ) - > onPeerState ( this ) ;
if ( genesisHash ! = host ( ) - > m_chain . genesisHash ( ) )
disable ( " Invalid genesis hash " ) ;
else if ( m_protocolVersion ! = host ( ) - > protocolVersion ( ) )
disable ( " Invalid protocol version. " ) ;
else if ( m_networkId ! = host ( ) - > networkId ( ) )
disable ( " Invalid network identifier. " ) ;
else if ( session ( ) - > info ( ) . clientVersion . find ( " /v0.7.0/ " ) ! = string : : npos )
disable ( " Blacklisted client version. " ) ;
else if ( host ( ) - > isBanned ( session ( ) - > id ( ) ) )
disable ( " Peer banned for previous bad behaviour. " ) ;
else
transition ( Asking : : Nothing ) ;
break ;
break ;
}
}
case TransactionsPacket :
case TransactionsPacket :
@ -356,6 +242,31 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
}
case GetBlockHashesPacket :
case GetBlockHashesPacket :
{
{
if ( m_protocolVersion = = host ( ) - > protocolVersion ( ) )
{
u256 number256 = _r [ 0 ] . toInt < u256 > ( ) ;
unsigned number = ( unsigned ) number256 ;
unsigned limit = _r [ 1 ] . toInt < unsigned > ( ) ;
clog ( NetMessageSummary ) < < " GetBlockHashes ( " < < number < < " - " < < number + limit < < " ) " ;
RLPStream s ;
if ( number < = host ( ) - > m_chain . number ( ) )
{
unsigned c = min < unsigned > ( host ( ) - > m_chain . number ( ) - number + 1 , limit ) ;
prep ( s , BlockHashesPacket , c ) ;
for ( unsigned n = number ; n < number + c ; n + + )
{
h256 p = host ( ) - > m_chain . numberHash ( n ) ;
s < < p ;
}
}
else
prep ( s , BlockHashesPacket , 0 ) ;
sealAndSend ( s ) ;
addRating ( 0 ) ;
}
else
{
// Support V60 protocol
h256 later = _r [ 0 ] . toHash < h256 > ( ) ;
h256 later = _r [ 0 ] . toHash < h256 > ( ) ;
unsigned limit = _r [ 1 ] . toInt < unsigned > ( ) ;
unsigned limit = _r [ 1 ] . toInt < unsigned > ( ) ;
clog ( NetMessageSummary ) < < " GetBlockHashes ( " < < limit < < " entries, " < < later < < " ) " ;
clog ( NetMessageSummary ) < < " GetBlockHashes ( " < < limit < < " entries, " < < later < < " ) " ;
@ -369,6 +280,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
s < < p ;
s < < p ;
sealAndSend ( s ) ;
sealAndSend ( s ) ;
addRating ( 0 ) ;
addRating ( 0 ) ;
}
break ;
break ;
}
}
case BlockHashesPacket :
case BlockHashesPacket :
@ -383,40 +295,24 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
}
if ( itemCount = = 0 )
if ( itemCount = = 0 )
{
{
transition ( Asking : : Blocks ) ;
host ( ) - > onPeerDoneHashes ( this , false ) ;
return true ;
return true ;
}
}
unsigned knowns = 0 ;
h256s hashes ( itemCount ) ;
unsigned unknowns = 0 ;
for ( unsigned i = 0 ; i < itemCount ; + + i )
for ( unsigned i = 0 ; i < itemCount ; + + i )
{
{
addRating ( 1 ) ;
hashes [ i ] = _r [ i ] . toHash < h256 > ( ) ;
auto h = _r [ i ] . toHash < h256 > ( ) ;
m_hashSub . noteHash ( m_syncHashNumber + i , 1 ) ;
auto status = host ( ) - > m_bq . blockStatus ( h ) ;
if ( status = = QueueStatus : : Importing | | status = = QueueStatus : : Ready | | host ( ) - > m_chain . isKnown ( h ) )
{
clog ( NetMessageSummary ) < < " block hash ready: " < < h < < " . Start blocks download... " ;
transition ( Asking : : Blocks ) ;
return true ;
}
else if ( status = = QueueStatus : : Bad )
{
cwarn < < " block hash bad! " < < h < < " . Bailing... " ;
transition ( Asking : : Nothing ) ;
return true ;
}
}
else if ( status = = QueueStatus : : Unknown )
if ( m_protocolVersion = = host ( ) - > protocolVersion ( ) )
{
{
unknowns + + ;
//v61, report hashes ordered by number
m_syncingNeededBlocks . push_back ( h ) ;
host ( ) - > onPeerHashes ( this , m_syncHashNumber , hashes ) ;
}
}
else
else
knowns + + ;
host ( ) - > onPeerHashes ( this , hashes ) ;
m_syncingLastReceivedHash = h ;
m_syncHashNumber + = itemCount ;
}
clog ( NetMessageSummary ) < < knowns < < " knowns, " < < unknowns < < " unknowns; now at " < < m_syncingLastReceivedHash ;
// run through - ask for more.
transition ( Asking : : Hashes ) ;
break ;
break ;
}
}
case GetBlocksPacket :
case GetBlocksPacket :
@ -455,74 +351,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
}
case BlocksPacket :
case BlocksPacket :
{
{
unsigned itemCount = _r . itemCount ( ) ;
host ( ) - > onPeerBlocks ( this , _r ) ;
clog ( NetMessageSummary ) < < " Blocks ( " < < dec < < itemCount < < " entries) " < < ( itemCount ? " " : " : NoMoreBlocks " ) ;
if ( m_asking ! = Asking : : Blocks )
clog ( NetWarn ) < < " Unexpected Blocks received! " ;
if ( itemCount = = 0 )
{
// Got to this peer's latest block - just give up.
transition ( Asking : : Nothing ) ;
break ;
}
unsigned success = 0 ;
unsigned future = 0 ;
unsigned unknown = 0 ;
unsigned got = 0 ;
unsigned repeated = 0 ;
for ( unsigned i = 0 ; i < itemCount ; + + i )
{
auto h = BlockInfo : : headerHash ( _r [ i ] . data ( ) ) ;
if ( m_sub . noteBlock ( h ) )
{
addRating ( 10 ) ;
switch ( host ( ) - > m_bq . import ( _r [ i ] . data ( ) , host ( ) - > m_chain ) )
{
case ImportResult : : Success :
success + + ;
break ;
case ImportResult : : Malformed :
case ImportResult : : BadChain :
disable ( " Malformed block received. " ) ;
return true ;
case ImportResult : : FutureTime :
future + + ;
break ;
case ImportResult : : AlreadyInChain :
case ImportResult : : AlreadyKnown :
got + + ;
break ;
case ImportResult : : UnknownParent :
unknown + + ;
break ;
default : ;
}
}
else
{
addRating ( 0 ) ; // -1?
repeated + + ;
}
}
clog ( NetMessageSummary ) < < dec < < success < < " imported OK, " < < unknown < < " with unknown parents, " < < future < < " with future timestamps, " < < got < < " already known, " < < repeated < < " repeats received. " ;
if ( m_asking = = Asking : : Blocks )
{
if ( ! got )
transition ( Asking : : Blocks ) ;
else
transition ( Asking : : Nothing ) ;
}
break ;
break ;
}
}
case NewBlockPacket :
case NewBlockPacket :
@ -571,39 +400,16 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
clog ( NetMessageSummary ) < < " Ignoring since we're already downloading. " ;
clog ( NetMessageSummary ) < < " Ignoring since we're already downloading. " ;
else
else
{
{
unsigned knowns = 0 ;
unsigned unknowns = 0 ;
unsigned itemCount = _r . itemCount ( ) ;
unsigned itemCount = _r . itemCount ( ) ;
clog ( NetMessageSummary ) < < " BlockHashes ( " < < dec < < itemCount < < " entries) " < < ( itemCount ? " " : " : NoMoreHashes " ) ;
h256s hashes ( itemCount ) ;
for ( unsigned i = 0 ; i < itemCount ; + + i )
for ( unsigned i = 0 ; i < itemCount ; + + i )
{
hashes [ i ] = _r [ i ] . toHash < h256 > ( ) ;
addRating ( 1 ) ;
auto h = _r [ i ] . toHash < h256 > ( ) ;
DEV_GUARDED ( x_knownBlocks )
m_knownBlocks . insert ( h ) ;
auto status = host ( ) - > m_bq . blockStatus ( h ) ;
if ( status = = QueueStatus : : Importing | | status = = QueueStatus : : Ready | | host ( ) - > m_chain . isKnown ( h ) )
knowns + + ;
else if ( status = = QueueStatus : : Bad )
{
cwarn < < " block hash bad! " < < h < < " . Bailing... " ;
return true ;
}
else if ( status = = QueueStatus : : Unknown )
{
unknowns + + ;
m_syncingNeededBlocks . push_back ( h ) ;
}
else
knowns + + ;
}
clog ( NetMessageSummary ) < < knowns < < " knowns, " < < unknowns < < " unknowns " ;
if ( unknowns > 0 )
{
clog ( NetNote ) < < " Not syncing and new block hash discovered: syncing without help. " ;
clog ( NetNote ) < < " Not syncing and new block hash discovered: syncing without help. " ;
host ( ) - > m_man . resetToChain ( m_syncingNeededBlocks ) ;
host ( ) - > onPeerHashes ( this , hashes ) ;
host ( ) - > changeSyncer ( this , false ) ;
host ( ) - > onPeerDoneHashes ( this , true ) ;
transition ( Asking : : Blocks , false , false ) ; // TODO: transaction(Asking::NewBlocks, false)
}
return true ;
return true ;
}
}
break ;
break ;