github.com/decred/dcrlnd@v0.7.6/watchtower/wtwire/message.go (about)

     1  package wtwire
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  )
     9  
    10  // MaxMessagePayload is the maximum bytes a message can be regardless of other
    11  // individual limits imposed by messages themselves.
    12  const MaxMessagePayload = 65535 // 65KB
    13  
    14  // MessageType is the unique 2 byte big-endian integer that indicates the type
    15  // of message on the wire. All messages have a very simple header which
    16  // consists simply of 2-byte message type. We omit a length field, and checksum
    17  // as the Watchtower Protocol is intended to be encapsulated within a
    18  // confidential+authenticated cryptographic messaging protocol.
    19  type MessageType uint16
    20  
    21  // The currently defined message types within this current version of the
    22  // Watchtower protocol.
    23  const (
    24  	// MsgInit identifies an encoded Init message.
    25  	MsgInit MessageType = 600
    26  
    27  	// MsgError identifies an encoded Error message.
    28  	MsgError MessageType = 601
    29  
    30  	// MsgCreateSession identifies an encoded CreateSession message.
    31  	MsgCreateSession MessageType = 602
    32  
    33  	// MsgCreateSessionReply identifies an encoded CreateSessionReply message.
    34  	MsgCreateSessionReply MessageType = 603
    35  
    36  	// MsgStateUpdate identifies an encoded StateUpdate message.
    37  	MsgStateUpdate MessageType = 604
    38  
    39  	// MsgStateUpdateReply identifies an encoded StateUpdateReply message.
    40  	MsgStateUpdateReply MessageType = 605
    41  
    42  	// MsgDeleteSession identifies an encoded DeleteSession message.
    43  	MsgDeleteSession MessageType = 606
    44  
    45  	// MsgDeleteSessionReply identifies an encoded DeleteSessionReply
    46  	// message.
    47  	MsgDeleteSessionReply MessageType = 607
    48  )
    49  
    50  // String returns a human readable description of the message type.
    51  func (m MessageType) String() string {
    52  	switch m {
    53  	case MsgInit:
    54  		return "Init"
    55  	case MsgCreateSession:
    56  		return "MsgCreateSession"
    57  	case MsgCreateSessionReply:
    58  		return "MsgCreateSessionReply"
    59  	case MsgStateUpdate:
    60  		return "MsgStateUpdate"
    61  	case MsgStateUpdateReply:
    62  		return "MsgStateUpdateReply"
    63  	case MsgDeleteSession:
    64  		return "MsgDeleteSession"
    65  	case MsgDeleteSessionReply:
    66  		return "MsgDeleteSessionReply"
    67  	case MsgError:
    68  		return "Error"
    69  	default:
    70  		return "<unknown>"
    71  	}
    72  }
    73  
    74  // Serializable is an interface which defines a lightning wire serializable
    75  // object.
    76  type Serializable interface {
    77  	// Decode reads the bytes stream and converts it to the object.
    78  	Decode(io.Reader, uint32) error
    79  
    80  	// Encode converts object to the bytes stream and write it into the
    81  	// write buffer.
    82  	Encode(io.Writer, uint32) error
    83  }
    84  
    85  // Message is an interface that defines a lightning wire protocol message. The
    86  // interface is general in order to allow implementing types full control over
    87  // the representation of its data.
    88  type Message interface {
    89  	Serializable
    90  
    91  	// MsgType returns a MessageType that uniquely identifies the message to
    92  	// be encoded.
    93  	MsgType() MessageType
    94  
    95  	// MaxMessagePayload is the maximum serialized length that a particular
    96  	// message type can take.
    97  	MaxPayloadLength(uint32) uint32
    98  }
    99  
   100  // makeEmptyMessage creates a new empty message of the proper concrete type
   101  // based on the passed message type.
   102  func makeEmptyMessage(msgType MessageType) (Message, error) {
   103  	var msg Message
   104  
   105  	switch msgType {
   106  	case MsgInit:
   107  		msg = &Init{}
   108  	case MsgCreateSession:
   109  		msg = &CreateSession{}
   110  	case MsgCreateSessionReply:
   111  		msg = &CreateSessionReply{}
   112  	case MsgStateUpdate:
   113  		msg = &StateUpdate{}
   114  	case MsgStateUpdateReply:
   115  		msg = &StateUpdateReply{}
   116  	case MsgDeleteSession:
   117  		msg = &DeleteSession{}
   118  	case MsgDeleteSessionReply:
   119  		msg = &DeleteSessionReply{}
   120  	case MsgError:
   121  		msg = &Error{}
   122  	default:
   123  		return nil, fmt.Errorf("unknown message type [%d]", msgType)
   124  	}
   125  
   126  	return msg, nil
   127  }
   128  
   129  // WriteMessage writes a lightning Message to w including the necessary header
   130  // information and returns the number of bytes written.
   131  func WriteMessage(w io.Writer, msg Message, pver uint32) (int, error) {
   132  	totalBytes := 0
   133  
   134  	// Encode the message payload itself into a temporary buffer.
   135  	// TODO(roasbeef): create buffer pool
   136  	var bw bytes.Buffer
   137  	if err := msg.Encode(&bw, pver); err != nil {
   138  		return totalBytes, err
   139  	}
   140  	payload := bw.Bytes()
   141  	lenp := len(payload)
   142  
   143  	// Enforce maximum overall message payload.
   144  	if lenp > MaxMessagePayload {
   145  		return totalBytes, fmt.Errorf("message payload is too large - "+
   146  			"encoded %d bytes, but maximum message payload is %d bytes",
   147  			lenp, MaxMessagePayload)
   148  	}
   149  
   150  	// Enforce maximum message payload on the message type.
   151  	mpl := msg.MaxPayloadLength(pver)
   152  	if uint32(lenp) > mpl {
   153  		return totalBytes, fmt.Errorf("message payload is too large - "+
   154  			"encoded %d bytes, but maximum message payload of "+
   155  			"type %v is %d bytes", lenp, msg.MsgType(), mpl)
   156  	}
   157  
   158  	// With the initial sanity checks complete, we'll now write out the
   159  	// message type itself.
   160  	var mType [2]byte
   161  	binary.BigEndian.PutUint16(mType[:], uint16(msg.MsgType()))
   162  	n, err := w.Write(mType[:])
   163  	totalBytes += n
   164  	if err != nil {
   165  		return totalBytes, err
   166  	}
   167  
   168  	// With the message type written, we'll now write out the raw payload
   169  	// itself.
   170  	n, err = w.Write(payload)
   171  	totalBytes += n
   172  
   173  	return totalBytes, err
   174  }
   175  
   176  // ReadMessage reads, validates, and parses the next Watchtower message from r
   177  // for the provided protocol version.
   178  func ReadMessage(r io.Reader, pver uint32) (Message, error) {
   179  	// First, we'll read out the first two bytes of the message so we can
   180  	// create the proper empty message.
   181  	var mType [2]byte
   182  	if _, err := io.ReadFull(r, mType[:]); err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	msgType := MessageType(binary.BigEndian.Uint16(mType[:]))
   187  
   188  	// Now that we know the target message type, we can create the proper
   189  	// empty message type and decode the message into it.
   190  	msg, err := makeEmptyMessage(msgType)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	if err := msg.Decode(r, pver); err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	return msg, nil
   199  }