github.com/energicryptocurrency/go-energi@v1.1.7/eth/fetcher/fetcher_test.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package fetcher
    18  
    19  import (
    20  	"errors"
    21  	"math/big"
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/energicryptocurrency/go-energi/common"
    28  	"github.com/energicryptocurrency/go-energi/consensus/ethash"
    29  	"github.com/energicryptocurrency/go-energi/core"
    30  	"github.com/energicryptocurrency/go-energi/core/types"
    31  	"github.com/energicryptocurrency/go-energi/crypto"
    32  	"github.com/energicryptocurrency/go-energi/ethdb"
    33  	"github.com/energicryptocurrency/go-energi/params"
    34  )
    35  
    36  var (
    37  	testdb       = ethdb.NewMemDatabase()
    38  	testKey, _   = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    39  	testAddress  = crypto.PubkeyToAddress(testKey.PublicKey)
    40  	genesis      = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
    41  	unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil)
    42  )
    43  
    44  // makeChain creates a chain of n blocks starting at and including parent.
    45  // the returned hash chain is ordered head->parent. In addition, every 3rd block
    46  // contains a transaction and every 5th an uncle to allow testing correct block
    47  // reassembly.
    48  func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
    49  	blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) {
    50  		block.SetCoinbase(common.Address{seed})
    51  
    52  		// If the block number is multiple of 3, send a bonus transaction to the miner
    53  		if parent == genesis && i%3 == 0 {
    54  			signer := types.MakeSigner(params.TestChainConfig, block.Number())
    55  			tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
    56  			if err != nil {
    57  				panic(err)
    58  			}
    59  			block.AddTx(tx)
    60  		}
    61  		// If the block number is a multiple of 5, add a bonus uncle to the block
    62  		if i%5 == 0 {
    63  			block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))})
    64  		}
    65  	})
    66  	hashes := make([]common.Hash, n+1)
    67  	hashes[len(hashes)-1] = parent.Hash()
    68  	blockm := make(map[common.Hash]*types.Block, n+1)
    69  	blockm[parent.Hash()] = parent
    70  	for i, b := range blocks {
    71  		hashes[len(hashes)-i-2] = b.Hash()
    72  		blockm[b.Hash()] = b
    73  	}
    74  	return hashes, blockm
    75  }
    76  
    77  // fetcherTester is a test simulator for mocking out local block chain.
    78  type fetcherTester struct {
    79  	fetcher *Fetcher
    80  
    81  	hashes []common.Hash                // Hash chain belonging to the tester
    82  	blocks map[common.Hash]*types.Block // Blocks belonging to the tester
    83  	drops  map[string]bool              // Map of peers dropped by the fetcher
    84  
    85  	lock sync.RWMutex
    86  }
    87  
    88  // newTester creates a new fetcher test mocker.
    89  func newTester() *fetcherTester {
    90  	tester := &fetcherTester{
    91  		hashes: []common.Hash{genesis.Hash()},
    92  		blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
    93  		drops:  make(map[string]bool),
    94  	}
    95  	tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer)
    96  	tester.fetcher.Start()
    97  
    98  	return tester
    99  }
   100  
   101  // getBlock retrieves a block from the tester's block chain.
   102  func (f *fetcherTester) getBlock(hash common.Hash) *types.Block {
   103  	f.lock.RLock()
   104  	defer f.lock.RUnlock()
   105  
   106  	return f.blocks[hash]
   107  }
   108  
   109  // verifyHeader is a nop placeholder for the block header verification.
   110  func (f *fetcherTester) verifyHeader(header *types.Header) error {
   111  	return nil
   112  }
   113  
   114  // broadcastBlock is a nop placeholder for the block broadcasting.
   115  func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) {
   116  }
   117  
   118  // chainHeight retrieves the current height (block number) of the chain.
   119  func (f *fetcherTester) chainHeight() uint64 {
   120  	f.lock.RLock()
   121  	defer f.lock.RUnlock()
   122  
   123  	return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64()
   124  }
   125  
   126  // insertChain injects a new blocks into the simulated chain.
   127  func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) {
   128  	f.lock.Lock()
   129  	defer f.lock.Unlock()
   130  
   131  	for i, block := range blocks {
   132  		// Make sure the parent in known
   133  		if _, ok := f.blocks[block.ParentHash()]; !ok {
   134  			return i, errors.New("unknown parent")
   135  		}
   136  		// Discard any new blocks if the same height already exists
   137  		if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() {
   138  			return i, nil
   139  		}
   140  		// Otherwise build our current chain
   141  		f.hashes = append(f.hashes, block.Hash())
   142  		f.blocks[block.Hash()] = block
   143  	}
   144  	return 0, nil
   145  }
   146  
   147  // dropPeer is an emulator for the peer removal, simply accumulating the various
   148  // peers dropped by the fetcher.
   149  func (f *fetcherTester) dropPeer(peer string) {
   150  	f.lock.Lock()
   151  	defer f.lock.Unlock()
   152  
   153  	f.drops[peer] = true
   154  }
   155  
   156  // makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer.
   157  func (f *fetcherTester) makeHeaderFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn {
   158  	closure := make(map[common.Hash]*types.Block)
   159  	for hash, block := range blocks {
   160  		closure[hash] = block
   161  	}
   162  	// Create a function that return a header from the closure
   163  	return func(hash common.Hash) error {
   164  		// Gather the blocks to return
   165  		headers := make([]*types.Header, 0, 1)
   166  		if block, ok := closure[hash]; ok {
   167  			headers = append(headers, block.Header())
   168  		}
   169  		// Return on a new thread
   170  		go f.fetcher.FilterHeaders(peer, headers, time.Now().Add(drift))
   171  
   172  		return nil
   173  	}
   174  }
   175  
   176  // makeBodyFetcher retrieves a block body fetcher associated with a simulated peer.
   177  func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) bodyRequesterFn {
   178  	closure := make(map[common.Hash]*types.Block)
   179  	for hash, block := range blocks {
   180  		closure[hash] = block
   181  	}
   182  	// Create a function that returns blocks from the closure
   183  	return func(hashes []common.Hash) error {
   184  		// Gather the block bodies to return
   185  		transactions := make([][]*types.Transaction, 0, len(hashes))
   186  		uncles := make([][]*types.Header, 0, len(hashes))
   187  
   188  		for _, hash := range hashes {
   189  			if block, ok := closure[hash]; ok {
   190  				transactions = append(transactions, block.Transactions())
   191  				uncles = append(uncles, block.Uncles())
   192  			}
   193  		}
   194  		// Return on a new thread
   195  		go f.fetcher.FilterBodies(peer, transactions, uncles, time.Now().Add(drift))
   196  
   197  		return nil
   198  	}
   199  }
   200  
   201  // verifyFetchingEvent verifies that one single event arrive on a fetching channel.
   202  func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) {
   203  	if arrive {
   204  		select {
   205  		case <-fetching:
   206  		case <-time.After(time.Second):
   207  			t.Fatalf("fetching timeout")
   208  		}
   209  	} else {
   210  		select {
   211  		case <-fetching:
   212  			t.Fatalf("fetching invoked")
   213  		case <-time.After(10 * time.Millisecond):
   214  		}
   215  	}
   216  }
   217  
   218  // verifyCompletingEvent verifies that one single event arrive on an completing channel.
   219  func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) {
   220  	if arrive {
   221  		select {
   222  		case <-completing:
   223  		case <-time.After(time.Second):
   224  			t.Fatalf("completing timeout")
   225  		}
   226  	} else {
   227  		select {
   228  		case <-completing:
   229  			t.Fatalf("completing invoked")
   230  		case <-time.After(10 * time.Millisecond):
   231  		}
   232  	}
   233  }
   234  
   235  // verifyImportEvent verifies that one single event arrive on an import channel.
   236  func verifyImportEvent(t *testing.T, imported chan *types.Block, arrive bool) {
   237  	if arrive {
   238  		select {
   239  		case <-imported:
   240  		case <-time.After(time.Second):
   241  			t.Fatalf("import timeout")
   242  		}
   243  	} else {
   244  		select {
   245  		case <-imported:
   246  			t.Fatalf("import invoked")
   247  		case <-time.After(10 * time.Millisecond):
   248  		}
   249  	}
   250  }
   251  
   252  // verifyImportCount verifies that exactly count number of events arrive on an
   253  // import hook channel.
   254  func verifyImportCount(t *testing.T, imported chan *types.Block, count int) {
   255  	for i := 0; i < count; i++ {
   256  		select {
   257  		case <-imported:
   258  		case <-time.After(time.Second):
   259  			t.Fatalf("block %d: import timeout", i+1)
   260  		}
   261  	}
   262  	verifyImportDone(t, imported)
   263  }
   264  
   265  // verifyImportDone verifies that no more events are arriving on an import channel.
   266  func verifyImportDone(t *testing.T, imported chan *types.Block) {
   267  	select {
   268  	case <-imported:
   269  		t.Fatalf("extra block imported")
   270  	case <-time.After(50 * time.Millisecond):
   271  	}
   272  }
   273  
   274  // Tests that a fetcher accepts block announcements and initiates retrievals for
   275  // them, successfully importing into the local chain.
   276  func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
   277  func TestSequentialAnnouncements63(t *testing.T) { testSequentialAnnouncements(t, 63) }
   278  func TestSequentialAnnouncements64(t *testing.T) { testSequentialAnnouncements(t, 64) }
   279  
   280  func testSequentialAnnouncements(t *testing.T, protocol int) {
   281  	// Create a chain of blocks to import
   282  	targetBlocks := 4 * hashLimit
   283  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   284  
   285  	tester := newTester()
   286  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   287  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   288  
   289  	// Iteratively announce blocks until all are imported
   290  	imported := make(chan *types.Block)
   291  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   292  
   293  	for i := len(hashes) - 2; i >= 0; i-- {
   294  		_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   295  			time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   296  		verifyImportEvent(t, imported, true)
   297  	}
   298  	verifyImportDone(t, imported)
   299  }
   300  
   301  // Tests that if blocks are announced by multiple peers (or even the same buggy
   302  // peer), they will only get downloaded at most once.
   303  func TestConcurrentAnnouncements62(t *testing.T) { testConcurrentAnnouncements(t, 62) }
   304  func TestConcurrentAnnouncements63(t *testing.T) { testConcurrentAnnouncements(t, 63) }
   305  func TestConcurrentAnnouncements64(t *testing.T) { testConcurrentAnnouncements(t, 64) }
   306  
   307  func testConcurrentAnnouncements(t *testing.T, protocol int) {
   308  	// Create a chain of blocks to import
   309  	targetBlocks := 4 * hashLimit
   310  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   311  
   312  	// Assemble a tester with a built in counter for the requests
   313  	tester := newTester()
   314  	firstHeaderFetcher := tester.makeHeaderFetcher("first", blocks, -gatherSlack)
   315  	firstBodyFetcher := tester.makeBodyFetcher("first", blocks, 0)
   316  	secondHeaderFetcher := tester.makeHeaderFetcher("second", blocks, -gatherSlack)
   317  	secondBodyFetcher := tester.makeBodyFetcher("second", blocks, 0)
   318  
   319  	counter := uint32(0)
   320  	firstHeaderWrapper := func(hash common.Hash) error {
   321  		atomic.AddUint32(&counter, 1)
   322  		return firstHeaderFetcher(hash)
   323  	}
   324  	secondHeaderWrapper := func(hash common.Hash) error {
   325  		atomic.AddUint32(&counter, 1)
   326  		return secondHeaderFetcher(hash)
   327  	}
   328  	// Iteratively announce blocks until all are imported
   329  	imported := make(chan *types.Block)
   330  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   331  
   332  	for i := len(hashes) - 2; i >= 0; i-- {
   333  		_ = tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1),
   334  			time.Now().Add(-arriveTimeout), firstHeaderWrapper, firstBodyFetcher)
   335  		_ = tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1),
   336  			time.Now().Add(-arriveTimeout+time.Millisecond),
   337  			secondHeaderWrapper, secondBodyFetcher)
   338  		_ = tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1),
   339  			time.Now().Add(-arriveTimeout-time.Millisecond),
   340  			secondHeaderWrapper, secondBodyFetcher)
   341  		verifyImportEvent(t, imported, true)
   342  	}
   343  	verifyImportDone(t, imported)
   344  
   345  	// Make sure no blocks were retrieved twice
   346  	if int(counter) != targetBlocks {
   347  		t.Fatalf("retrieval count mismatch: have %v, want %v", counter, targetBlocks)
   348  	}
   349  }
   350  
   351  // Tests that announcements arriving while a previous is being fetched still
   352  // results in a valid import.
   353  func TestOverlappingAnnouncements62(t *testing.T) { testOverlappingAnnouncements(t, 62) }
   354  func TestOverlappingAnnouncements63(t *testing.T) { testOverlappingAnnouncements(t, 63) }
   355  func TestOverlappingAnnouncements64(t *testing.T) { testOverlappingAnnouncements(t, 64) }
   356  
   357  func testOverlappingAnnouncements(t *testing.T, protocol int) {
   358  	// Create a chain of blocks to import
   359  	targetBlocks := 4 * hashLimit
   360  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   361  
   362  	tester := newTester()
   363  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   364  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   365  
   366  	// Iteratively announce blocks, but overlap them continuously
   367  	overlap := 16
   368  	imported := make(chan *types.Block, len(hashes)-1)
   369  	for i := 0; i < overlap; i++ {
   370  		imported <- nil
   371  	}
   372  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   373  
   374  	for i := len(hashes) - 2; i >= 0; i-- {
   375  		_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   376  			time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   377  		select {
   378  		case <-imported:
   379  		case <-time.After(time.Second):
   380  			t.Fatalf("block %d: import timeout", len(hashes)-i)
   381  		}
   382  	}
   383  	// Wait for all the imports to complete and check count
   384  	verifyImportCount(t, imported, overlap)
   385  }
   386  
   387  // Tests that announces already being retrieved will not be duplicated.
   388  func TestPendingDeduplication62(t *testing.T) { testPendingDeduplication(t, 62) }
   389  func TestPendingDeduplication63(t *testing.T) { testPendingDeduplication(t, 63) }
   390  func TestPendingDeduplication64(t *testing.T) { testPendingDeduplication(t, 64) }
   391  
   392  func testPendingDeduplication(t *testing.T, protocol int) {
   393  	// Create a hash and corresponding block
   394  	hashes, blocks := makeChain(1, 0, genesis)
   395  
   396  	// Assemble a tester with a built in counter and delayed fetcher
   397  	tester := newTester()
   398  	headerFetcher := tester.makeHeaderFetcher("repeater", blocks, -gatherSlack)
   399  	bodyFetcher := tester.makeBodyFetcher("repeater", blocks, 0)
   400  
   401  	delay := 50 * time.Millisecond
   402  	counter := uint32(0)
   403  	headerWrapper := func(hash common.Hash) error {
   404  		atomic.AddUint32(&counter, 1)
   405  
   406  		// Simulate a long running fetch
   407  		go func() {
   408  			time.Sleep(delay)
   409  			_ = headerFetcher(hash)
   410  		}()
   411  		return nil
   412  	}
   413  	// Announce the same block many times until it's fetched (wait for any pending ops)
   414  	for tester.getBlock(hashes[0]) == nil {
   415  		_ = tester.fetcher.Notify("repeater", hashes[0], 1,
   416  			time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher)
   417  		time.Sleep(time.Millisecond)
   418  	}
   419  	time.Sleep(delay)
   420  
   421  	// Check that all blocks were imported and none fetched twice
   422  	if imported := len(tester.blocks); imported != 2 {
   423  		t.Fatalf("synchronised block mismatch: have %v, want %v", imported, 2)
   424  	}
   425  	if int(counter) != 1 {
   426  		t.Fatalf("retrieval count mismatch: have %v, want %v", counter, 1)
   427  	}
   428  }
   429  
   430  // Tests that announcements retrieved in a random order are cached and eventually
   431  // imported when all the gaps are filled in.
   432  func TestRandomArrivalImport62(t *testing.T) { testRandomArrivalImport(t, 62) }
   433  func TestRandomArrivalImport63(t *testing.T) { testRandomArrivalImport(t, 63) }
   434  func TestRandomArrivalImport64(t *testing.T) { testRandomArrivalImport(t, 64) }
   435  
   436  func testRandomArrivalImport(t *testing.T, protocol int) {
   437  	// Create a chain of blocks to import, and choose one to delay
   438  	targetBlocks := maxQueueDist
   439  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   440  	skip := targetBlocks / 2
   441  
   442  	tester := newTester()
   443  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   444  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   445  
   446  	// Iteratively announce blocks, skipping one entry
   447  	imported := make(chan *types.Block, len(hashes)-1)
   448  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   449  
   450  	for i := len(hashes) - 1; i >= 0; i-- {
   451  		if i != skip {
   452  			_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   453  				time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   454  			time.Sleep(time.Millisecond)
   455  		}
   456  	}
   457  	// Finally announce the skipped entry and check full import
   458  	_ = tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1),
   459  		time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   460  	verifyImportCount(t, imported, len(hashes)-1)
   461  }
   462  
   463  // Tests that direct block enqueues (due to block propagation vs. hash announce)
   464  // are correctly schedule, filling and import queue gaps.
   465  func TestQueueGapFill62(t *testing.T) { testQueueGapFill(t, 62) }
   466  func TestQueueGapFill63(t *testing.T) { testQueueGapFill(t, 63) }
   467  func TestQueueGapFill64(t *testing.T) { testQueueGapFill(t, 64) }
   468  
   469  func testQueueGapFill(t *testing.T, protocol int) {
   470  	// Create a chain of blocks to import, and choose one to not announce at all
   471  	targetBlocks := maxQueueDist
   472  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   473  	skip := targetBlocks / 2
   474  
   475  	tester := newTester()
   476  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   477  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   478  
   479  	// Iteratively announce blocks, skipping one entry
   480  	imported := make(chan *types.Block, len(hashes)-1)
   481  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   482  
   483  	for i := len(hashes) - 1; i >= 0; i-- {
   484  		if i != skip {
   485  			_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   486  				time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   487  			time.Sleep(time.Millisecond)
   488  		}
   489  	}
   490  	// Fill the missing block directly as if propagated
   491  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[skip]])
   492  	verifyImportCount(t, imported, len(hashes)-1)
   493  }
   494  
   495  // Tests that blocks arriving from various sources (multiple propagations, hash
   496  // announces, etc) do not get scheduled for import multiple times.
   497  func TestImportDeduplication62(t *testing.T) { testImportDeduplication(t, 62) }
   498  func TestImportDeduplication63(t *testing.T) { testImportDeduplication(t, 63) }
   499  func TestImportDeduplication64(t *testing.T) { testImportDeduplication(t, 64) }
   500  
   501  func testImportDeduplication(t *testing.T, protocol int) {
   502  	// Create two blocks to import (one for duplication, the other for stalling)
   503  	hashes, blocks := makeChain(2, 0, genesis)
   504  
   505  	// Create the tester and wrap the importer with a counter
   506  	tester := newTester()
   507  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   508  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   509  
   510  	counter := uint32(0)
   511  	tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) {
   512  		atomic.AddUint32(&counter, uint32(len(blocks)))
   513  		return tester.insertChain(blocks)
   514  	}
   515  	// Instrument the fetching and imported events
   516  	fetching := make(chan []common.Hash)
   517  	imported := make(chan *types.Block, len(hashes)-1)
   518  	tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes }
   519  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   520  
   521  	// Announce the duplicating block, wait for retrieval, and also propagate directly
   522  	_ = tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout),
   523  		headerFetcher, bodyFetcher)
   524  	<-fetching
   525  
   526  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[0]])
   527  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[0]])
   528  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[0]])
   529  
   530  	// Fill the missing block directly as if propagated, and check import uniqueness
   531  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[1]])
   532  	verifyImportCount(t, imported, 2)
   533  
   534  	if counter != 2 {
   535  		t.Fatalf("import invocation count mismatch: have %v, want %v", counter, 2)
   536  	}
   537  }
   538  
   539  // Tests that blocks with numbers much lower or higher than out current head get
   540  // discarded to prevent wasting resources on useless blocks from faulty peers.
   541  func TestDistantPropagationDiscarding(t *testing.T) {
   542  	// Create a long chain to import and define the discard boundaries
   543  	hashes, blocks := makeChain(3*maxQueueDist, 0, genesis)
   544  	head := hashes[len(hashes)/2]
   545  
   546  	low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1
   547  
   548  	// Create a tester and simulate a head block being the middle of the above chain
   549  	tester := newTester()
   550  
   551  	tester.lock.Lock()
   552  	tester.hashes = []common.Hash{head}
   553  	tester.blocks = map[common.Hash]*types.Block{head: blocks[head]}
   554  	tester.lock.Unlock()
   555  
   556  	// Ensure that a block with a lower number than the threshold is discarded
   557  	_ = tester.fetcher.Enqueue("lower", blocks[hashes[low]])
   558  	time.Sleep(10 * time.Millisecond)
   559  	if !tester.fetcher.queue.Empty() {
   560  		t.Fatalf("fetcher queued stale block")
   561  	}
   562  	// Ensure that a block with a higher number than the threshold is discarded
   563  	_ = tester.fetcher.Enqueue("higher", blocks[hashes[high]])
   564  	time.Sleep(10 * time.Millisecond)
   565  	if !tester.fetcher.queue.Empty() {
   566  		t.Fatalf("fetcher queued future block")
   567  	}
   568  }
   569  
   570  // Tests that announcements with numbers much lower or higher than out current
   571  // head get discarded to prevent wasting resources on useless blocks from faulty
   572  // peers.
   573  func TestDistantAnnouncementDiscarding62(t *testing.T) { testDistantAnnouncementDiscarding(t, 62) }
   574  func TestDistantAnnouncementDiscarding63(t *testing.T) { testDistantAnnouncementDiscarding(t, 63) }
   575  func TestDistantAnnouncementDiscarding64(t *testing.T) { testDistantAnnouncementDiscarding(t, 64) }
   576  
   577  func testDistantAnnouncementDiscarding(t *testing.T, protocol int) {
   578  	// Create a long chain to import and define the discard boundaries
   579  	hashes, blocks := makeChain(3*maxQueueDist, 0, genesis)
   580  	head := hashes[len(hashes)/2]
   581  
   582  	low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1
   583  
   584  	// Create a tester and simulate a head block being the middle of the above chain
   585  	tester := newTester()
   586  
   587  	tester.lock.Lock()
   588  	tester.hashes = []common.Hash{head}
   589  	tester.blocks = map[common.Hash]*types.Block{head: blocks[head]}
   590  	tester.lock.Unlock()
   591  
   592  	headerFetcher := tester.makeHeaderFetcher("lower", blocks, -gatherSlack)
   593  	bodyFetcher := tester.makeBodyFetcher("lower", blocks, 0)
   594  
   595  	fetching := make(chan struct{}, 2)
   596  	tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} }
   597  
   598  	// Ensure that a block with a lower number than the threshold is discarded
   599  	_ = tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(),
   600  		time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   601  	select {
   602  	case <-time.After(50 * time.Millisecond):
   603  	case <-fetching:
   604  		t.Fatalf("fetcher requested stale header")
   605  	}
   606  	// Ensure that a block with a higher number than the threshold is discarded
   607  	_ = tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(),
   608  		time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   609  	select {
   610  	case <-time.After(50 * time.Millisecond):
   611  	case <-fetching:
   612  		t.Fatalf("fetcher requested future header")
   613  	}
   614  }
   615  
   616  // Tests that peers announcing blocks with invalid numbers (i.e. not matching
   617  // the headers provided afterwards) get dropped as malicious.
   618  func TestInvalidNumberAnnouncement62(t *testing.T) { testInvalidNumberAnnouncement(t, 62) }
   619  func TestInvalidNumberAnnouncement63(t *testing.T) { testInvalidNumberAnnouncement(t, 63) }
   620  func TestInvalidNumberAnnouncement64(t *testing.T) { testInvalidNumberAnnouncement(t, 64) }
   621  
   622  func testInvalidNumberAnnouncement(t *testing.T, protocol int) {
   623  	// Create a single block to import and check numbers against
   624  	hashes, blocks := makeChain(1, 0, genesis)
   625  
   626  	tester := newTester()
   627  	badHeaderFetcher := tester.makeHeaderFetcher("bad", blocks, -gatherSlack)
   628  	badBodyFetcher := tester.makeBodyFetcher("bad", blocks, 0)
   629  
   630  	imported := make(chan *types.Block)
   631  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   632  
   633  	// Announce a block with a bad number, check for immediate drop
   634  	_ = tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout),
   635  		badHeaderFetcher, badBodyFetcher)
   636  	verifyImportEvent(t, imported, false)
   637  
   638  	tester.lock.RLock()
   639  	dropped := tester.drops["bad"]
   640  	tester.lock.RUnlock()
   641  
   642  	if !dropped {
   643  		t.Fatalf("peer with invalid numbered announcement not dropped")
   644  	}
   645  
   646  	goodHeaderFetcher := tester.makeHeaderFetcher("good", blocks, -gatherSlack)
   647  	goodBodyFetcher := tester.makeBodyFetcher("good", blocks, 0)
   648  	// Make sure a good announcement passes without a drop
   649  	_ = tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout),
   650  		goodHeaderFetcher, goodBodyFetcher)
   651  	verifyImportEvent(t, imported, true)
   652  
   653  	tester.lock.RLock()
   654  	dropped = tester.drops["good"]
   655  	tester.lock.RUnlock()
   656  
   657  	if dropped {
   658  		t.Fatalf("peer with valid numbered announcement dropped")
   659  	}
   660  	verifyImportDone(t, imported)
   661  }
   662  
   663  // Tests that if a block is empty (i.e. header only), no body request should be
   664  // made, and instead the header should be assembled into a whole block in itself.
   665  func TestEmptyBlockShortCircuit62(t *testing.T) { testEmptyBlockShortCircuit(t, 62) }
   666  func TestEmptyBlockShortCircuit63(t *testing.T) { testEmptyBlockShortCircuit(t, 63) }
   667  func TestEmptyBlockShortCircuit64(t *testing.T) { testEmptyBlockShortCircuit(t, 64) }
   668  
   669  func testEmptyBlockShortCircuit(t *testing.T, protocol int) {
   670  	// Create a chain of blocks to import
   671  	hashes, blocks := makeChain(32, 0, genesis)
   672  
   673  	tester := newTester()
   674  	headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   675  	bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   676  
   677  	// Add a monitoring hook for all internal events
   678  	fetching := make(chan []common.Hash)
   679  	tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes }
   680  
   681  	completing := make(chan []common.Hash)
   682  	tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes }
   683  
   684  	imported := make(chan *types.Block)
   685  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   686  
   687  	// Iteratively announce blocks until all are imported
   688  	for i := len(hashes) - 2; i >= 0; i-- {
   689  		_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   690  			time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
   691  
   692  		// All announces should fetch the header
   693  		verifyFetchingEvent(t, fetching, true)
   694  
   695  		// Only blocks with data contents should request bodies
   696  		verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0)
   697  
   698  		// Irrelevant of the construct, import should succeed
   699  		verifyImportEvent(t, imported, true)
   700  	}
   701  	verifyImportDone(t, imported)
   702  }
   703  
   704  // Tests that a peer is unable to use unbounded memory with sending infinite
   705  // block announcements to a node, but that even in the face of such an attack,
   706  // the fetcher remains operational.
   707  func TestHashMemoryExhaustionAttack62(t *testing.T) { testHashMemoryExhaustionAttack(t, 62) }
   708  func TestHashMemoryExhaustionAttack63(t *testing.T) { testHashMemoryExhaustionAttack(t, 63) }
   709  func TestHashMemoryExhaustionAttack64(t *testing.T) { testHashMemoryExhaustionAttack(t, 64) }
   710  
   711  func testHashMemoryExhaustionAttack(t *testing.T, protocol int) {
   712  	// Create a tester with instrumented import hooks
   713  	tester := newTester()
   714  
   715  	imported, announces := make(chan *types.Block), int32(0)
   716  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   717  	tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) {
   718  		if added {
   719  			atomic.AddInt32(&announces, 1)
   720  		} else {
   721  			atomic.AddInt32(&announces, -1)
   722  		}
   723  	}
   724  	// Create a valid chain and an infinite junk chain
   725  	targetBlocks := hashLimit + 2*maxQueueDist
   726  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   727  	validHeaderFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
   728  	validBodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
   729  
   730  	attack, _ := makeChain(targetBlocks, 0, unknownBlock)
   731  	attackerHeaderFetcher := tester.makeHeaderFetcher("attacker", nil, -gatherSlack)
   732  	attackerBodyFetcher := tester.makeBodyFetcher("attacker", nil, 0)
   733  
   734  	// Feed the tester a huge hashset from the attacker, and a limited from the valid peer
   735  	for i := 0; i < len(attack); i++ {
   736  		if i < maxQueueDist {
   737  			_ = tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1),
   738  				time.Now(), validHeaderFetcher, validBodyFetcher)
   739  		}
   740  		_ = tester.fetcher.Notify("attacker", attack[i], 1, /* don't distance drop */
   741  			time.Now(), attackerHeaderFetcher, attackerBodyFetcher)
   742  	}
   743  	if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist {
   744  		t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist)
   745  	}
   746  	// Wait for fetches to complete
   747  	verifyImportCount(t, imported, maxQueueDist)
   748  
   749  	// Feed the remaining valid hashes to ensure DOS protection state remains clean
   750  	for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- {
   751  		_ = tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1),
   752  			time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher)
   753  		verifyImportEvent(t, imported, true)
   754  	}
   755  	verifyImportDone(t, imported)
   756  }
   757  
   758  // Tests that blocks sent to the fetcher (either through propagation or via hash
   759  // announces and retrievals) don't pile up indefinitely, exhausting available
   760  // system memory.
   761  func TestBlockMemoryExhaustionAttack(t *testing.T) {
   762  	// Create a tester with instrumented import hooks
   763  	tester := newTester()
   764  
   765  	imported, enqueued := make(chan *types.Block), int32(0)
   766  	tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
   767  	tester.fetcher.queueChangeHook = func(hash common.Hash, added bool) {
   768  		if added {
   769  			atomic.AddInt32(&enqueued, 1)
   770  		} else {
   771  			atomic.AddInt32(&enqueued, -1)
   772  		}
   773  	}
   774  	// Create a valid chain and a batch of dangling (but in range) blocks
   775  	targetBlocks := hashLimit + 2*maxQueueDist
   776  	hashes, blocks := makeChain(targetBlocks, 0, genesis)
   777  	attack := make(map[common.Hash]*types.Block)
   778  	for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ {
   779  		hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock)
   780  		for _, hash := range hashes[:maxQueueDist-2] {
   781  			attack[hash] = blocks[hash]
   782  		}
   783  	}
   784  	// Try to feed all the attacker blocks make sure only a limited batch is accepted
   785  	for _, block := range attack {
   786  		_ = tester.fetcher.Enqueue("attacker", block)
   787  	}
   788  	time.Sleep(200 * time.Millisecond)
   789  	if queued := atomic.LoadInt32(&enqueued); queued != blockLimit {
   790  		t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit)
   791  	}
   792  	// Queue up a batch of valid blocks, and check that a new peer is allowed to do so
   793  	for i := 0; i < maxQueueDist-1; i++ {
   794  		_ = tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-3-i]])
   795  	}
   796  	time.Sleep(100 * time.Millisecond)
   797  	if queued := atomic.LoadInt32(&enqueued); queued != blockLimit+maxQueueDist-1 {
   798  		t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit+maxQueueDist-1)
   799  	}
   800  	// Insert the missing piece (and sanity check the import)
   801  	_ = tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2]])
   802  	verifyImportCount(t, imported, maxQueueDist)
   803  
   804  	// Insert the remaining blocks in chunks to ensure clean DOS protection
   805  	for i := maxQueueDist; i < len(hashes)-1; i++ {
   806  		_ = tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2-i]])
   807  		verifyImportEvent(t, imported, true)
   808  	}
   809  	verifyImportDone(t, imported)
   810  }