github.com/klaytn/klaytn@v1.12.1/datasync/fetcher/fetcher_test.go (about)

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