2015-02-25 14:34:12 +02:00
package blockpool
import (
"math/big"
"math/rand"
"sort"
"sync"
"time"
2015-03-20 01:33:52 +02:00
"github.com/ethereum/go-ethereum/common"
2015-02-25 14:34:12 +02:00
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/errs"
)
2015-03-20 01:14:08 +02:00
// the blockpool's model of a peer
2015-02-25 14:34:12 +02:00
type peer struct {
lock sync . RWMutex
// last known blockchain status
td * big . Int
2015-04-10 18:31:00 +03:00
tdAdvertised bool
2015-03-17 00:10:26 +02:00
currentBlockHash common . Hash
2015-02-25 14:34:12 +02:00
currentBlock * types . Block
2015-03-17 00:10:26 +02:00
parentHash common . Hash
2015-02-25 14:34:12 +02:00
headSection * section
id string
// peer callbacks
2015-03-17 00:10:26 +02:00
requestBlockHashes func ( common . Hash ) error
requestBlocks func ( [ ] common . Hash ) error
2015-02-25 14:34:12 +02:00
peerError func ( * errs . Error )
errors * errs . Errors
2015-03-17 00:10:26 +02:00
sections [ ] common . Hash
2015-02-25 14:34:12 +02:00
// channels to push new head block and head section for peer a
currentBlockC chan * types . Block
headSectionC chan * section
2015-03-03 21:06:15 +02:00
// channels to signal peer switch and peer quit to section processes
2015-02-25 14:34:12 +02:00
idleC chan bool
switchC chan bool
2015-03-05 20:33:57 +02:00
bp * BlockPool
2015-02-25 14:34:12 +02:00
// timers for head section process
blockHashesRequestTimer <- chan time . Time
blocksRequestTimer <- chan time . Time
2015-03-20 22:52:29 +02:00
headInfoTimer <- chan time . Time
bestIdleTimer <- chan time . Time
2015-02-25 14:34:12 +02:00
2015-03-20 00:46:54 +02:00
addToBlacklist func ( id string )
2015-02-25 14:34:12 +02:00
idle bool
}
// peers is the component keeping a record of peers in a hashmap
//
type peers struct {
2015-04-09 15:58:11 +03:00
lock sync . RWMutex
bllock sync . Mutex
2015-02-25 14:34:12 +02:00
2015-03-20 00:46:54 +02:00
bp * BlockPool
errors * errs . Errors
peers map [ string ] * peer
best * peer
status * status
blacklist map [ string ] time . Time
2015-02-25 14:34:12 +02:00
}
// peer constructor
func ( self * peers ) newPeer (
td * big . Int ,
2015-03-17 00:10:26 +02:00
currentBlockHash common . Hash ,
2015-02-25 14:34:12 +02:00
id string ,
2015-03-17 00:10:26 +02:00
requestBlockHashes func ( common . Hash ) error ,
requestBlocks func ( [ ] common . Hash ) error ,
2015-02-25 14:34:12 +02:00
peerError func ( * errs . Error ) ,
) ( p * peer ) {
p = & peer {
errors : self . errors ,
td : td ,
currentBlockHash : currentBlockHash ,
id : id ,
requestBlockHashes : requestBlockHashes ,
requestBlocks : requestBlocks ,
peerError : peerError ,
currentBlockC : make ( chan * types . Block ) ,
headSectionC : make ( chan * section ) ,
bp : self . bp ,
idle : true ,
2015-03-20 00:46:54 +02:00
addToBlacklist : self . addToBlacklist ,
2015-02-25 14:34:12 +02:00
}
// at creation the peer is recorded in the peer pool
self . peers [ id ] = p
return
}
2015-03-20 00:46:54 +02:00
// dispatches an error to a peer if still connected, adds it to the blacklist
2015-02-25 14:34:12 +02:00
func ( self * peers ) peerError ( id string , code int , format string , params ... interface { } ) {
self . lock . RLock ( )
peer , ok := self . peers [ id ]
2015-03-20 00:46:54 +02:00
self . lock . RUnlock ( )
2015-02-25 14:34:12 +02:00
if ok {
peer . addError ( code , format , params )
}
2015-03-20 00:46:54 +02:00
self . addToBlacklist ( id )
}
2015-03-20 01:14:08 +02:00
// record time of offence in blacklist to implement suspension for PeerSuspensionInterval
2015-03-20 00:46:54 +02:00
func ( self * peers ) addToBlacklist ( id string ) {
2015-04-09 15:58:11 +03:00
self . bllock . Lock ( )
defer self . bllock . Unlock ( )
2015-03-20 00:46:54 +02:00
self . blacklist [ id ] = time . Now ( )
}
2015-04-08 22:33:54 +03:00
// suspended checks if peer is still suspended, caller should hold peers.lock
2015-03-20 00:46:54 +02:00
func ( self * peers ) suspended ( id string ) ( s bool ) {
2015-04-09 15:58:11 +03:00
self . bllock . Lock ( )
defer self . bllock . Unlock ( )
2015-03-20 00:46:54 +02:00
if suspendedAt , ok := self . blacklist [ id ] ; ok {
if s = suspendedAt . Add ( self . bp . Config . PeerSuspensionInterval ) . After ( time . Now ( ) ) ; ! s {
// no longer suspended, delete entry
delete ( self . blacklist , id )
}
}
return
2015-02-25 14:34:12 +02:00
}
func ( self * peer ) addError ( code int , format string , params ... interface { } ) {
err := self . errors . New ( code , format , params ... )
self . peerError ( err )
2015-03-20 00:46:54 +02:00
self . addToBlacklist ( self . id )
2015-02-25 14:34:12 +02:00
}
2015-04-02 18:30:48 +03:00
// caller must hold peer lock
2015-04-10 18:31:00 +03:00
func ( self * peer ) setChainInfo ( td * big . Int , currentBlockHash common . Hash ) {
self . lock . Lock ( )
defer self . lock . Unlock ( )
if self . currentBlockHash != currentBlockHash {
previousBlockHash := self . currentBlockHash
plog . Debugf ( "addPeer: Update peer <%s> with td %v and current block %s (was %v)" , self . id , td , hex ( currentBlockHash ) , hex ( previousBlockHash ) )
self . td = td
self . currentBlockHash = currentBlockHash
self . currentBlock = nil
self . parentHash = common . Hash { }
self . headSection = nil
}
self . tdAdvertised = true
2015-02-25 14:34:12 +02:00
}
2015-04-10 18:31:00 +03:00
func ( self * peer ) setChainInfoFromBlock ( block * types . Block ) ( td * big . Int , currentBlockHash common . Hash ) {
self . lock . Lock ( )
defer self . lock . Unlock ( )
hash := block . Hash ( )
// this happens when block came in a newblock message but
// also if sent in a blockmsg (for instance, if we requested, only if we
// dont apply on blockrequests the restriction of flood control)
currentBlockHash = self . currentBlockHash
if currentBlockHash == hash && self . currentBlock == nil {
// signal to head section process
plog . DebugDetailf ( "AddBlock: head block %s for peer <%s> (head: %s) received\n" , hex ( hash ) , self . id , hex ( currentBlockHash ) )
select {
case self . currentBlockC <- block :
case <- self . switchC :
}
return self . td , currentBlockHash
} else {
plog . DebugDetailf ( "AddBlock: head block %s for peer <%s> (head: %s) already known" , hex ( hash ) , self . id , hex ( currentBlockHash ) )
return nil , currentBlockHash
}
}
// this will use the TD given by the first peer to update peer td, this helps second best peer selection
// :FIXME: node
func ( self * peer ) setChainInfoFromNode ( n * node ) {
2015-02-25 14:34:12 +02:00
// in case best peer is lost
2015-04-10 18:31:00 +03:00
block := n . block
hash := block . Hash ( )
if n . td != nil && n . td . Cmp ( self . td ) > 0 {
plog . DebugDetailf ( "AddBlock: update peer <%s> - head: %v->%v - TD: %v->%v" , self . id , hex ( self . currentBlockHash ) , hex ( hash ) , self . td , n . td )
self . td = n . td
2015-02-25 14:34:12 +02:00
self . currentBlockHash = block . Hash ( )
self . parentHash = block . ParentHash ( )
self . currentBlock = block
self . headSection = nil
}
}
2015-03-20 01:14:08 +02:00
// distribute block request among known peers
2015-03-17 00:10:26 +02:00
func ( self * peers ) requestBlocks ( attempts int , hashes [ ] common . Hash ) {
2015-02-25 14:34:12 +02:00
self . lock . RLock ( )
2015-04-07 20:29:35 +03:00
2015-02-25 14:34:12 +02:00
defer self . lock . RUnlock ( )
peerCount := len ( self . peers )
// on first attempt use the best peer
2015-04-02 18:30:48 +03:00
if attempts == 0 && self . best != nil {
2015-02-25 14:34:12 +02:00
plog . DebugDetailf ( "request %v missing blocks from best peer <%s>" , len ( hashes ) , self . best . id )
self . best . requestBlocks ( hashes )
return
}
repetitions := self . bp . Config . BlocksRequestRepetition
if repetitions > peerCount {
repetitions = peerCount
}
i := 0
indexes := rand . Perm ( peerCount ) [ 0 : repetitions ]
sort . Ints ( indexes )
plog . DebugDetailf ( "request %v missing blocks from %v/%v peers" , len ( hashes ) , repetitions , peerCount )
for _ , peer := range self . peers {
if i == indexes [ 0 ] {
plog . DebugDetailf ( "request length: %v" , len ( hashes ) )
plog . DebugDetailf ( "request %v missing blocks [%x/%x] from peer <%s>" , len ( hashes ) , hashes [ 0 ] [ : 4 ] , hashes [ len ( hashes ) - 1 ] [ : 4 ] , peer . id )
peer . requestBlocks ( hashes )
indexes = indexes [ 1 : ]
if len ( indexes ) == 0 {
break
}
}
i ++
}
self . bp . putHashSlice ( hashes )
}
// addPeer implements the logic for blockpool.AddPeer
2015-03-20 01:14:08 +02:00
// returns 2 bool values
// 1. true iff peer is promoted as best peer in the pool
// 2. true iff peer is still suspended
2015-02-25 14:34:12 +02:00
func ( self * peers ) addPeer (
td * big . Int ,
2015-03-17 00:10:26 +02:00
currentBlockHash common . Hash ,
2015-02-25 14:34:12 +02:00
id string ,
2015-03-17 00:10:26 +02:00
requestBlockHashes func ( common . Hash ) error ,
requestBlocks func ( [ ] common . Hash ) error ,
2015-02-25 14:34:12 +02:00
peerError func ( * errs . Error ) ,
2015-03-20 00:46:54 +02:00
) ( best bool , suspended bool ) {
2015-02-25 14:34:12 +02:00
2015-04-08 22:33:54 +03:00
self . lock . Lock ( )
defer self . lock . Unlock ( )
2015-03-17 00:10:26 +02:00
var previousBlockHash common . Hash
2015-03-20 00:46:54 +02:00
if self . suspended ( id ) {
suspended = true
return
}
2015-02-25 14:34:12 +02:00
p , found := self . peers [ id ]
if found {
2015-03-20 01:14:08 +02:00
// when called on an already connected peer, it means a newBlockMsg is received
// peer head info is updated
2015-04-10 18:31:00 +03:00
p . setChainInfo ( td , currentBlockHash )
// FIXME: only count the same block once
self . status . lock . Lock ( )
self . status . values . NewBlocks ++
self . status . lock . Unlock ( )
2015-02-25 14:34:12 +02:00
} else {
p = self . newPeer ( td , currentBlockHash , id , requestBlockHashes , requestBlocks , peerError )
self . status . lock . Lock ( )
self . status . peers [ id ] ++
self . status . values . NewBlocks ++
self . status . lock . Unlock ( )
plog . Debugf ( "addPeer: add new peer <%v> with td %v and current block %s" , id , td , hex ( currentBlockHash ) )
}
2015-03-20 01:14:08 +02:00
// check if peer's current head block is known
2015-02-25 14:34:12 +02:00
if self . bp . hasBlock ( currentBlockHash ) {
// peer not ahead
2015-03-02 22:39:21 +02:00
plog . Debugf ( "addPeer: peer <%v> with td %v and current block %s is behind" , id , td , hex ( currentBlockHash ) )
2015-03-20 00:46:54 +02:00
return false , false
2015-02-25 14:34:12 +02:00
}
if self . best == p {
// new block update for active current best peer -> request hashes
plog . Debugf ( "addPeer: <%s> already the best peer. Request new head section info from %s" , id , hex ( currentBlockHash ) )
2015-03-17 00:10:26 +02:00
if ( previousBlockHash != common . Hash { } ) {
2015-03-20 22:52:29 +02:00
plog . DebugDetailf ( "addPeer: <%s> head changed: %s -> %s " , id , hex ( previousBlockHash ) , hex ( currentBlockHash ) )
p . headSectionC <- nil
2015-02-25 14:34:12 +02:00
if entry := self . bp . get ( previousBlockHash ) ; entry != nil {
2015-03-20 22:52:29 +02:00
plog . DebugDetailf ( "addPeer: <%s> previous head : %v found in pool, activate" , id , hex ( previousBlockHash ) )
2015-04-08 22:33:54 +03:00
self . bp . activateChain ( entry . section , p , p . switchC , nil )
2015-02-25 14:34:12 +02:00
p . sections = append ( p . sections , previousBlockHash )
}
}
best = true
} else {
2015-03-20 01:14:08 +02:00
// baseline is our own TD
2015-03-20 00:53:15 +02:00
currentTD := self . bp . getTD ( )
2015-04-02 17:32:41 +03:00
bestpeer := self . best
if bestpeer != nil {
2015-04-08 22:33:54 +03:00
bestpeer . lock . RLock ( )
defer bestpeer . lock . RUnlock ( )
2015-02-25 14:34:12 +02:00
currentTD = self . best . td
}
if td . Cmp ( currentTD ) > 0 {
self . status . lock . Lock ( )
self . status . bestPeers [ p . id ] ++
self . status . lock . Unlock ( )
2015-03-20 00:53:15 +02:00
plog . Debugf ( "addPeer: peer <%v> (td: %v > current td %v) promoted best peer" , id , td , currentTD )
2015-04-02 17:32:41 +03:00
self . bp . switchPeer ( bestpeer , p )
2015-02-25 14:34:12 +02:00
self . best = p
best = true
}
}
2015-04-02 17:32:41 +03:00
2015-02-25 14:34:12 +02:00
return
}
// removePeer is called (via RemovePeer) by the eth protocol when the peer disconnects
func ( self * peers ) removePeer ( id string ) {
self . lock . Lock ( )
defer self . lock . Unlock ( )
p , found := self . peers [ id ]
if ! found {
return
}
delete ( self . peers , id )
2015-03-20 00:53:15 +02:00
plog . Debugf ( "addPeer: remove peer <%v> (td: %v)" , id , p . td )
2015-02-25 14:34:12 +02:00
// if current best peer is removed, need to find a better one
if self . best == p {
var newp * peer
2015-03-20 00:53:15 +02:00
// only peers that are ahead of us are considered
max := self . bp . getTD ( )
2015-02-25 14:34:12 +02:00
// peer with the highest self-acclaimed TD is chosen
for _ , pp := range self . peers {
if pp . td . Cmp ( max ) > 0 {
max = pp . td
newp = pp
}
}
if newp != nil {
self . status . lock . Lock ( )
self . status . bestPeers [ p . id ] ++
self . status . lock . Unlock ( )
2015-03-20 00:53:15 +02:00
plog . Debugf ( "addPeer: peer <%v> (td: %v) promoted best peer" , newp . id , newp . td )
2015-02-25 14:34:12 +02:00
} else {
plog . Warnln ( "addPeer: no suitable peers found" )
}
self . best = newp
self . bp . switchPeer ( p , newp )
}
}
2015-03-03 21:06:15 +02:00
// switchPeer launches section processes
2015-02-25 14:34:12 +02:00
func ( self * BlockPool ) switchPeer ( oldp , newp * peer ) {
// first quit AddBlockHashes, requestHeadSection and activateChain
2015-03-20 01:14:08 +02:00
// by closing the old peer's switchC channel
2015-02-25 14:34:12 +02:00
if oldp != nil {
plog . DebugDetailf ( "<%s> quit peer processes" , oldp . id )
close ( oldp . switchC )
}
if newp != nil {
2015-04-10 18:31:00 +03:00
// newp.idleC = make(chan bool)
// newp.switchC = make(chan bool)
2015-02-25 14:34:12 +02:00
// if new best peer has no head section yet, create it and run it
// otherwise head section is an element of peer.sections
if newp . headSection == nil {
plog . DebugDetailf ( "[%s] head section for [%s] not created, requesting info" , newp . id , hex ( newp . currentBlockHash ) )
if newp . idle {
self . wg . Add ( 1 )
newp . idle = false
self . syncing ( )
}
go func ( ) {
newp . run ( )
if ! newp . idle {
self . wg . Done ( )
newp . idle = true
}
} ( )
2015-04-10 18:31:00 +03:00
} else {
newp . idleC = make ( chan bool )
newp . switchC = make ( chan bool )
2015-02-25 14:34:12 +02:00
}
2015-03-25 13:09:50 +02:00
var connected = make ( map [ common . Hash ] * section )
2015-03-17 00:10:26 +02:00
var sections [ ] common . Hash
2015-02-25 14:34:12 +02:00
for _ , hash := range newp . sections {
plog . DebugDetailf ( "activate chain starting from section [%s]" , hex ( hash ) )
// if section not connected (ie, top of a contiguous sequence of sections)
2015-03-25 13:09:50 +02:00
if connected [ hash ] == nil {
2015-02-25 14:34:12 +02:00
// if not deleted, then reread from pool (it can be orphaned top half of a split section)
if entry := self . get ( hash ) ; entry != nil {
2015-04-08 22:33:54 +03:00
self . activateChain ( entry . section , newp , newp . switchC , connected )
2015-03-25 13:09:50 +02:00
connected [ hash ] = entry . section
2015-02-25 14:34:12 +02:00
sections = append ( sections , hash )
}
}
}
plog . DebugDetailf ( "<%s> section processes (%v non-contiguous sequences, was %v before)" , newp . id , len ( sections ) , len ( newp . sections ) )
2015-04-08 22:33:54 +03:00
// need to lock now that newp is exposed to section processesr
2015-02-25 14:34:12 +02:00
newp . lock . Lock ( )
newp . sections = sections
newp . lock . Unlock ( )
}
// finally deactivate section process for sections where newp didnt activate
// newp activating section process changes the quit channel for this reason
if oldp != nil {
plog . DebugDetailf ( "<%s> quit section processes" , oldp . id )
close ( oldp . idleC )
}
}
2015-03-20 01:14:08 +02:00
// getPeer looks up peer by id, returns peer and a bool value
// that is true iff peer is current best peer
2015-02-25 14:34:12 +02:00
func ( self * peers ) getPeer ( id string ) ( p * peer , best bool ) {
self . lock . RLock ( )
defer self . lock . RUnlock ( )
if self . best != nil && self . best . id == id {
return self . best , true
}
p = self . peers [ id ]
return
}
2015-03-20 01:14:08 +02:00
// head section process
2015-02-25 14:34:12 +02:00
func ( self * peer ) handleSection ( sec * section ) {
self . lock . Lock ( )
defer self . lock . Unlock ( )
2015-03-05 20:33:57 +02:00
plog . DebugDetailf ( "HeadSection: <%s> (head: %s) head section received [%s]-[%s]" , self . id , hex ( self . currentBlockHash ) , sectionhex ( self . headSection ) , sectionhex ( sec ) )
2015-02-25 14:34:12 +02:00
self . headSection = sec
self . blockHashesRequestTimer = nil
if sec == nil {
if self . idle {
self . idle = false
self . bp . wg . Add ( 1 )
self . bp . syncing ( )
}
2015-03-20 22:52:29 +02:00
self . headInfoTimer = time . After ( self . bp . Config . BlockHashesTimeout )
self . bestIdleTimer = nil
2015-02-25 14:34:12 +02:00
plog . DebugDetailf ( "HeadSection: <%s> head block hash changed (mined block received). New head %s" , self . id , hex ( self . currentBlockHash ) )
} else {
if ! self . idle {
self . idle = true
self . bp . wg . Done ( )
}
2015-03-20 22:52:29 +02:00
self . headInfoTimer = nil
self . bestIdleTimer = time . After ( self . bp . Config . IdleBestPeerTimeout )
plog . DebugDetailf ( "HeadSection: <%s> (head: %s) head section [%s] created. Idle..." , self . id , hex ( self . currentBlockHash ) , sectionhex ( sec ) )
2015-02-25 14:34:12 +02:00
}
}
func ( self * peer ) getCurrentBlock ( currentBlock * types . Block ) {
// called by update or after AddBlock signals that head block of current peer is received
if currentBlock == nil {
if entry := self . bp . get ( self . currentBlockHash ) ; entry != nil {
entry . node . lock . Lock ( )
currentBlock = entry . node . block
entry . node . lock . Unlock ( )
}
if currentBlock != nil {
plog . DebugDetailf ( "HeadSection: <%s> head block %s found in blockpool" , self . id , hex ( self . currentBlockHash ) )
} else {
plog . DebugDetailf ( "HeadSection: <%s> head block %s not found... requesting it" , self . id , hex ( self . currentBlockHash ) )
2015-03-17 00:10:26 +02:00
self . requestBlocks ( [ ] common . Hash { self . currentBlockHash } )
2015-02-25 14:34:12 +02:00
self . blocksRequestTimer = time . After ( self . bp . Config . BlocksRequestInterval )
return
}
} else {
plog . DebugDetailf ( "HeadSection: <%s> head block %s received (parent: %s)" , self . id , hex ( self . currentBlockHash ) , hex ( currentBlock . ParentHash ( ) ) )
}
self . lock . Lock ( )
defer self . lock . Unlock ( )
self . currentBlock = currentBlock
self . parentHash = currentBlock . ParentHash ( )
2015-03-05 20:33:57 +02:00
plog . DebugDetailf ( "HeadSection: <%s> head block %s found (parent: %s)... requesting hashes" , self . id , hex ( self . currentBlockHash ) , hex ( self . parentHash ) )
2015-02-25 14:34:12 +02:00
self . blockHashesRequestTimer = time . After ( 0 )
self . blocksRequestTimer = nil
}
2015-03-20 22:52:29 +02:00
func ( self * peer ) getBlockHashes ( ) bool {
2015-04-08 22:33:54 +03:00
self . lock . Lock ( )
defer self . lock . Unlock ( )
2015-02-25 14:34:12 +02:00
//if connecting parent is found
if self . bp . hasBlock ( self . parentHash ) {
plog . DebugDetailf ( "HeadSection: <%s> parent block %s found in blockchain" , self . id , hex ( self . parentHash ) )
err := self . bp . insertChain ( types . Blocks ( [ ] * types . Block { self . currentBlock } ) )
2015-03-05 20:33:57 +02:00
self . bp . status . lock . Lock ( )
self . bp . status . values . BlocksInChain ++
self . bp . status . values . BlocksInPool --
2015-02-25 14:34:12 +02:00
if err != nil {
self . addError ( ErrInvalidBlock , "%v" , err )
self . bp . status . badPeers [ self . id ] ++
2015-03-05 20:33:57 +02:00
} else {
2015-04-09 18:27:43 +03:00
/ * @ zelig : Commented out temp untill the rest of the network has been fixed .
2015-03-23 19:27:05 +02:00
// XXX added currentBlock check (?)
2015-04-08 22:33:54 +03:00
if self . currentBlock != nil && self . currentBlock . Td != nil && ! self . currentBlock . Queued ( ) {
2015-04-09 08:31:06 +03:00
plog . DebugDetailf ( "HeadSection: <%s> inserted %s to blockchain... check TD %v =?= %v" , self . id , hex ( self . parentHash ) , self . td , self . currentBlock . Td )
2015-03-20 01:00:19 +02:00
if self . td . Cmp ( self . currentBlock . Td ) != 0 {
2015-04-08 23:14:49 +03:00
self . addError ( ErrIncorrectTD , "on block %x" , self . currentBlockHash )
self . bp . status . badPeers [ self . id ] ++
2015-03-20 01:00:19 +02:00
}
}
2015-04-09 18:27:43 +03:00
* /
2015-03-20 22:52:29 +02:00
headKey := self . parentHash
2015-03-20 01:33:52 +02:00
height := self . bp . status . chain [ headKey ] + 1
2015-03-20 22:52:29 +02:00
self . bp . status . chain [ self . currentBlockHash ] = height
2015-03-05 20:33:57 +02:00
if height > self . bp . status . values . LongestChain {
self . bp . status . values . LongestChain = height
}
delete ( self . bp . status . chain , headKey )
2015-02-25 14:34:12 +02:00
}
2015-03-05 20:33:57 +02:00
self . bp . status . lock . Unlock ( )
2015-02-25 14:34:12 +02:00
} else {
if parent := self . bp . get ( self . parentHash ) ; parent != nil {
if self . bp . get ( self . currentBlockHash ) == nil {
plog . DebugDetailf ( "HeadSection: <%s> connecting parent %s found in pool... creating singleton section" , self . id , hex ( self . parentHash ) )
n := & node {
hash : self . currentBlockHash ,
block : self . currentBlock ,
hashBy : self . id ,
blockBy : self . id ,
2015-03-20 01:00:19 +02:00
td : self . td ,
2015-02-25 14:34:12 +02:00
}
self . bp . newSection ( [ ] * node { n } ) . activate ( self )
} else {
plog . DebugDetailf ( "HeadSection: <%s> connecting parent %s found in pool...head section [%s] exists...not requesting hashes" , self . id , hex ( self . parentHash ) , sectionhex ( parent . section ) )
2015-04-08 22:33:54 +03:00
self . bp . activateChain ( parent . section , self , self . switchC , nil )
2015-02-25 14:34:12 +02:00
}
} else {
plog . DebugDetailf ( "HeadSection: <%s> section [%s] requestBlockHashes" , self . id , sectionhex ( self . headSection ) )
self . requestBlockHashes ( self . currentBlockHash )
self . blockHashesRequestTimer = time . After ( self . bp . Config . BlockHashesRequestInterval )
2015-03-20 22:52:29 +02:00
return false
2015-02-25 14:34:12 +02:00
}
}
self . blockHashesRequestTimer = nil
if ! self . idle {
self . idle = true
2015-03-20 22:52:29 +02:00
self . headInfoTimer = nil
self . bestIdleTimer = time . After ( self . bp . Config . IdleBestPeerTimeout )
2015-02-25 14:34:12 +02:00
self . bp . wg . Done ( )
}
2015-03-20 22:52:29 +02:00
return true
2015-02-25 14:34:12 +02:00
}
// main loop for head section process
func ( self * peer ) run ( ) {
2015-04-10 18:31:00 +03:00
self . lock . Lock ( )
self . switchC = make ( chan bool )
self . idleC = make ( chan bool )
2015-02-25 14:34:12 +02:00
switchC := self . switchC
2015-04-08 22:33:54 +03:00
plog . Debugf ( "HeadSection: <%s> section process for head %s started" , self . id , hex ( self . currentBlockHash ) )
2015-04-10 18:31:00 +03:00
self . lock . Unlock ( )
2015-02-25 14:34:12 +02:00
self . blockHashesRequestTimer = nil
self . blocksRequestTimer = time . After ( 0 )
2015-03-20 22:52:29 +02:00
self . headInfoTimer = time . After ( self . bp . Config . BlockHashesTimeout )
2015-03-25 13:09:50 +02:00
self . bestIdleTimer = nil
2015-02-25 14:34:12 +02:00
var ping = time . NewTicker ( 5 * time . Second )
LOOP :
for {
select {
2015-03-20 01:14:08 +02:00
// to minitor section process behaviour
2015-02-25 14:34:12 +02:00
case <- ping . C :
plog . Debugf ( "HeadSection: <%s> section with head %s, idle: %v" , self . id , hex ( self . currentBlockHash ) , self . idle )
// signal from AddBlockHashes that head section for current best peer is created
// if sec == nil, it signals that chain info has updated (new block message)
case sec := <- self . headSectionC :
self . handleSection ( sec )
// periodic check for block hashes or parent block/section
case <- self . blockHashesRequestTimer :
self . getBlockHashes ( )
// signal from AddBlock that head block of current best peer has been received
case currentBlock := <- self . currentBlockC :
self . getCurrentBlock ( currentBlock )
// keep requesting until found or timed out
case <- self . blocksRequestTimer :
self . getCurrentBlock ( nil )
// quitting on timeout
2015-03-20 22:52:29 +02:00
case <- self . headInfoTimer :
2015-03-05 20:33:57 +02:00
self . peerError ( self . bp . peers . errors . New ( ErrInsufficientChainInfo , "timed out without providing block hashes or head block (td: %v, head: %s)" , self . td , hex ( self . currentBlockHash ) ) )
2015-02-25 14:34:12 +02:00
self . bp . status . lock . Lock ( )
self . bp . status . badPeers [ self . id ] ++
self . bp . status . lock . Unlock ( )
// there is no persistence here, so GC will just take care of cleaning up
// signal for peer switch, quit
case <- switchC :
var complete = "incomplete "
if self . idle {
complete = "complete"
}
plog . Debugf ( "HeadSection: <%s> section with head %s %s... quit request loop due to peer switch" , self . id , hex ( self . currentBlockHash ) , complete )
break LOOP
// global quit for blockpool
case <- self . bp . quit :
break LOOP
2015-03-25 13:09:50 +02:00
// best
2015-03-20 22:52:29 +02:00
case <- self . bestIdleTimer :
2015-03-18 22:30:34 +02:00
self . peerError ( self . bp . peers . errors . New ( ErrIdleTooLong , "timed out without providing new blocks (td: %v, head: %s)...quitting" , self . td , hex ( self . currentBlockHash ) ) )
2015-03-05 20:33:57 +02:00
self . bp . status . lock . Lock ( )
self . bp . status . badPeers [ self . id ] ++
self . bp . status . lock . Unlock ( )
plog . Debugf ( "HeadSection: <%s> (headsection [%s]) quit channel closed : timed out without providing new blocks...quitting" , self . id , sectionhex ( self . headSection ) )
2015-02-25 14:34:12 +02:00
}
}
2015-04-08 22:33:54 +03:00
2015-02-25 14:34:12 +02:00
if ! self . idle {
self . idle = true
self . bp . wg . Done ( )
}
}