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  }