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