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