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

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	libp2pcore "github.com/libp2p/go-libp2p-core"
     9  	"github.com/libp2p/go-libp2p-core/network"
    10  	"github.com/libp2p/go-libp2p-core/peer"
    11  	types "github.com/prysmaticlabs/eth2-types"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
    13  	p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
    14  	"github.com/prysmaticlabs/prysm/shared/mputil"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  var backOffTime = map[types.SSZUint64]time.Duration{
    19  	// Do not dial peers which are from a different/unverifiable
    20  	// network.
    21  	p2ptypes.GoodbyeCodeWrongNetwork:          24 * time.Hour,
    22  	p2ptypes.GoodbyeCodeUnableToVerifyNetwork: 24 * time.Hour,
    23  	// If local peer is banned, we back off for
    24  	// 2 hours to let the remote peer score us
    25  	// back up again.
    26  	p2ptypes.GoodbyeCodeBadScore:       2 * time.Hour,
    27  	p2ptypes.GoodbyeCodeBanned:         2 * time.Hour,
    28  	p2ptypes.GoodbyeCodeClientShutdown: 1 * time.Hour,
    29  	// Wait 5 minutes before dialing a peer who is
    30  	// 'full'
    31  	p2ptypes.GoodbyeCodeTooManyPeers: 5 * time.Minute,
    32  	p2ptypes.GoodbyeCodeGenericError: 2 * time.Minute,
    33  }
    34  
    35  // goodbyeRPCHandler reads the incoming goodbye rpc message from the peer.
    36  func (s *Service) goodbyeRPCHandler(_ context.Context, msg interface{}, stream libp2pcore.Stream) error {
    37  	SetRPCStreamDeadlines(stream)
    38  
    39  	m, ok := msg.(*types.SSZUint64)
    40  	if !ok {
    41  		return fmt.Errorf("wrong message type for goodbye, got %T, wanted *uint64", msg)
    42  	}
    43  	if err := s.rateLimiter.validateRequest(stream, 1); err != nil {
    44  		return err
    45  	}
    46  	s.rateLimiter.add(stream, 1)
    47  	log := log.WithField("Reason", goodbyeMessage(*m))
    48  	log.WithField("peer", stream.Conn().RemotePeer()).Debug("Peer has sent a goodbye message")
    49  	s.cfg.P2P.Peers().SetNextValidTime(stream.Conn().RemotePeer(), goodByeBackoff(*m))
    50  	// closes all streams with the peer
    51  	return s.cfg.P2P.Disconnect(stream.Conn().RemotePeer())
    52  }
    53  
    54  // disconnectBadPeer checks whether peer is considered bad by some scorer, and tries to disconnect
    55  // the peer, if that is the case. Additionally, disconnection reason is obtained from scorer.
    56  func (s *Service) disconnectBadPeer(ctx context.Context, id peer.ID) {
    57  	if !s.cfg.P2P.Peers().IsBad(id) {
    58  		return
    59  	}
    60  	goodbyeCode := p2ptypes.ErrToGoodbyeCode(s.cfg.P2P.Peers().Scorers().ValidationError(id))
    61  	if err := s.sendGoodByeAndDisconnect(ctx, goodbyeCode, id); err != nil {
    62  		log.Debugf("Error when disconnecting with bad peer: %v", err)
    63  	}
    64  }
    65  
    66  // A custom goodbye method that is used by our connection handler, in the
    67  // event we receive bad peers.
    68  func (s *Service) sendGoodbye(ctx context.Context, id peer.ID) error {
    69  	return s.sendGoodByeAndDisconnect(ctx, p2ptypes.GoodbyeCodeGenericError, id)
    70  }
    71  
    72  func (s *Service) sendGoodByeAndDisconnect(ctx context.Context, code p2ptypes.RPCGoodbyeCode, id peer.ID) error {
    73  	lock := mputil.NewMultilock(id.String())
    74  	lock.Lock()
    75  	defer lock.Unlock()
    76  	// In the event we are already disconnected, exit early from the
    77  	// goodbye method to prevent redundant streams from being created.
    78  	if s.cfg.P2P.Host().Network().Connectedness(id) == network.NotConnected {
    79  		return nil
    80  	}
    81  	if err := s.sendGoodByeMessage(ctx, code, id); err != nil {
    82  		log.WithFields(logrus.Fields{
    83  			"error": err,
    84  			"peer":  id,
    85  		}).Debug("Could not send goodbye message to peer")
    86  	}
    87  	return s.cfg.P2P.Disconnect(id)
    88  }
    89  
    90  func (s *Service) sendGoodByeMessage(ctx context.Context, code p2ptypes.RPCGoodbyeCode, id peer.ID) error {
    91  	ctx, cancel := context.WithTimeout(ctx, respTimeout)
    92  	defer cancel()
    93  
    94  	stream, err := s.cfg.P2P.Send(ctx, &code, p2p.RPCGoodByeTopicV1, id)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	defer closeStream(stream, log)
    99  
   100  	log := log.WithField("Reason", goodbyeMessage(code))
   101  	log.WithField("peer", stream.Conn().RemotePeer()).Debug("Sending Goodbye message to peer")
   102  
   103  	// Wait up to the response timeout for the peer to receive the goodbye
   104  	// and close the stream (or disconnect). We usually don't bother waiting
   105  	// around for an EOF, but we're going to close this connection
   106  	// immediately after we say goodbye.
   107  	//
   108  	// NOTE: we don't actually check the response as there's nothing we can
   109  	// do if something fails. We just need to wait for it.
   110  	SetStreamReadDeadline(stream, respTimeout)
   111  	_, _err := stream.Read([]byte{0})
   112  	_ = _err
   113  
   114  	return nil
   115  }
   116  
   117  func goodbyeMessage(num p2ptypes.RPCGoodbyeCode) string {
   118  	reason, ok := p2ptypes.GoodbyeCodeMessages[num]
   119  	if ok {
   120  		return reason
   121  	}
   122  	return fmt.Sprintf("unknown goodbye value of %d received", num)
   123  }
   124  
   125  // determines which backoff time to use depending on the
   126  // goodbye code provided.
   127  func goodByeBackoff(num p2ptypes.RPCGoodbyeCode) time.Time {
   128  	duration, ok := backOffTime[num]
   129  	if !ok {
   130  		return time.Time{}
   131  	}
   132  	return time.Now().Add(duration)
   133  }