github.com/koko1123/flow-go-1@v0.29.6/network/message_scope.go (about)

     1  package network
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/koko1123/flow-go-1/model/flow"
     8  	"github.com/koko1123/flow-go-1/network/channels"
     9  	"github.com/koko1123/flow-go-1/network/message"
    10  	"github.com/onflow/flow-go/crypto/hash"
    11  )
    12  
    13  const (
    14  	// eventIDPackingPrefix is used as a salt to generate payload hash for messages.
    15  	eventIDPackingPrefix = "libp2ppacking"
    16  )
    17  
    18  // ProtocolType defines the type of the protocol a message is sent over. Currently, we have two types of protocols:
    19  // - unicast: a message is sent to a single node through a direct connection.
    20  // - pubsub: a message is sent to a set of nodes through a pubsub channel.
    21  type ProtocolType string
    22  
    23  func (m ProtocolType) String() string {
    24  	return string(m)
    25  }
    26  
    27  const (
    28  	// ProtocolTypeUnicast is protocol type for unicast messages.
    29  	ProtocolTypeUnicast ProtocolType = "unicast"
    30  
    31  	// ProtocolTypePubSub is the protocol type for pubsub messages.
    32  	ProtocolTypePubSub ProtocolType = "pubsub"
    33  )
    34  
    35  // IncomingMessageScope captures the context around an incoming message that is received by the network layer.
    36  type IncomingMessageScope struct {
    37  	originId       flow.Identifier     // the origin node ID.
    38  	targetIds      flow.IdentifierList // the target node IDs (i.e., intended recipients).
    39  	eventId        hash.Hash           // hash of the payload and channel.
    40  	msg            *message.Message    // the raw message received.
    41  	decodedPayload interface{}         // decoded payload of the message.
    42  	protocol       ProtocolType        // the type of protocol used to receive the message.
    43  }
    44  
    45  // NewIncomingScope creates a new incoming message scope.
    46  // All errors returned by this function are benign and should not cause the node to crash, especially that it is not
    47  // safe to crash the node when receiving a message.
    48  // It errors if event id (i.e., hash of the payload and channel) cannot be computed, or if it fails to
    49  // convert the target IDs from bytes slice to a flow.IdentifierList.
    50  func NewIncomingScope(originId flow.Identifier, protocol ProtocolType, msg *message.Message, decodedPayload interface{}) (*IncomingMessageScope, error) {
    51  	eventId, err := EventId(channels.Channel(msg.ChannelID), msg.Payload)
    52  	if err != nil {
    53  		return nil, fmt.Errorf("could not compute event id: %w", err)
    54  	}
    55  
    56  	targetIds, err := flow.ByteSlicesToIds(msg.TargetIDs)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("could not convert target ids: %w", err)
    59  	}
    60  	return &IncomingMessageScope{
    61  		eventId:        eventId,
    62  		originId:       originId,
    63  		msg:            msg,
    64  		decodedPayload: decodedPayload,
    65  		protocol:       protocol,
    66  		targetIds:      targetIds,
    67  	}, nil
    68  }
    69  
    70  func (m IncomingMessageScope) OriginId() flow.Identifier {
    71  	return m.originId
    72  }
    73  
    74  func (m IncomingMessageScope) Proto() *message.Message {
    75  	return m.msg
    76  }
    77  
    78  func (m IncomingMessageScope) DecodedPayload() interface{} {
    79  	return m.decodedPayload
    80  }
    81  
    82  func (m IncomingMessageScope) Protocol() ProtocolType {
    83  	return m.protocol
    84  }
    85  
    86  func (m IncomingMessageScope) Channel() channels.Channel {
    87  	return channels.Channel(m.msg.ChannelID)
    88  }
    89  
    90  func (m IncomingMessageScope) Size() int {
    91  	return m.msg.Size()
    92  }
    93  
    94  func (m IncomingMessageScope) TargetIDs() flow.IdentifierList {
    95  	return m.targetIds
    96  }
    97  
    98  func (m IncomingMessageScope) EventID() []byte {
    99  	return m.eventId[:]
   100  }
   101  
   102  func (m IncomingMessageScope) PayloadType() string {
   103  	return MessageType(m.decodedPayload)
   104  }
   105  
   106  // OutgoingMessageScope captures the context around an outgoing message that is about to be sent.
   107  type OutgoingMessageScope struct {
   108  	targetIds flow.IdentifierList               // the target node IDs.
   109  	channelId channels.Channel                  // the channel ID.
   110  	payload   interface{}                       // the payload to be sent.
   111  	encoder   func(interface{}) ([]byte, error) // the encoder to encode the payload.
   112  	msg       *message.Message                  // raw proto message sent on wire.
   113  	protocol  ProtocolType                      // the type of protocol used to send the message.
   114  }
   115  
   116  // NewOutgoingScope creates a new outgoing message scope.
   117  // All errors returned by this function are benign and should not cause the node to crash.
   118  // It errors if the encoder fails to encode the payload into a protobuf message, or
   119  // if the number of target IDs does not match the protocol type (i.e., unicast messages
   120  // should have exactly one target ID, while pubsub messages should have at least one target ID).
   121  func NewOutgoingScope(
   122  	targetIds flow.IdentifierList,
   123  	channelId channels.Channel,
   124  	payload interface{},
   125  	encoder func(interface{}) ([]byte, error),
   126  	protocolType ProtocolType) (*OutgoingMessageScope, error) {
   127  	scope := &OutgoingMessageScope{
   128  		targetIds: targetIds,
   129  		channelId: channelId,
   130  		payload:   payload,
   131  		encoder:   encoder,
   132  		protocol:  protocolType,
   133  	}
   134  
   135  	if protocolType == ProtocolTypeUnicast {
   136  		// for unicast messages, we should have exactly one target.
   137  		if len(targetIds) != 1 {
   138  			return nil, fmt.Errorf("expected exactly one target id for unicast message, got: %d", len(targetIds))
   139  		}
   140  	}
   141  	if protocolType == ProtocolTypePubSub {
   142  		// for pubsub messages, we should have at least one target.
   143  		if len(targetIds) == 0 {
   144  			return nil, fmt.Errorf("expected at least one target id for pubsub message, got: %d", len(targetIds))
   145  		}
   146  	}
   147  
   148  	msg, err := scope.buildMessage()
   149  	if err != nil {
   150  		return nil, fmt.Errorf("could not build message: %w", err)
   151  	}
   152  	scope.msg = msg
   153  	return scope, nil
   154  }
   155  
   156  func (o OutgoingMessageScope) TargetIds() flow.IdentifierList {
   157  	return o.targetIds
   158  }
   159  
   160  func (o OutgoingMessageScope) Size() int {
   161  	return o.msg.Size()
   162  }
   163  
   164  func (o OutgoingMessageScope) PayloadType() string {
   165  	return MessageType(o.payload)
   166  }
   167  
   168  func (o OutgoingMessageScope) Channel() channels.Channel {
   169  	return o.channelId
   170  }
   171  
   172  // buildMessage builds the raw proto message to be sent on the wire.
   173  func (o OutgoingMessageScope) buildMessage() (*message.Message, error) {
   174  	payload, err := o.encoder(o.payload)
   175  	if err != nil {
   176  		return nil, fmt.Errorf("could not encode payload: %w", err)
   177  	}
   178  
   179  	emTargets := make([][]byte, 0)
   180  	for _, targetId := range o.targetIds {
   181  		tempID := targetId // avoid capturing loop variable
   182  		emTargets = append(emTargets, tempID[:])
   183  	}
   184  
   185  	return &message.Message{
   186  		TargetIDs: emTargets,
   187  		ChannelID: o.channelId.String(),
   188  		Payload:   payload,
   189  	}, nil
   190  }
   191  
   192  func (o OutgoingMessageScope) Proto() *message.Message {
   193  	return o.msg
   194  }
   195  
   196  // EventId computes the event ID for a given channel and payload (i.e., the hash of the payload and channel).
   197  // All errors returned by this function are benign and should not cause the node to crash.
   198  // It errors if the hash function fails to hash the payload and channel.
   199  func EventId(channel channels.Channel, payload []byte) (hash.Hash, error) {
   200  	// use a hash with an engine-specific salt to get the payload hash
   201  	h := hash.NewSHA3_384()
   202  	_, err := h.Write([]byte(eventIDPackingPrefix + channel))
   203  	if err != nil {
   204  		return nil, fmt.Errorf("could not hash channel as salt: %w", err)
   205  	}
   206  
   207  	_, err = h.Write(payload)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("could not hash event: %w", err)
   210  	}
   211  
   212  	return h.SumHash(), nil
   213  }
   214  
   215  // MessageType returns the type of the message payload.
   216  func MessageType(decodedPayload interface{}) string {
   217  	return strings.TrimLeft(fmt.Sprintf("%T", decodedPayload), "*")
   218  }