github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/downloader/downloader_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 downloader
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/consensus/ethash"
    30  	"github.com/ethereum/go-ethereum/core"
    31  	"github.com/ethereum/go-ethereum/core/rawdb"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/core/vm"
    34  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    35  	"github.com/ethereum/go-ethereum/eth/protocols/snap"
    36  	"github.com/ethereum/go-ethereum/event"
    37  	"github.com/ethereum/go-ethereum/log"
    38  	"github.com/ethereum/go-ethereum/params"
    39  	"github.com/ethereum/go-ethereum/rlp"
    40  	"github.com/ethereum/go-ethereum/trie"
    41  )
    42  
    43  // downloadTester is a test simulator for mocking out local block chain.
    44  type downloadTester struct {
    45  	chain      *core.BlockChain
    46  	downloader *Downloader
    47  
    48  	peers map[string]*downloadTesterPeer
    49  	lock  sync.RWMutex
    50  }
    51  
    52  // newTester creates a new downloader test mocker.
    53  func newTester(t *testing.T) *downloadTester {
    54  	return newTesterWithNotification(t, nil)
    55  }
    56  
    57  // newTesterWithNotification creates a new downloader test mocker.
    58  func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
    59  	db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
    60  	if err != nil {
    61  		panic(err)
    62  	}
    63  	t.Cleanup(func() {
    64  		db.Close()
    65  	})
    66  	gspec := &core.Genesis{
    67  		Config:  params.TestChainConfig,
    68  		Alloc:   types.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}},
    69  		BaseFee: big.NewInt(params.InitialBaseFee),
    70  	}
    71  	chain, err := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  	tester := &downloadTester{
    76  		chain: chain,
    77  		peers: make(map[string]*downloadTesterPeer),
    78  	}
    79  	tester.downloader = New(db, new(event.TypeMux), tester.chain, nil, tester.dropPeer, success)
    80  	return tester
    81  }
    82  
    83  // terminate aborts any operations on the embedded downloader and releases all
    84  // held resources.
    85  func (dl *downloadTester) terminate() {
    86  	dl.downloader.Terminate()
    87  	dl.chain.Stop()
    88  }
    89  
    90  // newPeer registers a new block download source into the downloader.
    91  func (dl *downloadTester) newPeer(id string, version uint, blocks []*types.Block) *downloadTesterPeer {
    92  	dl.lock.Lock()
    93  	defer dl.lock.Unlock()
    94  
    95  	peer := &downloadTesterPeer{
    96  		dl:             dl,
    97  		id:             id,
    98  		chain:          newTestBlockchain(blocks),
    99  		withholdBodies: make(map[common.Hash]struct{}),
   100  	}
   101  	dl.peers[id] = peer
   102  
   103  	if err := dl.downloader.RegisterPeer(id, version, peer); err != nil {
   104  		panic(err)
   105  	}
   106  	if err := dl.downloader.SnapSyncer.Register(peer); err != nil {
   107  		panic(err)
   108  	}
   109  	return peer
   110  }
   111  
   112  // dropPeer simulates a hard peer removal from the connection pool.
   113  func (dl *downloadTester) dropPeer(id string) {
   114  	dl.lock.Lock()
   115  	defer dl.lock.Unlock()
   116  
   117  	delete(dl.peers, id)
   118  	dl.downloader.SnapSyncer.Unregister(id)
   119  	dl.downloader.UnregisterPeer(id)
   120  }
   121  
   122  type downloadTesterPeer struct {
   123  	dl             *downloadTester
   124  	withholdBodies map[common.Hash]struct{}
   125  	id             string
   126  	chain          *core.BlockChain
   127  }
   128  
   129  // Head constructs a function to retrieve a peer's current head hash
   130  // and total difficulty.
   131  func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int) {
   132  	head := dlp.chain.CurrentBlock()
   133  	return head.Hash(), dlp.chain.GetTd(head.Hash(), head.Number.Uint64())
   134  }
   135  
   136  func unmarshalRlpHeaders(rlpdata []rlp.RawValue) []*types.Header {
   137  	var headers = make([]*types.Header, len(rlpdata))
   138  	for i, data := range rlpdata {
   139  		var h types.Header
   140  		if err := rlp.DecodeBytes(data, &h); err != nil {
   141  			panic(err)
   142  		}
   143  		headers[i] = &h
   144  	}
   145  	return headers
   146  }
   147  
   148  // RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed
   149  // origin; associated with a particular peer in the download tester. The returned
   150  // function can be used to retrieve batches of headers from the particular peer.
   151  func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
   152  	// Service the header query via the live handler code
   153  	rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, &eth.GetBlockHeadersRequest{
   154  		Origin: eth.HashOrNumber{
   155  			Hash: origin,
   156  		},
   157  		Amount:  uint64(amount),
   158  		Skip:    uint64(skip),
   159  		Reverse: reverse,
   160  	}, nil)
   161  	headers := unmarshalRlpHeaders(rlpHeaders)
   162  	hashes := make([]common.Hash, len(headers))
   163  	for i, header := range headers {
   164  		hashes[i] = header.Hash()
   165  	}
   166  	// Deliver the headers to the downloader
   167  	req := &eth.Request{
   168  		Peer: dlp.id,
   169  	}
   170  	res := &eth.Response{
   171  		Req:  req,
   172  		Res:  (*eth.BlockHeadersRequest)(&headers),
   173  		Meta: hashes,
   174  		Time: 1,
   175  		Done: make(chan error, 1), // Ignore the returned status
   176  	}
   177  	go func() {
   178  		sink <- res
   179  	}()
   180  	return req, nil
   181  }
   182  
   183  // RequestHeadersByNumber constructs a GetBlockHeaders function based on a numbered
   184  // origin; associated with a particular peer in the download tester. The returned
   185  // function can be used to retrieve batches of headers from the particular peer.
   186  func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) {
   187  	// Service the header query via the live handler code
   188  	rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, &eth.GetBlockHeadersRequest{
   189  		Origin: eth.HashOrNumber{
   190  			Number: origin,
   191  		},
   192  		Amount:  uint64(amount),
   193  		Skip:    uint64(skip),
   194  		Reverse: reverse,
   195  	}, nil)
   196  	headers := unmarshalRlpHeaders(rlpHeaders)
   197  	hashes := make([]common.Hash, len(headers))
   198  	for i, header := range headers {
   199  		hashes[i] = header.Hash()
   200  	}
   201  	// Deliver the headers to the downloader
   202  	req := &eth.Request{
   203  		Peer: dlp.id,
   204  	}
   205  	res := &eth.Response{
   206  		Req:  req,
   207  		Res:  (*eth.BlockHeadersRequest)(&headers),
   208  		Meta: hashes,
   209  		Time: 1,
   210  		Done: make(chan error, 1), // Ignore the returned status
   211  	}
   212  	go func() {
   213  		sink <- res
   214  	}()
   215  	return req, nil
   216  }
   217  
   218  // RequestBodies constructs a getBlockBodies method associated with a particular
   219  // peer in the download tester. The returned function can be used to retrieve
   220  // batches of block bodies from the particularly requested peer.
   221  func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
   222  	blobs := eth.ServiceGetBlockBodiesQuery(dlp.chain, hashes)
   223  
   224  	bodies := make([]*eth.BlockBody, len(blobs))
   225  	for i, blob := range blobs {
   226  		bodies[i] = new(eth.BlockBody)
   227  		rlp.DecodeBytes(blob, bodies[i])
   228  	}
   229  	var (
   230  		txsHashes        = make([]common.Hash, len(bodies))
   231  		uncleHashes      = make([]common.Hash, len(bodies))
   232  		withdrawalHashes = make([]common.Hash, len(bodies))
   233  	)
   234  	hasher := trie.NewStackTrie(nil)
   235  	for i, body := range bodies {
   236  		hash := types.DeriveSha(types.Transactions(body.Transactions), hasher)
   237  		if _, ok := dlp.withholdBodies[hash]; ok {
   238  			txsHashes = append(txsHashes[:i], txsHashes[i+1:]...)
   239  			uncleHashes = append(uncleHashes[:i], uncleHashes[i+1:]...)
   240  			continue
   241  		}
   242  		txsHashes[i] = hash
   243  		uncleHashes[i] = types.CalcUncleHash(body.Uncles)
   244  	}
   245  	req := &eth.Request{
   246  		Peer: dlp.id,
   247  	}
   248  	res := &eth.Response{
   249  		Req:  req,
   250  		Res:  (*eth.BlockBodiesResponse)(&bodies),
   251  		Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes},
   252  		Time: 1,
   253  		Done: make(chan error, 1), // Ignore the returned status
   254  	}
   255  	go func() {
   256  		sink <- res
   257  	}()
   258  	return req, nil
   259  }
   260  
   261  // RequestReceipts constructs a getReceipts method associated with a particular
   262  // peer in the download tester. The returned function can be used to retrieve
   263  // batches of block receipts from the particularly requested peer.
   264  func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
   265  	blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes)
   266  
   267  	receipts := make([][]*types.Receipt, len(blobs))
   268  	for i, blob := range blobs {
   269  		rlp.DecodeBytes(blob, &receipts[i])
   270  	}
   271  	hasher := trie.NewStackTrie(nil)
   272  	hashes = make([]common.Hash, len(receipts))
   273  	for i, receipt := range receipts {
   274  		hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher)
   275  	}
   276  	req := &eth.Request{
   277  		Peer: dlp.id,
   278  	}
   279  	res := &eth.Response{
   280  		Req:  req,
   281  		Res:  (*eth.ReceiptsResponse)(&receipts),
   282  		Meta: hashes,
   283  		Time: 1,
   284  		Done: make(chan error, 1), // Ignore the returned status
   285  	}
   286  	go func() {
   287  		sink <- res
   288  	}()
   289  	return req, nil
   290  }
   291  
   292  // ID retrieves the peer's unique identifier.
   293  func (dlp *downloadTesterPeer) ID() string {
   294  	return dlp.id
   295  }
   296  
   297  // RequestAccountRange fetches a batch of accounts rooted in a specific account
   298  // trie, starting with the origin.
   299  func (dlp *downloadTesterPeer) RequestAccountRange(id uint64, root, origin, limit common.Hash, bytes uint64) error {
   300  	// Create the request and service it
   301  	req := &snap.GetAccountRangePacket{
   302  		ID:     id,
   303  		Root:   root,
   304  		Origin: origin,
   305  		Limit:  limit,
   306  		Bytes:  bytes,
   307  	}
   308  	slimaccs, proofs := snap.ServiceGetAccountRangeQuery(dlp.chain, req)
   309  
   310  	// We need to convert to non-slim format, delegate to the packet code
   311  	res := &snap.AccountRangePacket{
   312  		ID:       id,
   313  		Accounts: slimaccs,
   314  		Proof:    proofs,
   315  	}
   316  	hashes, accounts, _ := res.Unpack()
   317  
   318  	go dlp.dl.downloader.SnapSyncer.OnAccounts(dlp, id, hashes, accounts, proofs)
   319  	return nil
   320  }
   321  
   322  // RequestStorageRanges fetches a batch of storage slots belonging to one or
   323  // more accounts. If slots from only one account is requested, an origin marker
   324  // may also be used to retrieve from there.
   325  func (dlp *downloadTesterPeer) RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error {
   326  	// Create the request and service it
   327  	req := &snap.GetStorageRangesPacket{
   328  		ID:       id,
   329  		Accounts: accounts,
   330  		Root:     root,
   331  		Origin:   origin,
   332  		Limit:    limit,
   333  		Bytes:    bytes,
   334  	}
   335  	storage, proofs := snap.ServiceGetStorageRangesQuery(dlp.chain, req)
   336  
   337  	// We need to convert to demultiplex, delegate to the packet code
   338  	res := &snap.StorageRangesPacket{
   339  		ID:    id,
   340  		Slots: storage,
   341  		Proof: proofs,
   342  	}
   343  	hashes, slots := res.Unpack()
   344  
   345  	go dlp.dl.downloader.SnapSyncer.OnStorage(dlp, id, hashes, slots, proofs)
   346  	return nil
   347  }
   348  
   349  // RequestByteCodes fetches a batch of bytecodes by hash.
   350  func (dlp *downloadTesterPeer) RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error {
   351  	req := &snap.GetByteCodesPacket{
   352  		ID:     id,
   353  		Hashes: hashes,
   354  		Bytes:  bytes,
   355  	}
   356  	codes := snap.ServiceGetByteCodesQuery(dlp.chain, req)
   357  	go dlp.dl.downloader.SnapSyncer.OnByteCodes(dlp, id, codes)
   358  	return nil
   359  }
   360  
   361  // RequestTrieNodes fetches a batch of account or storage trie nodes rooted in
   362  // a specific state trie.
   363  func (dlp *downloadTesterPeer) RequestTrieNodes(id uint64, root common.Hash, paths []snap.TrieNodePathSet, bytes uint64) error {
   364  	req := &snap.GetTrieNodesPacket{
   365  		ID:    id,
   366  		Root:  root,
   367  		Paths: paths,
   368  		Bytes: bytes,
   369  	}
   370  	nodes, _ := snap.ServiceGetTrieNodesQuery(dlp.chain, req, time.Now())
   371  	go dlp.dl.downloader.SnapSyncer.OnTrieNodes(dlp, id, nodes)
   372  	return nil
   373  }
   374  
   375  // Log retrieves the peer's own contextual logger.
   376  func (dlp *downloadTesterPeer) Log() log.Logger {
   377  	return log.New("peer", dlp.id)
   378  }
   379  
   380  // assertOwnChain checks if the local chain contains the correct number of items
   381  // of the various chain components.
   382  func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
   383  	// Mark this method as a helper to report errors at callsite, not in here
   384  	t.Helper()
   385  
   386  	headers, blocks, receipts := length, length, length
   387  	if tester.downloader.getMode() == LightSync {
   388  		blocks, receipts = 1, 1
   389  	}
   390  	if hs := int(tester.chain.CurrentHeader().Number.Uint64()) + 1; hs != headers {
   391  		t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers)
   392  	}
   393  	if bs := int(tester.chain.CurrentBlock().Number.Uint64()) + 1; bs != blocks {
   394  		t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks)
   395  	}
   396  	if rs := int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1; rs != receipts {
   397  		t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts)
   398  	}
   399  }
   400  
   401  func TestCanonicalSynchronisation68Full(t *testing.T)  { testCanonSync(t, eth.ETH68, FullSync) }
   402  func TestCanonicalSynchronisation68Snap(t *testing.T)  { testCanonSync(t, eth.ETH68, SnapSync) }
   403  func TestCanonicalSynchronisation68Light(t *testing.T) { testCanonSync(t, eth.ETH68, LightSync) }
   404  
   405  func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
   406  	success := make(chan struct{})
   407  	tester := newTesterWithNotification(t, func() {
   408  		close(success)
   409  	})
   410  	defer tester.terminate()
   411  
   412  	// Create a small enough block chain to download
   413  	chain := testChainBase.shorten(blockCacheMaxItems - 15)
   414  	tester.newPeer("peer", protocol, chain.blocks[1:])
   415  
   416  	// Synchronise with the peer and make sure all relevant data was retrieved
   417  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   418  		t.Fatalf("failed to beacon-sync chain: %v", err)
   419  	}
   420  	select {
   421  	case <-success:
   422  		assertOwnChain(t, tester, len(chain.blocks))
   423  	case <-time.NewTimer(time.Second * 3).C:
   424  		t.Fatalf("Failed to sync chain in three seconds")
   425  	}
   426  }
   427  
   428  // Tests that if a large batch of blocks are being downloaded, it is throttled
   429  // until the cached blocks are retrieved.
   430  func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH68, FullSync) }
   431  func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH68, SnapSync) }
   432  
   433  func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
   434  	tester := newTester(t)
   435  	defer tester.terminate()
   436  
   437  	// Create a long block chain to download and the tester
   438  	targetBlocks := len(testChainBase.blocks) - 1
   439  	tester.newPeer("peer", protocol, testChainBase.blocks[1:])
   440  
   441  	// Wrap the importer to allow stepping
   442  	var blocked atomic.Uint32
   443  	proceed := make(chan struct{})
   444  	tester.downloader.chainInsertHook = func(results []*fetchResult) {
   445  		blocked.Store(uint32(len(results)))
   446  		<-proceed
   447  	}
   448  	// Start a synchronisation concurrently
   449  	errc := make(chan error, 1)
   450  	go func() {
   451  		errc <- tester.downloader.BeaconSync(mode, testChainBase.blocks[len(testChainBase.blocks)-1].Header(), nil)
   452  	}()
   453  	// Iteratively take some blocks, always checking the retrieval count
   454  	for {
   455  		// Check the retrieval count synchronously (! reason for this ugly block)
   456  		tester.lock.RLock()
   457  		retrieved := int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1
   458  		tester.lock.RUnlock()
   459  		if retrieved >= targetBlocks+1 {
   460  			break
   461  		}
   462  		// Wait a bit for sync to throttle itself
   463  		var cached, frozen int
   464  		for start := time.Now(); time.Since(start) < 3*time.Second; {
   465  			time.Sleep(25 * time.Millisecond)
   466  
   467  			tester.lock.Lock()
   468  			tester.downloader.queue.lock.Lock()
   469  			tester.downloader.queue.resultCache.lock.Lock()
   470  			{
   471  				cached = tester.downloader.queue.resultCache.countCompleted()
   472  				frozen = int(blocked.Load())
   473  				retrieved = int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1
   474  			}
   475  			tester.downloader.queue.resultCache.lock.Unlock()
   476  			tester.downloader.queue.lock.Unlock()
   477  			tester.lock.Unlock()
   478  
   479  			if cached == blockCacheMaxItems ||
   480  				cached == blockCacheMaxItems-reorgProtHeaderDelay ||
   481  				retrieved+cached+frozen == targetBlocks+1 ||
   482  				retrieved+cached+frozen == targetBlocks+1-reorgProtHeaderDelay {
   483  				break
   484  			}
   485  		}
   486  		// Make sure we filled up the cache, then exhaust it
   487  		time.Sleep(25 * time.Millisecond) // give it a chance to screw up
   488  		tester.lock.RLock()
   489  		retrieved = int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1
   490  		tester.lock.RUnlock()
   491  		if cached != blockCacheMaxItems && cached != blockCacheMaxItems-reorgProtHeaderDelay && retrieved+cached+frozen != targetBlocks+1 && retrieved+cached+frozen != targetBlocks+1-reorgProtHeaderDelay {
   492  			t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheMaxItems, retrieved, frozen, targetBlocks+1)
   493  		}
   494  		// Permit the blocked blocks to import
   495  		if blocked.Load() > 0 {
   496  			blocked.Store(uint32(0))
   497  			proceed <- struct{}{}
   498  		}
   499  	}
   500  	// Check that we haven't pulled more blocks than available
   501  	assertOwnChain(t, tester, targetBlocks+1)
   502  	if err := <-errc; err != nil {
   503  		t.Fatalf("block synchronization failed: %v", err)
   504  	}
   505  }
   506  
   507  // Tests that a canceled download wipes all previously accumulated state.
   508  func TestCancel68Full(t *testing.T)  { testCancel(t, eth.ETH68, FullSync) }
   509  func TestCancel68Snap(t *testing.T)  { testCancel(t, eth.ETH68, SnapSync) }
   510  func TestCancel68Light(t *testing.T) { testCancel(t, eth.ETH68, LightSync) }
   511  
   512  func testCancel(t *testing.T, protocol uint, mode SyncMode) {
   513  	complete := make(chan struct{})
   514  	success := func() {
   515  		close(complete)
   516  	}
   517  	tester := newTesterWithNotification(t, success)
   518  	defer tester.terminate()
   519  
   520  	chain := testChainBase.shorten(MaxHeaderFetch)
   521  	tester.newPeer("peer", protocol, chain.blocks[1:])
   522  
   523  	// Make sure canceling works with a pristine downloader
   524  	tester.downloader.Cancel()
   525  	if !tester.downloader.queue.Idle() {
   526  		t.Errorf("download queue not idle")
   527  	}
   528  	// Synchronise with the peer, but cancel afterwards
   529  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   530  		t.Fatalf("failed to synchronise blocks: %v", err)
   531  	}
   532  	<-complete
   533  	tester.downloader.Cancel()
   534  	if !tester.downloader.queue.Idle() {
   535  		t.Errorf("download queue not idle")
   536  	}
   537  }
   538  
   539  // Tests that synchronisations behave well in multi-version protocol environments
   540  // and not wreak havoc on other nodes in the network.
   541  func TestMultiProtoSynchronisation68Full(t *testing.T)  { testMultiProtoSync(t, eth.ETH68, FullSync) }
   542  func TestMultiProtoSynchronisation68Snap(t *testing.T)  { testMultiProtoSync(t, eth.ETH68, SnapSync) }
   543  func TestMultiProtoSynchronisation68Light(t *testing.T) { testMultiProtoSync(t, eth.ETH68, LightSync) }
   544  
   545  func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
   546  	complete := make(chan struct{})
   547  	success := func() {
   548  		close(complete)
   549  	}
   550  	tester := newTesterWithNotification(t, success)
   551  	defer tester.terminate()
   552  
   553  	// Create a small enough block chain to download
   554  	chain := testChainBase.shorten(blockCacheMaxItems - 15)
   555  
   556  	// Create peers of every type
   557  	tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:])
   558  
   559  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   560  		t.Fatalf("failed to start beacon sync: #{err}")
   561  	}
   562  	select {
   563  	case <-complete:
   564  		break
   565  	case <-time.NewTimer(time.Second * 3).C:
   566  		t.Fatalf("Failed to sync chain in three seconds")
   567  	}
   568  	assertOwnChain(t, tester, len(chain.blocks))
   569  
   570  	// Check that no peers have been dropped off
   571  	for _, version := range []int{68} {
   572  		peer := fmt.Sprintf("peer %d", version)
   573  		if _, ok := tester.peers[peer]; !ok {
   574  			t.Errorf("%s dropped", peer)
   575  		}
   576  	}
   577  }
   578  
   579  // Tests that if a block is empty (e.g. header only), no body request should be
   580  // made, and instead the header should be assembled into a whole block in itself.
   581  func TestEmptyShortCircuit68Full(t *testing.T)  { testEmptyShortCircuit(t, eth.ETH68, FullSync) }
   582  func TestEmptyShortCircuit68Snap(t *testing.T)  { testEmptyShortCircuit(t, eth.ETH68, SnapSync) }
   583  func TestEmptyShortCircuit68Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, LightSync) }
   584  
   585  func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
   586  	success := make(chan struct{})
   587  	tester := newTesterWithNotification(t, func() {
   588  		close(success)
   589  	})
   590  	defer tester.terminate()
   591  
   592  	// Create a block chain to download
   593  	chain := testChainBase
   594  	tester.newPeer("peer", protocol, chain.blocks[1:])
   595  
   596  	// Instrument the downloader to signal body requests
   597  	var bodiesHave, receiptsHave atomic.Int32
   598  	tester.downloader.bodyFetchHook = func(headers []*types.Header) {
   599  		bodiesHave.Add(int32(len(headers)))
   600  	}
   601  	tester.downloader.receiptFetchHook = func(headers []*types.Header) {
   602  		receiptsHave.Add(int32(len(headers)))
   603  	}
   604  
   605  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   606  		t.Fatalf("failed to synchronise blocks: %v", err)
   607  	}
   608  	select {
   609  	case <-success:
   610  		checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
   611  			HighestBlock: uint64(len(chain.blocks) - 1),
   612  			CurrentBlock: uint64(len(chain.blocks) - 1),
   613  		})
   614  	case <-time.NewTimer(time.Second * 3).C:
   615  		t.Fatalf("Failed to sync chain in three seconds")
   616  	}
   617  	assertOwnChain(t, tester, len(chain.blocks))
   618  
   619  	// Validate the number of block bodies that should have been requested
   620  	bodiesNeeded, receiptsNeeded := 0, 0
   621  	for _, block := range chain.blocks[1:] {
   622  		if mode != LightSync && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) {
   623  			bodiesNeeded++
   624  		}
   625  	}
   626  	for _, block := range chain.blocks[1:] {
   627  		if mode == SnapSync && len(block.Transactions()) > 0 {
   628  			receiptsNeeded++
   629  		}
   630  	}
   631  	if int(bodiesHave.Load()) != bodiesNeeded {
   632  		t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave.Load(), bodiesNeeded)
   633  	}
   634  	if int(receiptsHave.Load()) != receiptsNeeded {
   635  		t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave.Load(), receiptsNeeded)
   636  	}
   637  }
   638  
   639  func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.SyncProgress) {
   640  	// Mark this method as a helper to report errors at callsite, not in here
   641  	t.Helper()
   642  
   643  	p := d.Progress()
   644  	if p.StartingBlock != want.StartingBlock || p.CurrentBlock != want.CurrentBlock || p.HighestBlock != want.HighestBlock {
   645  		t.Fatalf("%s progress mismatch:\nhave %+v\nwant %+v", stage, p, want)
   646  	}
   647  }
   648  
   649  // Tests that peers below a pre-configured checkpoint block are prevented from
   650  // being fast-synced from, avoiding potential cheap eclipse attacks.
   651  func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH68, FullSync) }
   652  func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH68, SnapSync) }
   653  
   654  func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
   655  	var cases = []struct {
   656  		name  string // The name of testing scenario
   657  		local int    // The length of local chain(canonical chain assumed), 0 means genesis is the head
   658  	}{
   659  		{name: "Beacon sync since genesis", local: 0},
   660  		{name: "Beacon sync with short local chain", local: 1},
   661  		{name: "Beacon sync with long local chain", local: blockCacheMaxItems - 15 - fsMinFullBlocks/2},
   662  		{name: "Beacon sync with full local chain", local: blockCacheMaxItems - 15 - 1},
   663  	}
   664  	for _, c := range cases {
   665  		t.Run(c.name, func(t *testing.T) {
   666  			success := make(chan struct{})
   667  			tester := newTesterWithNotification(t, func() {
   668  				close(success)
   669  			})
   670  			defer tester.terminate()
   671  
   672  			chain := testChainBase.shorten(blockCacheMaxItems - 15)
   673  			tester.newPeer("peer", protocol, chain.blocks[1:])
   674  
   675  			// Build the local chain segment if it's required
   676  			if c.local > 0 {
   677  				tester.chain.InsertChain(chain.blocks[1 : c.local+1])
   678  			}
   679  			if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   680  				t.Fatalf("Failed to beacon sync chain %v %v", c.name, err)
   681  			}
   682  			select {
   683  			case <-success:
   684  				// Ok, downloader fully cancelled after sync cycle
   685  				if bs := int(tester.chain.CurrentBlock().Number.Uint64()) + 1; bs != len(chain.blocks) {
   686  					t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(chain.blocks))
   687  				}
   688  			case <-time.NewTimer(time.Second * 3).C:
   689  				t.Fatalf("Failed to sync chain in three seconds")
   690  			}
   691  		})
   692  	}
   693  }
   694  
   695  // Tests that synchronisation progress (origin block number, current block number
   696  // and highest block number) is tracked and updated correctly.
   697  func TestSyncProgress68Full(t *testing.T)  { testSyncProgress(t, eth.ETH68, FullSync) }
   698  func TestSyncProgress68Snap(t *testing.T)  { testSyncProgress(t, eth.ETH68, SnapSync) }
   699  func TestSyncProgress68Light(t *testing.T) { testSyncProgress(t, eth.ETH68, LightSync) }
   700  
   701  func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
   702  	success := make(chan struct{})
   703  	tester := newTesterWithNotification(t, func() {
   704  		success <- struct{}{}
   705  	})
   706  	defer tester.terminate()
   707  	checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
   708  
   709  	chain := testChainBase.shorten(blockCacheMaxItems - 15)
   710  	shortChain := chain.shorten(len(chain.blocks) / 2).blocks[1:]
   711  
   712  	// Connect to peer that provides all headers and part of the bodies
   713  	faultyPeer := tester.newPeer("peer-half", protocol, shortChain)
   714  	for _, header := range shortChain {
   715  		faultyPeer.withholdBodies[header.Hash()] = struct{}{}
   716  	}
   717  
   718  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)/2-1].Header(), nil); err != nil {
   719  		t.Fatalf("failed to beacon-sync chain: %v", err)
   720  	}
   721  	select {
   722  	case <-success:
   723  		// Ok, downloader fully cancelled after sync cycle
   724  		checkProgress(t, tester.downloader, "peer-half", ethereum.SyncProgress{
   725  			CurrentBlock: uint64(len(chain.blocks)/2 - 1),
   726  			HighestBlock: uint64(len(chain.blocks)/2 - 1),
   727  		})
   728  	case <-time.NewTimer(time.Second * 3).C:
   729  		t.Fatalf("Failed to sync chain in three seconds")
   730  	}
   731  
   732  	// Synchronise all the blocks and check continuation progress
   733  	tester.newPeer("peer-full", protocol, chain.blocks[1:])
   734  	if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
   735  		t.Fatalf("failed to beacon-sync chain: %v", err)
   736  	}
   737  	var startingBlock uint64
   738  	if mode == LightSync {
   739  		// in light-sync mode:
   740  		// * the starting block is 0 on the second sync cycle because blocks
   741  		//   are never downloaded.
   742  		// * The current/highest blocks reported in the progress reflect the
   743  		//   current/highest header.
   744  		startingBlock = 0
   745  	} else {
   746  		startingBlock = uint64(len(chain.blocks)/2 - 1)
   747  	}
   748  
   749  	select {
   750  	case <-success:
   751  		// Ok, downloader fully cancelled after sync cycle
   752  		checkProgress(t, tester.downloader, "peer-full", ethereum.SyncProgress{
   753  			StartingBlock: startingBlock,
   754  			CurrentBlock:  uint64(len(chain.blocks) - 1),
   755  			HighestBlock:  uint64(len(chain.blocks) - 1),
   756  		})
   757  	case <-time.NewTimer(time.Second * 3).C:
   758  		t.Fatalf("Failed to sync chain in three seconds")
   759  	}
   760  }