587300aa80
This commit fixes a bug lingering in the decoding of the feature vectors which was masked by the prior method of reading the _entire_ message from the stream before parsing it. The issue was that performing a zero-byte Read on an io.Reader that’s purely streaming will result in an indefinite block. We fix this bug by properly using io.ReadFull in this context.
292 lines
8.8 KiB
Go
292 lines
8.8 KiB
Go
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 := io.ReadFull(r, l[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
length := binary.BigEndian.Uint16(l[:])
|
|
|
|
// Read the feature vector data.
|
|
data := make([]byte, length)
|
|
if _, err := io.ReadFull(r, 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
|
|
}
|