github.com/amazechain/amc@v0.1.3/internal/sync/rpc_goodbye.go (about) 1 package sync 2 3 import ( 4 "context" 5 "fmt" 6 ssztype "github.com/amazechain/amc/common/types/ssz" 7 "github.com/amazechain/amc/internal/p2p" 8 p2ptypes "github.com/amazechain/amc/internal/p2p/types" 9 "github.com/amazechain/amc/log" 10 "github.com/amazechain/amc/utils" 11 "time" 12 13 libp2pcore "github.com/libp2p/go-libp2p/core" 14 "github.com/libp2p/go-libp2p/core/network" 15 "github.com/libp2p/go-libp2p/core/peer" 16 ) 17 18 var backOffTime = map[ssztype.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: 1 * 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.(*ssztype.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.Info("Peer has sent a goodbye message", "peer", stream.Conn().RemotePeer(), "Reason", goodbyeMessage(*m)) 48 s.cfg.p2p.Peers().SetNextValidTime(stream.Conn().RemotePeer(), goodByeBackoff(*m)) 49 // closes all streams with the peer 50 return s.cfg.p2p.Disconnect(stream.Conn().RemotePeer()) 51 } 52 53 // disconnectBadPeer checks whether peer is considered bad by some scorer, and tries to disconnect 54 // the peer, if that is the case. Additionally, disconnection reason is obtained from scorer. 55 func (s *Service) disconnectBadPeer(ctx context.Context, id peer.ID) { 56 if !s.cfg.p2p.Peers().IsBad(id) { 57 return 58 } 59 err := s.cfg.p2p.Peers().Scorers().ValidationError(id) 60 goodbyeCode := p2ptypes.ErrToGoodbyeCode(err) 61 if err == nil { 62 goodbyeCode = p2ptypes.GoodbyeCodeBanned 63 } 64 if err := s.sendGoodByeAndDisconnect(ctx, goodbyeCode, id); err != nil { 65 log.Debug("Error when disconnecting with bad peer", "err", err) 66 } 67 } 68 69 // A custom goodbye method that is used by our connection handler, in the 70 // event we receive bad peers. 71 func (s *Service) sendGoodbye(ctx context.Context, id peer.ID) error { 72 return s.sendGoodByeAndDisconnect(ctx, p2ptypes.GoodbyeCodeGenericError, id) 73 } 74 75 func (s *Service) sendGoodByeAndDisconnect(ctx context.Context, code p2ptypes.RPCGoodbyeCode, id peer.ID) error { 76 lock := utils.NewMultilock(id.String()) 77 lock.Lock() 78 defer lock.Unlock() 79 // In the event we are already disconnected, exit early from the 80 // goodbye method to prevent redundant streams from being created. 81 if s.cfg.p2p.Host().Network().Connectedness(id) == network.NotConnected { 82 return nil 83 } 84 if err := s.sendGoodByeMessage(ctx, code, id); err != nil { 85 log.Debug("Could not send goodbye message to peer", "peer", id, "error", err) 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 topic, err := p2p.TopicFromMessage(p2p.GoodbyeMessageName) 95 if err != nil { 96 return err 97 } 98 stream, err := s.cfg.p2p.Send(ctx, &code, topic, id) 99 if err != nil { 100 return err 101 } 102 defer closeStream(stream) 103 104 log.Debug("Sending Goodbye message to peer", "Reason", goodbyeMessage(code), "peer", stream.Conn().RemotePeer()) 105 106 // Wait up to the response timeout for the peer to receive the goodbye 107 // and close the stream (or disconnect). We usually don't bother waiting 108 // around for an EOF, but we're going to close this connection 109 // immediately after we say goodbye. 110 // 111 // NOTE: we don't actually check the response as there's nothing we can 112 // do if something fails. We just need to wait for it. 113 SetStreamReadDeadline(stream, respTimeout) 114 _, _err := stream.Read([]byte{0}) 115 _ = _err 116 117 return nil 118 } 119 120 func goodbyeMessage(num p2ptypes.RPCGoodbyeCode) string { 121 reason, ok := p2ptypes.GoodbyeCodeMessages[num] 122 if ok { 123 return reason 124 } 125 return fmt.Sprintf("unknown goodbye value of %d received", num) 126 } 127 128 // determines which backoff time to use depending on the 129 // goodbye code provided. 130 func goodByeBackoff(num p2ptypes.RPCGoodbyeCode) time.Time { 131 duration, ok := backOffTime[num] 132 if !ok { 133 return time.Time{} 134 } 135 return time.Now().Add(duration) 136 }