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 }