github.com/aquanetwork/aquachain@v1.7.8/aqua/fetcher/fetcher.go (about)

     1  // Copyright 2015 The aquachain Authors
     2  // This file is part of the aquachain library.
     3  //
     4  // The aquachain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The aquachain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package fetcher contains the block announcement based synchronisation.
    18  package fetcher
    19  
    20  import (
    21  	"errors"
    22  	"math/big"
    23  	"math/rand"
    24  	"time"
    25  
    26  	"gitlab.com/aquachain/aquachain/common"
    27  	"gitlab.com/aquachain/aquachain/common/log"
    28  	"gitlab.com/aquachain/aquachain/common/prque"
    29  	"gitlab.com/aquachain/aquachain/consensus"
    30  	"gitlab.com/aquachain/aquachain/core/types"
    31  	"gitlab.com/aquachain/aquachain/params"
    32  )
    33  
    34  const (
    35  	arriveTimeout = 500 * time.Millisecond // Time allowance before an announced block is explicitly requested
    36  	gatherSlack   = 100 * time.Millisecond // Interval used to collate almost-expired announces with fetches
    37  	fetchTimeout  = 5 * time.Second        // Maximum allotted time to return an explicitly requested block
    38  	maxUncleDist  = 7                      // Maximum allowed backward distance from the chain head
    39  	maxQueueDist  = 32                     // Maximum allowed distance from the chain head to queue
    40  	hashLimit     = 256                    // Maximum number of unique blocks a peer may have announced
    41  	blockLimit    = 64                     // Maximum number of unique blocks a peer may have delivered
    42  )
    43  
    44  var (
    45  	errTerminated = errors.New("terminated")
    46  )
    47  
    48  // blockRetrievalFn is a callback type for retrieving a block from the local chain.
    49  type blockRetrievalFn func(common.Hash) *types.Block
    50  
    51  // headerRequesterFn is a callback type for sending a header retrieval request.
    52  type headerRequesterFn func(common.Hash) error
    53  
    54  // bodyRequesterFn is a callback type for sending a body retrieval request.
    55  type bodyRequesterFn func([]common.Hash) error
    56  
    57  // headerVerifierFn is a callback type to verify a block's header for fast propagation.
    58  type headerVerifierFn func(header *types.Header) error
    59  
    60  // blockBroadcasterFn is a callback type for broadcasting a block to connected peers.
    61  type blockBroadcasterFn func(block *types.Block, propagate bool)
    62  
    63  // chainHeightFn is a callback type to retrieve the current chain height.
    64  type chainHeightFn func() uint64
    65  
    66  // chainInsertFn is a callback type to insert a batch of blocks into the local chain.
    67  type chainInsertFn func(types.Blocks) (int, error)
    68  
    69  // peerDropFn is a callback type for dropping a peer detected as malicious.
    70  type peerDropFn func(id string)
    71  
    72  // announce is the hash notification of the availability of a new block in the
    73  // network.
    74  type announce struct {
    75  	hash   common.Hash   // Hash of the block being announced
    76  	number uint64        // Number of the block being announced (0 = unknown | old protocol)
    77  	header *types.Header // Header of the block partially reassembled (new protocol)
    78  	time   time.Time     // Timestamp of the announcement
    79  
    80  	origin string // Identifier of the peer originating the notification
    81  
    82  	fetchHeader headerRequesterFn // Fetcher function to retrieve the header of an announced block
    83  	fetchBodies bodyRequesterFn   // Fetcher function to retrieve the body of an announced block
    84  }
    85  
    86  // headerFilterTask represents a batch of headers needing fetcher filtering.
    87  type headerFilterTask struct {
    88  	peer    string          // The source peer of block headers
    89  	headers []*types.Header // Collection of headers to filter
    90  	time    time.Time       // Arrival time of the headers
    91  }
    92  
    93  // headerFilterTask represents a batch of block bodies (transactions and uncles)
    94  // needing fetcher filtering.
    95  type bodyFilterTask struct {
    96  	peer         string                 // The source peer of block bodies
    97  	transactions [][]*types.Transaction // Collection of transactions per block bodies
    98  	uncles       [][]*types.Header      // Collection of uncles per block bodies
    99  	time         time.Time              // Arrival time of the blocks' contents
   100  }
   101  
   102  // inject represents a schedules import operation.
   103  type inject struct {
   104  	origin string
   105  	block  *types.Block
   106  }
   107  
   108  // Fetcher is responsible for accumulating block announcements from various peers
   109  // and scheduling them for retrieval.
   110  type Fetcher struct {
   111  	// Various event channels
   112  	notify chan *announce
   113  	inject chan *inject
   114  
   115  	blockFilter  chan chan []*types.Block
   116  	headerFilter chan chan *headerFilterTask
   117  	bodyFilter   chan chan *bodyFilterTask
   118  
   119  	done chan common.Hash
   120  	quit chan struct{}
   121  
   122  	// Announce states
   123  	announces  map[string]int              // Per peer announce counts to prevent memory exhaustion
   124  	announced  map[common.Hash][]*announce // Announced blocks, scheduled for fetching
   125  	fetching   map[common.Hash]*announce   // Announced blocks, currently fetching
   126  	fetched    map[common.Hash][]*announce // Blocks with headers fetched, scheduled for body retrieval
   127  	completing map[common.Hash]*announce   // Blocks with headers, currently body-completing
   128  
   129  	// Block cache
   130  	queue  *prque.Prque            // Queue containing the import operations (block number sorted)
   131  	queues map[string]int          // Per peer block counts to prevent memory exhaustion
   132  	queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports)
   133  
   134  	// Callbacks
   135  	getBlock       blockRetrievalFn                    // Retrieves a block from the local chain
   136  	verifyHeader   headerVerifierFn                    // Checks if a block's headers have a valid proof of work
   137  	broadcastBlock blockBroadcasterFn                  // Broadcasts a block to connected peers
   138  	chainHeight    chainHeightFn                       // Retrieves the current chain's height
   139  	insertChain    chainInsertFn                       // Injects a batch of blocks into the chain
   140  	dropPeer       peerDropFn                          // Drops a peer for misbehaving
   141  	hashFunc       func(*big.Int) params.HeaderVersion // Chooses a hashing algorith
   142  
   143  	// Testing hooks
   144  	announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the announce list
   145  	queueChangeHook    func(common.Hash, bool) // Method to call upon adding or deleting a block from the import queue
   146  	fetchingHook       func([]common.Hash)     // Method to call upon starting a block (aqua/61) or header (aqua/62) fetch
   147  	completingHook     func([]common.Hash)     // Method to call upon starting a block body fetch (aqua/62)
   148  	importedHook       func(*types.Block)      // Method to call upon successful block import (both aqua/61 and aqua/62)
   149  }
   150  
   151  // New creates a block fetcher to retrieve blocks based on hash announcements.
   152  func New(hashfunc func(*big.Int) params.HeaderVersion, getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher {
   153  	return &Fetcher{
   154  		hashFunc:       hashfunc,
   155  		notify:         make(chan *announce),
   156  		inject:         make(chan *inject),
   157  		blockFilter:    make(chan chan []*types.Block),
   158  		headerFilter:   make(chan chan *headerFilterTask),
   159  		bodyFilter:     make(chan chan *bodyFilterTask),
   160  		done:           make(chan common.Hash),
   161  		quit:           make(chan struct{}),
   162  		announces:      make(map[string]int),
   163  		announced:      make(map[common.Hash][]*announce),
   164  		fetching:       make(map[common.Hash]*announce),
   165  		fetched:        make(map[common.Hash][]*announce),
   166  		completing:     make(map[common.Hash]*announce),
   167  		queue:          prque.New(nil),
   168  		queues:         make(map[string]int),
   169  		queued:         make(map[common.Hash]*inject),
   170  		getBlock:       getBlock,
   171  		verifyHeader:   verifyHeader,
   172  		broadcastBlock: broadcastBlock,
   173  		chainHeight:    chainHeight,
   174  		insertChain:    insertChain,
   175  		dropPeer:       dropPeer,
   176  	}
   177  }
   178  
   179  // Start boots up the announcement based synchroniser, accepting and processing
   180  // hash notifications and block fetches until termination requested.
   181  func (f *Fetcher) Start() {
   182  	go f.loop()
   183  }
   184  
   185  // Stop terminates the announcement based synchroniser, canceling all pending
   186  // operations.
   187  func (f *Fetcher) Stop() {
   188  	close(f.quit)
   189  }
   190  
   191  // Notify announces the fetcher of the potential availability of a new block in
   192  // the network.
   193  func (f *Fetcher) Notify(peer string, hash common.Hash, number uint64, time time.Time,
   194  	headerFetcher headerRequesterFn, bodyFetcher bodyRequesterFn) error {
   195  	block := &announce{
   196  		hash:        hash,
   197  		number:      number,
   198  		time:        time,
   199  		origin:      peer,
   200  		fetchHeader: headerFetcher,
   201  		fetchBodies: bodyFetcher,
   202  	}
   203  	select {
   204  	case f.notify <- block:
   205  		return nil
   206  	case <-f.quit:
   207  		return errTerminated
   208  	}
   209  }
   210  
   211  // Enqueue tries to fill gaps the the fetcher's future import queue.
   212  func (f *Fetcher) Enqueue(peer string, block *types.Block) error {
   213  	op := &inject{
   214  		origin: peer,
   215  		block:  block,
   216  	}
   217  	select {
   218  	case f.inject <- op:
   219  		return nil
   220  	case <-f.quit:
   221  		return errTerminated
   222  	}
   223  }
   224  
   225  // FilterHeaders extracts all the headers that were explicitly requested by the fetcher,
   226  // returning those that should be handled differently.
   227  func (f *Fetcher) FilterHeaders(peer string, headers []*types.Header, time time.Time) []*types.Header {
   228  	log.Trace("Filtering headers", "peer", peer, "headers", len(headers))
   229  
   230  	// Send the filter channel to the fetcher
   231  	filter := make(chan *headerFilterTask)
   232  
   233  	select {
   234  	case f.headerFilter <- filter:
   235  	case <-f.quit:
   236  		return nil
   237  	}
   238  	// Request the filtering of the header list
   239  	select {
   240  	case filter <- &headerFilterTask{peer: peer, headers: headers, time: time}:
   241  	case <-f.quit:
   242  		return nil
   243  	}
   244  	// Retrieve the headers remaining after filtering
   245  	select {
   246  	case task := <-filter:
   247  		return task.headers
   248  	case <-f.quit:
   249  		return nil
   250  	}
   251  }
   252  
   253  // FilterBodies extracts all the block bodies that were explicitly requested by
   254  // the fetcher, returning those that should be handled differently.
   255  func (f *Fetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) {
   256  	log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles))
   257  
   258  	// Send the filter channel to the fetcher
   259  	filter := make(chan *bodyFilterTask)
   260  
   261  	select {
   262  	case f.bodyFilter <- filter:
   263  	case <-f.quit:
   264  		return nil, nil
   265  	}
   266  	// Request the filtering of the body list
   267  	select {
   268  	case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, time: time}:
   269  	case <-f.quit:
   270  		return nil, nil
   271  	}
   272  	// Retrieve the bodies remaining after filtering
   273  	select {
   274  	case task := <-filter:
   275  		return task.transactions, task.uncles
   276  	case <-f.quit:
   277  		return nil, nil
   278  	}
   279  }
   280  
   281  // Loop is the main fetcher loop, checking and processing various notification
   282  // events.
   283  func (f *Fetcher) loop() {
   284  	// Iterate the block fetching until a quit is requested
   285  	fetchTimer := time.NewTimer(0)
   286  	completeTimer := time.NewTimer(0)
   287  
   288  	for {
   289  		// Clean up any expired block fetches
   290  		for hash, announce := range f.fetching {
   291  			if time.Since(announce.time) > fetchTimeout {
   292  				f.forgetHash(hash)
   293  			}
   294  		}
   295  		// Import any queued blocks that could potentially fit
   296  		height := f.chainHeight()
   297  		for !f.queue.Empty() {
   298  			op := f.queue.PopItem().(*inject)
   299  			if f.queueChangeHook != nil {
   300  				f.queueChangeHook(op.block.Hash(), false)
   301  			}
   302  			// If too high up the chain or phase, continue later
   303  			number := op.block.NumberU64()
   304  			if number > height+1 {
   305  				f.queue.Push(op, -int64(op.block.NumberU64()))
   306  				if f.queueChangeHook != nil {
   307  					f.queueChangeHook(op.block.Hash(), true)
   308  				}
   309  				break
   310  			}
   311  			// Otherwise if fresh and still unknown, try and import
   312  			hash := op.block.Hash()
   313  			if number+maxUncleDist < height || f.getBlock(hash) != nil {
   314  				f.forgetBlock(hash)
   315  				continue
   316  			}
   317  			f.insert(op.origin, op.block)
   318  		}
   319  		// Wait for an outside event to occur
   320  		select {
   321  		case <-f.quit:
   322  			// Fetcher terminating, abort all operations
   323  			return
   324  
   325  		case notification := <-f.notify:
   326  			// A block was announced, make sure the peer isn't DOSing us
   327  			propAnnounceInMeter.Mark(1)
   328  
   329  			count := f.announces[notification.origin] + 1
   330  			if count > hashLimit {
   331  				log.Debug("Peer exceeded outstanding announces", "peer", notification.origin, "limit", hashLimit)
   332  				propAnnounceDOSMeter.Mark(1)
   333  				break
   334  			}
   335  			// If we have a valid block number, check that it's potentially useful
   336  			if notification.number > 0 {
   337  				if dist := int64(notification.number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
   338  					log.Debug("Peer discarded announcement", "peer", notification.origin, "number", notification.number, "hash", notification.hash, "distance", dist)
   339  					propAnnounceDropMeter.Mark(1)
   340  					break
   341  				}
   342  			}
   343  			// All is well, schedule the announce if block's not yet downloading
   344  			if _, ok := f.fetching[notification.hash]; ok {
   345  				break
   346  			}
   347  			if _, ok := f.completing[notification.hash]; ok {
   348  				break
   349  			}
   350  			f.announces[notification.origin] = count
   351  			f.announced[notification.hash] = append(f.announced[notification.hash], notification)
   352  			if f.announceChangeHook != nil && len(f.announced[notification.hash]) == 1 {
   353  				f.announceChangeHook(notification.hash, true)
   354  			}
   355  			if len(f.announced) == 1 {
   356  				f.rescheduleFetch(fetchTimer)
   357  			}
   358  
   359  		case op := <-f.inject:
   360  			// A direct block insertion was requested, try and fill any pending gaps
   361  			propBroadcastInMeter.Mark(1)
   362  			f.enqueue(op.origin, op.block)
   363  
   364  		case hash := <-f.done:
   365  			// A pending import finished, remove all traces of the notification
   366  			f.forgetHash(hash)
   367  			f.forgetBlock(hash)
   368  
   369  		case <-fetchTimer.C:
   370  			// At least one block's timer ran out, check for needing retrieval
   371  			request := make(map[string][]common.Hash)
   372  
   373  			for hash, announces := range f.announced {
   374  				if time.Since(announces[0].time) > arriveTimeout-gatherSlack {
   375  					// Pick a random peer to retrieve from, reset all others
   376  					announce := announces[rand.Intn(len(announces))]
   377  					f.forgetHash(hash)
   378  
   379  					// If the block still didn't arrive, queue for fetching
   380  					if f.getBlock(hash) == nil {
   381  						request[announce.origin] = append(request[announce.origin], hash)
   382  						f.fetching[hash] = announce
   383  					}
   384  				}
   385  			}
   386  			// Send out all block header requests
   387  			for peer, hashes := range request {
   388  				log.Trace("Fetching scheduled headers", "peer", peer, "list", hashes)
   389  
   390  				// Create a closure of the fetch and schedule in on a new thread
   391  				fetchHeader, hashes := f.fetching[hashes[0]].fetchHeader, hashes
   392  				go func() {
   393  					if f.fetchingHook != nil {
   394  						f.fetchingHook(hashes)
   395  					}
   396  					for _, hash := range hashes {
   397  						headerFetchMeter.Mark(1)
   398  						fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals
   399  					}
   400  				}()
   401  			}
   402  			// Schedule the next fetch if blocks are still pending
   403  			f.rescheduleFetch(fetchTimer)
   404  
   405  		case <-completeTimer.C:
   406  			// At least one header's timer ran out, retrieve everything
   407  			request := make(map[string][]common.Hash)
   408  
   409  			for hash, announces := range f.fetched {
   410  				// Pick a random peer to retrieve from, reset all others
   411  				announce := announces[rand.Intn(len(announces))]
   412  				f.forgetHash(hash)
   413  
   414  				// If the block still didn't arrive, queue for completion
   415  				if f.getBlock(hash) == nil {
   416  					request[announce.origin] = append(request[announce.origin], hash)
   417  					f.completing[hash] = announce
   418  				}
   419  			}
   420  			// Send out all block body requests
   421  			for peer, hashes := range request {
   422  				log.Trace("Fetching scheduled bodies", "peer", peer, "list", hashes)
   423  
   424  				// Create a closure of the fetch and schedule in on a new thread
   425  				if f.completingHook != nil {
   426  					f.completingHook(hashes)
   427  				}
   428  				bodyFetchMeter.Mark(int64(len(hashes)))
   429  				go f.completing[hashes[0]].fetchBodies(hashes)
   430  			}
   431  			// Schedule the next fetch if blocks are still pending
   432  			f.rescheduleComplete(completeTimer)
   433  
   434  		case filter := <-f.headerFilter:
   435  			// Headers arrived from a remote peer. Extract those that were explicitly
   436  			// requested by the fetcher, and return everything else so it's delivered
   437  			// to other parts of the system.
   438  			var task *headerFilterTask
   439  			select {
   440  			case task = <-filter:
   441  			case <-f.quit:
   442  				return
   443  			}
   444  			headerFilterInMeter.Mark(int64(len(task.headers)))
   445  
   446  			// Split the batch of headers into unknown ones (to return to the caller),
   447  			// known incomplete ones (requiring body retrievals) and completed blocks.
   448  			unknown, incomplete, complete := []*types.Header{}, []*announce{}, []*types.Block{}
   449  			for _, header := range task.headers {
   450  				header.SetVersion(byte(f.hashFunc(header.Number)))
   451  				hash := header.Hash()
   452  
   453  				// Filter fetcher-requested headers from other synchronisation algorithms
   454  				if announce := f.fetching[hash]; announce != nil && announce.origin == task.peer && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil {
   455  					// If the delivered header does not match the promised number, drop the announcer
   456  					if header.Number.Uint64() != announce.number {
   457  						log.Trace("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number)
   458  						f.dropPeer(announce.origin)
   459  						f.forgetHash(hash)
   460  						continue
   461  					}
   462  					// Only keep if not imported by other means
   463  					if f.getBlock(hash) == nil {
   464  						announce.header = header
   465  						announce.time = task.time
   466  
   467  						// If the block is empty (header only), short circuit into the final import queue
   468  						if header.TxHash == types.DeriveSha(types.Transactions{}) && header.UncleHash == types.CalcUncleHash([]*types.Header{}) {
   469  							log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
   470  
   471  							block := types.NewBlockWithHeader(header)
   472  							block.ReceivedAt = task.time
   473  
   474  							complete = append(complete, block)
   475  							f.completing[hash] = announce
   476  							continue
   477  						}
   478  						// Otherwise add to the list of blocks needing completion
   479  						incomplete = append(incomplete, announce)
   480  					} else {
   481  						log.Trace("Block already imported, discarding header", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
   482  						f.forgetHash(hash)
   483  					}
   484  				} else {
   485  					// Fetcher doesn't know about it, add to the return list
   486  					unknown = append(unknown, header)
   487  				}
   488  			}
   489  			headerFilterOutMeter.Mark(int64(len(unknown)))
   490  			select {
   491  			case filter <- &headerFilterTask{headers: unknown, time: task.time}:
   492  			case <-f.quit:
   493  				return
   494  			}
   495  			// Schedule the retrieved headers for body completion
   496  			for _, announce := range incomplete {
   497  				hash := announce.header.Hash()
   498  				if _, ok := f.completing[hash]; ok {
   499  					continue
   500  				}
   501  				f.fetched[hash] = append(f.fetched[hash], announce)
   502  				if len(f.fetched) == 1 {
   503  					f.rescheduleComplete(completeTimer)
   504  				}
   505  			}
   506  			// Schedule the header-only blocks for import
   507  			for _, block := range complete {
   508  				if announce := f.completing[block.Hash()]; announce != nil {
   509  					f.enqueue(announce.origin, block)
   510  				}
   511  			}
   512  
   513  		case filter := <-f.bodyFilter:
   514  			// Block bodies arrived, extract any explicitly requested blocks, return the rest
   515  			var task *bodyFilterTask
   516  			select {
   517  			case task = <-filter:
   518  			case <-f.quit:
   519  				return
   520  			}
   521  			bodyFilterInMeter.Mark(int64(len(task.transactions)))
   522  
   523  			blocks := []*types.Block{}
   524  			for i := 0; i < len(task.transactions) && i < len(task.uncles); i++ {
   525  				// Match up a body to any possible completion request
   526  				matched := false
   527  
   528  				for hash, announce := range f.completing {
   529  					if f.queued[hash] == nil {
   530  						txnHash := types.DeriveSha(types.Transactions(task.transactions[i]))
   531  						uncleHash := types.CalcUncleHash(task.uncles[i])
   532  
   533  						if txnHash == announce.header.TxHash && uncleHash == announce.header.UncleHash && announce.origin == task.peer {
   534  							// Mark the body matched, reassemble if still unknown
   535  							matched = true
   536  
   537  							if f.getBlock(hash) == nil {
   538  								block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i])
   539  								block.ReceivedAt = task.time
   540  
   541  								blocks = append(blocks, block)
   542  							} else {
   543  								f.forgetHash(hash)
   544  							}
   545  						}
   546  					}
   547  				}
   548  				if matched {
   549  					task.transactions = append(task.transactions[:i], task.transactions[i+1:]...)
   550  					task.uncles = append(task.uncles[:i], task.uncles[i+1:]...)
   551  					i--
   552  					continue
   553  				}
   554  			}
   555  
   556  			bodyFilterOutMeter.Mark(int64(len(task.transactions)))
   557  			select {
   558  			case filter <- task:
   559  			case <-f.quit:
   560  				return
   561  			}
   562  			// Schedule the retrieved blocks for ordered import
   563  			for _, block := range blocks {
   564  				if announce := f.completing[block.Hash()]; announce != nil {
   565  					f.enqueue(announce.origin, block)
   566  				}
   567  			}
   568  		}
   569  	}
   570  }
   571  
   572  // rescheduleFetch resets the specified fetch timer to the next announce timeout.
   573  func (f *Fetcher) rescheduleFetch(fetch *time.Timer) {
   574  	// Short circuit if no blocks are announced
   575  	if len(f.announced) == 0 {
   576  		return
   577  	}
   578  	// Otherwise find the earliest expiring announcement
   579  	earliest := time.Now()
   580  	for _, announces := range f.announced {
   581  		if earliest.After(announces[0].time) {
   582  			earliest = announces[0].time
   583  		}
   584  	}
   585  	fetch.Reset(arriveTimeout - time.Since(earliest))
   586  }
   587  
   588  // rescheduleComplete resets the specified completion timer to the next fetch timeout.
   589  func (f *Fetcher) rescheduleComplete(complete *time.Timer) {
   590  	// Short circuit if no headers are fetched
   591  	if len(f.fetched) == 0 {
   592  		return
   593  	}
   594  	// Otherwise find the earliest expiring announcement
   595  	earliest := time.Now()
   596  	for _, announces := range f.fetched {
   597  		if earliest.After(announces[0].time) {
   598  			earliest = announces[0].time
   599  		}
   600  	}
   601  	complete.Reset(gatherSlack - time.Since(earliest))
   602  }
   603  
   604  // enqueue schedules a new future import operation, if the block to be imported
   605  // has not yet been seen.
   606  func (f *Fetcher) enqueue(peer string, block *types.Block) {
   607  	hash := block.Hash()
   608  
   609  	// Ensure the peer isn't DOSing us
   610  	count := f.queues[peer] + 1
   611  	if count > blockLimit {
   612  		log.Debug("Discarded propagated block, exceeded allowance", "peer", peer, "number", block.Number(), "hash", hash, "limit", blockLimit)
   613  		propBroadcastDOSMeter.Mark(1)
   614  		f.forgetHash(hash)
   615  		return
   616  	}
   617  	// Discard any past or too distant blocks
   618  	if dist := int64(block.NumberU64()) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
   619  		log.Debug("Discarded propagated block, too far away", "peer", peer, "number", block.Number(), "hash", hash, "distance", dist)
   620  		propBroadcastDropMeter.Mark(1)
   621  		f.forgetHash(hash)
   622  		return
   623  	}
   624  	// Schedule the block for future importing
   625  	if _, ok := f.queued[hash]; !ok {
   626  		op := &inject{
   627  			origin: peer,
   628  			block:  block,
   629  		}
   630  		f.queues[peer] = count
   631  		f.queued[hash] = op
   632  		f.queue.Push(op, -int64(block.NumberU64()))
   633  		if f.queueChangeHook != nil {
   634  			f.queueChangeHook(op.block.Hash(), true)
   635  		}
   636  		log.Debug("Queued propagated block", "peer", peer, "number", block.Number(), "hash", hash, "queued", f.queue.Size())
   637  	}
   638  }
   639  
   640  // insert spawns a new goroutine to run a block insertion into the chain. If the
   641  // block's number is at the same height as the current import phase, if updates
   642  // the phase states accordingly.
   643  func (f *Fetcher) insert(peer string, block *types.Block) {
   644  	hash := block.Hash()
   645  
   646  	// Run the import on a new thread
   647  	log.Debug("Importing propagated block", "peer", peer, "number", block.Number(), "hash", hash)
   648  	go func() {
   649  		defer func() { f.done <- hash }()
   650  
   651  		// If the parent's unknown, abort insertion
   652  		parent := f.getBlock(block.ParentHash())
   653  		if parent == nil {
   654  			log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash())
   655  			return
   656  		}
   657  		// Quickly validate the header and propagate the block if it passes
   658  		switch err := f.verifyHeader(block.Header()); err {
   659  		case nil:
   660  			// All ok, quickly propagate to our peers
   661  			propBroadcastOutTimer.UpdateSince(block.ReceivedAt)
   662  			go f.broadcastBlock(block, true)
   663  
   664  		case consensus.ErrFutureBlock:
   665  			// Weird future block, don't fail, but neither propagate
   666  
   667  		default:
   668  			// Something went very wrong, drop the peer
   669  			log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
   670  			f.dropPeer(peer)
   671  			return
   672  		}
   673  		// Run the actual import and log any issues
   674  		if _, err := f.insertChain(types.Blocks{block}); err != nil {
   675  			log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
   676  			return
   677  		}
   678  		// If import succeeded, broadcast the block
   679  		propAnnounceOutTimer.UpdateSince(block.ReceivedAt)
   680  		go f.broadcastBlock(block, false)
   681  
   682  		// Invoke the testing hook if needed
   683  		if f.importedHook != nil {
   684  			f.importedHook(block)
   685  		}
   686  	}()
   687  }
   688  
   689  // forgetHash removes all traces of a block announcement from the fetcher's
   690  // internal state.
   691  func (f *Fetcher) forgetHash(hash common.Hash) {
   692  	// Remove all pending announces and decrement DOS counters
   693  	for _, announce := range f.announced[hash] {
   694  		f.announces[announce.origin]--
   695  		if f.announces[announce.origin] == 0 {
   696  			delete(f.announces, announce.origin)
   697  		}
   698  	}
   699  	delete(f.announced, hash)
   700  	if f.announceChangeHook != nil {
   701  		f.announceChangeHook(hash, false)
   702  	}
   703  	// Remove any pending fetches and decrement the DOS counters
   704  	if announce := f.fetching[hash]; announce != nil {
   705  		f.announces[announce.origin]--
   706  		if f.announces[announce.origin] == 0 {
   707  			delete(f.announces, announce.origin)
   708  		}
   709  		delete(f.fetching, hash)
   710  	}
   711  
   712  	// Remove any pending completion requests and decrement the DOS counters
   713  	for _, announce := range f.fetched[hash] {
   714  		f.announces[announce.origin]--
   715  		if f.announces[announce.origin] == 0 {
   716  			delete(f.announces, announce.origin)
   717  		}
   718  	}
   719  	delete(f.fetched, hash)
   720  
   721  	// Remove any pending completions and decrement the DOS counters
   722  	if announce := f.completing[hash]; announce != nil {
   723  		f.announces[announce.origin]--
   724  		if f.announces[announce.origin] == 0 {
   725  			delete(f.announces, announce.origin)
   726  		}
   727  		delete(f.completing, hash)
   728  	}
   729  }
   730  
   731  // forgetBlock removes all traces of a queued block from the fetcher's internal
   732  // state.
   733  func (f *Fetcher) forgetBlock(hash common.Hash) {
   734  	if insert := f.queued[hash]; insert != nil {
   735  		f.queues[insert.origin]--
   736  		if f.queues[insert.origin] == 0 {
   737  			delete(f.queues, insert.origin)
   738  		}
   739  		delete(f.queued, hash)
   740  	}
   741  }