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 }