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 }