@ -7,8 +7,10 @@ import (
"time"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/errs"
"github.com/ethereum/go-ethereum/errs"
"github.com/ethereum/go-ethereum/event"
ethlogger "github.com/ethereum/go-ethereum/logger"
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow"
)
)
@ -32,12 +34,15 @@ var (
blockHashesTimeout = 60 * time . Second
blockHashesTimeout = 60 * time . Second
// timeout interval: max time allowed for peer without sending a block
// timeout interval: max time allowed for peer without sending a block
blocksTimeout = 60 * time . Second
blocksTimeout = 60 * time . Second
//
// timeout interval: max time allowed for best peer to remain idle (not send new block after sync complete)
idleBestPeerTimeout = 120 * time . Second
idleBestPeerTimeout = 120 * time . Second
// duration of suspension after peer fatal error during which peer is not allowed to reconnect
peerSuspensionInterval = 300 * time . Second
// status is logged every statusUpdateInterval
statusUpdateInterval = 3 * time . Second
)
)
// config embedded in components, by default fall back to constants
// blockpool config, values default to constants
// by default all resolved to local
type Config struct {
type Config struct {
BlockHashesBatchSize int
BlockHashesBatchSize int
BlockBatchSize int
BlockBatchSize int
@ -48,26 +53,41 @@ type Config struct {
BlockHashesTimeout time . Duration
BlockHashesTimeout time . Duration
BlocksTimeout time . Duration
BlocksTimeout time . Duration
IdleBestPeerTimeout time . Duration
IdleBestPeerTimeout time . Duration
PeerSuspensionInterval time . Duration
StatusUpdateInterval time . Duration
}
}
// blockpool errors
// blockpool errors
const (
const (
ErrInvalidBlock = iota
ErrInvalidBlock = iota
ErrInvalidPoW
ErrInvalidPoW
ErrUnrequestedBlock
ErrInsufficientChainInfo
ErrInsufficientChainInfo
ErrIdleTooLong
ErrIdleTooLong
ErrIncorrectTD
ErrUnrequestedBlock
)
)
// error descriptions
var errorToString = map [ int ] string {
var errorToString = map [ int ] string {
ErrInvalidBlock : "Invalid block" ,
ErrInvalidBlock : "Invalid block" , // fatal
ErrInvalidPoW : "Invalid PoW" ,
ErrInvalidPoW : "Invalid PoW" , // fatal
ErrInsufficientChainInfo : "Insufficient chain info" , // fatal
ErrIdleTooLong : "Idle too long" , // fatal
ErrIncorrectTD : "Incorrect Total Difficulty" , // fatal
ErrUnrequestedBlock : "Unrequested block" ,
ErrUnrequestedBlock : "Unrequested block" ,
ErrInsufficientChainInfo : "Insufficient chain info" ,
ErrIdleTooLong : "Idle too long" ,
}
}
// init initialises all your laundry
// error severity
func severity ( code int ) ethlogger . LogLevel {
switch code {
case ErrUnrequestedBlock :
return ethlogger . WarnLevel
default :
return ethlogger . ErrorLevel
}
}
// init initialises the Config, zero values fall back to constants
func ( self * Config ) init ( ) {
func ( self * Config ) init ( ) {
if self . BlockHashesBatchSize == 0 {
if self . BlockHashesBatchSize == 0 {
self . BlockHashesBatchSize = blockHashesBatchSize
self . BlockHashesBatchSize = blockHashesBatchSize
@ -96,6 +116,12 @@ func (self *Config) init() {
if self . IdleBestPeerTimeout == 0 {
if self . IdleBestPeerTimeout == 0 {
self . IdleBestPeerTimeout = idleBestPeerTimeout
self . IdleBestPeerTimeout = idleBestPeerTimeout
}
}
if self . PeerSuspensionInterval == 0 {
self . PeerSuspensionInterval = peerSuspensionInterval
}
if self . StatusUpdateInterval == 0 {
self . StatusUpdateInterval = statusUpdateInterval
}
}
}
// node is the basic unit of the internal model of block chain/tree in the blockpool
// node is the basic unit of the internal model of block chain/tree in the blockpool
@ -122,31 +148,41 @@ type entry struct {
type BlockPool struct {
type BlockPool struct {
Config * Config
Config * Config
// the minimal interface with blockchain
// the minimal interface with blockchain manager
hasBlock func ( hash common . Hash ) bool
hasBlock func ( hash common . Hash ) bool // query if block is known
insertChain func ( types . Blocks ) error
insertChain func ( types . Blocks ) error // add section to blockchain
verifyPoW func ( pow . Block ) bool
verifyPoW func ( pow . Block ) bool // soft PoW verification
chainEvents * event . TypeMux // ethereum eventer for chainEvents
tdSub event . Subscription // subscription to core.ChainHeadEvent
td * big . Int // our own total difficulty
pool map [ string ] * entry
pool map [ common . Hash ] * entry // the actual blockpool
peers * peers
peers * peers // peers manager in peers.go
status * status // info about blockpool (UI interface) in status.go
lock sync . RWMutex
lock sync . RWMutex
chainLock sync . RWMutex
chainLock sync . RWMutex
// alloc-easy pool of hash slices
// alloc-easy pool of hash slices
hashSlicePool chan [ ] common . Hash
hashSlicePool chan [ ] common . Hash
status * status
// waitgroup is used in tests to wait for result-critical routines
// as well as in determining idle / syncing status
quit chan bool
wg sync . WaitGroup //
wg sync . WaitGroup
quit chan bool // chan used for quitting parallel routines
running bool
running bool //
}
}
// public constructor
// public constructor
// after blockpool returned, config can be set
// BlockPool.Start will call Config.init to set missing values
func New (
func New (
hasBlock func ( hash common . Hash ) bool ,
hasBlock func ( hash common . Hash ) bool ,
insertChain func ( types . Blocks ) error ,
insertChain func ( types . Blocks ) error ,
verifyPoW func ( pow . Block ) bool ,
verifyPoW func ( pow . Block ) bool ,
chainEvents * event . TypeMux ,
td * big . Int ,
) * BlockPool {
) * BlockPool {
return & BlockPool {
return & BlockPool {
@ -154,15 +190,8 @@ func New(
hasBlock : hasBlock ,
hasBlock : hasBlock ,
insertChain : insertChain ,
insertChain : insertChain ,
verifyPoW : verifyPoW ,
verifyPoW : verifyPoW ,
}
chainEvents : chainEvents ,
}
td : td ,
func severity ( code int ) ethlogger . LogLevel {
switch code {
case ErrUnrequestedBlock :
return ethlogger . WarnLevel
default :
return ethlogger . ErrorLevel
}
}
}
}
@ -175,11 +204,13 @@ func (self *BlockPool) Start() {
return
return
}
}
// set missing values
self . Config . init ( )
self . Config . init ( )
self . hashSlicePool = make ( chan [ ] common . Hash , 150 )
self . hashSlicePool = make ( chan [ ] common . Hash , 150 )
self . status = newStatus ( )
self . status = newStatus ( )
self . quit = make ( chan bool )
self . quit = make ( chan bool )
self . pool = make ( map [ string ] * entry )
self . pool = make ( map [ common . Hash ] * entry )
self . running = true
self . running = true
self . peers = & peers {
self . peers = & peers {
@ -189,15 +220,36 @@ func (self *BlockPool) Start() {
Level : severity ,
Level : severity ,
} ,
} ,
peers : make ( map [ string ] * peer ) ,
peers : make ( map [ string ] * peer ) ,
blacklist : make ( map [ string ] time . Time ) ,
status : self . status ,
status : self . status ,
bp : self ,
bp : self ,
}
}
timer := time . NewTicker ( 3 * time . Second )
// subscribe and listen to core.ChainHeadEvent{} for uptodate TD
self . tdSub = self . chainEvents . Subscribe ( core . ChainHeadEvent { } )
// status update interval
timer := time . NewTicker ( self . Config . StatusUpdateInterval )
go func ( ) {
go func ( ) {
for {
for {
select {
select {
case <- self . quit :
case <- self . quit :
return
return
case event := <- self . tdSub . Chan ( ) :
if ev , ok := event . ( core . ChainHeadEvent ) ; ok {
td := ev . Block . Td
plog . DebugDetailf ( "td: %v" , td )
self . setTD ( td )
self . peers . lock . Lock ( )
if best := self . peers . best ; best != nil {
if td . Cmp ( best . td ) >= 0 {
self . peers . best = nil
self . switchPeer ( best , nil )
}
}
self . peers . lock . Unlock ( )
}
case <- timer . C :
case <- timer . C :
plog . DebugDetailf ( "status:\n%v" , self . Status ( ) )
plog . DebugDetailf ( "status:\n%v" , self . Status ( ) )
}
}
@ -218,6 +270,7 @@ func (self *BlockPool) Stop() {
plog . Infoln ( "Stopping..." )
plog . Infoln ( "Stopping..." )
self . tdSub . Unsubscribe ( )
close ( self . quit )
close ( self . quit )
self . lock . Lock ( )
self . lock . Lock ( )
@ -255,9 +308,14 @@ func (self *BlockPool) Wait(t time.Duration) {
/ *
/ *
AddPeer is called by the eth protocol instance running on the peer after
AddPeer is called by the eth protocol instance running on the peer after
the status message has been received with total difficulty and current block hash
the status message has been received with total difficulty and current block hash
Called a second time with the same peer id , it is used to update chain info for a peer . This is used when a new ( mined ) block message is received .
Called a second time with the same peer id , it is used to update chain info for a peer .
This is used when a new ( mined ) block message is received .
RemovePeer needs to be called when the peer disconnects .
RemovePeer needs to be called when the peer disconnects .
Peer info is currently not persisted across disconnects ( or sessions )
Peer info is currently not persisted across disconnects ( or sessions ) except for suspension
* /
* /
func ( self * BlockPool ) AddPeer (
func ( self * BlockPool ) AddPeer (
@ -267,7 +325,8 @@ func (self *BlockPool) AddPeer(
requestBlocks func ( [ ] common . Hash ) error ,
requestBlocks func ( [ ] common . Hash ) error ,
peerError func ( * errs . Error ) ,
peerError func ( * errs . Error ) ,
) ( best bool ) {
) ( best bool , suspended bool ) {
return self . peers . addPeer ( td , currentBlockHash , peerId , requestBlockHashes , requestBlocks , peerError )
return self . peers . addPeer ( td , currentBlockHash , peerId , requestBlockHashes , requestBlocks , peerError )
}
}
@ -281,12 +340,12 @@ AddBlockHashes
Entry point for eth protocol to add block hashes received via BlockHashesMsg
Entry point for eth protocol to add block hashes received via BlockHashesMsg
o nly hashes from the best peer are handled
O nly hashes from the best peer are handled
i nitiates further hash requests until a known parent is reached ( unless cancelled by a peerSwitch event , i . e . , when a better peer becomes best peer )
I nitiates further hash requests until a known parent is reached ( unless cancelled by a peerSwitch event , i . e . , when a better peer becomes best peer )
l aunches all block request processes on each chain section
L aunches all block request processes on each chain section
t he first argument is an iterator function . Using this block hashes are decoded from the rlp message payload on demand . As a result , AddBlockHashes needs to run synchronously for one peer since the message is discarded if the caller thread returns .
T he first argument is an iterator function . Using this block hashes are decoded from the rlp message payload on demand . As a result , AddBlockHashes needs to run synchronously for one peer since the message is discarded if the caller thread returns .
* /
* /
func ( self * BlockPool ) AddBlockHashes ( next func ( ) ( common . Hash , bool ) , peerId string ) {
func ( self * BlockPool ) AddBlockHashes ( next func ( ) ( common . Hash , bool ) , peerId string ) {
@ -297,7 +356,6 @@ func (self *BlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId st
// bestpeer is still the best peer
// bestpeer is still the best peer
self . wg . Add ( 1 )
self . wg . Add ( 1 )
defer func ( ) { self . wg . Done ( ) } ( )
defer func ( ) { self . wg . Done ( ) } ( )
self . status . lock . Lock ( )
self . status . lock . Lock ( )
@ -322,11 +380,11 @@ func (self *BlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId st
return
return
}
}
/ *
/ *
w hen peer is promoted in switchPeer , a new header section process is launched
W hen peer is promoted in switchPeer , a new header section process is launched .
as the head section skeleton is actually created here , it is signaled to the process
Once the head section skeleton is actually created here , it is signaled to the process
so that it can quit
so that it can quit .
i n the special case that the node for parent of the head block is found in the blockpool
I n the special case that the node for parent of the head block is found in the blockpool
( with or without fetched block )
( with or without fetched block ) , a singleton section containing only the head block node is created .
* /
* /
headSection = true
headSection = true
if entry := self . get ( bestpeer . currentBlockHash ) ; entry == nil {
if entry := self . get ( bestpeer . currentBlockHash ) ; entry == nil {
@ -337,6 +395,7 @@ func (self *BlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId st
block : bestpeer . currentBlock ,
block : bestpeer . currentBlock ,
hashBy : peerId ,
hashBy : peerId ,
blockBy : peerId ,
blockBy : peerId ,
td : bestpeer . td ,
}
}
// nodes is a list of nodes in one section ordered top-bottom (old to young)
// nodes is a list of nodes in one section ordered top-bottom (old to young)
nodes = append ( nodes , node )
nodes = append ( nodes , node )
@ -344,7 +403,7 @@ func (self *BlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId st
} else {
} else {
// otherwise set child section iff found node is the root of a section
// otherwise set child section iff found node is the root of a section
// this is a possible scenario when a singleton head section was created
// this is a possible scenario when a singleton head section was created
// on an earlier occasion this peer or another with the same block was best peer
// on an earlier occasion when this peer or another with the same block was best peer
if entry . node == entry . section . bottom {
if entry . node == entry . section . bottom {
child = entry . section
child = entry . section
plog . DebugDetailf ( "AddBlockHashes: peer <%s>: connects to child section root %s" , peerId , hex ( bestpeer . currentBlockHash ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s>: connects to child section root %s" , peerId , hex ( bestpeer . currentBlockHash ) )
@ -375,7 +434,7 @@ LOOP:
default :
default :
}
}
// if we reach the blockchain we stop reading more
// if we reach the blockchain we stop reading further blockhashes
if self . hasBlock ( hash ) {
if self . hasBlock ( hash ) {
// check if known block connecting the downloaded chain to our blockchain
// check if known block connecting the downloaded chain to our blockchain
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) found block %s in the blockchain" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) found block %s in the blockchain" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) )
@ -412,10 +471,11 @@ LOOP:
// reached a known chain in the pool
// reached a known chain in the pool
if entry . node == entry . section . bottom && n == 1 {
if entry . node == entry . section . bottom && n == 1 {
/ *
/ *
the first block hash received is an orphan in the pool
The first block hash received is an orphan node in the pool
this also supports clients that ( despite the spec ) include < from > hash in their
This also supports clients that ( despite the spec ) include < from > hash in their
response to hashes request . Note that by providing < from > we can link sections
response to hashes request . Note that by providing < from > we can link sections
without having to wait for the root block of the child section to arrive , so it allows for superior performance
without having to wait for the root block of the child section to arrive , so it allows for superior performance .
* /
* /
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) found head block [%s] as root of connecting child section [%s] skipping" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) , sectionhex ( entry . section ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) found head block [%s] as root of connecting child section [%s] skipping" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) , sectionhex ( entry . section ) )
// record the entry's chain section as child section
// record the entry's chain section as child section
@ -447,9 +507,8 @@ LOOP:
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): %v nodes in new section" , peerId , hex ( bestpeer . currentBlockHash ) , len ( nodes ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): %v nodes in new section" , peerId , hex ( bestpeer . currentBlockHash ) , len ( nodes ) )
/ *
/ *
handle forks where connecting node is mid - section
Handle forks where connecting node is mid - section by splitting section at fork .
by splitting section at fork
No splitting needed if connecting node is head of a section .
no splitting needed if connecting node is head of a section
* /
* /
if parent != nil && entry != nil && entry . node != parent . top && len ( nodes ) > 0 {
if parent != nil && entry != nil && entry . node != parent . top && len ( nodes ) > 0 {
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): fork after %s" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): fork after %s" , peerId , hex ( bestpeer . currentBlockHash ) , hex ( hash ) )
@ -461,10 +520,7 @@ LOOP:
self . status . lock . Unlock ( )
self . status . lock . Unlock ( )
}
}
/ *
// If new section is created, link it to parent/child sections.
if new section is created , link it to parent / child sections
and launch section process fetching blocks and further hashes
* /
sec = self . linkSections ( nodes , parent , child )
sec = self . linkSections ( nodes , parent , child )
if sec != nil {
if sec != nil {
@ -477,11 +533,12 @@ LOOP:
self . chainLock . Unlock ( )
self . chainLock . Unlock ( )
/ *
/ *
i f a blockpool node is reached ( parent section is not nil ) ,
I f a blockpool node is reached ( parent section is not nil ) ,
activate section ( unless our peer is demoted by now ) .
activate section ( unless our peer is demoted by now ) .
this can be the bottom half of a newly split section in case of a fork .
This can be the bottom half of a newly split section in case of a fork .
bestPeer is nil if we got here after our peer got demoted while processing .
bestPeer is nil if we got here after our peer got demoted while processing .
i n this case no activation should happen
I n this case no activation should happen
* /
* /
if parent != nil && ! peerswitch {
if parent != nil && ! peerswitch {
self . activateChain ( parent , bestpeer , nil )
self . activateChain ( parent , bestpeer , nil )
@ -489,9 +546,8 @@ LOOP:
}
}
/ *
/ *
if a new section was created ,
If a new section was created , register section iff head section or no child known
register section iff head section or no child known
Activate it with this peer .
activate it with this peer
* /
* /
if sec != nil {
if sec != nil {
// switch on section process (it is paused by switchC)
// switch on section process (it is paused by switchC)
@ -502,9 +558,9 @@ LOOP:
bestpeer . lock . Unlock ( )
bestpeer . lock . Unlock ( )
}
}
/ *
/ *
request next block hashes for parent section here .
Request another batch of older block hashes for parent section here .
b ut only once , repeating only when bottom block arrives ,
B ut only once , repeating only when the section ' s root block arrives .
o therwise no way to check if it arrived
O therwise no way to check if it arrived .
* /
* /
bestpeer . requestBlockHashes ( sec . bottom . hash )
bestpeer . requestBlockHashes ( sec . bottom . hash )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): start requesting blocks for section [%s]" , peerId , hex ( bestpeer . currentBlockHash ) , sectionhex ( sec ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s): start requesting blocks for section [%s]" , peerId , hex ( bestpeer . currentBlockHash ) , sectionhex ( sec ) )
@ -515,7 +571,7 @@ LOOP:
}
}
}
}
// i f we are processing peer's head section, signal it to headSection process that it is created
// I f we are processing peer's head section, signal it to headSection process that it is created.
if headSection {
if headSection {
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) head section registered on head section process" , peerId , hex ( bestpeer . currentBlockHash ) )
plog . DebugDetailf ( "AddBlockHashes: peer <%s> (head: %s) head section registered on head section process" , peerId , hex ( bestpeer . currentBlockHash ) )
@ -539,11 +595,13 @@ LOOP:
/ *
/ *
AddBlock is the entry point for the eth protocol to call when blockMsg is received .
AddBlock is the entry point for the eth protocol to call when blockMsg is received .
It has a strict interpretation of the protocol in that if the block received has not been requested , it results in an error
It has a strict interpretation of the protocol in that if the block received has not been requested , it results in an error .
At the same time it is opportunistic in that if a requested block may be provided by any peer .
At the same time it is opportunistic in that if a requested block may be provided by any peer .
The received block is checked for PoW . Only the first PoW - valid block for a hash is considered legit .
The received block is checked for PoW . Only the first PoW - valid block for a hash is considered legit .
If the block received is the head block of the current best peer , signal it to the head section process
* /
* /
func ( self * BlockPool ) AddBlock ( block * types . Block , peerId string ) {
func ( self * BlockPool ) AddBlock ( block * types . Block , peerId string ) {
hash := block . Hash ( )
hash := block . Hash ( )
@ -564,7 +622,6 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
if sender . currentBlock == nil {
if sender . currentBlock == nil {
plog . Debugf ( "AddBlock: add head block %s for peer <%s> (head: %s)" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
plog . Debugf ( "AddBlock: add head block %s for peer <%s> (head: %s)" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
sender . setChainInfoFromBlock ( block )
sender . setChainInfoFromBlock ( block )
// sender.currentBlockC <- block
self . status . lock . Lock ( )
self . status . lock . Lock ( )
self . status . values . BlockHashes ++
self . status . values . BlockHashes ++
@ -573,6 +630,7 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
self . status . lock . Unlock ( )
self . status . lock . Unlock ( )
} else {
} else {
plog . DebugDetailf ( "AddBlock: head block %s for peer <%s> (head: %s) already known" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
plog . DebugDetailf ( "AddBlock: head block %s for peer <%s> (head: %s) already known" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
// signal to head section process
sender . currentBlockC <- block
sender . currentBlockC <- block
}
}
} else {
} else {
@ -591,7 +649,6 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
sender . lock . Unlock ( )
sender . lock . Unlock ( )
if entry == nil {
if entry == nil {
// penalise peer for sending what we have not asked
plog . DebugDetailf ( "AddBlock: unrequested block %s received from peer <%s> (head: %s)" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
plog . DebugDetailf ( "AddBlock: unrequested block %s received from peer <%s> (head: %s)" , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) )
sender . addError ( ErrUnrequestedBlock , "%x" , hash )
sender . addError ( ErrUnrequestedBlock , "%x" , hash )
@ -609,7 +666,7 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
node . lock . Lock ( )
node . lock . Lock ( )
defer node . lock . Unlock ( )
defer node . lock . Unlock ( )
// check if block already present
// check if block already received
if node . block != nil {
if node . block != nil {
plog . DebugDetailf ( "AddBlock: block %s from peer <%s> (head: %s) already sent by <%s> " , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) , node . blockBy )
plog . DebugDetailf ( "AddBlock: block %s from peer <%s> (head: %s) already sent by <%s> " , hex ( hash ) , peerId , hex ( sender . currentBlockHash ) , node . blockBy )
return
return
@ -645,9 +702,9 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
}
}
/ *
/ *
iterates down a chain section by section
activateChain iterates down a chain section by section .
activating section process on incomplete sections with peer
It activates the section process on incomplete sections with peer .
relinking orphaned sections with their parent if root block ( and its parent hash ) is known )
It relinks orphaned sections with their parent if root block ( and its parent hash ) is known .
* /
* /
func ( self * BlockPool ) activateChain ( sec * section , p * peer , connected map [ string ] * section ) {
func ( self * BlockPool ) activateChain ( sec * section , p * peer , connected map [ string ] * section ) {
@ -666,8 +723,8 @@ LOOP:
connected [ sec . top . hash . Str ( ) ] = sec
connected [ sec . top . hash . Str ( ) ] = sec
}
}
/ *
/ *
we n eed to relink both complete and incomplete sections
N eed to relink both complete and incomplete sections
the latter could have been blockHashesRequestsComplete before being delinked from its parent
An incomplete section could have been blockHashesRequestsComplete before being delinked from its parent .
* /
* /
if parent == nil {
if parent == nil {
if sec . bottom . block != nil {
if sec . bottom . block != nil {
@ -682,7 +739,7 @@ LOOP:
}
}
sec = parent
sec = parent
// stop if peer got demoted
// stop if peer got demoted or global quit
select {
select {
case <- switchC :
case <- switchC :
break LOOP
break LOOP
@ -693,7 +750,19 @@ LOOP:
}
}
}
}
// must run in separate go routine, otherwise
// check if block's actual TD (calculated after successful insertChain) is identical to TD advertised for peer's head block.
func ( self * BlockPool ) checkTD ( nodes ... * node ) {
for _ , n := range nodes {
if n . td != nil {
plog . DebugDetailf ( "peer td %v =?= block td %v" , n . td , n . block . Td )
if n . td . Cmp ( n . block . Td ) != 0 {
self . peers . peerError ( n . blockBy , ErrIncorrectTD , "on block %x" , n . hash )
}
}
}
}
// requestBlocks must run in separate go routine, otherwise
// switchpeer -> activateChain -> activate deadlocks on section process select and peers.lock
// switchpeer -> activateChain -> activate deadlocks on section process select and peers.lock
func ( self * BlockPool ) requestBlocks ( attempts int , hashes [ ] common . Hash ) {
func ( self * BlockPool ) requestBlocks ( attempts int , hashes [ ] common . Hash ) {
self . wg . Add ( 1 )
self . wg . Add ( 1 )
@ -720,13 +789,26 @@ func (self *BlockPool) getChild(sec *section) *section {
func ( self * BlockPool ) get ( hash common . Hash ) * entry {
func ( self * BlockPool ) get ( hash common . Hash ) * entry {
self . lock . RLock ( )
self . lock . RLock ( )
defer self . lock . RUnlock ( )
defer self . lock . RUnlock ( )
return self . pool [ hash . Str ( ) ]
return self . pool [ hash ]
}
}
func ( self * BlockPool ) set ( hash common . Hash , e * entry ) {
func ( self * BlockPool ) set ( hash common . Hash , e * entry ) {
self . lock . Lock ( )
self . lock . Lock ( )
defer self . lock . Unlock ( )
defer self . lock . Unlock ( )
self . pool [ hash . Str ( ) ] = e
self . pool [ hash ] = e
}
// accessor and setter for total difficulty
func ( self * BlockPool ) getTD ( ) * big . Int {
self . lock . RLock ( )
defer self . lock . RUnlock ( )
return self . td
}
func ( self * BlockPool ) setTD ( td * big . Int ) {
self . lock . Lock ( )
defer self . lock . Unlock ( )
self . td = td
}
}
func ( self * BlockPool ) remove ( sec * section ) {
func ( self * BlockPool ) remove ( sec * section ) {
@ -735,7 +817,7 @@ func (self *BlockPool) remove(sec *section) {
defer self . lock . Unlock ( )
defer self . lock . Unlock ( )
for _ , node := range sec . nodes {
for _ , node := range sec . nodes {
delete ( self . pool , node . hash . Str ( ) )
delete ( self . pool , node . hash )
}
}
if sec . initialised && sec . poolRootIndex != 0 {
if sec . initialised && sec . poolRootIndex != 0 {
self . status . lock . Lock ( )
self . status . lock . Lock ( )
@ -744,6 +826,7 @@ func (self *BlockPool) remove(sec *section) {
}
}
}
}
// get/put for optimised allocation similar to sync.Pool
func ( self * BlockPool ) getHashSlice ( ) ( s [ ] common . Hash ) {
func ( self * BlockPool ) getHashSlice ( ) ( s [ ] common . Hash ) {
select {
select {
case s = <- self . hashSlicePool :
case s = <- self . hashSlicePool :
@ -753,7 +836,6 @@ func (self *BlockPool) getHashSlice() (s []common.Hash) {
return
return
}
}
// Return returns a Client to the pool.
func ( self * BlockPool ) putHashSlice ( s [ ] common . Hash ) {
func ( self * BlockPool ) putHashSlice ( s [ ] common . Hash ) {
if len ( s ) == self . Config . BlockBatchSize {
if len ( s ) == self . Config . BlockBatchSize {
select {
select {