github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/rpc_send_request_test.go (about) 1 package sync 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "testing" 9 10 "github.com/libp2p/go-libp2p-core/mux" 11 "github.com/libp2p/go-libp2p-core/network" 12 types "github.com/prysmaticlabs/eth2-types" 13 "github.com/prysmaticlabs/prysm/beacon-chain/p2p" 14 p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" 15 p2pTypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types" 16 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 17 eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 19 "github.com/prysmaticlabs/prysm/proto/interfaces" 20 "github.com/prysmaticlabs/prysm/shared/params" 21 "github.com/prysmaticlabs/prysm/shared/testutil" 22 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 23 "github.com/prysmaticlabs/prysm/shared/testutil/require" 24 ) 25 26 func TestSendRequest_SendBeaconBlocksByRangeRequest(t *testing.T) { 27 ctx, cancel := context.WithCancel(context.Background()) 28 defer cancel() 29 pcl := fmt.Sprintf("%s/ssz_snappy", p2p.RPCBlocksByRangeTopicV1) 30 31 t.Run("stream error", func(t *testing.T) { 32 p1 := p2ptest.NewTestP2P(t) 33 // Bogus peer doesn't support a given protocol, so stream error is expected. 34 bogusPeer := p2ptest.NewTestP2P(t) 35 p1.Connect(bogusPeer) 36 37 req := &pb.BeaconBlocksByRangeRequest{} 38 _, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, bogusPeer.PeerID(), req, nil) 39 assert.ErrorContains(t, "protocol not supported", err) 40 }) 41 42 knownBlocks := make([]*eth.SignedBeaconBlock, 0) 43 genesisBlk := testutil.NewBeaconBlock() 44 genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot() 45 require.NoError(t, err) 46 parentRoot := genesisBlkRoot 47 for i := 0; i < 255; i++ { 48 blk := testutil.NewBeaconBlock() 49 blk.Block.Slot = types.Slot(i) 50 blk.Block.ParentRoot = parentRoot[:] 51 knownBlocks = append(knownBlocks, blk) 52 parentRoot, err = blk.Block.HashTreeRoot() 53 require.NoError(t, err) 54 } 55 56 knownBlocksProvider := func(p2pProvider p2p.P2P, processor BeaconBlockProcessor) func(stream network.Stream) { 57 return func(stream network.Stream) { 58 defer func() { 59 assert.NoError(t, stream.Close()) 60 }() 61 62 req := &pb.BeaconBlocksByRangeRequest{} 63 assert.NoError(t, p2pProvider.Encoding().DecodeWithMaxLength(stream, req)) 64 65 for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) { 66 if processor != nil { 67 if processorErr := processor(wrapper.WrappedPhase0SignedBeaconBlock(knownBlocks[i])); processorErr != nil { 68 if errors.Is(processorErr, io.EOF) { 69 // Close stream, w/o any errors written. 70 return 71 } 72 _, err := stream.Write([]byte{0x01}) 73 assert.NoError(t, err) 74 msg := p2pTypes.ErrorMessage(processorErr.Error()) 75 _, err = p2pProvider.Encoding().EncodeWithMaxLength(stream, &msg) 76 assert.NoError(t, err) 77 return 78 } 79 } 80 if uint64(i) >= uint64(len(knownBlocks)) { 81 break 82 } 83 err = WriteChunk(stream, nil, p2pProvider.Encoding(), knownBlocks[i]) 84 if err != nil && err.Error() != mux.ErrReset.Error() { 85 require.NoError(t, err) 86 } 87 } 88 } 89 } 90 91 t.Run("no block processor", func(t *testing.T) { 92 p1 := p2ptest.NewTestP2P(t) 93 p2 := p2ptest.NewTestP2P(t) 94 p1.Connect(p2) 95 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 96 97 req := &pb.BeaconBlocksByRangeRequest{ 98 StartSlot: 20, 99 Count: 128, 100 Step: 1, 101 } 102 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, nil) 103 assert.NoError(t, err) 104 assert.Equal(t, 128, len(blocks)) 105 }) 106 107 t.Run("has block processor - no errors", func(t *testing.T) { 108 p1 := p2ptest.NewTestP2P(t) 109 p2 := p2ptest.NewTestP2P(t) 110 p1.Connect(p2) 111 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 112 113 // No error from block processor. 114 req := &pb.BeaconBlocksByRangeRequest{ 115 StartSlot: 20, 116 Count: 128, 117 Step: 1, 118 } 119 blocksFromProcessor := make([]interfaces.SignedBeaconBlock, 0) 120 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 121 blocksFromProcessor = append(blocksFromProcessor, block) 122 return nil 123 }) 124 assert.NoError(t, err) 125 assert.Equal(t, 128, len(blocks)) 126 assert.DeepEqual(t, blocks, blocksFromProcessor) 127 }) 128 129 t.Run("has block processor - throw error", func(t *testing.T) { 130 p1 := p2ptest.NewTestP2P(t) 131 p2 := p2ptest.NewTestP2P(t) 132 p1.Connect(p2) 133 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 134 135 // Send error from block processor. 136 req := &pb.BeaconBlocksByRangeRequest{ 137 StartSlot: 20, 138 Count: 128, 139 Step: 1, 140 } 141 errFromProcessor := errors.New("processor error") 142 _, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 143 return errFromProcessor 144 }) 145 assert.ErrorContains(t, errFromProcessor.Error(), err) 146 }) 147 148 t.Run("max request blocks", func(t *testing.T) { 149 p1 := p2ptest.NewTestP2P(t) 150 p2 := p2ptest.NewTestP2P(t) 151 p1.Connect(p2) 152 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 153 154 // No cap on max roots. 155 req := &pb.BeaconBlocksByRangeRequest{ 156 StartSlot: 20, 157 Count: 128, 158 Step: 1, 159 } 160 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, nil) 161 assert.NoError(t, err) 162 assert.Equal(t, 128, len(blocks)) 163 164 // Cap max returned roots. 165 cfg := params.BeaconNetworkConfig().Copy() 166 maxRequestBlocks := cfg.MaxRequestBlocks 167 defer func() { 168 cfg.MaxRequestBlocks = maxRequestBlocks 169 params.OverrideBeaconNetworkConfig(cfg) 170 }() 171 blocks, err = SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 172 // Since ssz checks the boundaries, and doesn't normally allow to send requests bigger than 173 // the max request size, we are updating max request size dynamically. Even when updated dynamically, 174 // no more than max request size of blocks is expected on return. 175 cfg.MaxRequestBlocks = 3 176 params.OverrideBeaconNetworkConfig(cfg) 177 return nil 178 }) 179 assert.ErrorContains(t, ErrInvalidFetchedData.Error(), err) 180 assert.Equal(t, 0, len(blocks)) 181 }) 182 183 t.Run("process custom error", func(t *testing.T) { 184 p1 := p2ptest.NewTestP2P(t) 185 p2 := p2ptest.NewTestP2P(t) 186 p1.Connect(p2) 187 blocksProcessed := 0 188 expectedErr := errors.New("some error") 189 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, func(block interfaces.SignedBeaconBlock) error { 190 if blocksProcessed > 2 { 191 return expectedErr 192 } 193 blocksProcessed++ 194 return nil 195 })) 196 197 req := &pb.BeaconBlocksByRangeRequest{ 198 StartSlot: 20, 199 Count: 128, 200 Step: 1, 201 } 202 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, nil) 203 assert.ErrorContains(t, expectedErr.Error(), err) 204 assert.Equal(t, 0, len(blocks)) 205 }) 206 207 t.Run("blocks out of order: step 1", func(t *testing.T) { 208 p1 := p2ptest.NewTestP2P(t) 209 p2 := p2ptest.NewTestP2P(t) 210 p1.Connect(p2) 211 212 // Switch known blocks, so that slots are out of order. 213 knownBlocks[30], knownBlocks[31] = knownBlocks[31], knownBlocks[30] 214 defer func() { 215 knownBlocks[31], knownBlocks[30] = knownBlocks[30], knownBlocks[31] 216 }() 217 218 p2.SetStreamHandler(pcl, func(stream network.Stream) { 219 defer func() { 220 assert.NoError(t, stream.Close()) 221 }() 222 223 req := &pb.BeaconBlocksByRangeRequest{} 224 assert.NoError(t, p2.Encoding().DecodeWithMaxLength(stream, req)) 225 226 for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) { 227 if uint64(i) >= uint64(len(knownBlocks)) { 228 break 229 } 230 err = WriteChunk(stream, nil, p2.Encoding(), knownBlocks[i]) 231 if err != nil && err.Error() != mux.ErrReset.Error() { 232 require.NoError(t, err) 233 } 234 } 235 }) 236 237 req := &pb.BeaconBlocksByRangeRequest{ 238 StartSlot: 20, 239 Count: 128, 240 Step: 1, 241 } 242 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, nil) 243 assert.ErrorContains(t, ErrInvalidFetchedData.Error(), err) 244 assert.Equal(t, 0, len(blocks)) 245 246 }) 247 248 t.Run("blocks out of order: step 10", func(t *testing.T) { 249 p1 := p2ptest.NewTestP2P(t) 250 p2 := p2ptest.NewTestP2P(t) 251 p1.Connect(p2) 252 253 // Switch known blocks, so that slots are out of order. 254 knownBlocks[30], knownBlocks[31] = knownBlocks[31], knownBlocks[30] 255 defer func() { 256 knownBlocks[31], knownBlocks[30] = knownBlocks[30], knownBlocks[31] 257 }() 258 259 p2.SetStreamHandler(pcl, func(stream network.Stream) { 260 defer func() { 261 assert.NoError(t, stream.Close()) 262 }() 263 264 req := &pb.BeaconBlocksByRangeRequest{} 265 assert.NoError(t, p2.Encoding().DecodeWithMaxLength(stream, req)) 266 267 for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) { 268 if uint64(i) >= uint64(len(knownBlocks)) { 269 break 270 } 271 err = WriteChunk(stream, nil, p2.Encoding(), knownBlocks[i]) 272 if err != nil && err.Error() != mux.ErrReset.Error() { 273 require.NoError(t, err) 274 } 275 } 276 }) 277 278 req := &pb.BeaconBlocksByRangeRequest{ 279 StartSlot: 20, 280 Count: 128, 281 Step: 10, 282 } 283 blocks, err := SendBeaconBlocksByRangeRequest(ctx, nil, p1, p2.PeerID(), req, nil) 284 assert.ErrorContains(t, ErrInvalidFetchedData.Error(), err) 285 assert.Equal(t, 0, len(blocks)) 286 287 }) 288 } 289 290 func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) { 291 ctx, cancel := context.WithCancel(context.Background()) 292 defer cancel() 293 pcl := fmt.Sprintf("%s/ssz_snappy", p2p.RPCBlocksByRootTopicV1) 294 295 knownBlocks := make(map[[32]byte]*eth.SignedBeaconBlock) 296 knownRoots := make([][32]byte, 0) 297 for i := 0; i < 5; i++ { 298 blk := testutil.NewBeaconBlock() 299 blkRoot, err := blk.Block.HashTreeRoot() 300 require.NoError(t, err) 301 knownRoots = append(knownRoots, blkRoot) 302 knownBlocks[knownRoots[len(knownRoots)-1]] = blk 303 } 304 305 t.Run("stream error", func(t *testing.T) { 306 p1 := p2ptest.NewTestP2P(t) 307 // Bogus peer doesn't support a given protocol, so stream error is expected. 308 bogusPeer := p2ptest.NewTestP2P(t) 309 p1.Connect(bogusPeer) 310 311 req := &p2pTypes.BeaconBlockByRootsReq{} 312 _, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, bogusPeer.PeerID(), req, nil) 313 assert.ErrorContains(t, "protocol not supported", err) 314 }) 315 316 knownBlocksProvider := func(p2pProvider p2p.P2P, processor BeaconBlockProcessor) func(stream network.Stream) { 317 return func(stream network.Stream) { 318 defer func() { 319 assert.NoError(t, stream.Close()) 320 }() 321 322 req := new(p2pTypes.BeaconBlockByRootsReq) 323 assert.NoError(t, p2pProvider.Encoding().DecodeWithMaxLength(stream, req)) 324 if len(*req) == 0 { 325 return 326 } 327 for _, root := range *req { 328 if blk, ok := knownBlocks[root]; ok { 329 if processor != nil { 330 if processorErr := processor(wrapper.WrappedPhase0SignedBeaconBlock(blk)); processorErr != nil { 331 if errors.Is(processorErr, io.EOF) { 332 // Close stream, w/o any errors written. 333 return 334 } 335 _, err := stream.Write([]byte{0x01}) 336 assert.NoError(t, err) 337 msg := p2pTypes.ErrorMessage(processorErr.Error()) 338 _, err = p2pProvider.Encoding().EncodeWithMaxLength(stream, &msg) 339 assert.NoError(t, err) 340 return 341 } 342 } 343 _, err := stream.Write([]byte{0x00}) 344 assert.NoError(t, err, "Could not write to stream") 345 _, err = p2pProvider.Encoding().EncodeWithMaxLength(stream, blk) 346 assert.NoError(t, err, "Could not send response back") 347 } 348 } 349 } 350 } 351 352 t.Run("no block processor", func(t *testing.T) { 353 p1 := p2ptest.NewTestP2P(t) 354 p2 := p2ptest.NewTestP2P(t) 355 p1.Connect(p2) 356 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 357 358 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]} 359 blocks, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, nil) 360 assert.NoError(t, err) 361 assert.Equal(t, 2, len(blocks)) 362 }) 363 364 t.Run("has block processor - no errors", func(t *testing.T) { 365 p1 := p2ptest.NewTestP2P(t) 366 p2 := p2ptest.NewTestP2P(t) 367 p1.Connect(p2) 368 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 369 370 // No error from block processor. 371 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]} 372 blocksFromProcessor := make([]interfaces.SignedBeaconBlock, 0) 373 blocks, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 374 blocksFromProcessor = append(blocksFromProcessor, block) 375 return nil 376 }) 377 assert.NoError(t, err) 378 assert.Equal(t, 2, len(blocks)) 379 assert.DeepEqual(t, blocks, blocksFromProcessor) 380 }) 381 382 t.Run("has block processor - throw error", func(t *testing.T) { 383 p1 := p2ptest.NewTestP2P(t) 384 p2 := p2ptest.NewTestP2P(t) 385 p1.Connect(p2) 386 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 387 388 // Send error from block processor. 389 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]} 390 errFromProcessor := errors.New("processor error") 391 _, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 392 return errFromProcessor 393 }) 394 assert.ErrorContains(t, errFromProcessor.Error(), err) 395 }) 396 397 t.Run("max request blocks", func(t *testing.T) { 398 p1 := p2ptest.NewTestP2P(t) 399 p2 := p2ptest.NewTestP2P(t) 400 p1.Connect(p2) 401 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil)) 402 403 // No cap on max roots. 404 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]} 405 blocks, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, nil) 406 assert.NoError(t, err) 407 assert.Equal(t, 4, len(blocks)) 408 409 // Cap max returned roots. 410 cfg := params.BeaconNetworkConfig().Copy() 411 maxRequestBlocks := cfg.MaxRequestBlocks 412 defer func() { 413 cfg.MaxRequestBlocks = maxRequestBlocks 414 params.OverrideBeaconNetworkConfig(cfg) 415 }() 416 blocks, err = SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, func(block interfaces.SignedBeaconBlock) error { 417 // Since ssz checks the boundaries, and doesn't normally allow to send requests bigger than 418 // the max request size, we are updating max request size dynamically. Even when updated dynamically, 419 // no more than max request size of blocks is expected on return. 420 cfg.MaxRequestBlocks = 3 421 params.OverrideBeaconNetworkConfig(cfg) 422 return nil 423 }) 424 assert.NoError(t, err) 425 assert.Equal(t, 3, len(blocks)) 426 }) 427 428 t.Run("process custom error", func(t *testing.T) { 429 p1 := p2ptest.NewTestP2P(t) 430 p2 := p2ptest.NewTestP2P(t) 431 p1.Connect(p2) 432 blocksProcessed := 0 433 expectedErr := errors.New("some error") 434 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, func(block interfaces.SignedBeaconBlock) error { 435 if blocksProcessed > 2 { 436 return expectedErr 437 } 438 blocksProcessed++ 439 return nil 440 })) 441 442 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]} 443 blocks, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, nil) 444 assert.ErrorContains(t, expectedErr.Error(), err) 445 assert.Equal(t, 0, len(blocks)) 446 }) 447 448 t.Run("process io.EOF error", func(t *testing.T) { 449 p1 := p2ptest.NewTestP2P(t) 450 p2 := p2ptest.NewTestP2P(t) 451 p1.Connect(p2) 452 blocksProcessed := 0 453 expectedErr := io.EOF 454 p2.SetStreamHandler(pcl, knownBlocksProvider(p2, func(block interfaces.SignedBeaconBlock) error { 455 if blocksProcessed > 2 { 456 return expectedErr 457 } 458 blocksProcessed++ 459 return nil 460 })) 461 462 req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]} 463 blocks, err := SendBeaconBlocksByRootRequest(ctx, nil, p1, p2.PeerID(), req, nil) 464 assert.NoError(t, err) 465 assert.Equal(t, 3, len(blocks)) 466 }) 467 }