@ -34,6 +34,9 @@ var (
errPeersUnavailable = errors . New ( "no peers available or all peers tried for block download process" )
errAlreadyInPool = errors . New ( "hash already in pool" )
errBlockNumberOverflow = errors . New ( "received block which overflows" )
errCancelHashFetch = errors . New ( "hash fetching cancelled (requested)" )
errCancelBlockFetch = errors . New ( "block downloading cancelled (requested)" )
errNoSyncActive = errors . New ( "no sync active" )
)
type hashCheckFn func ( common . Hash ) bool
@ -74,6 +77,7 @@ type Downloader struct {
newPeerCh chan * peer
hashCh chan hashPack
blockCh chan blockPack
cancelCh chan struct { }
}
func New ( hasBlock hashCheckFn , getBlock getBlockFn ) * Downloader {
@ -129,6 +133,9 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
}
defer atomic . StoreInt32 ( & d . synchronising , 0 )
// Create cancel channel for aborting midflight
d . cancelCh = make ( chan struct { } )
// Abort if the queue still contains some leftover data
if _ , cached := d . queue . Size ( ) ; cached > 0 && d . queue . GetHeadBlock ( ) != nil {
return errPendingQueue
@ -161,7 +168,6 @@ func (d *Downloader) Has(hash common.Hash) bool {
}
func ( d * Downloader ) getFromPeer ( p * peer , hash common . Hash , ignoreInitial bool ) ( err error ) {
d . activePeer = p . id
defer func ( ) {
// reset on error
@ -191,6 +197,42 @@ func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool)
return nil
}
// Cancel cancels all of the operations and resets the queue. It returns true
// if the cancel operation was completed.
func ( d * Downloader ) Cancel ( ) bool {
hs , bs := d . queue . Size ( )
// If we're not syncing just return.
if atomic . LoadInt32 ( & d . synchronising ) == 0 && hs == 0 && bs == 0 {
return false
}
close ( d . cancelCh )
// clean up
hashDone :
for {
select {
case <- d . hashCh :
default :
break hashDone
}
}
blockDone :
for {
select {
case <- d . blockCh :
default :
break blockDone
}
}
// reset the queue
d . queue . Reset ( )
return true
}
// XXX Make synchronous
func ( d * Downloader ) startFetchingHashes ( p * peer , h common . Hash , ignoreInitial bool ) error {
glog . V ( logger . Debug ) . Infof ( "Downloading hashes (%x) from %s" , h [ : 4 ] , p . id )
@ -217,6 +259,8 @@ func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial b
out :
for {
select {
case <- d . cancelCh :
return errCancelHashFetch
case hashPack := <- d . hashCh :
// Make sure the active peer is giving us the hashes
if hashPack . peerId != activePeer . id {
@ -305,6 +349,8 @@ func (d *Downloader) startFetchingBlocks(p *peer) error {
out :
for {
select {
case <- d . cancelCh :
return errCancelBlockFetch
case blockPack := <- d . blockCh :
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
@ -394,11 +440,23 @@ out:
// Deliver a chunk to the downloader. This is usually done through the BlocksMsg by
// the protocol handler.
func ( d * Downloader ) DeliverChunk ( id string , blocks [ ] * types . Block ) {
func ( d * Downloader ) DeliverChunk ( id string , blocks [ ] * types . Block ) error {
// Make sure the downloader is active
if atomic . LoadInt32 ( & d . synchronising ) == 0 {
return errNoSyncActive
}
d . blockCh <- blockPack { id , blocks }
return nil
}
func ( d * Downloader ) AddHashes ( id string , hashes [ ] common . Hash ) error {
// Make sure the downloader is active
if atomic . LoadInt32 ( & d . synchronising ) == 0 {
return errNoSyncActive
}
// make sure that the hashes that are being added are actually from the peer
// that's the current active peer. hashes that have been received from other
// peers are dropped and ignored.