github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go (about)

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/kevinms/leakybucket-go"
    11  	"github.com/libp2p/go-libp2p-core/network"
    12  	"github.com/libp2p/go-libp2p-core/protocol"
    13  	types "github.com/prysmaticlabs/eth2-types"
    14  	chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    16  	db2 "github.com/prysmaticlabs/prysm/beacon-chain/db"
    17  	db "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
    19  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
    20  	p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
    21  	p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
    22  	"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
    23  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    24  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    25  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    26  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    27  	"github.com/prysmaticlabs/prysm/shared/params"
    28  	"github.com/prysmaticlabs/prysm/shared/testutil"
    29  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    30  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    31  	logTest "github.com/sirupsen/logrus/hooks/test"
    32  )
    33  
    34  func TestRPCBeaconBlocksByRange_RPCHandlerReturnsBlocks(t *testing.T) {
    35  	p1 := p2ptest.NewTestP2P(t)
    36  	p2 := p2ptest.NewTestP2P(t)
    37  	p1.Connect(p2)
    38  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
    39  	d := db.SetupDB(t)
    40  
    41  	req := &pb.BeaconBlocksByRangeRequest{
    42  		StartSlot: 100,
    43  		Step:      64,
    44  		Count:     16,
    45  	}
    46  
    47  	// Populate the database with blocks that would match the request.
    48  	for i := req.StartSlot; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
    49  		blk := testutil.NewBeaconBlock()
    50  		blk.Block.Slot = i
    51  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
    52  	}
    53  
    54  	// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
    55  	r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
    56  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
    57  	topic := string(pcl)
    58  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, int64(req.Count*10), false)
    59  	var wg sync.WaitGroup
    60  	wg.Add(1)
    61  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
    62  		defer wg.Done()
    63  		for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
    64  			expectSuccess(t, stream)
    65  			res := testutil.NewBeaconBlock()
    66  			assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
    67  			if res.Block.Slot.SubSlot(req.StartSlot).Mod(req.Step) != 0 {
    68  				t.Errorf("Received unexpected block slot %d", res.Block.Slot)
    69  			}
    70  		}
    71  	})
    72  
    73  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
    74  	require.NoError(t, err)
    75  
    76  	err = r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1)
    77  	require.NoError(t, err)
    78  
    79  	// Make sure that rate limiter doesn't limit capacity exceedingly.
    80  	remainingCapacity := r.rateLimiter.limiterMap[topic].Remaining(p2.PeerID().String())
    81  	expectedCapacity := int64(req.Count*10 - req.Count)
    82  	require.Equal(t, expectedCapacity, remainingCapacity, "Unexpected rate limiting capacity")
    83  
    84  	if testutil.WaitTimeout(&wg, 1*time.Second) {
    85  		t.Fatal("Did not receive stream within 1 sec")
    86  	}
    87  }
    88  
    89  func TestRPCBeaconBlocksByRange_ReturnCorrectNumberBack(t *testing.T) {
    90  	p1 := p2ptest.NewTestP2P(t)
    91  	p2 := p2ptest.NewTestP2P(t)
    92  	p1.Connect(p2)
    93  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
    94  	d := db.SetupDB(t)
    95  
    96  	req := &pb.BeaconBlocksByRangeRequest{
    97  		StartSlot: 0,
    98  		Step:      1,
    99  		Count:     200,
   100  	}
   101  
   102  	genRoot := [32]byte{}
   103  	// Populate the database with blocks that would match the request.
   104  	for i := req.StartSlot; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
   105  		blk := testutil.NewBeaconBlock()
   106  		blk.Block.Slot = i
   107  		if i == 0 {
   108  			rt, err := blk.Block.HashTreeRoot()
   109  			require.NoError(t, err)
   110  			genRoot = rt
   111  		}
   112  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   113  	}
   114  	require.NoError(t, d.SaveGenesisBlockRoot(context.Background(), genRoot))
   115  
   116  	// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
   117  	r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   118  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   119  	topic := string(pcl)
   120  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, int64(req.Count*10), false)
   121  	var wg sync.WaitGroup
   122  	wg.Add(1)
   123  
   124  	// Use a new request to test this out
   125  	newReq := &pb.BeaconBlocksByRangeRequest{StartSlot: 0, Step: 1, Count: 1}
   126  
   127  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   128  		defer wg.Done()
   129  		for i := newReq.StartSlot; i < newReq.StartSlot.Add(newReq.Count*newReq.Step); i += types.Slot(newReq.Step) {
   130  			expectSuccess(t, stream)
   131  			res := testutil.NewBeaconBlock()
   132  			assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
   133  			if res.Block.Slot.SubSlot(newReq.StartSlot).Mod(newReq.Step) != 0 {
   134  				t.Errorf("Received unexpected block slot %d", res.Block.Slot)
   135  			}
   136  			// Expect EOF
   137  			b := make([]byte, 1)
   138  			_, err := stream.Read(b)
   139  			require.ErrorContains(t, io.EOF.Error(), err)
   140  		}
   141  	})
   142  
   143  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   144  	require.NoError(t, err)
   145  
   146  	err = r.beaconBlocksByRangeRPCHandler(context.Background(), newReq, stream1)
   147  	require.NoError(t, err)
   148  
   149  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   150  		t.Fatal("Did not receive stream within 1 sec")
   151  	}
   152  }
   153  
   154  func TestRPCBeaconBlocksByRange_RPCHandlerReturnsSortedBlocks(t *testing.T) {
   155  	p1 := p2ptest.NewTestP2P(t)
   156  	p2 := p2ptest.NewTestP2P(t)
   157  	p1.Connect(p2)
   158  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   159  	d := db.SetupDB(t)
   160  
   161  	req := &pb.BeaconBlocksByRangeRequest{
   162  		StartSlot: 200,
   163  		Step:      21,
   164  		Count:     33,
   165  	}
   166  
   167  	endSlot := req.StartSlot.Add(req.Step * (req.Count - 1))
   168  	expectedRoots := make([][32]byte, req.Count)
   169  	// Populate the database with blocks that would match the request.
   170  	for i, j := endSlot, req.Count-1; i >= req.StartSlot; i -= types.Slot(req.Step) {
   171  		blk := testutil.NewBeaconBlock()
   172  		blk.Block.Slot = i
   173  		rt, err := blk.Block.HashTreeRoot()
   174  		require.NoError(t, err)
   175  		expectedRoots[j] = rt
   176  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   177  		j--
   178  	}
   179  
   180  	// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
   181  	r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   182  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   183  	topic := string(pcl)
   184  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, int64(req.Count*10), false)
   185  
   186  	var wg sync.WaitGroup
   187  	wg.Add(1)
   188  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   189  		defer wg.Done()
   190  		prevSlot := types.Slot(0)
   191  		require.Equal(t, uint64(len(expectedRoots)), req.Count, "Number of roots not expected")
   192  		for i, j := req.StartSlot, 0; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
   193  			expectSuccess(t, stream)
   194  			res := &ethpb.SignedBeaconBlock{}
   195  			assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
   196  			if res.Block.Slot < prevSlot {
   197  				t.Errorf("Received block is unsorted with slot %d lower than previous slot %d", res.Block.Slot, prevSlot)
   198  			}
   199  			rt, err := res.Block.HashTreeRoot()
   200  			require.NoError(t, err)
   201  			assert.Equal(t, expectedRoots[j], rt, "roots not equal")
   202  			prevSlot = res.Block.Slot
   203  			j++
   204  		}
   205  	})
   206  
   207  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   208  	require.NoError(t, err)
   209  	require.NoError(t, r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1))
   210  
   211  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   212  		t.Fatal("Did not receive stream within 1 sec")
   213  	}
   214  }
   215  
   216  func TestRPCBeaconBlocksByRange_ReturnsGenesisBlock(t *testing.T) {
   217  	p1 := p2ptest.NewTestP2P(t)
   218  	p2 := p2ptest.NewTestP2P(t)
   219  	p1.Connect(p2)
   220  	assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   221  	d := db.SetupDB(t)
   222  
   223  	req := &pb.BeaconBlocksByRangeRequest{
   224  		StartSlot: 0,
   225  		Step:      1,
   226  		Count:     4,
   227  	}
   228  
   229  	prevRoot := [32]byte{}
   230  	// Populate the database with blocks that would match the request.
   231  	for i := req.StartSlot; i < req.StartSlot.Add(req.Step*req.Count); i++ {
   232  		blk := testutil.NewBeaconBlock()
   233  		blk.Block.Slot = i
   234  		blk.Block.ParentRoot = prevRoot[:]
   235  		rt, err := blk.Block.HashTreeRoot()
   236  		require.NoError(t, err)
   237  
   238  		// Save genesis block
   239  		if i == 0 {
   240  			require.NoError(t, d.SaveGenesisBlockRoot(context.Background(), rt))
   241  		}
   242  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   243  		prevRoot = rt
   244  	}
   245  
   246  	r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   247  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   248  	topic := string(pcl)
   249  	r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(10000, 10000, false)
   250  
   251  	var wg sync.WaitGroup
   252  	wg.Add(1)
   253  	p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   254  		defer wg.Done()
   255  		// check for genesis block
   256  		expectSuccess(t, stream)
   257  		res := &ethpb.SignedBeaconBlock{}
   258  		assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
   259  		assert.Equal(t, types.Slot(0), res.Block.Slot, "genesis block was not returned")
   260  		for i := req.StartSlot.Add(req.Step); i < types.Slot(req.Count*req.Step); i += types.Slot(req.Step) {
   261  			expectSuccess(t, stream)
   262  			res := &ethpb.SignedBeaconBlock{}
   263  			assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
   264  		}
   265  	})
   266  
   267  	stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   268  	require.NoError(t, err)
   269  	require.NoError(t, r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1))
   270  
   271  	if testutil.WaitTimeout(&wg, 1*time.Second) {
   272  		t.Fatal("Did not receive stream within 1 sec")
   273  	}
   274  }
   275  
   276  func TestRPCBeaconBlocksByRange_RPCHandlerRateLimitOverflow(t *testing.T) {
   277  	d := db.SetupDB(t)
   278  	saveBlocks := func(req *pb.BeaconBlocksByRangeRequest) {
   279  		// Populate the database with blocks that would match the request.
   280  		parentRoot := [32]byte{}
   281  		for i := req.StartSlot; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
   282  			block := testutil.NewBeaconBlock()
   283  			block.Block.Slot = i
   284  			if req.Step == 1 {
   285  				block.Block.ParentRoot = parentRoot[:]
   286  			}
   287  			require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(block)))
   288  			rt, err := block.Block.HashTreeRoot()
   289  			require.NoError(t, err)
   290  			parentRoot = rt
   291  		}
   292  	}
   293  	sendRequest := func(p1, p2 *p2ptest.TestP2P, r *Service,
   294  		req *pb.BeaconBlocksByRangeRequest, validateBlocks bool, success bool) error {
   295  		var wg sync.WaitGroup
   296  		wg.Add(1)
   297  		pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   298  		p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   299  			defer wg.Done()
   300  			if !validateBlocks {
   301  				return
   302  			}
   303  			for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
   304  				if !success {
   305  					continue
   306  				}
   307  				expectSuccess(t, stream)
   308  				res := testutil.NewBeaconBlock()
   309  				assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, res))
   310  				if res.Block.Slot.SubSlot(req.StartSlot).Mod(req.Step) != 0 {
   311  					t.Errorf("Received unexpected block slot %d", res.Block.Slot)
   312  				}
   313  			}
   314  		})
   315  		stream, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   316  		require.NoError(t, err)
   317  		if err := r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream); err != nil {
   318  			return err
   319  		}
   320  		if testutil.WaitTimeout(&wg, 1*time.Second) {
   321  			t.Fatal("Did not receive stream within 1 sec")
   322  		}
   323  		return nil
   324  	}
   325  
   326  	t.Run("high request count param and no overflow", func(t *testing.T) {
   327  		p1 := p2ptest.NewTestP2P(t)
   328  		p2 := p2ptest.NewTestP2P(t)
   329  		p1.Connect(p2)
   330  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   331  
   332  		capacity := int64(flags.Get().BlockBatchLimit * 3)
   333  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   334  
   335  		pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   336  		topic := string(pcl)
   337  		r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, capacity, false)
   338  		req := &pb.BeaconBlocksByRangeRequest{
   339  			StartSlot: 100,
   340  			Step:      5,
   341  			Count:     uint64(capacity),
   342  		}
   343  		saveBlocks(req)
   344  
   345  		assert.NoError(t, sendRequest(p1, p2, r, req, true, true))
   346  
   347  		remainingCapacity := r.rateLimiter.limiterMap[topic].Remaining(p2.PeerID().String())
   348  		expectedCapacity := int64(0) // Whole capacity is used, but no overflow.
   349  		assert.Equal(t, expectedCapacity, remainingCapacity, "Unexpected rate limiting capacity")
   350  	})
   351  
   352  	t.Run("high request count param and overflow", func(t *testing.T) {
   353  		p1 := p2ptest.NewTestP2P(t)
   354  		p2 := p2ptest.NewTestP2P(t)
   355  		p1.Connect(p2)
   356  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   357  
   358  		capacity := int64(flags.Get().BlockBatchLimit * 3)
   359  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   360  
   361  		pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   362  		topic := string(pcl)
   363  		r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, capacity, false)
   364  
   365  		req := &pb.BeaconBlocksByRangeRequest{
   366  			StartSlot: 100,
   367  			Step:      5,
   368  			Count:     uint64(capacity + 1),
   369  		}
   370  		saveBlocks(req)
   371  
   372  		for i := 0; i < p2.Peers().Scorers().BadResponsesScorer().Params().Threshold; i++ {
   373  			err := sendRequest(p1, p2, r, req, false, true)
   374  			assert.ErrorContains(t, p2ptypes.ErrRateLimited.Error(), err)
   375  		}
   376  
   377  		remainingCapacity := r.rateLimiter.limiterMap[topic].Remaining(p2.PeerID().String())
   378  		expectedCapacity := int64(0) // Whole capacity is used.
   379  		assert.Equal(t, expectedCapacity, remainingCapacity, "Unexpected rate limiting capacity")
   380  	})
   381  
   382  	t.Run("many requests with count set to max blocks per second", func(t *testing.T) {
   383  		p1 := p2ptest.NewTestP2P(t)
   384  		p2 := p2ptest.NewTestP2P(t)
   385  		p1.Connect(p2)
   386  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   387  
   388  		capacity := int64(flags.Get().BlockBatchLimit * flags.Get().BlockBatchLimitBurstFactor)
   389  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   390  		pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   391  		topic := string(pcl)
   392  		r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, capacity, false)
   393  
   394  		req := &pb.BeaconBlocksByRangeRequest{
   395  			StartSlot: 100,
   396  			Step:      1,
   397  			Count:     uint64(flags.Get().BlockBatchLimit),
   398  		}
   399  		saveBlocks(req)
   400  
   401  		for i := 0; i < flags.Get().BlockBatchLimitBurstFactor; i++ {
   402  			assert.NoError(t, sendRequest(p1, p2, r, req, true, false))
   403  		}
   404  
   405  		// One more request should result in overflow.
   406  		for i := 0; i < p2.Peers().Scorers().BadResponsesScorer().Params().Threshold; i++ {
   407  			err := sendRequest(p1, p2, r, req, false, false)
   408  			assert.ErrorContains(t, p2ptypes.ErrRateLimited.Error(), err)
   409  		}
   410  
   411  		remainingCapacity := r.rateLimiter.limiterMap[topic].Remaining(p2.PeerID().String())
   412  		expectedCapacity := int64(0) // Whole capacity is used.
   413  		assert.Equal(t, expectedCapacity, remainingCapacity, "Unexpected rate limiting capacity")
   414  	})
   415  }
   416  
   417  func TestRPCBeaconBlocksByRange_validateRangeRequest(t *testing.T) {
   418  	slotsSinceGenesis := types.Slot(1000)
   419  	offset := int64(slotsSinceGenesis.Mul(params.BeaconConfig().SecondsPerSlot))
   420  	r := &Service{
   421  		cfg: &Config{
   422  			Chain: &chainMock.ChainService{
   423  				Genesis: time.Now().Add(time.Second * time.Duration(-1*offset)),
   424  			},
   425  		},
   426  	}
   427  
   428  	tests := []struct {
   429  		name          string
   430  		req           *pb.BeaconBlocksByRangeRequest
   431  		expectedError error
   432  		errorToLog    string
   433  	}{
   434  		{
   435  			name: "Zero Count",
   436  			req: &pb.BeaconBlocksByRangeRequest{
   437  				Count: 0,
   438  				Step:  1,
   439  			},
   440  			expectedError: p2ptypes.ErrInvalidRequest,
   441  			errorToLog:    "validation did not fail with bad count",
   442  		},
   443  		{
   444  			name: "Over limit Count",
   445  			req: &pb.BeaconBlocksByRangeRequest{
   446  				Count: params.BeaconNetworkConfig().MaxRequestBlocks + 1,
   447  				Step:  1,
   448  			},
   449  			expectedError: p2ptypes.ErrInvalidRequest,
   450  			errorToLog:    "validation did not fail with bad count",
   451  		},
   452  		{
   453  			name: "Correct Count",
   454  			req: &pb.BeaconBlocksByRangeRequest{
   455  				Count: params.BeaconNetworkConfig().MaxRequestBlocks - 1,
   456  				Step:  1,
   457  			},
   458  			errorToLog: "validation failed with correct count",
   459  		},
   460  		{
   461  			name: "Zero Step",
   462  			req: &pb.BeaconBlocksByRangeRequest{
   463  				Step:  0,
   464  				Count: 1,
   465  			},
   466  			expectedError: p2ptypes.ErrInvalidRequest,
   467  			errorToLog:    "validation did not fail with bad step",
   468  		},
   469  		{
   470  			name: "Over limit Step",
   471  			req: &pb.BeaconBlocksByRangeRequest{
   472  				Step:  rangeLimit + 1,
   473  				Count: 1,
   474  			},
   475  			expectedError: p2ptypes.ErrInvalidRequest,
   476  			errorToLog:    "validation did not fail with bad step",
   477  		},
   478  		{
   479  			name: "Correct Step",
   480  			req: &pb.BeaconBlocksByRangeRequest{
   481  				Step:  rangeLimit - 1,
   482  				Count: 2,
   483  			},
   484  			errorToLog: "validation failed with correct step",
   485  		},
   486  		{
   487  			name: "Over Limit Start Slot",
   488  			req: &pb.BeaconBlocksByRangeRequest{
   489  				StartSlot: slotsSinceGenesis.Add((2 * rangeLimit) + 1),
   490  				Step:      1,
   491  				Count:     1,
   492  			},
   493  			expectedError: p2ptypes.ErrInvalidRequest,
   494  			errorToLog:    "validation did not fail with bad start slot",
   495  		},
   496  		{
   497  			name: "Over Limit End Slot",
   498  			req: &pb.BeaconBlocksByRangeRequest{
   499  				Step:  1,
   500  				Count: params.BeaconNetworkConfig().MaxRequestBlocks + 1,
   501  			},
   502  			expectedError: p2ptypes.ErrInvalidRequest,
   503  			errorToLog:    "validation did not fail with bad end slot",
   504  		},
   505  		{
   506  			name: "Exceed Range Limit",
   507  			req: &pb.BeaconBlocksByRangeRequest{
   508  				Step:  3,
   509  				Count: uint64(slotsSinceGenesis / 2),
   510  			},
   511  			expectedError: p2ptypes.ErrInvalidRequest,
   512  			errorToLog:    "validation did not fail with bad range",
   513  		},
   514  		{
   515  			name: "Valid Request",
   516  			req: &pb.BeaconBlocksByRangeRequest{
   517  				Step:      1,
   518  				Count:     params.BeaconNetworkConfig().MaxRequestBlocks - 1,
   519  				StartSlot: 50,
   520  			},
   521  			errorToLog: "validation failed with valid params",
   522  		},
   523  	}
   524  
   525  	for _, tt := range tests {
   526  		t.Run(tt.name, func(t *testing.T) {
   527  			if tt.expectedError != nil {
   528  				assert.ErrorContains(t, tt.expectedError.Error(), r.validateRangeRequest(tt.req), tt.errorToLog)
   529  			} else {
   530  				assert.NoError(t, r.validateRangeRequest(tt.req), tt.errorToLog)
   531  			}
   532  		})
   533  	}
   534  }
   535  
   536  func TestRPCBeaconBlocksByRange_EnforceResponseInvariants(t *testing.T) {
   537  	d := db.SetupDB(t)
   538  	hook := logTest.NewGlobal()
   539  	saveBlocks := func(req *pb.BeaconBlocksByRangeRequest) {
   540  		// Populate the database with blocks that would match the request.
   541  		parentRoot := [32]byte{}
   542  		for i := req.StartSlot; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
   543  			block := testutil.NewBeaconBlock()
   544  			block.Block.Slot = i
   545  			block.Block.ParentRoot = parentRoot[:]
   546  			require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(block)))
   547  			rt, err := block.Block.HashTreeRoot()
   548  			require.NoError(t, err)
   549  			parentRoot = rt
   550  		}
   551  	}
   552  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   553  	sendRequest := func(p1, p2 *p2ptest.TestP2P, r *Service,
   554  		req *pb.BeaconBlocksByRangeRequest, processBlocks func([]*ethpb.SignedBeaconBlock)) error {
   555  		var wg sync.WaitGroup
   556  		wg.Add(1)
   557  		p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   558  			defer wg.Done()
   559  			blocks := make([]*ethpb.SignedBeaconBlock, 0, req.Count)
   560  			for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
   561  				expectSuccess(t, stream)
   562  				blk := testutil.NewBeaconBlock()
   563  				assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, blk))
   564  				if blk.Block.Slot.SubSlot(req.StartSlot).Mod(req.Step) != 0 {
   565  					t.Errorf("Received unexpected block slot %d", blk.Block.Slot)
   566  				}
   567  				blocks = append(blocks, blk)
   568  			}
   569  			processBlocks(blocks)
   570  		})
   571  		stream, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   572  		require.NoError(t, err)
   573  		if err := r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream); err != nil {
   574  			return err
   575  		}
   576  		if testutil.WaitTimeout(&wg, 1*time.Second) {
   577  			t.Fatal("Did not receive stream within 1 sec")
   578  		}
   579  		return nil
   580  	}
   581  
   582  	t.Run("assert range", func(t *testing.T) {
   583  		p1 := p2ptest.NewTestP2P(t)
   584  		p2 := p2ptest.NewTestP2P(t)
   585  		p1.Connect(p2)
   586  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   587  
   588  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   589  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   590  		req := &pb.BeaconBlocksByRangeRequest{
   591  			StartSlot: 448,
   592  			Step:      1,
   593  			Count:     64,
   594  		}
   595  		saveBlocks(req)
   596  
   597  		hook.Reset()
   598  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   599  			assert.Equal(t, req.Count, uint64(len(blocks)))
   600  			for _, blk := range blocks {
   601  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= req.StartSlot.Add(req.Count*req.Step) {
   602  					t.Errorf("Block slot is out of range: %d is not within [%d, %d)",
   603  						blk.Block.Slot, req.StartSlot, req.StartSlot.Add(req.Count*req.Step))
   604  				}
   605  			}
   606  		})
   607  		assert.NoError(t, err)
   608  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   609  	})
   610  }
   611  
   612  func TestRPCBeaconBlocksByRange_FilterBlocks(t *testing.T) {
   613  	hook := logTest.NewGlobal()
   614  
   615  	saveBlocks := func(d db2.Database, chain *chainMock.ChainService, req *pb.BeaconBlocksByRangeRequest, finalized bool) {
   616  		blk := testutil.NewBeaconBlock()
   617  		blk.Block.Slot = 0
   618  		previousRoot, err := blk.Block.HashTreeRoot()
   619  		require.NoError(t, err)
   620  
   621  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   622  		require.NoError(t, d.SaveGenesisBlockRoot(context.Background(), previousRoot))
   623  		blocks := make([]*ethpb.SignedBeaconBlock, req.Count)
   624  		// Populate the database with blocks that would match the request.
   625  		for i, j := req.StartSlot, 0; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
   626  			parentRoot := make([]byte, 32)
   627  			copy(parentRoot, previousRoot[:])
   628  			blocks[j] = testutil.NewBeaconBlock()
   629  			blocks[j].Block.Slot = i
   630  			blocks[j].Block.ParentRoot = parentRoot
   631  			var err error
   632  			previousRoot, err = blocks[j].Block.HashTreeRoot()
   633  			require.NoError(t, err)
   634  			require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blocks[j])))
   635  			j++
   636  		}
   637  		stateSummaries := make([]*pb.StateSummary, len(blocks))
   638  
   639  		if finalized {
   640  			if chain.CanonicalRoots == nil {
   641  				chain.CanonicalRoots = map[[32]byte]bool{}
   642  			}
   643  			for i, b := range blocks {
   644  				bRoot, err := b.Block.HashTreeRoot()
   645  				require.NoError(t, err)
   646  				stateSummaries[i] = &pb.StateSummary{
   647  					Slot: b.Block.Slot,
   648  					Root: bRoot[:],
   649  				}
   650  				chain.CanonicalRoots[bRoot] = true
   651  			}
   652  			require.NoError(t, d.SaveStateSummaries(context.Background(), stateSummaries))
   653  			require.NoError(t, d.SaveFinalizedCheckpoint(context.Background(), &ethpb.Checkpoint{
   654  				Epoch: helpers.SlotToEpoch(stateSummaries[len(stateSummaries)-1].Slot),
   655  				Root:  stateSummaries[len(stateSummaries)-1].Root,
   656  			}))
   657  		}
   658  	}
   659  	saveBadBlocks := func(d db2.Database, chain *chainMock.ChainService,
   660  		req *pb.BeaconBlocksByRangeRequest, badBlockNum uint64, finalized bool) {
   661  		blk := testutil.NewBeaconBlock()
   662  		blk.Block.Slot = 0
   663  		previousRoot, err := blk.Block.HashTreeRoot()
   664  		require.NoError(t, err)
   665  		genRoot := previousRoot
   666  
   667  		require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk)))
   668  		require.NoError(t, d.SaveGenesisBlockRoot(context.Background(), previousRoot))
   669  		blocks := make([]*ethpb.SignedBeaconBlock, req.Count)
   670  		// Populate the database with blocks with non linear roots.
   671  		for i, j := req.StartSlot, 0; i < req.StartSlot.Add(req.Step*req.Count); i += types.Slot(req.Step) {
   672  			parentRoot := make([]byte, 32)
   673  			copy(parentRoot, previousRoot[:])
   674  			blocks[j] = testutil.NewBeaconBlock()
   675  			blocks[j].Block.Slot = i
   676  			blocks[j].Block.ParentRoot = parentRoot
   677  			// Make the 2nd block have a bad root.
   678  			if j == int(badBlockNum) {
   679  				blocks[j].Block.ParentRoot = genRoot[:]
   680  			}
   681  			var err error
   682  			previousRoot, err = blocks[j].Block.HashTreeRoot()
   683  			require.NoError(t, err)
   684  			require.NoError(t, d.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blocks[j])))
   685  			j++
   686  		}
   687  		stateSummaries := make([]*pb.StateSummary, len(blocks))
   688  		if finalized {
   689  			if chain.CanonicalRoots == nil {
   690  				chain.CanonicalRoots = map[[32]byte]bool{}
   691  			}
   692  			for i, b := range blocks {
   693  				bRoot, err := b.Block.HashTreeRoot()
   694  				require.NoError(t, err)
   695  				stateSummaries[i] = &pb.StateSummary{
   696  					Slot: b.Block.Slot,
   697  					Root: bRoot[:],
   698  				}
   699  				chain.CanonicalRoots[bRoot] = true
   700  			}
   701  			require.NoError(t, d.SaveStateSummaries(context.Background(), stateSummaries))
   702  			require.NoError(t, d.SaveFinalizedCheckpoint(context.Background(), &ethpb.Checkpoint{
   703  				Epoch: helpers.SlotToEpoch(stateSummaries[len(stateSummaries)-1].Slot),
   704  				Root:  stateSummaries[len(stateSummaries)-1].Root,
   705  			}))
   706  		}
   707  	}
   708  	pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
   709  	sendRequest := func(p1, p2 *p2ptest.TestP2P, r *Service,
   710  		req *pb.BeaconBlocksByRangeRequest, processBlocks func([]*ethpb.SignedBeaconBlock)) error {
   711  		var wg sync.WaitGroup
   712  		wg.Add(1)
   713  		p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
   714  			defer wg.Done()
   715  			blocks := make([]*ethpb.SignedBeaconBlock, 0, req.Count)
   716  			for i := req.StartSlot; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
   717  				code, _, err := ReadStatusCode(stream, &encoder.SszNetworkEncoder{})
   718  				if err != nil && err != io.EOF {
   719  					t.Fatal(err)
   720  				}
   721  				if code != 0 || err == io.EOF {
   722  					break
   723  				}
   724  				blk := testutil.NewBeaconBlock()
   725  				assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, blk))
   726  				if blk.Block.Slot.SubSlot(req.StartSlot).Mod(req.Step) != 0 {
   727  					t.Errorf("Received unexpected block slot %d", blk.Block.Slot)
   728  				}
   729  				blocks = append(blocks, blk)
   730  			}
   731  			processBlocks(blocks)
   732  		})
   733  		stream, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
   734  		require.NoError(t, err)
   735  		if err := r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream); err != nil {
   736  			return err
   737  		}
   738  		if testutil.WaitTimeout(&wg, 1*time.Second) {
   739  			t.Fatal("Did not receive stream within 1 sec")
   740  		}
   741  		return nil
   742  	}
   743  
   744  	t.Run("process normal range", func(t *testing.T) {
   745  		p1 := p2ptest.NewTestP2P(t)
   746  		p2 := p2ptest.NewTestP2P(t)
   747  		d := db.SetupDB(t)
   748  
   749  		p1.Connect(p2)
   750  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   751  
   752  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   753  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   754  		req := &pb.BeaconBlocksByRangeRequest{
   755  			StartSlot: 1,
   756  			Step:      1,
   757  			Count:     64,
   758  		}
   759  		saveBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, true)
   760  
   761  		hook.Reset()
   762  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   763  			assert.Equal(t, req.Count, uint64(len(blocks)))
   764  			for _, blk := range blocks {
   765  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= req.StartSlot.Add(req.Count*req.Step) {
   766  					t.Errorf("Block slot is out of range: %d is not within [%d, %d)",
   767  						blk.Block.Slot, req.StartSlot, req.StartSlot.Add(req.Count*req.Step))
   768  				}
   769  			}
   770  		})
   771  		assert.NoError(t, err)
   772  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   773  	})
   774  
   775  	t.Run("process non linear blocks", func(t *testing.T) {
   776  		p1 := p2ptest.NewTestP2P(t)
   777  		p2 := p2ptest.NewTestP2P(t)
   778  		d := db.SetupDB(t)
   779  
   780  		p1.Connect(p2)
   781  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   782  
   783  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   784  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   785  		req := &pb.BeaconBlocksByRangeRequest{
   786  			StartSlot: 1,
   787  			Step:      1,
   788  			Count:     64,
   789  		}
   790  		saveBadBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, 2, true)
   791  
   792  		hook.Reset()
   793  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   794  			assert.Equal(t, uint64(2), uint64(len(blocks)))
   795  			prevRoot := [32]byte{}
   796  			for _, blk := range blocks {
   797  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= req.StartSlot.Add(req.Count*req.Step) {
   798  					t.Errorf("Block slot is out of range: %d is not within [%d, %d)",
   799  						blk.Block.Slot, req.StartSlot, req.StartSlot.Add(req.Count*req.Step))
   800  				}
   801  				if prevRoot != [32]byte{} && bytesutil.ToBytes32(blk.Block.ParentRoot) != prevRoot {
   802  					t.Errorf("non linear chain received, expected %#x but got %#x", prevRoot, blk.Block.ParentRoot)
   803  				}
   804  			}
   805  		})
   806  		assert.NoError(t, err)
   807  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   808  	})
   809  
   810  	t.Run("process non linear blocks with 2nd bad batch", func(t *testing.T) {
   811  		p1 := p2ptest.NewTestP2P(t)
   812  		p2 := p2ptest.NewTestP2P(t)
   813  		d := db.SetupDB(t)
   814  
   815  		p1.Connect(p2)
   816  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   817  
   818  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   819  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   820  		req := &pb.BeaconBlocksByRangeRequest{
   821  			StartSlot: 1,
   822  			Step:      1,
   823  			Count:     128,
   824  		}
   825  		saveBadBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, 65, true)
   826  
   827  		hook.Reset()
   828  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   829  			assert.Equal(t, uint64(65), uint64(len(blocks)))
   830  			prevRoot := [32]byte{}
   831  			for _, blk := range blocks {
   832  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= req.StartSlot.Add(req.Count*req.Step) {
   833  					t.Errorf("Block slot is out of range: %d is not within [%d, %d)",
   834  						blk.Block.Slot, req.StartSlot, req.StartSlot.Add(req.Count*req.Step))
   835  				}
   836  				if prevRoot != [32]byte{} && bytesutil.ToBytes32(blk.Block.ParentRoot) != prevRoot {
   837  					t.Errorf("non linear chain received, expected %#x but got %#x", prevRoot, blk.Block.ParentRoot)
   838  				}
   839  			}
   840  		})
   841  		assert.NoError(t, err)
   842  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   843  	})
   844  
   845  	t.Run("only return finalized blocks", func(t *testing.T) {
   846  		p1 := p2ptest.NewTestP2P(t)
   847  		p2 := p2ptest.NewTestP2P(t)
   848  		d := db.SetupDB(t)
   849  
   850  		p1.Connect(p2)
   851  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   852  
   853  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   854  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   855  		req := &pb.BeaconBlocksByRangeRequest{
   856  			StartSlot: 1,
   857  			Step:      1,
   858  			Count:     64,
   859  		}
   860  		saveBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, true)
   861  		req.StartSlot = 65
   862  		req.Step = 1
   863  		req.Count = 128
   864  		// Save unfinalized chain.
   865  		saveBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, false)
   866  
   867  		req.StartSlot = 1
   868  		hook.Reset()
   869  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   870  			assert.Equal(t, uint64(64), uint64(len(blocks)))
   871  			prevRoot := [32]byte{}
   872  			for _, blk := range blocks {
   873  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= 65 {
   874  					t.Errorf("Block slot is out of range: %d is not within [%d, 64)",
   875  						blk.Block.Slot, req.StartSlot)
   876  				}
   877  				if prevRoot != [32]byte{} && bytesutil.ToBytes32(blk.Block.ParentRoot) != prevRoot {
   878  					t.Errorf("non linear chain received, expected %#x but got %#x", prevRoot, blk.Block.ParentRoot)
   879  				}
   880  			}
   881  		})
   882  		assert.NoError(t, err)
   883  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   884  	})
   885  	t.Run("reject duplicate and non canonical blocks", func(t *testing.T) {
   886  		p1 := p2ptest.NewTestP2P(t)
   887  		p2 := p2ptest.NewTestP2P(t)
   888  		d := db.SetupDB(t)
   889  
   890  		p1.Connect(p2)
   891  		assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
   892  
   893  		r := &Service{cfg: &Config{P2P: p1, DB: d, Chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
   894  		r.rateLimiter.limiterMap[string(pcl)] = leakybucket.NewCollector(0.000001, 640, false)
   895  		req := &pb.BeaconBlocksByRangeRequest{
   896  			StartSlot: 1,
   897  			Step:      1,
   898  			Count:     64,
   899  		}
   900  		saveBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, true)
   901  
   902  		// Create a duplicate set of unfinalized blocks.
   903  		req.StartSlot = 1
   904  		req.Step = 1
   905  		req.Count = 300
   906  		// Save unfinalized chain.
   907  		saveBlocks(d, r.cfg.Chain.(*chainMock.ChainService), req, false)
   908  
   909  		req.Count = 64
   910  		hook.Reset()
   911  		err := sendRequest(p1, p2, r, req, func(blocks []*ethpb.SignedBeaconBlock) {
   912  			assert.Equal(t, uint64(64), uint64(len(blocks)))
   913  			prevRoot := [32]byte{}
   914  			for _, blk := range blocks {
   915  				if blk.Block.Slot < req.StartSlot || blk.Block.Slot >= 65 {
   916  					t.Errorf("Block slot is out of range: %d is not within [%d, 64)",
   917  						blk.Block.Slot, req.StartSlot)
   918  				}
   919  				if prevRoot != [32]byte{} && bytesutil.ToBytes32(blk.Block.ParentRoot) != prevRoot {
   920  					t.Errorf("non linear chain received, expected %#x but got %#x", prevRoot, blk.Block.ParentRoot)
   921  				}
   922  			}
   923  		})
   924  		assert.NoError(t, err)
   925  		require.LogsDoNotContain(t, hook, "Disconnecting bad peer")
   926  	})
   927  }