github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/les/test_helper.go (about)

     1  // Copyright 2019 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  // This file contains some shares testing functionality, common to multiple
    18  // different files and modules being tested. Client based network and Server
    19  // based network can be created easily with available APIs.
    20  
    21  package les
    22  
    23  import (
    24  	"context"
    25  	"crypto/rand"
    26  	"fmt"
    27  	"math/big"
    28  	"sync/atomic"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/tirogen/go-ethereum/accounts/abi/bind"
    33  	"github.com/tirogen/go-ethereum/accounts/abi/bind/backends"
    34  	"github.com/tirogen/go-ethereum/common"
    35  	"github.com/tirogen/go-ethereum/common/mclock"
    36  	"github.com/tirogen/go-ethereum/consensus"
    37  	"github.com/tirogen/go-ethereum/consensus/ethash"
    38  	"github.com/tirogen/go-ethereum/contracts/checkpointoracle/contract"
    39  	"github.com/tirogen/go-ethereum/core"
    40  	"github.com/tirogen/go-ethereum/core/forkid"
    41  	"github.com/tirogen/go-ethereum/core/rawdb"
    42  	"github.com/tirogen/go-ethereum/core/txpool"
    43  	"github.com/tirogen/go-ethereum/core/types"
    44  	"github.com/tirogen/go-ethereum/crypto"
    45  	"github.com/tirogen/go-ethereum/eth/ethconfig"
    46  	"github.com/tirogen/go-ethereum/ethdb"
    47  	"github.com/tirogen/go-ethereum/event"
    48  	"github.com/tirogen/go-ethereum/les/checkpointoracle"
    49  	"github.com/tirogen/go-ethereum/les/flowcontrol"
    50  	vfs "github.com/tirogen/go-ethereum/les/vflux/server"
    51  	"github.com/tirogen/go-ethereum/light"
    52  	"github.com/tirogen/go-ethereum/p2p"
    53  	"github.com/tirogen/go-ethereum/p2p/enode"
    54  	"github.com/tirogen/go-ethereum/params"
    55  )
    56  
    57  var (
    58  	bankKey, _ = crypto.GenerateKey()
    59  	bankAddr   = crypto.PubkeyToAddress(bankKey.PublicKey)
    60  	bankFunds  = big.NewInt(1_000_000_000_000_000_000)
    61  
    62  	userKey1, _ = crypto.GenerateKey()
    63  	userKey2, _ = crypto.GenerateKey()
    64  	userAddr1   = crypto.PubkeyToAddress(userKey1.PublicKey)
    65  	userAddr2   = crypto.PubkeyToAddress(userKey2.PublicKey)
    66  
    67  	testContractAddr         common.Address
    68  	testContractCode         = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
    69  	testContractCodeDeployed = testContractCode[16:]
    70  	testContractDeployed     = uint64(2)
    71  
    72  	testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029")
    73  
    74  	// Checkpoint oracle relative fields
    75  	oracleAddr   common.Address
    76  	signerKey, _ = crypto.GenerateKey()
    77  	signerAddr   = crypto.PubkeyToAddress(signerKey.PublicKey)
    78  )
    79  
    80  var (
    81  	// The block frequency for creating checkpoint(only used in test)
    82  	sectionSize = big.NewInt(128)
    83  
    84  	// The number of confirmations needed to generate a checkpoint(only used in test).
    85  	processConfirms = big.NewInt(1)
    86  
    87  	// The token bucket buffer limit for testing purpose.
    88  	testBufLimit = uint64(1000000)
    89  
    90  	// The buffer recharging speed for testing purpose.
    91  	testBufRecharge = uint64(1000)
    92  )
    93  
    94  /*
    95  contract test {
    96  
    97      uint256[100] data;
    98  
    99      function Put(uint256 addr, uint256 value) {
   100          data[addr] = value;
   101      }
   102  
   103      function Get(uint256 addr) constant returns (uint256 value) {
   104          return data[addr];
   105      }
   106  }
   107  */
   108  
   109  // prepare pre-commits specified number customized blocks into chain.
   110  func prepare(n int, backend *backends.SimulatedBackend) {
   111  	var (
   112  		ctx    = context.Background()
   113  		signer = types.HomesteadSigner{}
   114  	)
   115  	for i := 0; i < n; i++ {
   116  		switch i {
   117  		case 0:
   118  			// Builtin-block
   119  			//    number: 1
   120  			//    txs:    2
   121  
   122  			// deploy checkpoint contract
   123  			auth, _ := bind.NewKeyedTransactorWithChainID(bankKey, big.NewInt(1337))
   124  			oracleAddr, _, _, _ = contract.DeployCheckpointOracle(auth, backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1))
   125  
   126  			// bankUser transfers some ether to user1
   127  			nonce, _ := backend.PendingNonceAt(ctx, bankAddr)
   128  			tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
   129  			backend.SendTransaction(ctx, tx)
   130  		case 1:
   131  			// Builtin-block
   132  			//    number: 2
   133  			//    txs:    4
   134  
   135  			bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
   136  			userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1)
   137  
   138  			// bankUser transfers more ether to user1
   139  			tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
   140  			backend.SendTransaction(ctx, tx1)
   141  
   142  			// user1 relays ether to user2
   143  			tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, userKey1)
   144  			backend.SendTransaction(ctx, tx2)
   145  
   146  			// user1 deploys a test contract
   147  			tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testContractCode), signer, userKey1)
   148  			backend.SendTransaction(ctx, tx3)
   149  			testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1)
   150  
   151  			// user1 deploys a event contract
   152  			tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testEventEmitterCode), signer, userKey1)
   153  			backend.SendTransaction(ctx, tx4)
   154  		case 2:
   155  			// Builtin-block
   156  			//    number: 3
   157  			//    txs:    2
   158  
   159  			// bankUser transfer some ether to signer
   160  			bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
   161  			tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey)
   162  			backend.SendTransaction(ctx, tx1)
   163  
   164  			// invoke test contract
   165  			data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
   166  			tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey)
   167  			backend.SendTransaction(ctx, tx2)
   168  		case 3:
   169  			// Builtin-block
   170  			//    number: 4
   171  			//    txs:    1
   172  
   173  			// invoke test contract
   174  			bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
   175  			data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
   176  			tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey)
   177  			backend.SendTransaction(ctx, tx)
   178  		}
   179  		backend.Commit()
   180  	}
   181  }
   182  
   183  // testIndexers creates a set of indexers with specified params for testing purpose.
   184  func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig, disablePruning bool) []*core.ChainIndexer {
   185  	var indexers [3]*core.ChainIndexer
   186  	indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning)
   187  	indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms)
   188  	indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning)
   189  	// make bloomTrieIndexer as a child indexer of bloom indexer.
   190  	indexers[1].AddChildIndexer(indexers[2])
   191  	return indexers[:]
   192  }
   193  
   194  func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *serverPeerSet, ulcServers []string, ulcFraction int) (*clientHandler, func()) {
   195  	var (
   196  		evmux  = new(event.TypeMux)
   197  		engine = ethash.NewFaker()
   198  		gspec  = core.Genesis{
   199  			Config:   params.AllEthashProtocolChanges,
   200  			Alloc:    core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
   201  			GasLimit: 100000000,
   202  			BaseFee:  big.NewInt(params.InitialBaseFee),
   203  		}
   204  		oracle *checkpointoracle.CheckpointOracle
   205  	)
   206  	genesis := gspec.MustCommit(db)
   207  	chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil)
   208  	if indexers != nil {
   209  		checkpointConfig := &params.CheckpointOracleConfig{
   210  			Address:   crypto.CreateAddress(bankAddr, 0),
   211  			Signers:   []common.Address{signerAddr},
   212  			Threshold: 1,
   213  		}
   214  		getLocal := func(index uint64) params.TrustedCheckpoint {
   215  			chtIndexer := indexers[0]
   216  			sectionHead := chtIndexer.SectionHead(index)
   217  			return params.TrustedCheckpoint{
   218  				SectionIndex: index,
   219  				SectionHead:  sectionHead,
   220  				CHTRoot:      light.GetChtRoot(db, index, sectionHead),
   221  				BloomRoot:    light.GetBloomTrieRoot(db, index, sectionHead),
   222  			}
   223  		}
   224  		oracle = checkpointoracle.New(checkpointConfig, getLocal)
   225  	}
   226  	client := &LightEthereum{
   227  		lesCommons: lesCommons{
   228  			genesis:     genesis.Hash(),
   229  			config:      &ethconfig.Config{LightPeers: 100, NetworkId: NetworkId},
   230  			chainConfig: params.AllEthashProtocolChanges,
   231  			iConfig:     light.TestClientIndexerConfig,
   232  			chainDb:     db,
   233  			oracle:      oracle,
   234  			chainReader: chain,
   235  			closeCh:     make(chan struct{}),
   236  		},
   237  		peers:      peers,
   238  		reqDist:    odr.retriever.dist,
   239  		retriever:  odr.retriever,
   240  		odr:        odr,
   241  		engine:     engine,
   242  		blockchain: chain,
   243  		eventMux:   evmux,
   244  		merger:     consensus.NewMerger(rawdb.NewMemoryDatabase()),
   245  	}
   246  	client.handler = newClientHandler(ulcServers, ulcFraction, nil, client)
   247  
   248  	if client.oracle != nil {
   249  		client.oracle.Start(backend)
   250  	}
   251  	client.handler.start()
   252  	return client.handler, func() {
   253  		client.handler.stop()
   254  	}
   255  }
   256  
   257  func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend, func()) {
   258  	var (
   259  		gspec = core.Genesis{
   260  			Config:   params.AllEthashProtocolChanges,
   261  			Alloc:    core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
   262  			GasLimit: 100000000,
   263  			BaseFee:  big.NewInt(params.InitialBaseFee),
   264  		}
   265  		oracle *checkpointoracle.CheckpointOracle
   266  	)
   267  	genesis := gspec.MustCommit(db)
   268  
   269  	// create a simulation backend and pre-commit several customized block to the database.
   270  	simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000)
   271  	prepare(blocks, simulation)
   272  
   273  	txpoolConfig := txpool.DefaultConfig
   274  	txpoolConfig.Journal = ""
   275  	txpool := txpool.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain())
   276  	if indexers != nil {
   277  		checkpointConfig := &params.CheckpointOracleConfig{
   278  			Address:   crypto.CreateAddress(bankAddr, 0),
   279  			Signers:   []common.Address{signerAddr},
   280  			Threshold: 1,
   281  		}
   282  		getLocal := func(index uint64) params.TrustedCheckpoint {
   283  			chtIndexer := indexers[0]
   284  			sectionHead := chtIndexer.SectionHead(index)
   285  			return params.TrustedCheckpoint{
   286  				SectionIndex: index,
   287  				SectionHead:  sectionHead,
   288  				CHTRoot:      light.GetChtRoot(db, index, sectionHead),
   289  				BloomRoot:    light.GetBloomTrieRoot(db, index, sectionHead),
   290  			}
   291  		}
   292  		oracle = checkpointoracle.New(checkpointConfig, getLocal)
   293  	}
   294  	server := &LesServer{
   295  		lesCommons: lesCommons{
   296  			genesis:     genesis.Hash(),
   297  			config:      &ethconfig.Config{LightPeers: 100, NetworkId: NetworkId},
   298  			chainConfig: params.AllEthashProtocolChanges,
   299  			iConfig:     light.TestServerIndexerConfig,
   300  			chainDb:     db,
   301  			chainReader: simulation.Blockchain(),
   302  			oracle:      oracle,
   303  			closeCh:     make(chan struct{}),
   304  		},
   305  		peers:        newClientPeerSet(),
   306  		servingQueue: newServingQueue(int64(time.Millisecond*10), 1),
   307  		defParams: flowcontrol.ServerParams{
   308  			BufLimit:    testBufLimit,
   309  			MinRecharge: testBufRecharge,
   310  		},
   311  		fcManager: flowcontrol.NewClientManager(nil, clock),
   312  	}
   313  	server.costTracker, server.minCapacity = newCostTracker(db, server.config)
   314  	server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism.
   315  	server.clientPool = vfs.NewClientPool(db, testBufRecharge, defaultConnectedBias, clock, alwaysTrueFn)
   316  	server.clientPool.Start()
   317  	server.clientPool.SetLimits(10000, 10000) // Assign enough capacity for clientpool
   318  	server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true })
   319  	if server.oracle != nil {
   320  		server.oracle.Start(simulation)
   321  	}
   322  	server.servingQueue.setThreads(4)
   323  	server.handler.start()
   324  	closer := func() { server.Stop() }
   325  	return server.handler, simulation, closer
   326  }
   327  
   328  func alwaysTrueFn() bool {
   329  	return true
   330  }
   331  
   332  // testPeer is a simulated peer to allow testing direct network calls.
   333  type testPeer struct {
   334  	cpeer *clientPeer
   335  	speer *serverPeer
   336  
   337  	net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
   338  	app *p2p.MsgPipeRW    // Application layer reader/writer to simulate the local side
   339  }
   340  
   341  // handshakeWithServer executes the handshake with the remote server peer.
   342  func (p *testPeer) handshakeWithServer(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID) {
   343  	// It only works for the simulated client peer
   344  	if p.cpeer == nil {
   345  		t.Fatal("handshake for client peer only")
   346  	}
   347  	var sendList keyValueList
   348  	sendList = sendList.add("protocolVersion", uint64(p.cpeer.version))
   349  	sendList = sendList.add("networkId", uint64(NetworkId))
   350  	sendList = sendList.add("headTd", td)
   351  	sendList = sendList.add("headHash", head)
   352  	sendList = sendList.add("headNum", headNum)
   353  	sendList = sendList.add("genesisHash", genesis)
   354  	if p.cpeer.version >= lpv4 {
   355  		sendList = sendList.add("forkID", &forkID)
   356  	}
   357  	if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil {
   358  		t.Fatalf("status recv: %v", err)
   359  	}
   360  	if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil {
   361  		t.Fatalf("status send: %v", err)
   362  	}
   363  }
   364  
   365  // handshakeWithClient executes the handshake with the remote client peer.
   366  func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, costList RequestCostList, recentTxLookup uint64) {
   367  	// It only works for the simulated client peer
   368  	if p.speer == nil {
   369  		t.Fatal("handshake for server peer only")
   370  	}
   371  	var sendList keyValueList
   372  	sendList = sendList.add("protocolVersion", uint64(p.speer.version))
   373  	sendList = sendList.add("networkId", uint64(NetworkId))
   374  	sendList = sendList.add("headTd", td)
   375  	sendList = sendList.add("headHash", head)
   376  	sendList = sendList.add("headNum", headNum)
   377  	sendList = sendList.add("genesisHash", genesis)
   378  	sendList = sendList.add("serveHeaders", nil)
   379  	sendList = sendList.add("serveChainSince", uint64(0))
   380  	sendList = sendList.add("serveStateSince", uint64(0))
   381  	sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4))
   382  	sendList = sendList.add("txRelay", nil)
   383  	sendList = sendList.add("flowControl/BL", testBufLimit)
   384  	sendList = sendList.add("flowControl/MRR", testBufRecharge)
   385  	sendList = sendList.add("flowControl/MRC", costList)
   386  	if p.speer.version >= lpv4 {
   387  		sendList = sendList.add("forkID", &forkID)
   388  		sendList = sendList.add("recentTxLookup", recentTxLookup)
   389  	}
   390  	if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil {
   391  		t.Fatalf("status recv: %v", err)
   392  	}
   393  	if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil {
   394  		t.Fatalf("status send: %v", err)
   395  	}
   396  }
   397  
   398  // close terminates the local side of the peer, notifying the remote protocol
   399  // manager of termination.
   400  func (p *testPeer) close() {
   401  	p.app.Close()
   402  }
   403  
   404  func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler, noInitAnnounce bool) (*testPeer, *testPeer, error) {
   405  	// Create a message pipe to communicate through
   406  	app, net := p2p.MsgPipe()
   407  
   408  	// Generate a random id and create the peer
   409  	var id enode.ID
   410  	rand.Read(id[:])
   411  
   412  	peer1 := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
   413  	peer2 := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), app)
   414  
   415  	// Start the peer on a new thread
   416  	errc1 := make(chan error, 1)
   417  	errc2 := make(chan error, 1)
   418  	go func() {
   419  		select {
   420  		case <-server.closeCh:
   421  			errc1 <- p2p.DiscQuitting
   422  		case errc1 <- server.handle(peer1):
   423  		}
   424  	}()
   425  	go func() {
   426  		select {
   427  		case <-client.closeCh:
   428  			errc2 <- p2p.DiscQuitting
   429  		case errc2 <- client.handle(peer2, noInitAnnounce):
   430  		}
   431  	}()
   432  	// Ensure the connection is established or exits when any error occurs
   433  	for {
   434  		select {
   435  		case err := <-errc1:
   436  			return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
   437  		case err := <-errc2:
   438  			return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
   439  		default:
   440  		}
   441  		if atomic.LoadUint32(&peer1.serving) == 1 && atomic.LoadUint32(&peer2.serving) == 1 {
   442  			break
   443  		}
   444  		time.Sleep(50 * time.Millisecond)
   445  	}
   446  	return &testPeer{cpeer: peer1, net: net, app: app}, &testPeer{speer: peer2, net: app, app: net}, nil
   447  }
   448  
   449  type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer)
   450  
   451  // testClient represents a client object for testing with necessary auxiliary fields.
   452  type testClient struct {
   453  	clock   mclock.Clock
   454  	db      ethdb.Database
   455  	peer    *testPeer
   456  	handler *clientHandler
   457  
   458  	chtIndexer       *core.ChainIndexer
   459  	bloomIndexer     *core.ChainIndexer
   460  	bloomTrieIndexer *core.ChainIndexer
   461  }
   462  
   463  // newRawPeer creates a new server peer connects to the server and do the handshake.
   464  func (client *testClient) newRawPeer(t *testing.T, name string, version int, recentTxLookup uint64) (*testPeer, func(), <-chan error) {
   465  	// Create a message pipe to communicate through
   466  	app, net := p2p.MsgPipe()
   467  
   468  	// Generate a random id and create the peer
   469  	var id enode.ID
   470  	rand.Read(id[:])
   471  	peer := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net)
   472  
   473  	// Start the peer on a new thread
   474  	errCh := make(chan error, 1)
   475  	go func() {
   476  		select {
   477  		case <-client.handler.closeCh:
   478  			errCh <- p2p.DiscQuitting
   479  		case errCh <- client.handler.handle(peer, false):
   480  		}
   481  	}()
   482  	tp := &testPeer{
   483  		app:   app,
   484  		net:   net,
   485  		speer: peer,
   486  	}
   487  	var (
   488  		genesis = client.handler.backend.blockchain.Genesis()
   489  		head    = client.handler.backend.blockchain.CurrentHeader()
   490  		td      = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64())
   491  	)
   492  	forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64())
   493  	tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default
   494  
   495  	// Ensure the connection is established or exits when any error occurs
   496  	for {
   497  		select {
   498  		case <-errCh:
   499  			return nil, nil, nil
   500  		default:
   501  		}
   502  		if atomic.LoadUint32(&peer.serving) == 1 {
   503  			break
   504  		}
   505  		time.Sleep(50 * time.Millisecond)
   506  	}
   507  	closePeer := func() {
   508  		tp.speer.close()
   509  		tp.close()
   510  	}
   511  	return tp, closePeer, errCh
   512  }
   513  
   514  // testServer represents a server object for testing with necessary auxiliary fields.
   515  type testServer struct {
   516  	clock   mclock.Clock
   517  	backend *backends.SimulatedBackend
   518  	db      ethdb.Database
   519  	peer    *testPeer
   520  	handler *serverHandler
   521  
   522  	chtIndexer       *core.ChainIndexer
   523  	bloomIndexer     *core.ChainIndexer
   524  	bloomTrieIndexer *core.ChainIndexer
   525  }
   526  
   527  // newRawPeer creates a new client peer connects to the server and do the handshake.
   528  func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*testPeer, func(), <-chan error) {
   529  	// Create a message pipe to communicate through
   530  	app, net := p2p.MsgPipe()
   531  
   532  	// Generate a random id and create the peer
   533  	var id enode.ID
   534  	rand.Read(id[:])
   535  	peer := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
   536  
   537  	// Start the peer on a new thread
   538  	errCh := make(chan error, 1)
   539  	go func() {
   540  		select {
   541  		case <-server.handler.closeCh:
   542  			errCh <- p2p.DiscQuitting
   543  		case errCh <- server.handler.handle(peer):
   544  		}
   545  	}()
   546  	tp := &testPeer{
   547  		app:   app,
   548  		net:   net,
   549  		cpeer: peer,
   550  	}
   551  	var (
   552  		genesis = server.handler.blockchain.Genesis()
   553  		head    = server.handler.blockchain.CurrentHeader()
   554  		td      = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64())
   555  	)
   556  	forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64())
   557  	tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID)
   558  
   559  	// Ensure the connection is established or exits when any error occurs
   560  	for {
   561  		select {
   562  		case <-errCh:
   563  			return nil, nil, nil
   564  		default:
   565  		}
   566  		if atomic.LoadUint32(&peer.serving) == 1 {
   567  			break
   568  		}
   569  		time.Sleep(50 * time.Millisecond)
   570  	}
   571  	closePeer := func() {
   572  		tp.cpeer.close()
   573  		tp.close()
   574  	}
   575  	return tp, closePeer, errCh
   576  }
   577  
   578  // testnetConfig wraps all the configurations for testing network.
   579  type testnetConfig struct {
   580  	blocks      int
   581  	protocol    int
   582  	indexFn     indexerCallback
   583  	ulcServers  []string
   584  	ulcFraction int
   585  	simClock    bool
   586  	connect     bool
   587  	nopruning   bool
   588  }
   589  
   590  func newClientServerEnv(t *testing.T, config testnetConfig) (*testServer, *testClient, func()) {
   591  	var (
   592  		sdb    = rawdb.NewMemoryDatabase()
   593  		cdb    = rawdb.NewMemoryDatabase()
   594  		speers = newServerPeerSet()
   595  	)
   596  	var clock mclock.Clock = &mclock.System{}
   597  	if config.simClock {
   598  		clock = &mclock.Simulated{}
   599  	}
   600  	dist := newRequestDistributor(speers, clock)
   601  	rm := newRetrieveManager(speers, dist, func() time.Duration { return time.Millisecond * 500 })
   602  	odr := NewLesOdr(cdb, light.TestClientIndexerConfig, speers, rm)
   603  
   604  	sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig, true)
   605  	cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig, config.nopruning)
   606  
   607  	scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2]
   608  	ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2]
   609  	odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer)
   610  
   611  	server, b, serverClose := newTestServerHandler(config.blocks, sindexers, sdb, clock)
   612  	client, clientClose := newTestClientHandler(b, odr, cIndexers, cdb, speers, config.ulcServers, config.ulcFraction)
   613  
   614  	scIndexer.Start(server.blockchain)
   615  	sbIndexer.Start(server.blockchain)
   616  	ccIndexer.Start(client.backend.blockchain)
   617  	cbIndexer.Start(client.backend.blockchain)
   618  
   619  	if config.indexFn != nil {
   620  		config.indexFn(scIndexer, sbIndexer, sbtIndexer)
   621  	}
   622  	var (
   623  		err          error
   624  		speer, cpeer *testPeer
   625  	)
   626  	if config.connect {
   627  		done := make(chan struct{})
   628  		client.syncEnd = func(_ *types.Header) { close(done) }
   629  		cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client, false)
   630  		if err != nil {
   631  			t.Fatalf("Failed to connect testing peers %v", err)
   632  		}
   633  		select {
   634  		case <-done:
   635  		case <-time.After(10 * time.Second):
   636  			t.Fatal("test peer did not connect and sync within 3s")
   637  		}
   638  	}
   639  	s := &testServer{
   640  		clock:            clock,
   641  		backend:          b,
   642  		db:               sdb,
   643  		peer:             cpeer,
   644  		handler:          server,
   645  		chtIndexer:       scIndexer,
   646  		bloomIndexer:     sbIndexer,
   647  		bloomTrieIndexer: sbtIndexer,
   648  	}
   649  	c := &testClient{
   650  		clock:            clock,
   651  		db:               cdb,
   652  		peer:             speer,
   653  		handler:          client,
   654  		chtIndexer:       ccIndexer,
   655  		bloomIndexer:     cbIndexer,
   656  		bloomTrieIndexer: cbtIndexer,
   657  	}
   658  	teardown := func() {
   659  		if config.connect {
   660  			speer.close()
   661  			cpeer.close()
   662  			cpeer.cpeer.close()
   663  			speer.speer.close()
   664  		}
   665  		ccIndexer.Close()
   666  		cbIndexer.Close()
   667  		scIndexer.Close()
   668  		sbIndexer.Close()
   669  		dist.close()
   670  		serverClose()
   671  		b.Close()
   672  		clientClose()
   673  	}
   674  	return s, c, teardown
   675  }
   676  
   677  // NewFuzzerPeer creates a client peer for test purposes, and also returns
   678  // a function to close the peer: this is needed to avoid goroutine leaks in the
   679  // exec queue.
   680  func NewFuzzerPeer(version int) (p *clientPeer, closer func()) {
   681  	p = newClientPeer(version, 0, p2p.NewPeer(enode.ID{}, "", nil), nil)
   682  	return p, func() { p.peerCommons.close() }
   683  }