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