github.com/koko1123/flow-go-1@v0.29.6/network/validator/pubsub/topic_validator.go (about) 1 package validator 2 3 import ( 4 "context" 5 "fmt" 6 7 pubsub "github.com/libp2p/go-libp2p-pubsub" 8 "github.com/libp2p/go-libp2p/core/crypto" 9 "github.com/libp2p/go-libp2p/core/peer" 10 "github.com/rs/zerolog" 11 12 "github.com/koko1123/flow-go-1/network/channels" 13 "github.com/koko1123/flow-go-1/network/codec" 14 "github.com/koko1123/flow-go-1/network/p2p" 15 "github.com/koko1123/flow-go-1/network/slashing" 16 17 "github.com/koko1123/flow-go-1/network" 18 "github.com/koko1123/flow-go-1/network/message" 19 "github.com/koko1123/flow-go-1/network/validator" 20 _ "github.com/koko1123/flow-go-1/utils/binstat" 21 "github.com/koko1123/flow-go-1/utils/logging" 22 ) 23 24 // messagePubKey extracts the public key of the envelope signer from a libp2p message. 25 // The location of that key depends on the type of the key, see: 26 // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md 27 // This reproduces the exact logic of the private function doing the same decoding in libp2p: 28 // https://github.com/libp2p/go-libp2p-pubsub/blob/ba28f8ecfc551d4d916beb748d3384951bce3ed0/sign.go#L77 29 func messageSigningID(m *pubsub.Message) (peer.ID, error) { 30 var pubk crypto.PubKey 31 32 // m.From is the original sender of the message (versus `m.ReceivedFrom` which is the last hop which sent us this message) 33 pid, err := peer.IDFromBytes(m.From) 34 if err != nil { 35 return "", err 36 } 37 38 if m.Key == nil { 39 // no attached key, it must be extractable from the source ID 40 pubk, err = pid.ExtractPublicKey() 41 if err != nil { 42 return "", fmt.Errorf("cannot extract signing key: %s", err.Error()) 43 } 44 if pubk == nil { 45 return "", fmt.Errorf("cannot extract signing key") 46 } 47 } else { 48 pubk, err = crypto.UnmarshalPublicKey(m.Key) 49 if err != nil { 50 return "", fmt.Errorf("cannot unmarshal signing key: %s", err.Error()) 51 } 52 53 // verify that the source ID matches the attached key 54 if !pid.MatchesPublicKey(pubk) { 55 return "", fmt.Errorf("bad signing key; source ID %s doesn't match key", pid) 56 } 57 } 58 59 // the pid either contains or matches the signing pubKey 60 return pid, nil 61 } 62 63 // TopicValidatorData includes information about the message being sent. 64 type TopicValidatorData struct { 65 Message *message.Message 66 DecodedMsgPayload interface{} 67 From peer.ID 68 } 69 70 // TopicValidator is the topic validator that is registered with libP2P whenever a flow libP2P node subscribes to a topic. 71 // The TopicValidator will decode and perform validation on the raw pubsub message. 72 func TopicValidator(log zerolog.Logger, c network.Codec, slashingViolationsConsumer slashing.ViolationsConsumer, peerFilter func(peer.ID) error, validators ...validator.PubSubMessageValidator) p2p.TopicValidatorFunc { 73 log = log.With(). 74 Str("component", "libp2p-node-topic-validator"). 75 Logger() 76 77 return func(ctx context.Context, receivedFrom peer.ID, rawMsg *pubsub.Message) p2p.ValidationResult { 78 var msg message.Message 79 // convert the incoming raw message payload to Message type 80 //bs := binstat.EnterTimeVal(binstat.BinNet+":wire>1protobuf2message", int64(len(rawMsg.Data))) 81 err := msg.Unmarshal(rawMsg.Data) 82 //binstat.Leave(bs) 83 if err != nil { 84 return p2p.ValidationReject 85 } 86 87 from, err := messageSigningID(rawMsg) 88 if err != nil { 89 return p2p.ValidationReject 90 } 91 92 lg := log.With(). 93 Str("peer_id", from.String()). 94 Str("topic", rawMsg.GetTopic()). 95 Int("raw_msg_size", len(rawMsg.Data)). 96 Int("msg_size", msg.Size()). 97 Logger() 98 99 // verify sender is a known peer 100 if err := peerFilter(from); err != nil { 101 lg.Warn(). 102 Err(err). 103 Bool(logging.KeySuspicious, true). 104 Msg("filtering message from un-allowed peer") 105 return p2p.ValidationReject 106 } 107 108 // verify ChannelID in message matches the topic over which the message was received 109 topic := channels.Topic(rawMsg.GetTopic()) 110 actualChannel, ok := channels.ChannelFromTopic(topic) 111 if !ok { 112 lg.Warn(). 113 Bool(logging.KeySuspicious, true). 114 Msg("could not convert topic to channel") 115 return p2p.ValidationReject 116 } 117 118 lg = lg.With().Str("channel", msg.ChannelID).Logger() 119 120 channel := channels.Channel(msg.ChannelID) 121 if channel != actualChannel { 122 log.Warn(). 123 Str("actual_channel", actualChannel.String()). 124 Bool(logging.KeySuspicious, true). 125 Msg("channel id in message does not match pubsub topic") 126 return p2p.ValidationReject 127 } 128 129 // Convert message payload to a known message type 130 decodedMsgPayload, err := c.Decode(msg.Payload) 131 switch { 132 case err == nil: 133 break 134 case codec.IsErrUnknownMsgCode(err): 135 // slash peer if message contains unknown message code byte 136 slashingViolationsConsumer.OnUnknownMsgTypeError(violation(from, channel, err)) 137 return p2p.ValidationReject 138 case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): 139 // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte 140 slashingViolationsConsumer.OnInvalidMsgError(violation(from, channel, err)) 141 return p2p.ValidationReject 142 default: 143 // unexpected error condition. this indicates there's a bug 144 // don't crash as a result of external inputs since that creates a DoS vector. 145 lg.Error(). 146 Err(fmt.Errorf("unexpected error while decoding message: %w", err)). 147 Bool(logging.KeySuspicious, true). 148 Msg("rejecting message") 149 return p2p.ValidationReject 150 } 151 152 rawMsg.ValidatorData = TopicValidatorData{ 153 Message: &msg, 154 DecodedMsgPayload: decodedMsgPayload, 155 From: from, 156 } 157 158 result := p2p.ValidationAccept 159 for _, v := range validators { 160 switch res := v(from, decodedMsgPayload); res { 161 case p2p.ValidationReject: 162 return res 163 case p2p.ValidationIgnore: 164 result = res 165 } 166 } 167 168 return result 169 } 170 } 171 172 func violation(pid peer.ID, channel channels.Channel, err error) *slashing.Violation { 173 return &slashing.Violation{ 174 PeerID: pid.String(), 175 Channel: channel, 176 Protocol: message.ProtocolPublish, 177 Err: err, 178 } 179 }