Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flat features #3685

Merged
merged 5 commits into from
Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions autopilot/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,9 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(nil,
lnwire.GlobalFeatures),
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
graphNode.AddPubKey(pub)
Expand All @@ -183,7 +184,9 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures),
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
Expand Down Expand Up @@ -287,7 +290,9 @@ func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) {
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures),
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
Expand Down
2 changes: 1 addition & 1 deletion channeldb/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -3506,7 +3506,7 @@ func deserializeLightningNode(r io.Reader) (LightningNode, error) {
return LightningNode{}, err
}

fv := lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures)
fv := lnwire.NewFeatureVector(nil, lnwire.Features)
err = fv.Decode(r)
if err != nil {
return LightningNode{}, err
Expand Down
2 changes: 1 addition & 1 deletion channeldb/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var (
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)

testFeatures = lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures)
testFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
)

func createLightningNode(db *DB, priv *btcec.PrivateKey) (*LightningNode, error) {
Expand Down
2 changes: 1 addition & 1 deletion channeldb/migration_01_to_11/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ func deserializeLightningNode(r io.Reader) (LightningNode, error) {
return LightningNode{}, err
}

fv := lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures)
fv := lnwire.NewFeatureVector(nil, nil)
err = fv.Decode(r)
if err != nil {
return LightningNode{}, err
Expand Down
2 changes: 1 addition & 1 deletion channeldb/migration_01_to_11/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)

testFeatures = lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures)
testFeatures = lnwire.NewFeatureVector(nil, nil)
)

func createLightningNode(db *DB, priv *btcec.PrivateKey) (*LightningNode, error) {
Expand Down
4 changes: 1 addition & 3 deletions discovery/gossiper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1459,9 +1459,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement) error {
}

timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector(
msg.Features, lnwire.GlobalFeatures,
)
features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)
node := &channeldb.LightningNode{
HaveNodeAnnouncement: true,
LastUpdate: timestamp,
Expand Down
4 changes: 2 additions & 2 deletions discovery/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ func (p *mockPeer) Address() net.Addr { return nil }
func (p *mockPeer) QuitSignal() <-chan struct{} {
return p.quit
}
func (p *mockPeer) LocalGlobalFeatures() *lnwire.FeatureVector {
func (p *mockPeer) LocalFeatures() *lnwire.FeatureVector {
return nil
}
func (p *mockPeer) RemoteGlobalFeatures() *lnwire.FeatureVector {
func (p *mockPeer) RemoteFeatures() *lnwire.FeatureVector {
return nil
}

Expand Down
32 changes: 32 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package feature

import "github.com/lightningnetwork/lnd/lnwire"

// setDesc describes which feature bits should be advertised in which feature
// sets.
type setDesc map[lnwire.FeatureBit]map[Set]struct{}

// defaultSetDesc are the default set descriptors for generating feature
// vectors. Each set is annotated with the corresponding identifier from BOLT 9
// indicating where it should be advertised.
var defaultSetDesc = setDesc{
lnwire.DataLossProtectRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.GossipQueriesOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.TLVOnionPayloadOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
SetInvoice: {}, // 9
SetLegacyGlobal: {},
},
lnwire.StaticRemoteKeyOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
SetLegacyGlobal: {},
},
}
97 changes: 97 additions & 0 deletions feature/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package feature

import (
"fmt"

"github.com/lightningnetwork/lnd/lnwire"
)

// Config houses any runtime modifications to the default set descriptors. For
// our purposes, this typically means disabling certain features to test legacy
// protocol interoperability or functionality.
type Config struct {
// NoTLVOnion unsets any optional or required TLVOnionPaylod bits from
// all feature sets.
NoTLVOnion bool

// NoStaticRemoteKey unsets any optional or required StaticRemoteKey
// bits from all feature sets.
NoStaticRemoteKey bool
}

// Manager is responsible for generating feature vectors for different requested
// feature sets.
type Manager struct {
// fsets is a static map of feature set to raw feature vectors. Requests
// are fulfilled by cloning these interal feature vectors.
fsets map[Set]*lnwire.RawFeatureVector
}

// NewManager creates a new feature Manager, applying any custom modifications
// to its feature sets before returning.
func NewManager(cfg Config) (*Manager, error) {
cfromknecht marked this conversation as resolved.
Show resolved Hide resolved
return newManager(cfg, defaultSetDesc)
}

// newManager creates a new feeature Manager, applying any custom modifications
cfromknecht marked this conversation as resolved.
Show resolved Hide resolved
// to its feature sets before returning. This method accepts the setDesc as its
// own parameter so that it can be unit tested.
func newManager(cfg Config, desc setDesc) (*Manager, error) {
// First build the default feature vector for all known sets.
fsets := make(map[Set]*lnwire.RawFeatureVector)
for bit, sets := range desc {
for set := range sets {
// Fetch the feature vector for this set, allocating a
// new one if it doesn't exist.
fv, ok := fsets[set]
if !ok {
fv = lnwire.NewRawFeatureVector()
}

// Set the configured bit on the feature vector,
// ensuring that we don't set two feature bits for the
// same pair.
err := fv.SafeSet(bit)
if err != nil {
return nil, fmt.Errorf("unable to set "+
"%v in %v: %v", bit, set, err)
}

// Write the updated feature vector under its set.
fsets[set] = fv
}
}

// Now, remove any features as directed by the config.
for _, fv := range fsets {
if cfg.NoTLVOnion {
fv.Unset(lnwire.TLVOnionPayloadOptional)
fv.Unset(lnwire.TLVOnionPayloadRequired)
}
if cfg.NoStaticRemoteKey {
fv.Unset(lnwire.StaticRemoteKeyOptional)
fv.Unset(lnwire.StaticRemoteKeyRequired)
}
}

return &Manager{
fsets: fsets,
}, nil
}

// GetRaw returns a raw feature vector for the passed set. If no set is known,
// an empty raw feature vector is returned.
func (m *Manager) GetRaw(set Set) *lnwire.RawFeatureVector {
if fv, ok := m.fsets[set]; ok {
return fv.Clone()
}

return lnwire.NewRawFeatureVector()
}

// Get returns a feature vector for the passed set. If no set is known, an empty
// feature vector is returned.
func (m *Manager) Get(set Set) *lnwire.FeatureVector {
raw := m.GetRaw(set)
return lnwire.NewFeatureVector(raw, lnwire.Features)
}
128 changes: 128 additions & 0 deletions feature/manager_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package feature

import (
"reflect"
"testing"

"github.com/lightningnetwork/lnd/lnwire"
)

type managerTest struct {
name string
cfg Config
}

const unknownFeature lnwire.FeatureBit = 30

var testSetDesc = setDesc{
lnwire.DataLossProtectRequired: {
SetNodeAnn: {}, // I
},
lnwire.TLVOnionPayloadOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.StaticRemoteKeyOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
}

var managerTests = []managerTest{
{
name: "default",
cfg: Config{},
},
{
name: "no tlv",
cfg: Config{
NoTLVOnion: true,
},
},
{
name: "no static remote key",
cfg: Config{
NoStaticRemoteKey: true,
},
},
{
name: "no tlv or static remote key",
cfg: Config{
NoTLVOnion: true,
NoStaticRemoteKey: true,
},
},
}

// TestManager asserts basic initialazation and operation of a feature manager,
// including that the proper features are removed in response to config changes.
func TestManager(t *testing.T) {
for _, test := range managerTests {
test := test
t.Run(test.name, func(t *testing.T) {
testManager(t, test)
})
}
}

func testManager(t *testing.T, test managerTest) {
m, err := newManager(test.cfg, testSetDesc)
if err != nil {
t.Fatalf("unable to create feature manager: %v", err)
}

sets := []Set{
SetInit,
SetLegacyGlobal,
SetNodeAnn,
SetInvoice,
}

for _, set := range sets {
raw := m.GetRaw(set)
fv := m.Get(set)

fv2 := lnwire.NewFeatureVector(raw, lnwire.Features)

if !reflect.DeepEqual(fv, fv2) {
t.Fatalf("mismatch Get vs GetRaw, raw: %v vs fv: %v",
fv2, fv)
}

assertUnset := func(bit lnwire.FeatureBit) {
hasBit := fv.HasFeature(bit) || fv.HasFeature(bit^1)
if hasBit {
t.Fatalf("bit %v or %v is set", bit, bit^1)
}
}

// Assert that the manager properly unset the configured feature
// bits from all sets.
if test.cfg.NoTLVOnion {
assertUnset(lnwire.TLVOnionPayloadOptional)
}
if test.cfg.NoStaticRemoteKey {
assertUnset(lnwire.StaticRemoteKeyOptional)
}

assertUnset(unknownFeature)
}

// Do same basic sanity checks on features that are always present.
nodeFeatures := m.Get(SetNodeAnn)

assertSet := func(bit lnwire.FeatureBit) {
has := nodeFeatures.HasFeature(bit)
if !has {
t.Fatalf("node features don't advertised %v", bit)
}
}

assertSet(lnwire.DataLossProtectOptional)
if !test.cfg.NoTLVOnion {
assertSet(lnwire.TLVOnionPayloadRequired)
}
if !test.cfg.NoStaticRemoteKey {
assertSet(lnwire.StaticRemoteKeyOptional)
}
}
41 changes: 41 additions & 0 deletions feature/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package feature

// Set is an enum identifying various feature sets, which separates the single
// feature namespace into distinct categories depending what context a feature
// vector is being used.
type Set uint8

const (
// SetInit identifies features that should be sent in an Init message to
// a remote peer.
SetInit Set = iota

// SetLegacyGlobal identifies features that should be set in the legacy
// GlobalFeatures field of an Init message, which maintains backwards
// compatibility with nodes that haven't implemented flat features.
SetLegacyGlobal

// SetNodeAnn identifies features that should be advertised on node
// announcements.
SetNodeAnn

// SetInvoice identifies features that should be advertised on invoices
// generated by the daemon.
SetInvoice
)

// String returns a human-readable description of a Set.
func (s Set) String() string {
switch s {
case SetInit:
return "SetInit"
case SetLegacyGlobal:
return "SetLegacyGlobal"
case SetNodeAnn:
return "SetNodeAnn"
case SetInvoice:
return "SetInvoice"
default:
return "SetUnknown"
}
}
Loading