-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
features.go
291 lines (247 loc) · 8.82 KB
/
features.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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
package lnwire
import (
"encoding/binary"
"io"
"math"
"github.com/go-errors/errors"
)
// featureFlag represent the status of the feature optional/required and needed
// to allow future incompatible changes, or backward compatible changes.
type featureFlag uint8
// String returns the string representation for the featureFlag.
func (f featureFlag) String() string {
switch f {
case OptionalFlag:
return "optional"
case RequiredFlag:
return "required"
default:
return "<unknown>"
}
}
// featureName represent the name of the feature and needed in order to have
// the compile errors if we specify wrong feature name.
type featureName string
const (
// OptionalFlag represent the feature which we already have but it
// isn't required yet, and if remote peer doesn't have this feature we
// may turn it off without disconnecting with peer.
OptionalFlag featureFlag = 2 // 0b10
// RequiredFlag represent the features which is required for proper
// peer interaction, we disconnect with peer if it doesn't have this
// particular feature.
RequiredFlag featureFlag = 1 // 0b01
// flagMask is a mask which is needed to extract feature flag value.
flagMask = 3 // 0b11
// flagBitsSize represent the size of the feature flag in bits. For
// more information read the init message specification.
flagBitsSize = 2
// maxAllowedSize is a maximum allowed size of feature vector.
//
// NOTE: Within the protocol, the maximum allowed message size is 65535
// bytes. Adding the overhead from the crypto protocol (the 2-byte
// packet length and 16-byte MAC), we arrive at 65569 bytes. Accounting
// for the overhead within the feature message to signal the type of
// the message, that leaves 65567 bytes for the init message itself.
// Next, we reserve 4-bytes to encode the lengths of both the local and
// global feature vectors, so 65563 for the global and local features.
// Knocking off one byte for the sake of the calculation, that leads to
// a max allowed size of 32781 bytes for each feature vector, or 131124
// different features.
maxAllowedSize = 32781
)
// Feature represent the feature which is used on stage of initialization of
// feature vector. Initial feature flags might be changed dynamically later.
type Feature struct {
Name featureName
Flag featureFlag
}
// FeatureVector represents the global/local feature vector. With this
// structure you may set/get the feature by name and compare feature vector
// with remote one.
type FeatureVector struct {
// featuresMap is the map which stores the correspondence between
// feature name and its index within feature vector. Index within
// feature vector and actual binary position of feature are different
// things)
featuresMap map[featureName]int // name -> index
// flags is the map which stores the correspondence between feature
// index and its flag.
flags map[int]featureFlag // index -> flag
}
// NewFeatureVector creates new instance of feature vector.
func NewFeatureVector(features []Feature) *FeatureVector {
featuresMap := make(map[featureName]int)
flags := make(map[int]featureFlag)
for index, feature := range features {
featuresMap[feature.Name] = index
flags[index] = feature.Flag
}
return &FeatureVector{
featuresMap: featuresMap,
flags: flags,
}
}
// SetFeatureFlag assign flag to the feature.
func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error {
position, ok := f.featuresMap[name]
if !ok {
return errors.Errorf("can't find feature with name: %v", name)
}
f.flags[position] = flag
return nil
}
// serializedSize returns the number of bytes which is needed to represent
// feature vector in byte format.
func (f *FeatureVector) serializedSize() uint16 {
return uint16(math.Ceil(float64(flagBitsSize*len(f.flags)) / 8))
}
// NewFeatureVectorFromReader decodes the feature vector from binary
// representation and creates the instance of it. Every feature decoded as 2
// bits where odd bit determine whether the feature is "optional" and even bit
// told us whether the feature is "required". The even/odd semantic allows
// future incompatible changes, or backward compatible changes. Bits generally
// assigned in pairs, so that optional features can later become compulsory.
func NewFeatureVectorFromReader(r io.Reader) (*FeatureVector, error) {
f := &FeatureVector{
flags: make(map[int]featureFlag),
}
getFlag := func(data []byte, position int) featureFlag {
byteNumber := uint(position / 8)
bitNumber := uint(position % 8)
return featureFlag((data[byteNumber] >> bitNumber) & flagMask)
}
// Read the length of the feature vector.
var l [2]byte
if _, err := r.Read(l[:]); err != nil {
return nil, err
}
length := binary.BigEndian.Uint16(l[:])
// Read the feature vector data.
data := make([]byte, length)
if _, err := r.Read(data); err != nil {
return nil, err
}
// Initialize feature vector.
bitsNumber := len(data) * 8
for position := 0; position <= bitsNumber-flagBitsSize; position += flagBitsSize {
flag := getFlag(data, position)
switch flag {
case OptionalFlag, RequiredFlag:
// Every feature/flag takes 2 bits, so in order to get
// the feature/flag index we should divide position
// on 2.
index := position / flagBitsSize
f.flags[index] = flag
default:
continue
}
}
return f, nil
}
// Encode encodes the features vector into bytes representation, every feature
// encoded as 2 bits where odd bit determine whether the feature is "optional"
// and even bit told us whether the feature is "required". The even/odd
// semantic allows future incompatible changes, or backward compatible changes.
// Bits generally assigned in pairs, so that optional features can later become
// compulsory.
func (f *FeatureVector) Encode(w io.Writer) error {
setFlag := func(data []byte, position int, flag featureFlag) {
byteNumber := uint(position / 8)
bitNumber := uint(position % 8)
data[byteNumber] |= (byte(flag) << bitNumber)
}
// Write length of feature vector.
var l [2]byte
length := f.serializedSize()
binary.BigEndian.PutUint16(l[:], length)
if _, err := w.Write(l[:]); err != nil {
return err
}
// Generate the data and write it.
data := make([]byte, length)
for index, flag := range f.flags {
// Every feature takes 2 bits, so in order to get the feature
// bits position we should multiply index by 2.
position := index * flagBitsSize
setFlag(data, position, flag)
}
_, err := w.Write(data)
return err
}
// Compare checks that features are compatible and returns the features which
// were present in both remote and local feature vectors. If remote/local node
// doesn't have the feature and local/remote node require it than such vectors
// are incompatible.
func (f *FeatureVector) Compare(f2 *FeatureVector) (*SharedFeatures, error) {
shared := newSharedFeatures(f.Copy())
for index, flag := range f.flags {
if _, exist := f2.flags[index]; !exist {
switch flag {
case RequiredFlag:
return nil, errors.New("Remote node hasn't " +
"locally required feature")
case OptionalFlag:
// If feature is optional and remote side
// haven't it than it might be safely disabled.
delete(shared.flags, index)
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
for index, flag := range f2.flags {
if _, exist := f.flags[index]; !exist {
switch flag {
case RequiredFlag:
return nil, errors.New("Local node hasn't " +
"locally required feature")
case OptionalFlag:
// If feature is optional and local side
// haven't it than it might be safely disabled.
delete(shared.flags, index)
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
return shared, nil
}
// Copy generate new distinct instance of the feature vector.
func (f *FeatureVector) Copy() *FeatureVector {
features := make([]Feature, len(f.featuresMap))
for name, index := range f.featuresMap {
features[index] = Feature{
Name: name,
Flag: f.flags[index],
}
}
return NewFeatureVector(features)
}
// SharedFeatures is a product of comparison of two features vector which
// consist of features which are present in both local and remote features
// vectors.
type SharedFeatures struct {
*FeatureVector
}
// newSharedFeatures creates new shared features instance.
func newSharedFeatures(f *FeatureVector) *SharedFeatures {
return &SharedFeatures{f}
}
// IsActive checks is feature active or not, it might be disabled during
// comparision with remote feature vector if it was optional and remote peer
// doesn't support it.
func (f *SharedFeatures) IsActive(name featureName) bool {
index, ok := f.featuresMap[name]
if !ok {
// If we even have no such feature in feature map, than it
// can't be active in any circumstances.
return false
}
_, exist := f.flags[index]
return exist
}