github.com/FusionFoundation/efsn/v4@v4.2.0/les/handler_test.go (about)

     1  // Copyright 2016 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 les
    18  
    19  import (
    20  	"encoding/binary"
    21  	"math/big"
    22  	"math/rand"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/FusionFoundation/efsn/v4/common"
    27  
    28  	"github.com/FusionFoundation/efsn/v4/core"
    29  	"github.com/FusionFoundation/efsn/v4/core/rawdb"
    30  	"github.com/FusionFoundation/efsn/v4/core/types"
    31  	"github.com/FusionFoundation/efsn/v4/crypto"
    32  	"github.com/FusionFoundation/efsn/v4/eth/downloader"
    33  	"github.com/FusionFoundation/efsn/v4/ethdb"
    34  	"github.com/FusionFoundation/efsn/v4/light"
    35  	"github.com/FusionFoundation/efsn/v4/p2p"
    36  	"github.com/FusionFoundation/efsn/v4/params"
    37  	"github.com/FusionFoundation/efsn/v4/rlp"
    38  	"github.com/FusionFoundation/efsn/v4/trie"
    39  )
    40  
    41  func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error {
    42  	type resp struct {
    43  		ReqID, BV uint64
    44  		Data      interface{}
    45  	}
    46  	return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data})
    47  }
    48  
    49  // Tests that block headers can be retrieved from a remote chain based on user queries.
    50  func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) }
    51  func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) }
    52  
    53  func testGetBlockHeaders(t *testing.T, protocol int) {
    54  	server, tearDown := newServerEnv(t, downloader.MaxHashFetch+15, protocol, nil)
    55  	defer tearDown()
    56  	bc := server.pm.blockchain.(*core.BlockChain)
    57  
    58  	// Create a "random" unknown hash for testing
    59  	var unknown common.Hash
    60  	for i := range unknown {
    61  		unknown[i] = byte(i)
    62  	}
    63  	// Create a batch of tests for various scenarios
    64  	limit := uint64(MaxHeaderFetch)
    65  	tests := []struct {
    66  		query  *getBlockHeadersData // The query to execute for header retrieval
    67  		expect []common.Hash        // The hashes of the block whose headers are expected
    68  	}{
    69  		// A single random block should be retrievable by hash and number too
    70  		{
    71  			&getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1},
    72  			[]common.Hash{bc.GetBlockByNumber(limit / 2).Hash()},
    73  		}, {
    74  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
    75  			[]common.Hash{bc.GetBlockByNumber(limit / 2).Hash()},
    76  		},
    77  		// Multiple headers should be retrievable in both directions
    78  		{
    79  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
    80  			[]common.Hash{
    81  				bc.GetBlockByNumber(limit / 2).Hash(),
    82  				bc.GetBlockByNumber(limit/2 + 1).Hash(),
    83  				bc.GetBlockByNumber(limit/2 + 2).Hash(),
    84  			},
    85  		}, {
    86  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
    87  			[]common.Hash{
    88  				bc.GetBlockByNumber(limit / 2).Hash(),
    89  				bc.GetBlockByNumber(limit/2 - 1).Hash(),
    90  				bc.GetBlockByNumber(limit/2 - 2).Hash(),
    91  			},
    92  		},
    93  		// Multiple headers with skip lists should be retrievable
    94  		{
    95  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
    96  			[]common.Hash{
    97  				bc.GetBlockByNumber(limit / 2).Hash(),
    98  				bc.GetBlockByNumber(limit/2 + 4).Hash(),
    99  				bc.GetBlockByNumber(limit/2 + 8).Hash(),
   100  			},
   101  		}, {
   102  			&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
   103  			[]common.Hash{
   104  				bc.GetBlockByNumber(limit / 2).Hash(),
   105  				bc.GetBlockByNumber(limit/2 - 4).Hash(),
   106  				bc.GetBlockByNumber(limit/2 - 8).Hash(),
   107  			},
   108  		},
   109  		// The chain endpoints should be retrievable
   110  		{
   111  			&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
   112  			[]common.Hash{bc.GetBlockByNumber(0).Hash()},
   113  		}, {
   114  			&getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1},
   115  			[]common.Hash{bc.CurrentBlock().Hash()},
   116  		},
   117  		// Ensure protocol limits are honored
   118  		/*{
   119  			&getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
   120  			bc.GetBlockHashesFromHash(bc.CurrentBlock().Hash(), limit),
   121  		},*/
   122  		// Check that requesting more than available is handled gracefully
   123  		{
   124  			&getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
   125  			[]common.Hash{
   126  				bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(),
   127  				bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(),
   128  			},
   129  		}, {
   130  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
   131  			[]common.Hash{
   132  				bc.GetBlockByNumber(4).Hash(),
   133  				bc.GetBlockByNumber(0).Hash(),
   134  			},
   135  		},
   136  		// Check that requesting more than available is handled gracefully, even if mid skip
   137  		{
   138  			&getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
   139  			[]common.Hash{
   140  				bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(),
   141  				bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(),
   142  			},
   143  		}, {
   144  			&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
   145  			[]common.Hash{
   146  				bc.GetBlockByNumber(4).Hash(),
   147  				bc.GetBlockByNumber(1).Hash(),
   148  			},
   149  		},
   150  		// Check that non existing headers aren't returned
   151  		{
   152  			&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
   153  			[]common.Hash{},
   154  		}, {
   155  			&getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1},
   156  			[]common.Hash{},
   157  		},
   158  	}
   159  	// Run each of the tests and verify the results against the chain
   160  	var reqID uint64
   161  	for i, tt := range tests {
   162  		// Collect the headers to expect in the response
   163  		headers := []*types.Header{}
   164  		for _, hash := range tt.expect {
   165  			headers = append(headers, bc.GetHeaderByHash(hash))
   166  		}
   167  		// Send the hash request and verify the response
   168  		reqID++
   169  		cost := server.tPeer.GetRequestCost(GetBlockHeadersMsg, int(tt.query.Amount))
   170  		sendRequest(server.tPeer.app, GetBlockHeadersMsg, reqID, cost, tt.query)
   171  		if err := expectResponse(server.tPeer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil {
   172  			t.Errorf("test %d: headers mismatch: %v", i, err)
   173  		}
   174  	}
   175  }
   176  
   177  // Tests that block contents can be retrieved from a remote chain based on their hashes.
   178  func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) }
   179  func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) }
   180  
   181  func testGetBlockBodies(t *testing.T, protocol int) {
   182  	server, tearDown := newServerEnv(t, downloader.MaxBlockFetch+15, protocol, nil)
   183  	defer tearDown()
   184  	bc := server.pm.blockchain.(*core.BlockChain)
   185  
   186  	// Create a batch of tests for various scenarios
   187  	limit := MaxBodyFetch
   188  	tests := []struct {
   189  		random    int           // Number of blocks to fetch randomly from the chain
   190  		explicit  []common.Hash // Explicitly requested blocks
   191  		available []bool        // Availability of explicitly requested blocks
   192  		expected  int           // Total number of existing blocks to expect
   193  	}{
   194  		{1, nil, nil, 1},         // A single random block should be retrievable
   195  		{10, nil, nil, 10},       // Multiple random blocks should be retrievable
   196  		{limit, nil, nil, limit}, // The maximum possible blocks should be retrievable
   197  		//{limit + 1, nil, nil, limit},                                  // No more than the possible block count should be returned
   198  		{0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1},      // The genesis block should be retrievable
   199  		{0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
   200  		{0, []common.Hash{{}}, []bool{false}, 0},                      // A non existent block should not be returned
   201  
   202  		// Existing and non-existing blocks interleaved should not cause problems
   203  		{0, []common.Hash{
   204  			{},
   205  			bc.GetBlockByNumber(1).Hash(),
   206  			{},
   207  			bc.GetBlockByNumber(10).Hash(),
   208  			{},
   209  			bc.GetBlockByNumber(100).Hash(),
   210  			{},
   211  		}, []bool{false, true, false, true, false, true, false}, 3},
   212  	}
   213  	// Run each of the tests and verify the results against the chain
   214  	var reqID uint64
   215  	for i, tt := range tests {
   216  		// Collect the hashes to request, and the response to expect
   217  		hashes, seen := []common.Hash{}, make(map[int64]bool)
   218  		bodies := []*types.Body{}
   219  
   220  		for j := 0; j < tt.random; j++ {
   221  			for {
   222  				num := rand.Int63n(int64(bc.CurrentBlock().NumberU64()))
   223  				if !seen[num] {
   224  					seen[num] = true
   225  
   226  					block := bc.GetBlockByNumber(uint64(num))
   227  					hashes = append(hashes, block.Hash())
   228  					if len(bodies) < tt.expected {
   229  						bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
   230  					}
   231  					break
   232  				}
   233  			}
   234  		}
   235  		for j, hash := range tt.explicit {
   236  			hashes = append(hashes, hash)
   237  			if tt.available[j] && len(bodies) < tt.expected {
   238  				block := bc.GetBlockByHash(hash)
   239  				bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
   240  			}
   241  		}
   242  		reqID++
   243  		// Send the hash request and verify the response
   244  		cost := server.tPeer.GetRequestCost(GetBlockBodiesMsg, len(hashes))
   245  		sendRequest(server.tPeer.app, GetBlockBodiesMsg, reqID, cost, hashes)
   246  		if err := expectResponse(server.tPeer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil {
   247  			t.Errorf("test %d: bodies mismatch: %v", i, err)
   248  		}
   249  	}
   250  }
   251  
   252  // Tests that the contract codes can be retrieved based on account addresses.
   253  func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) }
   254  func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) }
   255  
   256  func testGetCode(t *testing.T, protocol int) {
   257  	// Assemble the test environment
   258  	server, tearDown := newServerEnv(t, 4, protocol, nil)
   259  	defer tearDown()
   260  	bc := server.pm.blockchain.(*core.BlockChain)
   261  
   262  	var codereqs []*CodeReq
   263  	var codes [][]byte
   264  
   265  	for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
   266  		header := bc.GetHeaderByNumber(i)
   267  		req := &CodeReq{
   268  			BHash:  header.Hash(),
   269  			AccKey: crypto.Keccak256(testContractAddr[:]),
   270  		}
   271  		codereqs = append(codereqs, req)
   272  		if i >= testContractDeployed {
   273  			codes = append(codes, testContractCodeDeployed)
   274  		}
   275  	}
   276  
   277  	cost := server.tPeer.GetRequestCost(GetCodeMsg, len(codereqs))
   278  	sendRequest(server.tPeer.app, GetCodeMsg, 42, cost, codereqs)
   279  	if err := expectResponse(server.tPeer.app, CodeMsg, 42, testBufLimit, codes); err != nil {
   280  		t.Errorf("codes mismatch: %v", err)
   281  	}
   282  }
   283  
   284  // Tests that the transaction receipts can be retrieved based on hashes.
   285  func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) }
   286  func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) }
   287  
   288  func testGetReceipt(t *testing.T, protocol int) {
   289  	// Assemble the test environment
   290  	server, tearDown := newServerEnv(t, 4, protocol, nil)
   291  	defer tearDown()
   292  	bc := server.pm.blockchain.(*core.BlockChain)
   293  
   294  	// Collect the hashes to request, and the response to expect
   295  	hashes, receipts := []common.Hash{}, []types.Receipts{}
   296  	for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
   297  		block := bc.GetBlockByNumber(i)
   298  
   299  		hashes = append(hashes, block.Hash())
   300  		receipts = append(receipts, rawdb.ReadReceipts(server.db, block.Hash(), block.NumberU64()))
   301  	}
   302  	// Send the hash request and verify the response
   303  	cost := server.tPeer.GetRequestCost(GetReceiptsMsg, len(hashes))
   304  	sendRequest(server.tPeer.app, GetReceiptsMsg, 42, cost, hashes)
   305  	if err := expectResponse(server.tPeer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil {
   306  		t.Errorf("receipts mismatch: %v", err)
   307  	}
   308  }
   309  
   310  // Tests that trie merkle proofs can be retrieved
   311  func TestGetProofsLes1(t *testing.T) { testGetProofs(t, 1) }
   312  func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) }
   313  
   314  func testGetProofs(t *testing.T, protocol int) {
   315  	// Assemble the test environment
   316  	server, tearDown := newServerEnv(t, 4, protocol, nil)
   317  	defer tearDown()
   318  	bc := server.pm.blockchain.(*core.BlockChain)
   319  
   320  	var (
   321  		proofreqs []ProofReq
   322  		proofsV1  [][]rlp.RawValue
   323  	)
   324  	proofsV2 := light.NewNodeSet()
   325  
   326  	accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}}
   327  	for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
   328  		header := bc.GetHeaderByNumber(i)
   329  		root := header.Root
   330  		trie, _ := trie.New(root, trie.NewDatabase(server.db))
   331  
   332  		for _, acc := range accounts {
   333  			req := ProofReq{
   334  				BHash: header.Hash(),
   335  				Key:   crypto.Keccak256(acc[:]),
   336  			}
   337  			proofreqs = append(proofreqs, req)
   338  
   339  			switch protocol {
   340  			case 1:
   341  				var proof light.NodeList
   342  				trie.Prove(crypto.Keccak256(acc[:]), 0, &proof)
   343  				proofsV1 = append(proofsV1, proof)
   344  			case 2:
   345  				trie.Prove(crypto.Keccak256(acc[:]), 0, proofsV2)
   346  			}
   347  		}
   348  	}
   349  	// Send the proof request and verify the response
   350  	switch protocol {
   351  	case 1:
   352  		cost := server.tPeer.GetRequestCost(GetProofsV1Msg, len(proofreqs))
   353  		sendRequest(server.tPeer.app, GetProofsV1Msg, 42, cost, proofreqs)
   354  		if err := expectResponse(server.tPeer.app, ProofsV1Msg, 42, testBufLimit, proofsV1); err != nil {
   355  			t.Errorf("proofs mismatch: %v", err)
   356  		}
   357  	case 2:
   358  		cost := server.tPeer.GetRequestCost(GetProofsV2Msg, len(proofreqs))
   359  		sendRequest(server.tPeer.app, GetProofsV2Msg, 42, cost, proofreqs)
   360  		if err := expectResponse(server.tPeer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.NodeList()); err != nil {
   361  			t.Errorf("proofs mismatch: %v", err)
   362  		}
   363  	}
   364  }
   365  
   366  // Tests that CHT proofs can be correctly retrieved.
   367  func TestGetCHTProofsLes1(t *testing.T) { testGetCHTProofs(t, 1) }
   368  func TestGetCHTProofsLes2(t *testing.T) { testGetCHTProofs(t, 2) }
   369  
   370  func testGetCHTProofs(t *testing.T, protocol int) {
   371  	config := light.TestServerIndexerConfig
   372  	frequency := config.ChtSize
   373  	if protocol == 2 {
   374  		frequency = config.PairChtSize
   375  	}
   376  
   377  	waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
   378  		expectSections := frequency / config.ChtSize
   379  		for {
   380  			cs, _, _ := cIndexer.Sections()
   381  			bs, _, _ := bIndexer.Sections()
   382  			if cs >= expectSections && bs >= expectSections {
   383  				break
   384  			}
   385  			time.Sleep(10 * time.Millisecond)
   386  		}
   387  	}
   388  	server, tearDown := newServerEnv(t, int(frequency+config.ChtConfirms), protocol, waitIndexers)
   389  	defer tearDown()
   390  	bc := server.pm.blockchain.(*core.BlockChain)
   391  
   392  	// Assemble the proofs from the different protocols
   393  	header := bc.GetHeaderByNumber(frequency - 1)
   394  	rlp, _ := rlp.EncodeToBytes(header)
   395  
   396  	key := make([]byte, 8)
   397  	binary.BigEndian.PutUint64(key, frequency-1)
   398  
   399  	proofsV1 := []ChtResp{{
   400  		Header: header,
   401  	}}
   402  	proofsV2 := HelperTrieResps{
   403  		AuxData: [][]byte{rlp},
   404  	}
   405  	switch protocol {
   406  	case 1:
   407  		root := light.GetChtRoot(server.db, 0, bc.GetHeaderByNumber(frequency-1).Hash())
   408  		trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.ChtTablePrefix)))
   409  
   410  		var proof light.NodeList
   411  		trie.Prove(key, 0, &proof)
   412  		proofsV1[0].Proof = proof
   413  
   414  	case 2:
   415  		root := light.GetChtRoot(server.db, (frequency/config.ChtSize)-1, bc.GetHeaderByNumber(frequency-1).Hash())
   416  		trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.ChtTablePrefix)))
   417  		trie.Prove(key, 0, &proofsV2.Proofs)
   418  	}
   419  	// Assemble the requests for the different protocols
   420  	requestsV1 := []ChtReq{{
   421  		ChtNum:   frequency / config.ChtSize,
   422  		BlockNum: frequency - 1,
   423  	}}
   424  	requestsV2 := []HelperTrieReq{{
   425  		Type:    htCanonical,
   426  		TrieIdx: frequency/config.PairChtSize - 1,
   427  		Key:     key,
   428  		AuxReq:  auxHeader,
   429  	}}
   430  	// Send the proof request and verify the response
   431  	switch protocol {
   432  	case 1:
   433  		cost := server.tPeer.GetRequestCost(GetHeaderProofsMsg, len(requestsV1))
   434  		sendRequest(server.tPeer.app, GetHeaderProofsMsg, 42, cost, requestsV1)
   435  		if err := expectResponse(server.tPeer.app, HeaderProofsMsg, 42, testBufLimit, proofsV1); err != nil {
   436  			t.Errorf("proofs mismatch: %v", err)
   437  		}
   438  	case 2:
   439  		cost := server.tPeer.GetRequestCost(GetHelperTrieProofsMsg, len(requestsV2))
   440  		sendRequest(server.tPeer.app, GetHelperTrieProofsMsg, 42, cost, requestsV2)
   441  		if err := expectResponse(server.tPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil {
   442  			t.Errorf("proofs mismatch: %v", err)
   443  		}
   444  	}
   445  }
   446  
   447  // Tests that bloombits proofs can be correctly retrieved.
   448  func TestGetBloombitsProofs(t *testing.T) {
   449  	config := light.TestServerIndexerConfig
   450  
   451  	waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
   452  		for {
   453  			cs, _, _ := cIndexer.Sections()
   454  			bs, _, _ := bIndexer.Sections()
   455  			bts, _, _ := btIndexer.Sections()
   456  			if cs >= 8 && bs >= 8 && bts >= 1 {
   457  				break
   458  			}
   459  			time.Sleep(10 * time.Millisecond)
   460  		}
   461  	}
   462  	server, tearDown := newServerEnv(t, int(config.BloomTrieSize+config.BloomTrieConfirms), 2, waitIndexers)
   463  	defer tearDown()
   464  	bc := server.pm.blockchain.(*core.BlockChain)
   465  
   466  	// Request and verify each bit of the bloom bits proofs
   467  	for bit := 0; bit < 2048; bit++ {
   468  		// Assemble the request and proofs for the bloombits
   469  		key := make([]byte, 10)
   470  
   471  		binary.BigEndian.PutUint16(key[:2], uint16(bit))
   472  		// Only the first bloom section has data.
   473  		binary.BigEndian.PutUint64(key[2:], 0)
   474  
   475  		requests := []HelperTrieReq{{
   476  			Type:    htBloomBits,
   477  			TrieIdx: 0,
   478  			Key:     key,
   479  		}}
   480  		var proofs HelperTrieResps
   481  
   482  		root := light.GetBloomTrieRoot(server.db, 0, bc.GetHeaderByNumber(config.BloomTrieSize-1).Hash())
   483  		trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.BloomTrieTablePrefix)))
   484  		trie.Prove(key, 0, &proofs.Proofs)
   485  
   486  		// Send the proof request and verify the response
   487  		cost := server.tPeer.GetRequestCost(GetHelperTrieProofsMsg, len(requests))
   488  		sendRequest(server.tPeer.app, GetHelperTrieProofsMsg, 42, cost, requests)
   489  		if err := expectResponse(server.tPeer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil {
   490  			t.Errorf("bit %d: proofs mismatch: %v", bit, err)
   491  		}
   492  	}
   493  }
   494  
   495  func TestTransactionStatusLes2(t *testing.T) {
   496  	db := ethdb.NewMemDatabase()
   497  	pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db)
   498  	chain := pm.blockchain.(*core.BlockChain)
   499  	config := core.DefaultTxPoolConfig
   500  	config.Journal = ""
   501  	txpool := core.NewTxPool(config, params.TestChainConfig, chain)
   502  	pm.txpool = txpool
   503  	peer, _ := newTestPeer(t, "peer", 2, pm, true)
   504  	defer peer.close()
   505  
   506  	var reqID uint64
   507  
   508  	test := func(tx *types.Transaction, send bool, expStatus txStatus) {
   509  		reqID++
   510  		if send {
   511  			cost := peer.GetRequestCost(SendTxV2Msg, 1)
   512  			sendRequest(peer.app, SendTxV2Msg, reqID, cost, types.Transactions{tx})
   513  		} else {
   514  			cost := peer.GetRequestCost(GetTxStatusMsg, 1)
   515  			sendRequest(peer.app, GetTxStatusMsg, reqID, cost, []common.Hash{tx.Hash()})
   516  		}
   517  		if err := expectResponse(peer.app, TxStatusMsg, reqID, testBufLimit, []txStatus{expStatus}); err != nil {
   518  			t.Errorf("transaction status mismatch")
   519  		}
   520  	}
   521  
   522  	signer := types.HomesteadSigner{}
   523  
   524  	// test error status by sending an underpriced transaction
   525  	tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
   526  	test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
   527  
   528  	tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
   529  	test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
   530  	test(tx1, true, txStatus{Status: core.TxStatusPending})  // send valid processable tx, should return pending
   531  	test(tx1, true, txStatus{Status: core.TxStatusPending})  // adding it again should not return an error
   532  
   533  	tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
   534  	tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
   535  	// send transactions in the wrong order, tx3 should be queued
   536  	test(tx3, true, txStatus{Status: core.TxStatusQueued})
   537  	test(tx2, true, txStatus{Status: core.TxStatusPending})
   538  	// query again, now tx3 should be pending too
   539  	test(tx3, false, txStatus{Status: core.TxStatusPending})
   540  
   541  	// generate and add a block with tx1 and tx2 included
   542  	gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) {
   543  		block.AddTx(tx1)
   544  		block.AddTx(tx2)
   545  	})
   546  	if _, err := chain.InsertChain(gchain); err != nil {
   547  		panic(err)
   548  	}
   549  	// wait until TxPool processes the inserted block
   550  	for i := 0; i < 10; i++ {
   551  		if pending, _ := txpool.Stats(); pending == 1 {
   552  			break
   553  		}
   554  		time.Sleep(100 * time.Millisecond)
   555  	}
   556  	if pending, _ := txpool.Stats(); pending != 1 {
   557  		t.Fatalf("pending count mismatch: have %d, want 1", pending)
   558  	}
   559  
   560  	// check if their status is included now
   561  	block1hash := rawdb.ReadCanonicalHash(db, 1)
   562  	test(tx1, false, txStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
   563  	test(tx2, false, txStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
   564  
   565  	// create a reorg that rolls them back
   566  	gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {})
   567  	if _, err := chain.InsertChain(gchain); err != nil {
   568  		panic(err)
   569  	}
   570  	// wait until TxPool processes the reorg
   571  	for i := 0; i < 10; i++ {
   572  		if pending, _ := txpool.Stats(); pending == 3 {
   573  			break
   574  		}
   575  		time.Sleep(100 * time.Millisecond)
   576  	}
   577  	if pending, _ := txpool.Stats(); pending != 3 {
   578  		t.Fatalf("pending count mismatch: have %d, want 3", pending)
   579  	}
   580  	// check if their status is pending again
   581  	test(tx1, false, txStatus{Status: core.TxStatusPending})
   582  	test(tx2, false, txStatus{Status: core.TxStatusPending})
   583  }