lnd.xprv/lnwire/features.go
Andrey Samokhvalov 3dd619fb35 lnwire: Add BOLT#1 'init' message
'init' message is the first message reveals the features supported or required
by this node. Nodes wait for receipt of the other's features to simplify error
diagnosis where features are incompatible. This message will help negotioate the
the features which are supported by both sides.
2017-02-17 13:27:29 +08:00

284 lines
8.5 KiB
Go

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
}