| 
									
										
										
										
											2015-05-11 14:26:20 +03:00
										 |  |  | // Contains the block download scheduler to collect download tasks and schedule | 
					
						
							|  |  |  | // them in an ordered, and throttled way. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | package downloader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-05-03 14:11:00 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-13 16:38:32 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2015-05-11 14:26:20 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	"gopkg.in/karalabe/cookiejar.v2/collections/prque" | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-08 14:06:36 +03:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2015-05-21 18:16:04 +03:00
										 |  |  | 	blockCacheLimit = 8 * MaxBlockFetch // Maximum number of blocks to cache before throttling the download | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	errNoFetchesPending = errors.New("no fetches pending") | 
					
						
							|  |  |  | 	errStaleDelivery    = errors.New("stale delivery") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // fetchRequest is a currently running block retrieval operation. | 
					
						
							|  |  |  | type fetchRequest struct { | 
					
						
							|  |  |  | 	Peer   *peer               // Peer to which the request was sent | 
					
						
							|  |  |  | 	Hashes map[common.Hash]int // Requested hashes with their insertion index (priority) | 
					
						
							|  |  |  | 	Time   time.Time           // Time when the request was made | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | // queue represents hashes that are either need fetching or are being fetched | 
					
						
							|  |  |  | type queue struct { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	hashPool    map[common.Hash]int // Pending hashes, mapping to their insertion index (priority) | 
					
						
							|  |  |  | 	hashQueue   *prque.Prque        // Priority queue of the block hashes to fetch | 
					
						
							|  |  |  | 	hashCounter int                 // Counter indexing the added hashes to ensure retrieval order | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-07 12:59:19 +03:00
										 |  |  | 	pendPool map[string]*fetchRequest // Currently pending block retrieval operations | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	blockPool   map[common.Hash]int // Hash-set of the downloaded data blocks, mapping to cache indexes | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | 	blockCache  []*Block            // Downloaded but not yet delivered blocks | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	blockOffset int                 // Offset of the first cached block in the block-chain | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	lock sync.RWMutex | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // newQueue creates a new download queue for scheduling block retrieval. | 
					
						
							|  |  |  | func newQueue() *queue { | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	return &queue{ | 
					
						
							| 
									
										
										
										
											2015-06-03 19:00:54 +03:00
										 |  |  | 		hashPool:   make(map[common.Hash]int), | 
					
						
							|  |  |  | 		hashQueue:  prque.New(), | 
					
						
							|  |  |  | 		pendPool:   make(map[string]*fetchRequest), | 
					
						
							|  |  |  | 		blockPool:  make(map[common.Hash]int), | 
					
						
							|  |  |  | 		blockCache: make([]*Block, blockCacheLimit), | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Reset clears out the queue contents. | 
					
						
							|  |  |  | func (q *queue) Reset() { | 
					
						
							|  |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2015-04-18 15:14:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.hashPool = make(map[common.Hash]int) | 
					
						
							|  |  |  | 	q.hashQueue.Reset() | 
					
						
							|  |  |  | 	q.hashCounter = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	q.pendPool = make(map[string]*fetchRequest) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	q.blockPool = make(map[common.Hash]int) | 
					
						
							|  |  |  | 	q.blockOffset = 0 | 
					
						
							| 
									
										
										
										
											2015-06-03 19:00:54 +03:00
										 |  |  | 	q.blockCache = make([]*Block, blockCacheLimit) | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Size retrieves the number of hashes in the queue, returning separately for | 
					
						
							|  |  |  | // pending and already downloaded. | 
					
						
							|  |  |  | func (q *queue) Size() (int, int) { | 
					
						
							|  |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return len(q.hashPool), len(q.blockPool) | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Pending retrieves the number of hashes pending for retrieval. | 
					
						
							|  |  |  | func (q *queue) Pending() int { | 
					
						
							|  |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	return q.hashQueue.Size() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // InFlight retrieves the number of fetch requests currently in flight. | 
					
						
							|  |  |  | func (q *queue) InFlight() int { | 
					
						
							|  |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	return len(q.pendPool) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-19 13:30:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Throttle checks if the download should be throttled (active block fetches | 
					
						
							|  |  |  | // exceed block cache). | 
					
						
							|  |  |  | func (q *queue) Throttle() bool { | 
					
						
							|  |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-07 12:59:19 +03:00
										 |  |  | 	// Calculate the currently in-flight block requests | 
					
						
							|  |  |  | 	pending := 0 | 
					
						
							|  |  |  | 	for _, request := range q.pendPool { | 
					
						
							|  |  |  | 		pending += len(request.Hashes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Throttle if more blocks are in-flight than free space in the cache | 
					
						
							|  |  |  | 	return pending >= len(q.blockCache)-len(q.blockPool) | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Has checks if a hash is within the download queue or not. | 
					
						
							|  |  |  | func (q *queue) Has(hash common.Hash) bool { | 
					
						
							|  |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, ok := q.hashPool[hash]; ok { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, ok := q.blockPool[hash]; ok { | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2015-04-18 20:25:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-18 18:54:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-15 01:40:16 +03:00
										 |  |  | // Insert adds a set of hashes for the download queue for scheduling, returning | 
					
						
							| 
									
										
										
										
											2015-05-15 13:14:46 +03:00
										 |  |  | // the new hashes encountered. | 
					
						
							|  |  |  | func (q *queue) Insert(hashes []common.Hash) []common.Hash { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2015-04-13 16:38:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	// Insert all the hashes prioritized in the arrival order | 
					
						
							| 
									
										
										
										
											2015-05-15 13:14:46 +03:00
										 |  |  | 	inserts := make([]common.Hash, 0, len(hashes)) | 
					
						
							| 
									
										
										
										
											2015-05-15 01:40:16 +03:00
										 |  |  | 	for _, hash := range hashes { | 
					
						
							|  |  |  | 		// Skip anything we already have | 
					
						
							| 
									
										
										
										
											2015-05-11 14:26:20 +03:00
										 |  |  | 		if old, ok := q.hashPool[hash]; ok { | 
					
						
							|  |  |  | 			glog.V(logger.Warn).Infof("Hash %x already scheduled at index %v", hash, old) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-15 01:40:16 +03:00
										 |  |  | 		// Update the counters and insert the hash | 
					
						
							| 
									
										
										
										
											2015-05-15 13:14:46 +03:00
										 |  |  | 		q.hashCounter = q.hashCounter + 1 | 
					
						
							|  |  |  | 		inserts = append(inserts, hash) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-15 01:40:16 +03:00
										 |  |  | 		q.hashPool[hash] = q.hashCounter | 
					
						
							|  |  |  | 		q.hashQueue.Push(hash, float32(q.hashCounter)) // Highest gets schedules first | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-15 01:40:16 +03:00
										 |  |  | 	return inserts | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // GetHeadBlock retrieves the first block from the cache, or nil if it hasn't | 
					
						
							|  |  |  | // been downloaded yet (or simply non existent). | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | func (q *queue) GetHeadBlock() *Block { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(q.blockCache) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return q.blockCache[0] | 
					
						
							| 
									
										
										
										
											2015-04-13 16:38:32 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // GetBlock retrieves a downloaded block, or nil if non-existent. | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | func (q *queue) GetBlock(hash common.Hash) *Block { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.RLock() | 
					
						
							|  |  |  | 	defer q.lock.RUnlock() | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	// Short circuit if the block hasn't been downloaded yet | 
					
						
							|  |  |  | 	index, ok := q.blockPool[hash] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	// Return the block if it's still available in the cache | 
					
						
							|  |  |  | 	if q.blockOffset <= index && index < q.blockOffset+len(q.blockCache) { | 
					
						
							|  |  |  | 		return q.blockCache[index-q.blockOffset] | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // TakeBlocks retrieves and permanently removes a batch of blocks from the cache. | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | func (q *queue) TakeBlocks() []*Block { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-14 15:38:49 +03:00
										 |  |  | 	// Accumulate all available blocks | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | 	blocks := []*Block{} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	for _, block := range q.blockCache { | 
					
						
							|  |  |  | 		if block == nil { | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		blocks = append(blocks, block) | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | 		delete(q.blockPool, block.RawBlock.Hash()) | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Delete the blocks from the slice and let them be garbage collected | 
					
						
							|  |  |  | 	// without this slice trick the blocks would stay in memory until nil | 
					
						
							|  |  |  | 	// would be assigned to q.blocks | 
					
						
							|  |  |  | 	copy(q.blockCache, q.blockCache[len(blocks):]) | 
					
						
							|  |  |  | 	for k, n := len(q.blockCache)-len(blocks), len(q.blockCache); k < n; k++ { | 
					
						
							|  |  |  | 		q.blockCache[k] = nil | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.blockOffset += len(blocks) | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	return blocks | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Reserve reserves a set of hashes for the given peer, skipping any previously | 
					
						
							|  |  |  | // failed download. | 
					
						
							| 
									
										
										
										
											2015-06-03 19:00:54 +03:00
										 |  |  | func (q *queue) Reserve(p *peer, count int) *fetchRequest { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-07 12:59:19 +03:00
										 |  |  | 	// Short circuit if the pool has been depleted, or if the peer's already | 
					
						
							|  |  |  | 	// downloading something (sanity check not to corrupt state) | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	if q.hashQueue.Empty() { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-07 12:59:19 +03:00
										 |  |  | 	if _, ok := q.pendPool[p.id]; ok { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-03 14:39:21 +03:00
										 |  |  | 	// Calculate an upper limit on the hashes we might fetch (i.e. throttling) | 
					
						
							|  |  |  | 	space := len(q.blockCache) - len(q.blockPool) | 
					
						
							|  |  |  | 	for _, request := range q.pendPool { | 
					
						
							|  |  |  | 		space -= len(request.Hashes) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	// Retrieve a batch of hashes, skipping previously failed ones | 
					
						
							|  |  |  | 	send := make(map[common.Hash]int) | 
					
						
							|  |  |  | 	skip := make(map[common.Hash]int) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-07 18:41:05 +03:00
										 |  |  | 	for proc := 0; proc < space && len(send) < count && !q.hashQueue.Empty(); proc++ { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		hash, priority := q.hashQueue.Pop() | 
					
						
							|  |  |  | 		if p.ignored.Has(hash) { | 
					
						
							|  |  |  | 			skip[hash.(common.Hash)] = int(priority) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			send[hash.(common.Hash)] = int(priority) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Merge all the skipped hashes back | 
					
						
							|  |  |  | 	for hash, index := range skip { | 
					
						
							|  |  |  | 		q.hashQueue.Push(hash, float32(index)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Assemble and return the block download request | 
					
						
							|  |  |  | 	if len(send) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2015-05-01 00:23:51 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	request := &fetchRequest{ | 
					
						
							|  |  |  | 		Peer:   p, | 
					
						
							|  |  |  | 		Hashes: send, | 
					
						
							|  |  |  | 		Time:   time.Now(), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	q.pendPool[p.id] = request | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return request | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Cancel aborts a fetch request, returning all pending hashes to the queue. | 
					
						
							|  |  |  | func (q *queue) Cancel(request *fetchRequest) { | 
					
						
							|  |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	for hash, index := range request.Hashes { | 
					
						
							|  |  |  | 		q.hashQueue.Push(hash, float32(index)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	delete(q.pendPool, request.Peer.id) | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Expire checks for in flight requests that exceeded a timeout allowance, | 
					
						
							|  |  |  | // canceling them and returning the responsible peers for penalization. | 
					
						
							|  |  |  | func (q *queue) Expire(timeout time.Duration) []string { | 
					
						
							|  |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Iterate over the expired requests and return each to the queue | 
					
						
							|  |  |  | 	peers := []string{} | 
					
						
							|  |  |  | 	for id, request := range q.pendPool { | 
					
						
							|  |  |  | 		if time.Since(request.Time) > timeout { | 
					
						
							|  |  |  | 			for hash, index := range request.Hashes { | 
					
						
							|  |  |  | 				q.hashQueue.Push(hash, float32(index)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			peers = append(peers, id) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Remove the expired requests from the pending pool | 
					
						
							|  |  |  | 	for _, id := range peers { | 
					
						
							|  |  |  | 		delete(q.pendPool, id) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return peers | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | // Deliver injects a block retrieval response into the download queue. | 
					
						
							|  |  |  | func (q *queue) Deliver(id string, blocks []*types.Block) (err error) { | 
					
						
							|  |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Short circuit if the blocks were never requested | 
					
						
							|  |  |  | 	request := q.pendPool[id] | 
					
						
							|  |  |  | 	if request == nil { | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | 		return errNoFetchesPending | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	delete(q.pendPool, id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If no blocks were retrieved, mark them as unavailable for the origin peer | 
					
						
							|  |  |  | 	if len(blocks) == 0 { | 
					
						
							|  |  |  | 		for hash, _ := range request.Hashes { | 
					
						
							|  |  |  | 			request.Peer.ignored.Add(hash) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Iterate over the downloaded blocks and add each of them | 
					
						
							|  |  |  | 	errs := make([]error, 0) | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	for _, block := range blocks { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		// Skip any blocks that were not requested | 
					
						
							|  |  |  | 		hash := block.Hash() | 
					
						
							|  |  |  | 		if _, ok := request.Hashes[hash]; !ok { | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | 			errs = append(errs, fmt.Errorf("non-requested block %x", hash)) | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-15 11:58:37 +03:00
										 |  |  | 		// If a requested block falls out of the range, the hash chain is invalid | 
					
						
							|  |  |  | 		index := int(block.NumberU64()) - q.blockOffset | 
					
						
							|  |  |  | 		if index >= len(q.blockCache) || index < 0 { | 
					
						
							| 
									
										
										
										
											2015-06-11 15:56:08 +03:00
										 |  |  | 			return errInvalidChain | 
					
						
							| 
									
										
										
										
											2015-05-15 11:58:37 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		// Otherwise merge the block and mark the hash block | 
					
						
							| 
									
										
										
										
											2015-05-26 14:00:21 +03:00
										 |  |  | 		q.blockCache[index] = &Block{ | 
					
						
							|  |  |  | 			RawBlock:   block, | 
					
						
							|  |  |  | 			OriginPeer: id, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		delete(request.Hashes, hash) | 
					
						
							|  |  |  | 		delete(q.hashPool, hash) | 
					
						
							|  |  |  | 		q.blockPool[hash] = int(block.NumberU64()) | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | 	// Return all failed or missing fetches to the queue | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	for hash, index := range request.Hashes { | 
					
						
							|  |  |  | 		q.hashQueue.Push(hash, float32(index)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | 	// If none of the blocks were good, it's a stale delivery | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	if len(errs) != 0 { | 
					
						
							| 
									
										
										
										
											2015-06-05 12:37:48 +03:00
										 |  |  | 		if len(errs) == len(blocks) { | 
					
						
							|  |  |  | 			return errStaleDelivery | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 		return fmt.Errorf("multiple failures: %v", errs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-13 16:38:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-03 19:00:54 +03:00
										 |  |  | // Prepare configures the block cache offset to allow accepting inbound blocks. | 
					
						
							|  |  |  | func (q *queue) Prepare(offset int) { | 
					
						
							| 
									
										
										
										
											2015-05-06 15:32:53 +03:00
										 |  |  | 	q.lock.Lock() | 
					
						
							|  |  |  | 	defer q.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if q.blockOffset < offset { | 
					
						
							|  |  |  | 		q.blockOffset = offset | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-12 12:38:25 +02:00
										 |  |  | } |