lnwire+features: transition to the user friendly list of features
This commit is contained in:
parent
4d7ca825d4
commit
ae15a193e2
14
features.go
14
features.go
@ -2,12 +2,10 @@ package main
|
|||||||
|
|
||||||
import "github.com/lightningnetwork/lnd/lnwire"
|
import "github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
|
||||||
// globalFeaturesMap is a map which binds the name of the global feature with it
|
// globalFeatures feature vector which affects HTLCs and thus are also
|
||||||
// index. The index is just an order of the feature and the final binary
|
// advertised to other nodes.
|
||||||
// representation of feature vector is determined by decode function.
|
var globalFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
|
||||||
var globalFeaturesMap = lnwire.FeaturesMap{}
|
|
||||||
|
|
||||||
// localFeaturesMap is a map which binds the name of the local feature with it
|
// localFeatures is an feature vector which represent the features which
|
||||||
// index. The index is just an order of the feature and the final binary
|
// only affect the protocol between these two nodes.
|
||||||
// representation of feature vector is determined by decode function.
|
var localFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
|
||||||
var localFeaturesMap = lnwire.FeaturesMap{}
|
|
||||||
|
@ -27,17 +27,6 @@ func (f featureFlag) String() string {
|
|||||||
// compile errors if we specify wrong feature name.
|
// compile errors if we specify wrong feature name.
|
||||||
type featureName string
|
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 (
|
const (
|
||||||
// OptionalFlag represent the feature which we already have but it isn't
|
// 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
|
// required yet, and if remote peer doesn't have this feature we may
|
||||||
@ -70,25 +59,47 @@ const (
|
|||||||
maxAllowedSize = 32781
|
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
|
// FeatureVector represents the global/local feature vector. With this structure
|
||||||
// you may set/get the feature by name and compare feature vector with remote
|
// you may set/get the feature by name and compare feature vector with remote
|
||||||
// one.
|
// one.
|
||||||
type FeatureVector struct {
|
type FeatureVector struct {
|
||||||
features FeaturesMap // name -> index
|
// featuresMap is the map which stores the correspondence between
|
||||||
flags indexToFlag // index -> flag
|
// 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.
|
// NewFeatureVector creates new instance of feature vector.
|
||||||
func NewFeatureVector(features FeaturesMap) *FeatureVector {
|
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{
|
return &FeatureVector{
|
||||||
features: features,
|
featuresMap: featuresMap,
|
||||||
flags: make(indexToFlag),
|
flags: flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFeatureFlag assign flag to the feature.
|
// SetFeatureFlag assign flag to the feature.
|
||||||
func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error {
|
func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error {
|
||||||
position, ok := f.features[name]
|
position, ok := f.featuresMap[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.Errorf("can't find feature with name: %v", name)
|
return errors.Errorf("can't find feature with name: %v", name)
|
||||||
}
|
}
|
||||||
@ -97,16 +108,16 @@ func (f *FeatureVector) SetFeatureFlag(name featureName, flag featureFlag) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializedSize returns the number of bytes which is needed to represent feature
|
// serializedSize returns the number of bytes which is needed to represent
|
||||||
// vector in byte format.
|
// feature vector in byte format.
|
||||||
func (f *FeatureVector) SerializedSize() uint16 {
|
func (f *FeatureVector) serializedSize() uint16 {
|
||||||
return uint16(math.Ceil(float64(flagBitsSize*len(f.flags)) / 8))
|
return uint16(math.Ceil(float64(flagBitsSize*len(f.flags)) / 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the feature vector description.
|
// String returns the feature vector description.
|
||||||
func (f *FeatureVector) String() string {
|
func (f *FeatureVector) String() string {
|
||||||
var description string
|
var description string
|
||||||
for name, index := range f.features {
|
for name, index := range f.featuresMap {
|
||||||
if flag, ok := f.flags[index]; ok {
|
if flag, ok := f.flags[index]; ok {
|
||||||
description += fmt.Sprintf("%s: %s\n", name, flag)
|
description += fmt.Sprintf("%s: %s\n", name, flag)
|
||||||
}
|
}
|
||||||
@ -128,7 +139,7 @@ func (f *FeatureVector) String() string {
|
|||||||
// later become compulsory.
|
// later become compulsory.
|
||||||
func NewFeatureVectorFromReader(r io.Reader) (*FeatureVector, error) {
|
func NewFeatureVectorFromReader(r io.Reader) (*FeatureVector, error) {
|
||||||
f := &FeatureVector{
|
f := &FeatureVector{
|
||||||
flags: make(indexToFlag),
|
flags: make(map[int]featureFlag),
|
||||||
}
|
}
|
||||||
|
|
||||||
getFlag := func(data []byte, position int) featureFlag {
|
getFlag := func(data []byte, position int) featureFlag {
|
||||||
@ -186,7 +197,7 @@ func (f *FeatureVector) Encode(w io.Writer) error {
|
|||||||
|
|
||||||
// Write length of feature vector.
|
// Write length of feature vector.
|
||||||
var l [2]byte
|
var l [2]byte
|
||||||
length := f.SerializedSize()
|
length := f.serializedSize()
|
||||||
binary.BigEndian.PutUint16(l[:], length)
|
binary.BigEndian.PutUint16(l[:], length)
|
||||||
if _, err := w.Write(l[:]); err != nil {
|
if _, err := w.Write(l[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -214,7 +225,7 @@ func (f *FeatureVector) Encode(w io.Writer) error {
|
|||||||
// are incompatible.
|
// are incompatible.
|
||||||
func (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
|
func (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
|
||||||
error) {
|
error) {
|
||||||
shared := NewSharedFeatures(local.features)
|
shared := newSharedFeatures(local.Copy())
|
||||||
|
|
||||||
for index, flag := range local.flags {
|
for index, flag := range local.flags {
|
||||||
if _, exist := remote.flags[index]; !exist {
|
if _, exist := remote.flags[index]; !exist {
|
||||||
@ -225,6 +236,7 @@ func (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
|
|||||||
case OptionalFlag:
|
case OptionalFlag:
|
||||||
// If feature is optional and remote side
|
// If feature is optional and remote side
|
||||||
// haven't it than it might be safely disabled.
|
// haven't it than it might be safely disabled.
|
||||||
|
delete(shared.flags, index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,6 +255,7 @@ func (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
|
|||||||
case OptionalFlag:
|
case OptionalFlag:
|
||||||
// If feature is optional and local side
|
// If feature is optional and local side
|
||||||
// haven't it than it might be safely disabled.
|
// haven't it than it might be safely disabled.
|
||||||
|
delete(shared.flags, index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,6 +268,20 @@ func (local *FeatureVector) Compare(remote *FeatureVector) (*SharedFeatures,
|
|||||||
return shared, nil
|
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
|
// SharedFeatures is a product of comparison of two features vector
|
||||||
// which consist of features which are present in both local and remote
|
// which consist of features which are present in both local and remote
|
||||||
// features vectors.
|
// features vectors.
|
||||||
@ -262,22 +289,22 @@ type SharedFeatures struct {
|
|||||||
*FeatureVector
|
*FeatureVector
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSharedFeatures creates new shared features instance.
|
// newSharedFeatures creates new shared features instance.
|
||||||
func NewSharedFeatures(features FeaturesMap) *SharedFeatures {
|
func newSharedFeatures(f *FeatureVector) *SharedFeatures {
|
||||||
return &SharedFeatures{NewFeatureVector(features)}
|
return &SharedFeatures{f}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsActive checks is feature active or not, it might be disabled during
|
// IsActive checks is feature active or not, it might be disabled during
|
||||||
// comparision with remote feature vector if it was optional and
|
// comparision with remote feature vector if it was optional and
|
||||||
// remote peer doesn't support it.
|
// remote peer doesn't support it.
|
||||||
func (f *SharedFeatures) IsActive(name featureName) bool {
|
func (f *SharedFeatures) IsActive(name featureName) bool {
|
||||||
position, ok := f.features[name]
|
index, ok := f.featuresMap[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// If we even have no such feature in feature map, than it
|
// If we even have no such feature in feature map, than it
|
||||||
// can't be active in any circumstances.
|
// can't be active in any circumstances.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
_, exist := f.flags[position]
|
_, exist := f.flags[index]
|
||||||
return exist
|
return exist
|
||||||
}
|
}
|
||||||
|
@ -9,26 +9,19 @@ import (
|
|||||||
// TestFeaturesRemoteRequireError checks that we throw an error if remote peer
|
// TestFeaturesRemoteRequireError checks that we throw an error if remote peer
|
||||||
// has required feature which we don't support.
|
// has required feature which we don't support.
|
||||||
func TestFeaturesRemoteRequireError(t *testing.T) {
|
func TestFeaturesRemoteRequireError(t *testing.T) {
|
||||||
var (
|
const (
|
||||||
first featureName = "first"
|
first = "first"
|
||||||
second featureName = "second"
|
second = "second"
|
||||||
)
|
)
|
||||||
|
|
||||||
var localFeaturesMap = FeaturesMap{
|
localFeatures := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
var remoteFeaturesMap = FeaturesMap{
|
remoteFeatures := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
second: 1,
|
{second, RequiredFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
localFeatures := NewFeatureVector(localFeaturesMap)
|
|
||||||
localFeatures.SetFeatureFlag(first, OptionalFlag)
|
|
||||||
|
|
||||||
remoteFeatures := NewFeatureVector(remoteFeaturesMap)
|
|
||||||
remoteFeatures.SetFeatureFlag(first, RequiredFlag)
|
|
||||||
remoteFeatures.SetFeatureFlag(second, RequiredFlag)
|
|
||||||
|
|
||||||
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
||||||
t.Fatal("error wasn't received")
|
t.Fatal("error wasn't received")
|
||||||
@ -38,26 +31,19 @@ func TestFeaturesRemoteRequireError(t *testing.T) {
|
|||||||
// TestFeaturesLocalRequireError checks that we throw an error if local peer has
|
// TestFeaturesLocalRequireError checks that we throw an error if local peer has
|
||||||
// required feature which remote peer don't support.
|
// required feature which remote peer don't support.
|
||||||
func TestFeaturesLocalRequireError(t *testing.T) {
|
func TestFeaturesLocalRequireError(t *testing.T) {
|
||||||
var (
|
const (
|
||||||
first featureName = "first"
|
first = "first"
|
||||||
second featureName = "second"
|
second = "second"
|
||||||
)
|
)
|
||||||
|
|
||||||
var localFeaturesMap = FeaturesMap{
|
localFeatures := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
second: 1,
|
{second, RequiredFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
var remoteFeaturesMap = FeaturesMap{
|
remoteFeatures := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
localFeatures := NewFeatureVector(localFeaturesMap)
|
|
||||||
localFeatures.SetFeatureFlag(first, OptionalFlag)
|
|
||||||
localFeatures.SetFeatureFlag(second, RequiredFlag)
|
|
||||||
|
|
||||||
remoteFeatures := NewFeatureVector(remoteFeaturesMap)
|
|
||||||
remoteFeatures.SetFeatureFlag(first, RequiredFlag)
|
|
||||||
|
|
||||||
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
if _, err := localFeatures.Compare(remoteFeatures); err == nil {
|
||||||
t.Fatal("error wasn't received")
|
t.Fatal("error wasn't received")
|
||||||
@ -67,16 +53,13 @@ func TestFeaturesLocalRequireError(t *testing.T) {
|
|||||||
// TestOptionalFeature checks that if remote peer don't have the feature but
|
// 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.
|
// on our side this feature is optional than we mark this feature as disabled.
|
||||||
func TestOptionalFeature(t *testing.T) {
|
func TestOptionalFeature(t *testing.T) {
|
||||||
var first featureName = "first"
|
const first = "first"
|
||||||
|
|
||||||
var localFeaturesMap = FeaturesMap{
|
localFeatures := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
localFeatures := NewFeatureVector(localFeaturesMap)
|
remoteFeatures := NewFeatureVector([]Feature{})
|
||||||
localFeatures.SetFeatureFlag(first, OptionalFlag)
|
|
||||||
|
|
||||||
remoteFeatures := NewFeatureVector(FeaturesMap{})
|
|
||||||
|
|
||||||
shared, err := localFeatures.Compare(remoteFeatures)
|
shared, err := localFeatures.Compare(remoteFeatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,17 +72,32 @@ func TestOptionalFeature(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSetRequireAfterInit checks that we can change the feature flag after
|
||||||
|
// initialization.
|
||||||
|
func TestSetRequireAfterInit(t *testing.T) {
|
||||||
|
const first = "first"
|
||||||
|
|
||||||
|
localFeatures := NewFeatureVector([]Feature{
|
||||||
|
{first, OptionalFlag},
|
||||||
|
})
|
||||||
|
localFeatures.SetFeatureFlag(first, RequiredFlag)
|
||||||
|
remoteFeatures := NewFeatureVector([]Feature{})
|
||||||
|
|
||||||
|
_, 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
|
// TestDecodeEncodeFeaturesVector checks that feature vector might be
|
||||||
// successfully encoded and decoded.
|
// successfully encoded and decoded.
|
||||||
func TestDecodeEncodeFeaturesVector(t *testing.T) {
|
func TestDecodeEncodeFeaturesVector(t *testing.T) {
|
||||||
var first featureName = "first"
|
const first = "first"
|
||||||
|
|
||||||
var localFeaturesMap = FeaturesMap{
|
f := NewFeatureVector([]Feature{
|
||||||
first: 0,
|
{first, OptionalFlag},
|
||||||
}
|
})
|
||||||
|
|
||||||
f := NewFeatureVector(localFeaturesMap)
|
|
||||||
f.SetFeatureFlag(first, OptionalFlag)
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
if err := f.Encode(&b); err != nil {
|
if err := f.Encode(&b); err != nil {
|
||||||
|
@ -86,12 +86,12 @@ func (msg *Init) MaxPayloadLength(uint32) uint32 {
|
|||||||
//
|
//
|
||||||
// This is part of the lnwire.Message interface.
|
// This is part of the lnwire.Message interface.
|
||||||
func (msg *Init) Validate() error {
|
func (msg *Init) Validate() error {
|
||||||
if msg.GlobalFeatures.SerializedSize() > maxAllowedSize {
|
if msg.GlobalFeatures.serializedSize() > maxAllowedSize {
|
||||||
return errors.Errorf("global feature vector exceed max allowed "+
|
return errors.Errorf("global feature vector exceed max allowed "+
|
||||||
"size %v", maxAllowedSize)
|
"size %v", maxAllowedSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.LocalFeatures.SerializedSize() > maxAllowedSize {
|
if msg.LocalFeatures.serializedSize() > maxAllowedSize {
|
||||||
return errors.Errorf("local feature vector exceed max allowed "+
|
return errors.Errorf("local feature vector exceed max allowed "+
|
||||||
"size %v", maxAllowedSize)
|
"size %v", maxAllowedSize)
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInitEncodeDecode(t *testing.T) {
|
func TestInitEncodeDecode(t *testing.T) {
|
||||||
fm := FeaturesMap{
|
const somefeature = "somefeature"
|
||||||
"somefeature": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
gf := NewFeatureVector(fm)
|
gf := NewFeatureVector([]Feature{
|
||||||
gf.SetFeatureFlag("somefeature", OptionalFlag)
|
{somefeature, OptionalFlag},
|
||||||
|
})
|
||||||
lf := NewFeatureVector(fm)
|
lf := NewFeatureVector([]Feature{
|
||||||
lf.SetFeatureFlag("somefeature", OptionalFlag)
|
{somefeature, OptionalFlag},
|
||||||
|
})
|
||||||
|
|
||||||
init1 := &Init{
|
init1 := &Init{
|
||||||
GlobalFeatures: gf,
|
GlobalFeatures: gf,
|
||||||
@ -35,10 +34,10 @@ func TestInitEncodeDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We not encode the feature map in feature vector, for that reason the
|
// We not encode the feature map in feature vector, for that reason the
|
||||||
// init messages will differ. Initialize decoded feature map in
|
// init messages will differ. Set feature map with nil in
|
||||||
// order to use deep equal function.
|
// order to use deep equal function.
|
||||||
init2.GlobalFeatures.features = fm
|
init1.GlobalFeatures.featuresMap = nil
|
||||||
init2.LocalFeatures.features = fm
|
init1.LocalFeatures.featuresMap = nil
|
||||||
|
|
||||||
// Assert equality of the two instances.
|
// Assert equality of the two instances.
|
||||||
if !reflect.DeepEqual(init1, init2) {
|
if !reflect.DeepEqual(init1, init2) {
|
||||||
|
4
peer.go
4
peer.go
@ -199,8 +199,8 @@ func newPeer(conn net.Conn, server *server, addr *lnwire.NetAddress,
|
|||||||
localCloseChanReqs: make(chan *closeLinkReq),
|
localCloseChanReqs: make(chan *closeLinkReq),
|
||||||
remoteCloseChanReqs: make(chan *lnwire.CloseRequest),
|
remoteCloseChanReqs: make(chan *lnwire.CloseRequest),
|
||||||
|
|
||||||
localSharedFeatures: lnwire.NewSharedFeatures(localFeaturesMap),
|
localSharedFeatures: nil,
|
||||||
globalSharedFeatures: lnwire.NewSharedFeatures(globalFeaturesMap),
|
globalSharedFeatures: nil,
|
||||||
|
|
||||||
queueQuit: make(chan struct{}),
|
queueQuit: make(chan struct{}),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
|
@ -137,8 +137,8 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
|||||||
broadcastRequests: make(chan *broadcastReq),
|
broadcastRequests: make(chan *broadcastReq),
|
||||||
sendRequests: make(chan *sendReq),
|
sendRequests: make(chan *sendReq),
|
||||||
|
|
||||||
globalFeatures: lnwire.NewFeatureVector(globalFeaturesMap),
|
globalFeatures: globalFeatures,
|
||||||
localFeatures: lnwire.NewFeatureVector(localFeaturesMap),
|
localFeatures: localFeatures,
|
||||||
|
|
||||||
queries: make(chan interface{}),
|
queries: make(chan interface{}),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
|
Loading…
Reference in New Issue
Block a user