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