mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
lnwire: New API for feature vectors.
This is a rewrite of feature vectors in lnwire. This has a few benefits: 1) a simpler interface 2) separate structs for a plain set of feature bits and a feature vector with associated feature names their respective feature sets 3) loosened requirements that bits MUST be assigned in pairs 4) fix endianness of encoding/decoding
This commit is contained in:
parent
b4273d1eaa
commit
1633ab180f
@ -2,49 +2,24 @@ package lnwire
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"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
|
||||
// FeatureBit represents a feature that can be enabled in either a local or
|
||||
// global feature vector at a specific bit position. Feature bits follow the
|
||||
// "it's OK to be odd" rule, where features at even bit positions must be known
|
||||
// to a node receiving them from a peer while odd bits do not. In accordance,
|
||||
// feature bits are usually assigned in pairs, first being assigned an odd bit
|
||||
// position which may later be changed to the preceding even position once
|
||||
// knowledge of the feature becomes required on the network.
|
||||
type FeatureBit uint16
|
||||
|
||||
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
|
||||
// InitialRoutingSync is a local feature bit meaning that the receiving node
|
||||
// should send a complete dump of routing information when a new connection
|
||||
// is established.
|
||||
InitialRoutingSync FeatureBit = 3
|
||||
|
||||
// maxAllowedSize is a maximum allowed size of feature vector.
|
||||
//
|
||||
@ -61,61 +36,62 @@ const (
|
||||
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
|
||||
// LocalFeatures is a mapping of known connection-local feature bits to a
|
||||
// descriptive name. All known local feature bits must be assigned a name in
|
||||
// this mapping. Local features are those which are only sent to the peer and
|
||||
// not advertised to the entire network. A full description of these feature
|
||||
// bits is provided in the BOLT-09 specification.
|
||||
var LocalFeatures = map[FeatureBit]string{
|
||||
InitialRoutingSync: "initial-routing-sync",
|
||||
}
|
||||
|
||||
// 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
|
||||
// GlobalFeatures is a mapping of known global feature bits to a descriptive
|
||||
// name. All known global feature bits must be assigned a name in this mapping.
|
||||
// Global features are those which are advertised to the entire network. A full
|
||||
// description of these feature bits is provided in the BOLT-09 specification.
|
||||
var GlobalFeatures map[FeatureBit]string
|
||||
|
||||
// flags is the map which stores the correspondence between feature
|
||||
// index and its flag.
|
||||
flags map[int]featureFlag // index -> flag
|
||||
// RawFeatureVector represents a set of feature bits as defined in BOLT-09.
|
||||
// A RawFeatureVector itself just stores a set of bit flags but can be used to
|
||||
// construct a FeatureVector which binds meaning to each bit. Feature vectors
|
||||
//can be serialized and deserialized to/from a byte representation that is
|
||||
// transmitted in Lightning network messages.
|
||||
type RawFeatureVector struct {
|
||||
features map[FeatureBit]bool
|
||||
}
|
||||
|
||||
// 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,
|
||||
// NewRawFeatureVector creates a feature vector with all of the feature bits
|
||||
// given as arguments enabled.
|
||||
func NewRawFeatureVector(bits ...FeatureBit) *RawFeatureVector {
|
||||
fv := &RawFeatureVector{features: make(map[FeatureBit]bool)}
|
||||
for _, bit := range bits {
|
||||
fv.Set(bit)
|
||||
}
|
||||
return fv
|
||||
}
|
||||
|
||||
// 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
|
||||
// IsSet returns whether a particular feature bit is enabled in the vector.
|
||||
func (fv *RawFeatureVector) IsSet(feature FeatureBit) bool {
|
||||
return fv.features[feature]
|
||||
}
|
||||
|
||||
// serializedSize returns the number of bytes which is needed to represent
|
||||
// feature vector in byte format.
|
||||
func (f *FeatureVector) serializedSize() uint16 {
|
||||
// Find the largest index in f.flags
|
||||
// Set marks a feature as enabled in the vector.
|
||||
func (fv *RawFeatureVector) Set(feature FeatureBit) {
|
||||
fv.features[feature] = true
|
||||
}
|
||||
|
||||
// Unset marks a feature as disabled in the vector.
|
||||
func (fv *RawFeatureVector) Unset(feature FeatureBit) {
|
||||
delete(fv.features, feature)
|
||||
}
|
||||
|
||||
// SerializeSize returns the number of bytes needed to represent feature vector
|
||||
// in byte format.
|
||||
func (fv *RawFeatureVector) SerializeSize() int {
|
||||
// Find the largest feature bit index
|
||||
max := -1
|
||||
for index := range f.flags {
|
||||
for feature := range fv.features {
|
||||
index := int(feature)
|
||||
if index > max {
|
||||
max = index
|
||||
}
|
||||
@ -123,181 +99,134 @@ func (f *FeatureVector) serializedSize() uint16 {
|
||||
if max == -1 {
|
||||
return 0
|
||||
}
|
||||
// We calculate length via the largest index in f.flags so as to not
|
||||
// get an index out of bounds in Encode's setFlag function.
|
||||
return uint16(math.Ceil(float64(flagBitsSize*(max+1)) / 8))
|
||||
|
||||
// We calculate byte-length via the largest bit index
|
||||
return max/8 + 1
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Encode writes the feature vector in byte representation. Every feature
|
||||
// encoded as a bit, and the bit vector is serialized using the least number of
|
||||
// bytes. Since the bit vector length is variable, the first two bytes of the
|
||||
// serialization represent the length.
|
||||
func (fv *RawFeatureVector) Encode(w io.Writer) error {
|
||||
// Write length of feature vector.
|
||||
var l [2]byte
|
||||
length := f.serializedSize()
|
||||
binary.BigEndian.PutUint16(l[:], length)
|
||||
length := fv.SerializeSize()
|
||||
binary.BigEndian.PutUint16(l[:], uint16(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)
|
||||
for feature := range fv.features {
|
||||
byteIndex := int(feature / 8)
|
||||
bitIndex := feature % 8
|
||||
data[length-byteIndex-1] |= 1 << bitIndex
|
||||
}
|
||||
|
||||
_, 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())
|
||||
// Decode reads the feature vector from its byte representation. Every feature
|
||||
// encoded as a bit, and the bit vector is serialized using the least number of
|
||||
// bytes. Since the bit vector length is variable, the first two bytes of the
|
||||
// serialization represent the length.
|
||||
func (fv *RawFeatureVector) Decode(r io.Reader) error {
|
||||
// Read the length of the feature vector.
|
||||
var l [2]byte
|
||||
if _, err := io.ReadFull(r, l[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
length := binary.BigEndian.Uint16(l[:])
|
||||
|
||||
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
|
||||
// Read the feature vector data.
|
||||
data := make([]byte, length)
|
||||
if _, err := io.ReadFull(r, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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],
|
||||
// Set feature bits from parsed data.
|
||||
bitsNumber := len(data) * 8
|
||||
for i := 0; i < bitsNumber; i++ {
|
||||
byteIndex := uint16(i / 8)
|
||||
bitIndex := uint(i % 8)
|
||||
if (data[length-byteIndex-1]>>bitIndex)&1 == 1 {
|
||||
fv.Set(FeatureBit(i))
|
||||
}
|
||||
}
|
||||
|
||||
return NewFeatureVector(features)
|
||||
return 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
|
||||
// FeatureVector represents a set of enabled features. The set stores
|
||||
// information on enabled flags and metadata about the feature names. A feature
|
||||
// vector is serializable to a compact byte representation that is included in
|
||||
// Lightning network messages.
|
||||
type FeatureVector struct {
|
||||
*RawFeatureVector
|
||||
featureNames map[FeatureBit]string
|
||||
}
|
||||
|
||||
// newSharedFeatures creates new shared features instance.
|
||||
func newSharedFeatures(f *FeatureVector) *SharedFeatures {
|
||||
return &SharedFeatures{f}
|
||||
}
|
||||
// NewFeatureVector constructs a new FeatureVector from a raw feature vector and
|
||||
// mapping of feature definitions.
|
||||
func NewFeatureVector(featureVector *RawFeatureVector,
|
||||
featureNames map[FeatureBit]string) *FeatureVector {
|
||||
|
||||
// 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
|
||||
return &FeatureVector{
|
||||
RawFeatureVector: featureVector,
|
||||
featureNames: featureNames,
|
||||
}
|
||||
|
||||
_, exist := f.flags[index]
|
||||
return exist
|
||||
}
|
||||
|
||||
// HasFeature returns whether a particular feature is included in the set. The
|
||||
// feature can be seen as set either if the bit is set directly OR the queried
|
||||
// bit has the same meaning as its corresponding even/odd bit, which is set
|
||||
// instead. The second case is because feature bits are generally assigned in
|
||||
// pairs where both the even and odd position represent the same feature.
|
||||
func (fv *FeatureVector) HasFeature(feature FeatureBit) bool {
|
||||
return fv.IsSet(feature) ||
|
||||
(fv.isFeatureBitPair(feature) && fv.IsSet(feature^1))
|
||||
}
|
||||
|
||||
// UnknownRequiredFeatures returns a list of feature bits set in the vector that
|
||||
// are unknown and in an even bit position. Feature bits with an even index must
|
||||
// be known to a node receiving the feature vector in a message.
|
||||
func (fv *FeatureVector) UnknownRequiredFeatures() []FeatureBit {
|
||||
var unknown []FeatureBit
|
||||
for feature := range fv.features {
|
||||
if feature%2 == 0 && !fv.IsKnown(feature) {
|
||||
unknown = append(unknown, feature)
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
// Name returns a string identifier for the feature represented by this bit. If
|
||||
// the bit does not represent a known feature, this returns a string indicating
|
||||
// as much.
|
||||
func (fv *FeatureVector) Name(bit FeatureBit) string {
|
||||
name, known := fv.featureNames[bit]
|
||||
if !known {
|
||||
name = "unknown"
|
||||
}
|
||||
return fmt.Sprintf("%s(%d)", name, bit)
|
||||
}
|
||||
|
||||
// IsKnown returns whether this feature bit represents a known feature.
|
||||
func (fv *FeatureVector) IsKnown(bit FeatureBit) bool {
|
||||
_, known := fv.featureNames[bit]
|
||||
return known
|
||||
}
|
||||
|
||||
// isFeatureBitPair returns whether this feature bit and its corresponding
|
||||
// even/odd bit both represent the same feature. This may often be the case as
|
||||
// bits are generally assigned in pairs, first being assigned an odd bit
|
||||
// position then being promoted to an even bit position once the network is
|
||||
// ready.
|
||||
func (fv *FeatureVector) isFeatureBitPair(bit FeatureBit) bool {
|
||||
name1, known1 := fv.featureNames[bit]
|
||||
name2, known2 := fv.featureNames[bit^1]
|
||||
return known1 && known2 && name1 == name2
|
||||
}
|
||||
|
@ -3,152 +3,260 @@ package lnwire
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// TestFeaturesRemoteRequireError checks that we throw an error if remote peer
|
||||
// has required feature which we don't support.
|
||||
func TestFeaturesRemoteRequireError(t *testing.T) {
|
||||
var testFeatureNames = map[FeatureBit]string{
|
||||
0: "feature1",
|
||||
3: "feature2",
|
||||
4: "feature3",
|
||||
5: "feature3",
|
||||
}
|
||||
|
||||
func TestFeatureVectorSetUnset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
first = "first"
|
||||
second = "second"
|
||||
)
|
||||
tests := []struct {
|
||||
bits []FeatureBit
|
||||
expectedFeatures []bool
|
||||
}{
|
||||
// No features are enabled if no bits are set.
|
||||
{
|
||||
bits: nil,
|
||||
expectedFeatures: []bool{false, false, false, false, false, false, false, false},
|
||||
},
|
||||
// Test setting an even bit for an even-only bit feature. The
|
||||
// corresponding odd bit should not be seen as set.
|
||||
{
|
||||
bits: []FeatureBit{0},
|
||||
expectedFeatures: []bool{true, false, false, false, false, false, false, false},
|
||||
},
|
||||
// Test setting an odd bit for an even-only bit feature. The
|
||||
// corresponding even bit should not be seen as set.
|
||||
{
|
||||
bits: []FeatureBit{1},
|
||||
expectedFeatures: []bool{false, true, false, false, false, false, false, false},
|
||||
},
|
||||
// Test setting an even bit for an odd-only bit feature. The bit should
|
||||
// be seen as set and the odd bit should not.
|
||||
{
|
||||
bits: []FeatureBit{2},
|
||||
expectedFeatures: []bool{false, false, true, false, false, false, false, false},
|
||||
},
|
||||
// Test setting an odd bit for an odd-only bit feature. The bit should
|
||||
// be seen as set and the even bit should not.
|
||||
{
|
||||
bits: []FeatureBit{3},
|
||||
expectedFeatures: []bool{false, false, false, true, false, false, false, false},
|
||||
},
|
||||
// Test setting an even bit for even-odd pair feature. Both bits in the
|
||||
// pair should be seen as set.
|
||||
{
|
||||
bits: []FeatureBit{4},
|
||||
expectedFeatures: []bool{false, false, false, false, true, true, false, false},
|
||||
},
|
||||
// Test setting an odd bit for even-odd pair feature. Both bits in the
|
||||
// pair should be seen as set.
|
||||
{
|
||||
bits: []FeatureBit{5},
|
||||
expectedFeatures: []bool{false, false, false, false, true, true, false, false},
|
||||
},
|
||||
// Test setting an even bit for an unknown feature. The bit should be
|
||||
// seen as set and the odd bit should not.
|
||||
{
|
||||
bits: []FeatureBit{6},
|
||||
expectedFeatures: []bool{false, false, false, false, false, false, true, false},
|
||||
},
|
||||
// Test setting an odd bit for an unknown feature. The bit should be
|
||||
// seen as set and the odd bit should not.
|
||||
{
|
||||
bits: []FeatureBit{7},
|
||||
expectedFeatures: []bool{false, false, false, false, false, false, false, true},
|
||||
},
|
||||
}
|
||||
|
||||
localFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
})
|
||||
fv := NewFeatureVector(nil, testFeatureNames)
|
||||
for i, test := range tests {
|
||||
for _, bit := range test.bits {
|
||||
fv.Set(bit)
|
||||
}
|
||||
|
||||
remoteFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
{second, RequiredFlag},
|
||||
})
|
||||
for j, expectedSet := range test.expectedFeatures {
|
||||
if fv.HasFeature(FeatureBit(j)) != expectedSet {
|
||||
t.Errorf("Expection failed in case %d, bit %d", i, j)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
||||
t.Fatal("error wasn't received")
|
||||
for _, bit := range test.bits {
|
||||
fv.Unset(bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFeaturesLocalRequireError checks that we throw an error if local peer has
|
||||
// required feature which remote peer don't support.
|
||||
func TestFeaturesLocalRequireError(t *testing.T) {
|
||||
func TestFeatureVectorEncodeDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
first = "first"
|
||||
second = "second"
|
||||
)
|
||||
tests := []struct {
|
||||
bits []FeatureBit
|
||||
expectedEncoded []byte
|
||||
}{
|
||||
{
|
||||
bits: nil,
|
||||
expectedEncoded: []byte{0x00, 0x00},
|
||||
},
|
||||
{
|
||||
bits: []FeatureBit{2, 3, 7},
|
||||
expectedEncoded: []byte{0x00, 0x01, 0x8C},
|
||||
},
|
||||
{
|
||||
bits: []FeatureBit{2, 3, 8},
|
||||
expectedEncoded: []byte{0x00, 0x02, 0x01, 0x0C},
|
||||
},
|
||||
}
|
||||
|
||||
localFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
{second, RequiredFlag},
|
||||
})
|
||||
for i, test := range tests {
|
||||
fv := NewRawFeatureVector(test.bits...)
|
||||
|
||||
remoteFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
})
|
||||
// Test that Encode produces the correct serialization.
|
||||
buffer := new(bytes.Buffer)
|
||||
err := fv.Encode(buffer)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to encode feature vector in case %d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
||||
t.Fatal("error wasn't received")
|
||||
encoded := buffer.Bytes()
|
||||
if !bytes.Equal(encoded, test.expectedEncoded) {
|
||||
t.Errorf("Wrong encoding in case %d: got %v, expected %v",
|
||||
i, encoded, test.expectedEncoded)
|
||||
continue
|
||||
}
|
||||
|
||||
// Test that decoding then re-encoding produces the same result.
|
||||
fv2 := NewRawFeatureVector()
|
||||
err = fv2.Decode(bytes.NewReader(encoded))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decode feature vector in case %d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
buffer2 := new(bytes.Buffer)
|
||||
err = fv2.Encode(buffer2)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to re-encode feature vector in case %d: %v",
|
||||
i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
reencoded := buffer2.Bytes()
|
||||
if !bytes.Equal(reencoded, test.expectedEncoded) {
|
||||
t.Errorf("Wrong re-encoding in case %d: got %v, expected %v",
|
||||
i, reencoded, test.expectedEncoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptionalFeature checks that if remote peer don't have the feature but
|
||||
// on our side this feature is optional than we mark this feature as disabled.
|
||||
func TestOptionalFeature(t *testing.T) {
|
||||
func TestFeatureVectorUnknownFeatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const first = "first"
|
||||
|
||||
localFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
})
|
||||
|
||||
remoteFeatures := NewFeatureVector([]Feature{})
|
||||
|
||||
shared, err := localFeatures.Compare(remoteFeatures)
|
||||
if err != nil {
|
||||
t.Fatalf("error while feature vector compare: %v", err)
|
||||
tests := []struct {
|
||||
bits []FeatureBit
|
||||
expectedUnknown []FeatureBit
|
||||
}{
|
||||
{
|
||||
bits: nil,
|
||||
expectedUnknown: nil,
|
||||
},
|
||||
// Since bits {0, 3, 4, 5} are known, and only even bits are considered
|
||||
// required (according to the "it's OK to be odd rule"), that leaves
|
||||
// {2, 6} as both unknown and required.
|
||||
{
|
||||
bits: []FeatureBit{0, 1, 2, 3, 4, 5, 6, 7},
|
||||
expectedUnknown: []FeatureBit{2, 6},
|
||||
},
|
||||
}
|
||||
|
||||
if shared.IsActive(first) {
|
||||
t.Fatal("locally feature was set but remote peer notified us" +
|
||||
" that it don't have it")
|
||||
}
|
||||
for i, test := range tests {
|
||||
rawVector := NewRawFeatureVector(test.bits...)
|
||||
fv := NewFeatureVector(rawVector, testFeatureNames)
|
||||
|
||||
// A feature with a non-existent name shouldn't be active.
|
||||
if shared.IsActive("nothere") {
|
||||
t.Fatal("non-existent feature shouldn't be active")
|
||||
unknown := fv.UnknownRequiredFeatures()
|
||||
|
||||
// Sort to make comparison independent of order
|
||||
sort.Slice(unknown, func(i, j int) bool {
|
||||
return unknown[i] < unknown[j]
|
||||
})
|
||||
if !reflect.DeepEqual(unknown, test.expectedUnknown) {
|
||||
t.Errorf("Wrong unknown features in case %d: got %v, expected %v",
|
||||
i, unknown, test.expectedUnknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetRequireAfterInit checks that we can change the feature flag after
|
||||
// initialization.
|
||||
func TestSetRequireAfterInit(t *testing.T) {
|
||||
func TestFeatureNames(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const first = "first"
|
||||
tests := []struct {
|
||||
bit FeatureBit
|
||||
expectedName string
|
||||
expectedKnown bool
|
||||
}{
|
||||
{
|
||||
bit: 0,
|
||||
expectedName: "feature1(0)",
|
||||
expectedKnown: true,
|
||||
},
|
||||
{
|
||||
bit: 1,
|
||||
expectedName: "unknown(1)",
|
||||
expectedKnown: false,
|
||||
},
|
||||
{
|
||||
bit: 2,
|
||||
expectedName: "unknown(2)",
|
||||
expectedKnown: false,
|
||||
},
|
||||
{
|
||||
bit: 3,
|
||||
expectedName: "feature2(3)",
|
||||
expectedKnown: true,
|
||||
},
|
||||
{
|
||||
bit: 4,
|
||||
expectedName: "feature3(4)",
|
||||
expectedKnown: true,
|
||||
},
|
||||
{
|
||||
bit: 5,
|
||||
expectedName: "feature3(5)",
|
||||
expectedKnown: true,
|
||||
},
|
||||
{
|
||||
bit: 6,
|
||||
expectedName: "unknown(6)",
|
||||
expectedKnown: false,
|
||||
},
|
||||
{
|
||||
bit: 7,
|
||||
expectedName: "unknown(7)",
|
||||
expectedKnown: false,
|
||||
},
|
||||
}
|
||||
|
||||
localFeatures := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
})
|
||||
localFeatures.SetFeatureFlag(first, RequiredFlag)
|
||||
remoteFeatures := NewFeatureVector([]Feature{})
|
||||
fv := NewFeatureVector(nil, testFeatureNames)
|
||||
for _, test := range tests {
|
||||
name := fv.Name(test.bit)
|
||||
if name != test.expectedName {
|
||||
t.Errorf("Name for feature bit %d is incorrect: "+
|
||||
"expected %s, got %s", test.bit, name, test.expectedName)
|
||||
}
|
||||
|
||||
_, err := localFeatures.Compare(remoteFeatures)
|
||||
if err == nil {
|
||||
t.Fatalf("feature was set as required but error wasn't "+
|
||||
"returned: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeEncodeFeaturesVector checks that feature vector might be
|
||||
// successfully encoded and decoded.
|
||||
func TestDecodeEncodeFeaturesVector(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const first = "first"
|
||||
|
||||
f := NewFeatureVector([]Feature{
|
||||
{first, OptionalFlag},
|
||||
})
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := f.Encode(&b); err != nil {
|
||||
t.Fatalf("error while encoding feature vector: %v", err)
|
||||
}
|
||||
|
||||
nf, err := NewFeatureVectorFromReader(&b)
|
||||
if err != nil {
|
||||
t.Fatalf("error while decoding feature vector: %v", err)
|
||||
}
|
||||
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(f.flags, nf.flags) {
|
||||
t.Fatalf("encode/decode feature vector don't match %v vs "+
|
||||
"%v", spew.Sdump(f), spew.Sdump(nf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeatureFlagString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if OptionalFlag.String() != "optional" {
|
||||
t.Fatalf("incorrect string, expected optional got %v",
|
||||
OptionalFlag.String())
|
||||
}
|
||||
|
||||
if RequiredFlag.String() != "required" {
|
||||
t.Fatalf("incorrect string, expected required got %v",
|
||||
OptionalFlag.String())
|
||||
}
|
||||
|
||||
fakeFlag := featureFlag(9)
|
||||
if fakeFlag.String() != "<unknown>" {
|
||||
t.Fatalf("incorrect string, expected <unknown> got %v",
|
||||
fakeFlag.String())
|
||||
known := fv.IsKnown(test.bit)
|
||||
if known != test.expectedKnown {
|
||||
t.Errorf("IsKnown for feature bit %d is incorrect: "+
|
||||
"expected %v, got %v", test.bit, known, test.expectedKnown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user