lnd.xprv/lnwire/features.go

284 lines
8.5 KiB
Go
Raw Normal View History

package lnwire
import (
"encoding/binary"
"fmt"
"github.com/go-errors/errors"
"io"
"math"
)
// featureFlag represent the status of the feature optional/required and needed
// to allow future incompatible changes, or backward compatible changes.
type featureFlag uint8
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
// FeaturesMap is the map which stores the correspondence between feature name
// and its index within feature vector.
//
// NOTE: Index within feature vector and actual binary position of feature
// are different things)
type FeaturesMap map[featureName]int
// indexToFlag is the map which stores the correspondence between feature
// position and its flag.
type indexToFlag map[int]featureFlag
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
)
// 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 {
features FeaturesMap // name -> index
flags indexToFlag // index -> flag
}
// NewFeatureVector creates new instance of feature vector.
func NewFeatureVector(features FeaturesMap) *FeatureVector {
return &FeatureVector{
features: features,
flags: make(indexToFlag),
}
}
// SetFeatureFlag assign flag to the feature.
func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error {
position, ok := f.features[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))
}
// String returns the feature vector description.
func (f *FeatureVector) String() string {
var description string
for name, index := range f.features {
if flag, ok := f.flags[index]; ok {
description += fmt.Sprintf("%s: %s\n", name, flag)
}
}
if description == "" {
description = "<empty>"
}
return "\n" + description
}
// 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(indexToFlag),
}
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)
}
if _, err := w.Write(data); err != nil {
return err
}
return nil
}
// 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 (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
error) {
shared := NewSharedFeatures(local.features)
for index, flag := range local.flags {
if _, exist := remote.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.
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
for index, flag := range remote.flags {
if _, exist := local.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.
continue
}
}
// If feature exists on both sides than such feature might be
// considered as active.
shared.flags[index] = flag
}
return shared, nil
}
// 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(features FeaturesMap) *SharedFeatures {
return &SharedFeatures{NewFeatureVector(features)}
}
// 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 {
position, ok := f.features[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[position]
return exist
}