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