Merge pull request #3685 from cfromknecht/flat-features

flat features
This commit is contained in:
Joost Jager 2019-11-09 11:43:45 +01:00 committed by GitHub
commit b222b6e625
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 460 additions and 114 deletions

@ -157,8 +157,9 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
IP: bytes.Repeat([]byte("a"), 16), IP: bytes.Repeat([]byte("a"), 16),
}, },
}, },
Features: lnwire.NewFeatureVector(nil, Features: lnwire.NewFeatureVector(
lnwire.GlobalFeatures), nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(), AuthSigBytes: testSig.Serialize(),
} }
graphNode.AddPubKey(pub) graphNode.AddPubKey(pub)
@ -183,7 +184,9 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
IP: bytes.Repeat([]byte("a"), 16), IP: bytes.Repeat([]byte("a"), 16),
}, },
}, },
Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(), AuthSigBytes: testSig.Serialize(),
} }
dbNode.AddPubKey(nodeKey) dbNode.AddPubKey(nodeKey)
@ -287,7 +290,9 @@ func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) {
IP: bytes.Repeat([]byte("a"), 16), IP: bytes.Repeat([]byte("a"), 16),
}, },
}, },
Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(), AuthSigBytes: testSig.Serialize(),
} }
dbNode.AddPubKey(nodeKey) dbNode.AddPubKey(nodeKey)

@ -3506,7 +3506,7 @@ func deserializeLightningNode(r io.Reader) (LightningNode, error) {
return LightningNode{}, err return LightningNode{}, err
} }
fv := lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures) fv := lnwire.NewFeatureVector(nil, lnwire.Features)
err = fv.Decode(r) err = fv.Decode(r)
if err != nil { if err != nil {
return LightningNode{}, err return LightningNode{}, err

@ -36,7 +36,7 @@ var (
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10) _, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 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) { func createLightningNode(db *DB, priv *btcec.PrivateKey) (*LightningNode, error) {

@ -744,7 +744,7 @@ func deserializeLightningNode(r io.Reader) (LightningNode, error) {
return LightningNode{}, err return LightningNode{}, err
} }
fv := lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures) fv := lnwire.NewFeatureVector(nil, nil)
err = fv.Decode(r) err = fv.Decode(r)
if err != nil { if err != nil {
return LightningNode{}, err return LightningNode{}, err

@ -25,7 +25,7 @@ var (
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10) _, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 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) { func createLightningNode(db *DB, priv *btcec.PrivateKey) (*LightningNode, error) {

@ -1459,9 +1459,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement) error {
} }
timestamp := time.Unix(int64(msg.Timestamp), 0) timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector( features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)
msg.Features, lnwire.GlobalFeatures,
)
node := &channeldb.LightningNode{ node := &channeldb.LightningNode{
HaveNodeAnnouncement: true, HaveNodeAnnouncement: true,
LastUpdate: timestamp, LastUpdate: timestamp,

@ -56,10 +56,10 @@ func (p *mockPeer) Address() net.Addr { return nil }
func (p *mockPeer) QuitSignal() <-chan struct{} { func (p *mockPeer) QuitSignal() <-chan struct{} {
return p.quit return p.quit
} }
func (p *mockPeer) LocalGlobalFeatures() *lnwire.FeatureVector { func (p *mockPeer) LocalFeatures() *lnwire.FeatureVector {
return nil return nil
} }
func (p *mockPeer) RemoteGlobalFeatures() *lnwire.FeatureVector { func (p *mockPeer) RemoteFeatures() *lnwire.FeatureVector {
return nil return nil
} }

32
feature/default_sets.go Normal file

@ -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
feature/manager.go Normal file

@ -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) {
return newManager(cfg, defaultSetDesc)
}
// newManager creates a new feeature Manager, applying any custom modifications
// 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)
}

@ -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
feature/set.go Normal file

@ -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"
}
}

@ -1205,10 +1205,10 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
// negotiated the new tweakless commitment format. This is only the // negotiated the new tweakless commitment format. This is only the
// case if *both* us and the remote peer are signaling the proper // case if *both* us and the remote peer are signaling the proper
// feature bit. // feature bit.
localTweakless := fmsg.peer.LocalGlobalFeatures().HasFeature( localTweakless := fmsg.peer.LocalFeatures().HasFeature(
lnwire.StaticRemoteKeyOptional, lnwire.StaticRemoteKeyOptional,
) )
remoteTweakless := fmsg.peer.RemoteGlobalFeatures().HasFeature( remoteTweakless := fmsg.peer.RemoteFeatures().HasFeature(
lnwire.StaticRemoteKeyOptional, lnwire.StaticRemoteKeyOptional,
) )
tweaklessCommitment := localTweakless && remoteTweakless tweaklessCommitment := localTweakless && remoteTweakless
@ -2780,10 +2780,10 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// negotiated the new tweakless commitment format. This is only the // negotiated the new tweakless commitment format. This is only the
// case if *both* us and the remote peer are signaling the proper // case if *both* us and the remote peer are signaling the proper
// feature bit. // feature bit.
localTweakless := msg.peer.LocalGlobalFeatures().HasFeature( localTweakless := msg.peer.LocalFeatures().HasFeature(
lnwire.StaticRemoteKeyOptional, lnwire.StaticRemoteKeyOptional,
) )
remoteTweakless := msg.peer.RemoteGlobalFeatures().HasFeature( remoteTweakless := msg.peer.RemoteFeatures().HasFeature(
lnwire.StaticRemoteKeyOptional, lnwire.StaticRemoteKeyOptional,
) )
tweaklessCommitment := localTweakless && remoteTweakless tweaklessCommitment := localTweakless && remoteTweakless

@ -184,11 +184,11 @@ func (n *testNode) QuitSignal() <-chan struct{} {
return n.shutdownChannel return n.shutdownChannel
} }
func (n *testNode) LocalGlobalFeatures() *lnwire.FeatureVector { func (n *testNode) LocalFeatures() *lnwire.FeatureVector {
return lnwire.NewFeatureVector(nil, nil) return lnwire.NewFeatureVector(nil, nil)
} }
func (n *testNode) RemoteGlobalFeatures() *lnwire.FeatureVector { func (n *testNode) RemoteFeatures() *lnwire.FeatureVector {
return lnwire.NewFeatureVector(nil, nil) return lnwire.NewFeatureVector(nil, nil)
} }

@ -1653,10 +1653,10 @@ func (m *mockPeer) IdentityKey() *btcec.PublicKey {
func (m *mockPeer) Address() net.Addr { func (m *mockPeer) Address() net.Addr {
return nil return nil
} }
func (m *mockPeer) LocalGlobalFeatures() *lnwire.FeatureVector { func (m *mockPeer) LocalFeatures() *lnwire.FeatureVector {
return nil return nil
} }
func (m *mockPeer) RemoteGlobalFeatures() *lnwire.FeatureVector { func (m *mockPeer) RemoteFeatures() *lnwire.FeatureVector {
return nil return nil
} }

@ -607,11 +607,11 @@ func (s *mockServer) WipeChannel(*wire.OutPoint) error {
return nil return nil
} }
func (s *mockServer) LocalGlobalFeatures() *lnwire.FeatureVector { func (s *mockServer) LocalFeatures() *lnwire.FeatureVector {
return nil return nil
} }
func (s *mockServer) RemoteGlobalFeatures() *lnwire.FeatureVector { func (s *mockServer) RemoteFeatures() *lnwire.FeatureVector {
return nil return nil
} }

@ -47,15 +47,14 @@ type Peer interface {
// implementation exits. // implementation exits.
QuitSignal() <-chan struct{} QuitSignal() <-chan struct{}
// LocalGlobalFeatures returns the set of global features that has been // LocalFeatures returns the set of features that has been advertised by
// advertised by the local peer. This allows sub-systems that use this // the us to the remote peer. This allows sub-systems that use this
// interface to gate their behavior off the set of negotiated feature // interface to gate their behavior off the set of negotiated feature
// bits. // bits.
LocalGlobalFeatures() *lnwire.FeatureVector LocalFeatures() *lnwire.FeatureVector
// RemoteGlobalFeatures returns the set of global features that has // RemoteFeatures returns the set of features that has been advertised
// been advertised by the remote peer. This allows sub-systems that use // by the remote peer. This allows sub-systems that use this interface
// this interface to gate their behavior off the set of negotiated // to gate their behavior off the set of negotiated feature bits.
// feature bits. RemoteFeatures() *lnwire.FeatureVector
RemoteGlobalFeatures() *lnwire.FeatureVector
} }

@ -2,10 +2,17 @@ package lnwire
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
) )
var (
// ErrFeaturePairExists signals an error in feature vector construction
// where the opposing bit in a feature pair has already been set.
ErrFeaturePairExists = errors.New("feature pair exists")
)
// FeatureBit represents a feature that can be enabled in either a local or // 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 // 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 // "it's OK to be odd" rule, where features at even bit positions must be known
@ -78,24 +85,15 @@ const (
maxAllowedSize = 32764 maxAllowedSize = 32764
) )
// LocalFeatures is a mapping of known connection-local feature bits to a // Features is a mapping of known feature bits to a descriptive name. All known
// descriptive name. All known local feature bits must be assigned a name in // feature bits must be assigned a name in this mapping, and feature bit pairs
// this mapping. Local features are those which are only sent to the peer and // must be assigned together for correct behavior.
// not advertised to the entire network. A full description of these feature var Features = map[FeatureBit]string{
// bits is provided in the BOLT-09 specification.
var LocalFeatures = map[FeatureBit]string{
DataLossProtectRequired: "data-loss-protect", DataLossProtectRequired: "data-loss-protect",
DataLossProtectOptional: "data-loss-protect", DataLossProtectOptional: "data-loss-protect",
InitialRoutingSync: "initial-routing-sync", InitialRoutingSync: "initial-routing-sync",
GossipQueriesRequired: "gossip-queries", GossipQueriesRequired: "gossip-queries",
GossipQueriesOptional: "gossip-queries", GossipQueriesOptional: "gossip-queries",
}
// 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{
TLVOnionPayloadRequired: "tlv-onion", TLVOnionPayloadRequired: "tlv-onion",
TLVOnionPayloadOptional: "tlv-onion", TLVOnionPayloadOptional: "tlv-onion",
StaticRemoteKeyOptional: "static-remote-key", StaticRemoteKeyOptional: "static-remote-key",
@ -121,6 +119,26 @@ func NewRawFeatureVector(bits ...FeatureBit) *RawFeatureVector {
return fv return fv
} }
// Merges sets all feature bits in other on the receiver's feature vector.
func (fv *RawFeatureVector) Merge(other *RawFeatureVector) error {
for bit := range other.features {
err := fv.SafeSet(bit)
if err != nil {
return err
}
}
return nil
}
// Clone makes a copy of a feature vector.
func (fv *RawFeatureVector) Clone() *RawFeatureVector {
newFeatures := NewRawFeatureVector()
for bit := range fv.features {
newFeatures.Set(bit)
}
return newFeatures
}
// IsSet returns whether a particular feature bit is enabled in the vector. // IsSet returns whether a particular feature bit is enabled in the vector.
func (fv *RawFeatureVector) IsSet(feature FeatureBit) bool { func (fv *RawFeatureVector) IsSet(feature FeatureBit) bool {
return fv.features[feature] return fv.features[feature]
@ -131,6 +149,20 @@ func (fv *RawFeatureVector) Set(feature FeatureBit) {
fv.features[feature] = true fv.features[feature] = true
} }
// SafeSet sets the chosen feature bit in the feature vector, but returns an
// error if the opposing feature bit is already set. This ensures both that we
// are creating properly structured feature vectors, and in some cases, that
// peers are sending properly encoded ones, i.e. it can't be both optional and
// required.
func (fv *RawFeatureVector) SafeSet(feature FeatureBit) error {
if _, ok := fv.features[feature^1]; ok {
return ErrFeaturePairExists
}
fv.Set(feature)
return nil
}
// Unset marks a feature as disabled in the vector. // Unset marks a feature as disabled in the vector.
func (fv *RawFeatureVector) Unset(feature FeatureBit) { func (fv *RawFeatureVector) Unset(feature FeatureBit) {
delete(fv.features, feature) delete(fv.features, feature)

@ -7,20 +7,26 @@ import "io"
// diagnosis where features are incompatible. Each node MUST wait to receive // diagnosis where features are incompatible. Each node MUST wait to receive
// init before sending any other messages. // init before sending any other messages.
type Init struct { type Init struct {
// GlobalFeatures is feature vector which affects HTLCs and thus are // GlobalFeatures is a legacy feature vector used for backwards
// also advertised to other nodes. // compatibility with older nodes. Any features defined here should be
// merged with those presented in Features.
GlobalFeatures *RawFeatureVector GlobalFeatures *RawFeatureVector
// LocalFeatures is feature vector which only affect the protocol // Features is a feature vector containing a the features supported by
// between two nodes. // the remote node.
LocalFeatures *RawFeatureVector //
// NOTE: Older nodes may place some features in GlobalFeatures, but all
// new features are to be added in Features. When handling an Init
// message, any GlobalFeatures should be merged into the unified
// Features field.
Features *RawFeatureVector
} }
// NewInitMessage creates new instance of init message object. // NewInitMessage creates new instance of init message object.
func NewInitMessage(gf *RawFeatureVector, lf *RawFeatureVector) *Init { func NewInitMessage(gf *RawFeatureVector, f *RawFeatureVector) *Init {
return &Init{ return &Init{
GlobalFeatures: gf, GlobalFeatures: gf,
LocalFeatures: lf, Features: f,
} }
} }
@ -35,7 +41,7 @@ var _ Message = (*Init)(nil)
func (msg *Init) Decode(r io.Reader, pver uint32) error { func (msg *Init) Decode(r io.Reader, pver uint32) error {
return ReadElements(r, return ReadElements(r,
&msg.GlobalFeatures, &msg.GlobalFeatures,
&msg.LocalFeatures, &msg.Features,
) )
} }
@ -46,7 +52,7 @@ func (msg *Init) Decode(r io.Reader, pver uint32) error {
func (msg *Init) Encode(w io.Writer, pver uint32) error { func (msg *Init) Encode(w io.Writer, pver uint32) error {
return WriteElements(w, return WriteElements(w,
msg.GlobalFeatures, msg.GlobalFeatures,
msg.LocalFeatures, msg.Features,
) )
} }

87
peer.go

@ -190,21 +190,24 @@ type peer struct {
server *server server *server
// localFeatures is the set of local features that we advertised to the // features is the set of features that we advertised to the remote
// remote node. // node.
localFeatures *lnwire.RawFeatureVector features *lnwire.FeatureVector
// legacyFeatures is the set of features that we advertised to the remote
// node for backwards compatibility. Nodes that have not implemented
// flat featurs will still be able to read our feature bits from the
// legacy global field, but we will also advertise everything in the
// default features field.
legacyFeatures *lnwire.FeatureVector
// outgoingCltvRejectDelta defines the number of blocks before expiry of // outgoingCltvRejectDelta defines the number of blocks before expiry of
// an htlc where we don't offer an htlc anymore. // an htlc where we don't offer an htlc anymore.
outgoingCltvRejectDelta uint32 outgoingCltvRejectDelta uint32
// remoteLocalFeatures is the local feature vector received from the // remoteFeatures is the feature vector received from the peer during
// peer during the connection handshake. // the connection handshake.
remoteLocalFeatures *lnwire.FeatureVector remoteFeatures *lnwire.FeatureVector
// remoteGlobalFeatures is the global feature vector received from the
// peer during the connection handshake.
remoteGlobalFeatures *lnwire.FeatureVector
// failedChannels is a set that tracks channels we consider `failed`. // failedChannels is a set that tracks channels we consider `failed`.
// This is a temporary measure until we have implemented real failure // This is a temporary measure until we have implemented real failure
@ -234,7 +237,7 @@ var _ lnpeer.Peer = (*peer)(nil)
// pointer to the main server. // pointer to the main server.
func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server, func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server,
addr *lnwire.NetAddress, inbound bool, addr *lnwire.NetAddress, inbound bool,
localFeatures *lnwire.RawFeatureVector, features, legacyFeatures *lnwire.FeatureVector,
chanActiveTimeout time.Duration, chanActiveTimeout time.Duration,
outgoingCltvRejectDelta uint32) ( outgoingCltvRejectDelta uint32) (
*peer, error) { *peer, error) {
@ -252,7 +255,8 @@ func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server,
server: server, server: server,
localFeatures: localFeatures, features: features,
legacyFeatures: legacyFeatures,
outgoingCltvRejectDelta: outgoingCltvRejectDelta, outgoingCltvRejectDelta: outgoingCltvRejectDelta,
@ -399,7 +403,7 @@ func (p *peer) initGossipSync() {
// If the remote peer knows of the new gossip queries feature, then // If the remote peer knows of the new gossip queries feature, then
// we'll create a new gossipSyncer in the AuthenticatedGossiper for it. // we'll create a new gossipSyncer in the AuthenticatedGossiper for it.
case p.remoteLocalFeatures.HasFeature(lnwire.GossipQueriesOptional): case p.remoteFeatures.HasFeature(lnwire.GossipQueriesOptional):
srvrLog.Infof("Negotiated chan series queries with %x", srvrLog.Infof("Negotiated chan series queries with %x",
p.pubKeyBytes[:]) p.pubKeyBytes[:])
@ -2387,62 +2391,63 @@ func (p *peer) WipeChannel(chanPoint *wire.OutPoint) error {
// handleInitMsg handles the incoming init message which contains global and // handleInitMsg handles the incoming init message which contains global and
// local features vectors. If feature vectors are incompatible then disconnect. // local features vectors. If feature vectors are incompatible then disconnect.
func (p *peer) handleInitMsg(msg *lnwire.Init) error { func (p *peer) handleInitMsg(msg *lnwire.Init) error {
p.remoteLocalFeatures = lnwire.NewFeatureVector( // First, merge any features from the legacy global features field into
msg.LocalFeatures, lnwire.LocalFeatures, // those presented in the local features fields.
) err := msg.Features.Merge(msg.GlobalFeatures)
p.remoteGlobalFeatures = lnwire.NewFeatureVector( if err != nil {
msg.GlobalFeatures, lnwire.GlobalFeatures, return fmt.Errorf("unable to merge legacy global featues: %v",
err)
}
// Then, finalize the remote feature vector providing the flatteneed
// feature bit namespace.
p.remoteFeatures = lnwire.NewFeatureVector(
msg.Features, lnwire.Features,
) )
// Now that we have their features loaded, we'll ensure that they // Now that we have their features loaded, we'll ensure that they
// didn't set any required bits that we don't know of. // didn't set any required bits that we don't know of.
unknownLocalFeatures := p.remoteLocalFeatures.UnknownRequiredFeatures() unknownFeatures := p.remoteFeatures.UnknownRequiredFeatures()
if len(unknownLocalFeatures) > 0 { if len(unknownFeatures) > 0 {
err := fmt.Errorf("Peer set unknown local feature bits: %v", err := fmt.Errorf("peer set unknown feature bits: %v",
unknownLocalFeatures) unknownFeatures)
return err
}
unknownGlobalFeatures := p.remoteGlobalFeatures.UnknownRequiredFeatures()
if len(unknownGlobalFeatures) > 0 {
err := fmt.Errorf("Peer set unknown global feature bits: %v",
unknownGlobalFeatures)
return err return err
} }
// Now that we know we understand their requirements, we'll check to // Now that we know we understand their requirements, we'll check to
// see if they don't support anything that we deem to be mandatory. // see if they don't support anything that we deem to be mandatory.
switch { switch {
case !p.remoteLocalFeatures.HasFeature(lnwire.DataLossProtectRequired): case !p.remoteFeatures.HasFeature(lnwire.DataLossProtectRequired):
return fmt.Errorf("data loss protection required") return fmt.Errorf("data loss protection required")
} }
return nil return nil
} }
// LocalGlobalFeatures returns the set of global features that has been // LocalFeatures returns the set of global features that has been advertised by
// advertised by the local node. This allows sub-systems that use this // the local node. This allows sub-systems that use this interface to gate their
// interface to gate their behavior off the set of negotiated feature bits. // behavior off the set of negotiated feature bits.
// //
// NOTE: Part of the lnpeer.Peer interface. // NOTE: Part of the lnpeer.Peer interface.
func (p *peer) LocalGlobalFeatures() *lnwire.FeatureVector { func (p *peer) LocalFeatures() *lnwire.FeatureVector {
return p.server.globalFeatures return p.features
} }
// RemoteGlobalFeatures returns the set of global features that has been // RemoteFeatures returns the set of global features that has been advertised by
// advertised by the remote node. This allows sub-systems that use this // the remote node. This allows sub-systems that use this interface to gate
// interface to gate their behavior off the set of negotiated feature bits. // their behavior off the set of negotiated feature bits.
// //
// NOTE: Part of the lnpeer.Peer interface. // NOTE: Part of the lnpeer.Peer interface.
func (p *peer) RemoteGlobalFeatures() *lnwire.FeatureVector { func (p *peer) RemoteFeatures() *lnwire.FeatureVector {
return p.remoteGlobalFeatures return p.remoteFeatures
} }
// sendInitMsg sends init message to remote peer which contains our currently // sendInitMsg sends init message to remote peer which contains our currently
// supported local and global features. // supported local and global features.
func (p *peer) sendInitMsg() error { func (p *peer) sendInitMsg() error {
msg := lnwire.NewInitMessage( msg := lnwire.NewInitMessage(
p.server.globalFeatures.RawFeatureVector, p.legacyFeatures.RawFeatureVector,
p.localFeatures, p.features.RawFeatureVector,
) )
return p.writeMessage(msg) return p.writeMessage(msg)

@ -28,7 +28,7 @@ var (
Port: 9000} Port: 9000}
testAddrs = []net.Addr{testAddr} testAddrs = []net.Addr{testAddr}
testFeatures = lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures) testFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
testHash = [32]byte{ testHash = [32]byte{
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,

@ -35,6 +35,7 @@ import (
"github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/channelnotifier"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
@ -232,9 +233,9 @@ type server struct {
readPool *pool.Read readPool *pool.Read
// globalFeatures feature vector which affects HTLCs and thus are also // featureMgr dispatches feature vectors for various contexts within the
// advertised to other nodes. // daemon.
globalFeatures *lnwire.FeatureVector featureMgr *feature.Manager
// currentNodeAnn is the node announcement that has been broadcast to // currentNodeAnn is the node announcement that has been broadcast to
// the network upon startup, if the attributes of the node (us) has // the network upon startup, if the attributes of the node (us) has
@ -369,6 +370,14 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout, readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout,
) )
featureMgr, err := feature.NewManager(feature.Config{
NoTLVOnion: cfg.LegacyProtocol.LegacyOnion(),
NoStaticRemoteKey: cfg.LegacyProtocol.LegacyCommitment(),
})
if err != nil {
return nil, err
}
s := &server{ s := &server{
chanDB: chanDB, chanDB: chanDB,
cc: cc, cc: cc,
@ -405,10 +414,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer), peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer),
peerDisconnectedListeners: make(map[string][]chan<- struct{}), peerDisconnectedListeners: make(map[string][]chan<- struct{}),
globalFeatures: lnwire.NewFeatureVector( featureMgr: featureMgr,
globalFeatures, lnwire.GlobalFeatures, quit: make(chan struct{}),
),
quit: make(chan struct{}),
} }
s.witnessBeacon = &preimageBeacon{ s.witnessBeacon = &preimageBeacon{
@ -594,7 +601,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
LastUpdate: time.Now(), LastUpdate: time.Now(),
Addresses: selfAddrs, Addresses: selfAddrs,
Alias: nodeAlias.String(), Alias: nodeAlias.String(),
Features: s.globalFeatures, Features: s.featureMgr.Get(feature.SetNodeAnn),
Color: color, Color: color,
} }
copy(selfNode.PubKeyBytes[:], privKey.PubKey().SerializeCompressed()) copy(selfNode.PubKeyBytes[:], privKey.PubKey().SerializeCompressed())
@ -1965,7 +1972,7 @@ func (s *server) initTorController() error {
Addresses: newNodeAnn.Addresses, Addresses: newNodeAnn.Addresses,
Alias: newNodeAnn.Alias.String(), Alias: newNodeAnn.Alias.String(),
Features: lnwire.NewFeatureVector( Features: lnwire.NewFeatureVector(
newNodeAnn.Features, lnwire.GlobalFeatures, newNodeAnn.Features, lnwire.Features,
), ),
Color: newNodeAnn.RGBColor, Color: newNodeAnn.RGBColor,
AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(), AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(),
@ -2728,14 +2735,10 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
ChainNet: activeNetParams.Net, ChainNet: activeNetParams.Net,
} }
// With the brontide connection established, we'll now craft the local // With the brontide connection established, we'll now craft the feature
// feature vector to advertise to the remote node. // vectors to advertise to the remote node.
localFeatures := lnwire.NewRawFeatureVector() initFeatures := s.featureMgr.Get(feature.SetInit)
legacyFeatures := s.featureMgr.Get(feature.SetLegacyGlobal)
// We'll signal that we understand the data loss protection feature,
// and also that we support the new gossip query features.
localFeatures.Set(lnwire.DataLossProtectRequired)
localFeatures.Set(lnwire.GossipQueriesOptional)
// Now that we've established a connection, create a peer, and it to the // Now that we've established a connection, create a peer, and it to the
// set of currently active peers. Configure the peer with the incoming // set of currently active peers. Configure the peer with the incoming
@ -2744,8 +2747,8 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
// htlcs, an extra block is added to prevent the channel from being // htlcs, an extra block is added to prevent the channel from being
// closed when the htlc is outstanding and a new block comes in. // closed when the htlc is outstanding and a new block comes in.
p, err := newPeer( p, err := newPeer(
conn, connReq, s, peerAddr, inbound, localFeatures, conn, connReq, s, peerAddr, inbound, initFeatures,
cfg.ChanEnableTimeout, legacyFeatures, cfg.ChanEnableTimeout,
defaultOutgoingCltvRejectDelta, defaultOutgoingCltvRejectDelta,
) )
if err != nil { if err != nil {