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  }