github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/handlers/block_request_test.go (about)

     1  // (c) 2021-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package handlers
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	"github.com/MetalBlockchain/metalgo/ids"
    11  	"github.com/MetalBlockchain/subnet-evm/consensus/dummy"
    12  	"github.com/MetalBlockchain/subnet-evm/core"
    13  	"github.com/MetalBlockchain/subnet-evm/core/types"
    14  	"github.com/MetalBlockchain/subnet-evm/ethdb/memorydb"
    15  	"github.com/MetalBlockchain/subnet-evm/params"
    16  	"github.com/MetalBlockchain/subnet-evm/plugin/evm/message"
    17  	"github.com/MetalBlockchain/subnet-evm/sync/handlers/stats"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	"github.com/ethereum/go-ethereum/rlp"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func TestBlockRequestHandler(t *testing.T) {
    24  	var gspec = &core.Genesis{
    25  		Config: params.TestChainConfig,
    26  	}
    27  	memdb := memorydb.New()
    28  	genesis := gspec.MustCommit(memdb)
    29  	engine := dummy.NewETHFaker()
    30  	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 96, 0, func(i int, b *core.BlockGen) {})
    31  	if err != nil {
    32  		t.Fatal("unexpected error when generating test blockchain", err)
    33  	}
    34  
    35  	assert.Len(t, blocks, 96)
    36  
    37  	// convert into map
    38  	blocksDB := make(map[common.Hash]*types.Block, len(blocks))
    39  	for _, blk := range blocks {
    40  		blocksDB[blk.Hash()] = blk
    41  	}
    42  
    43  	mockHandlerStats := &stats.MockHandlerStats{}
    44  	blockProvider := &TestBlockProvider{
    45  		GetBlockFn: func(hash common.Hash, height uint64) *types.Block {
    46  			blk, ok := blocksDB[hash]
    47  			if !ok || blk.NumberU64() != height {
    48  				return nil
    49  			}
    50  			return blk
    51  		},
    52  	}
    53  	blockRequestHandler := NewBlockRequestHandler(blockProvider, message.Codec, mockHandlerStats)
    54  
    55  	tests := []struct {
    56  		name string
    57  
    58  		// starting block, specify either Index or (hash+height)
    59  		startBlockIndex  int
    60  		startBlockHash   common.Hash
    61  		startBlockHeight uint64
    62  
    63  		requestedParents  uint16
    64  		expectedBlocks    int
    65  		expectNilResponse bool
    66  		assertResponse    func(t *testing.T, response []byte)
    67  	}{
    68  		{
    69  			name:             "handler_returns_blocks_as_requested",
    70  			startBlockIndex:  64,
    71  			requestedParents: 32,
    72  			expectedBlocks:   32,
    73  		},
    74  		{
    75  			name:             "handler_caps_blocks_parent_limit",
    76  			startBlockIndex:  95,
    77  			requestedParents: 96,
    78  			expectedBlocks:   64,
    79  		},
    80  		{
    81  			name:             "handler_handles_genesis",
    82  			startBlockIndex:  0,
    83  			requestedParents: 64,
    84  			expectedBlocks:   1,
    85  		},
    86  		{
    87  			name:              "handler_unknown_block",
    88  			startBlockHash:    common.BytesToHash([]byte("some block pls k thx bye")),
    89  			startBlockHeight:  1_000_000,
    90  			requestedParents:  64,
    91  			expectNilResponse: true,
    92  			assertResponse: func(t *testing.T, _ []byte) {
    93  				assert.Equal(t, uint32(1), mockHandlerStats.MissingBlockHashCount)
    94  			},
    95  		},
    96  	}
    97  	for _, test := range tests {
    98  		t.Run(test.name, func(t *testing.T) {
    99  			var blockRequest message.BlockRequest
   100  			if test.startBlockHash != (common.Hash{}) {
   101  				blockRequest.Hash = test.startBlockHash
   102  				blockRequest.Height = test.startBlockHeight
   103  			} else {
   104  				startingBlock := blocks[test.startBlockIndex]
   105  				blockRequest.Hash = startingBlock.Hash()
   106  				blockRequest.Height = startingBlock.NumberU64()
   107  			}
   108  			blockRequest.Parents = test.requestedParents
   109  
   110  			responseBytes, err := blockRequestHandler.OnBlockRequest(context.Background(), ids.GenerateTestNodeID(), 1, blockRequest)
   111  			if err != nil {
   112  				t.Fatal("unexpected error during block request", err)
   113  			}
   114  			if test.assertResponse != nil {
   115  				test.assertResponse(t, responseBytes)
   116  			}
   117  
   118  			if test.expectNilResponse {
   119  				assert.Nil(t, responseBytes)
   120  				return
   121  			}
   122  
   123  			assert.NotEmpty(t, responseBytes)
   124  
   125  			var response message.BlockResponse
   126  			if _, err = message.Codec.Unmarshal(responseBytes, &response); err != nil {
   127  				t.Fatal("error unmarshalling", err)
   128  			}
   129  			assert.Len(t, response.Blocks, test.expectedBlocks)
   130  
   131  			for _, blockBytes := range response.Blocks {
   132  				block := new(types.Block)
   133  				if err := rlp.DecodeBytes(blockBytes, block); err != nil {
   134  					t.Fatal("could not parse block", err)
   135  				}
   136  				assert.GreaterOrEqual(t, test.startBlockIndex, 0)
   137  				assert.Equal(t, blocks[test.startBlockIndex].Hash(), block.Hash())
   138  				test.startBlockIndex--
   139  			}
   140  			mockHandlerStats.Reset()
   141  		})
   142  	}
   143  }
   144  
   145  func TestBlockRequestHandlerCtxExpires(t *testing.T) {
   146  	var gspec = &core.Genesis{
   147  		Config: params.TestChainConfig,
   148  	}
   149  	memdb := memorydb.New()
   150  	genesis := gspec.MustCommit(memdb)
   151  	engine := dummy.NewETHFaker()
   152  	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 11, 0, func(i int, b *core.BlockGen) {})
   153  	if err != nil {
   154  		t.Fatal("unexpected error when generating test blockchain", err)
   155  	}
   156  
   157  	assert.Len(t, blocks, 11)
   158  
   159  	// convert into map
   160  	blocksDB := make(map[common.Hash]*types.Block, 11)
   161  	for _, blk := range blocks {
   162  		blocksDB[blk.Hash()] = blk
   163  	}
   164  
   165  	cancelAfterNumRequests := 2
   166  	ctx, cancel := context.WithCancel(context.Background())
   167  	defer cancel()
   168  	blockRequestCallCount := 0
   169  	blockProvider := &TestBlockProvider{
   170  		GetBlockFn: func(hash common.Hash, height uint64) *types.Block {
   171  			blockRequestCallCount++
   172  			// cancel ctx after the 2nd call to simulate ctx expiring due to deadline exceeding
   173  			if blockRequestCallCount >= cancelAfterNumRequests {
   174  				cancel()
   175  			}
   176  			blk, ok := blocksDB[hash]
   177  			if !ok || blk.NumberU64() != height {
   178  				return nil
   179  			}
   180  			return blk
   181  		},
   182  	}
   183  	blockRequestHandler := NewBlockRequestHandler(blockProvider, message.Codec, stats.NewNoopHandlerStats())
   184  
   185  	responseBytes, err := blockRequestHandler.OnBlockRequest(ctx, ids.GenerateTestNodeID(), 1, message.BlockRequest{
   186  		Hash:    blocks[10].Hash(),
   187  		Height:  blocks[10].NumberU64(),
   188  		Parents: uint16(8),
   189  	})
   190  	if err != nil {
   191  		t.Fatal("unexpected error from BlockRequestHandler", err)
   192  	}
   193  	assert.NotEmpty(t, responseBytes)
   194  
   195  	var response message.BlockResponse
   196  	if _, err = message.Codec.Unmarshal(responseBytes, &response); err != nil {
   197  		t.Fatal("error unmarshalling", err)
   198  	}
   199  	// requested 8 blocks, received cancelAfterNumRequests because of timeout
   200  	assert.Len(t, response.Blocks, cancelAfterNumRequests)
   201  
   202  	for i, blockBytes := range response.Blocks {
   203  		block := new(types.Block)
   204  		if err := rlp.DecodeBytes(blockBytes, block); err != nil {
   205  			t.Fatal("could not parse block", err)
   206  		}
   207  		assert.Equal(t, blocks[len(blocks)-i-1].Hash(), block.Hash())
   208  	}
   209  }