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),
},
},
Features: lnwire.NewFeatureVector(nil,
lnwire.GlobalFeatures),
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
graphNode.AddPubKey(pub)
@ -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)
@ -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)

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

@ -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) {

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

@ -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) {

@ -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,

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

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

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

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

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

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

@ -2,10 +2,17 @@ package lnwire
import (
"encoding/binary"
"errors"
"fmt"
"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
// 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
@ -78,24 +85,15 @@ const (
maxAllowedSize = 32764
)
// 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{
// Features is a mapping of known feature bits to a descriptive name. All known
// feature bits must be assigned a name in this mapping, and feature bit pairs
// must be assigned together for correct behavior.
var Features = map[FeatureBit]string{
DataLossProtectRequired: "data-loss-protect",
DataLossProtectOptional: "data-loss-protect",
InitialRoutingSync: "initial-routing-sync",
GossipQueriesRequired: "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",
TLVOnionPayloadOptional: "tlv-onion",
StaticRemoteKeyOptional: "static-remote-key",
@ -121,6 +119,26 @@ func NewRawFeatureVector(bits ...FeatureBit) *RawFeatureVector {
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.
func (fv *RawFeatureVector) IsSet(feature FeatureBit) bool {
return fv.features[feature]
@ -131,6 +149,20 @@ func (fv *RawFeatureVector) Set(feature FeatureBit) {
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.
func (fv *RawFeatureVector) Unset(feature FeatureBit) {
delete(fv.features, feature)

@ -7,20 +7,26 @@ import "io"
// diagnosis where features are incompatible. Each node MUST wait to receive
// init before sending any other messages.
type Init struct {
// GlobalFeatures is feature vector which affects HTLCs and thus are
// also advertised to other nodes.
// GlobalFeatures is a legacy feature vector used for backwards
// compatibility with older nodes. Any features defined here should be
// merged with those presented in Features.
GlobalFeatures *RawFeatureVector
// LocalFeatures is feature vector which only affect the protocol
// between two nodes.
LocalFeatures *RawFeatureVector
// Features is a feature vector containing a the features supported by
// the remote node.
//
// 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.
func NewInitMessage(gf *RawFeatureVector, lf *RawFeatureVector) *Init {
func NewInitMessage(gf *RawFeatureVector, f *RawFeatureVector) *Init {
return &Init{
GlobalFeatures: gf,
LocalFeatures: lf,
Features: f,
}
}
@ -35,7 +41,7 @@ var _ Message = (*Init)(nil)
func (msg *Init) Decode(r io.Reader, pver uint32) error {
return ReadElements(r,
&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 {
return WriteElements(w,
msg.GlobalFeatures,
msg.LocalFeatures,
msg.Features,
)
}

87
peer.go

@ -190,21 +190,24 @@ type peer struct {
server *server
// localFeatures is the set of local features that we advertised to the
// remote node.
localFeatures *lnwire.RawFeatureVector
// features is the set of features that we advertised to the remote
// node.
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
// an htlc where we don't offer an htlc anymore.
outgoingCltvRejectDelta uint32
// remoteLocalFeatures is the local feature vector received from the
// peer during the connection handshake.
remoteLocalFeatures *lnwire.FeatureVector
// remoteGlobalFeatures is the global feature vector received from the
// peer during the connection handshake.
remoteGlobalFeatures *lnwire.FeatureVector
// remoteFeatures is the feature vector received from the peer during
// the connection handshake.
remoteFeatures *lnwire.FeatureVector
// failedChannels is a set that tracks channels we consider `failed`.
// 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.
func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server,
addr *lnwire.NetAddress, inbound bool,
localFeatures *lnwire.RawFeatureVector,
features, legacyFeatures *lnwire.FeatureVector,
chanActiveTimeout time.Duration,
outgoingCltvRejectDelta uint32) (
*peer, error) {
@ -252,7 +255,8 @@ func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server,
server: server,
localFeatures: localFeatures,
features: features,
legacyFeatures: legacyFeatures,
outgoingCltvRejectDelta: outgoingCltvRejectDelta,
@ -399,7 +403,7 @@ func (p *peer) initGossipSync() {
// If the remote peer knows of the new gossip queries feature, then
// 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",
p.pubKeyBytes[:])
@ -2387,62 +2391,63 @@ func (p *peer) WipeChannel(chanPoint *wire.OutPoint) error {
// handleInitMsg handles the incoming init message which contains global and
// local features vectors. If feature vectors are incompatible then disconnect.
func (p *peer) handleInitMsg(msg *lnwire.Init) error {
p.remoteLocalFeatures = lnwire.NewFeatureVector(
msg.LocalFeatures, lnwire.LocalFeatures,
)
p.remoteGlobalFeatures = lnwire.NewFeatureVector(
msg.GlobalFeatures, lnwire.GlobalFeatures,
// First, merge any features from the legacy global features field into
// those presented in the local features fields.
err := msg.Features.Merge(msg.GlobalFeatures)
if err != nil {
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
// didn't set any required bits that we don't know of.
unknownLocalFeatures := p.remoteLocalFeatures.UnknownRequiredFeatures()
if len(unknownLocalFeatures) > 0 {
err := fmt.Errorf("Peer set unknown local feature bits: %v",
unknownLocalFeatures)
return err
}
unknownGlobalFeatures := p.remoteGlobalFeatures.UnknownRequiredFeatures()
if len(unknownGlobalFeatures) > 0 {
err := fmt.Errorf("Peer set unknown global feature bits: %v",
unknownGlobalFeatures)
unknownFeatures := p.remoteFeatures.UnknownRequiredFeatures()
if len(unknownFeatures) > 0 {
err := fmt.Errorf("peer set unknown feature bits: %v",
unknownFeatures)
return err
}
// 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.
switch {
case !p.remoteLocalFeatures.HasFeature(lnwire.DataLossProtectRequired):
case !p.remoteFeatures.HasFeature(lnwire.DataLossProtectRequired):
return fmt.Errorf("data loss protection required")
}
return nil
}
// LocalGlobalFeatures returns the set of global features that has been
// advertised by the local node. This allows sub-systems that use this
// interface to gate their behavior off the set of negotiated feature bits.
// LocalFeatures returns the set of global features that has been advertised by
// the local node. This allows sub-systems that use this interface to gate their
// behavior off the set of negotiated feature bits.
//
// NOTE: Part of the lnpeer.Peer interface.
func (p *peer) LocalGlobalFeatures() *lnwire.FeatureVector {
return p.server.globalFeatures
func (p *peer) LocalFeatures() *lnwire.FeatureVector {
return p.features
}
// RemoteGlobalFeatures returns the set of global features that has been
// advertised by the remote node. This allows sub-systems that use this
// interface to gate their behavior off the set of negotiated feature bits.
// RemoteFeatures returns the set of global features that has been advertised by
// the remote node. This allows sub-systems that use this interface to gate
// their behavior off the set of negotiated feature bits.
//
// NOTE: Part of the lnpeer.Peer interface.
func (p *peer) RemoteGlobalFeatures() *lnwire.FeatureVector {
return p.remoteGlobalFeatures
func (p *peer) RemoteFeatures() *lnwire.FeatureVector {
return p.remoteFeatures
}
// sendInitMsg sends init message to remote peer which contains our currently
// supported local and global features.
func (p *peer) sendInitMsg() error {
msg := lnwire.NewInitMessage(
p.server.globalFeatures.RawFeatureVector,
p.localFeatures,
p.legacyFeatures.RawFeatureVector,
p.features.RawFeatureVector,
)
return p.writeMessage(msg)

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

@ -35,6 +35,7 @@ import (
"github.com/lightningnetwork/lnd/channelnotifier"
"github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/input"
@ -232,9 +233,9 @@ type server struct {
readPool *pool.Read
// globalFeatures feature vector which affects HTLCs and thus are also
// advertised to other nodes.
globalFeatures *lnwire.FeatureVector
// featureMgr dispatches feature vectors for various contexts within the
// daemon.
featureMgr *feature.Manager
// currentNodeAnn is the node announcement that has been broadcast to
// 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,
)
featureMgr, err := feature.NewManager(feature.Config{
NoTLVOnion: cfg.LegacyProtocol.LegacyOnion(),
NoStaticRemoteKey: cfg.LegacyProtocol.LegacyCommitment(),
})
if err != nil {
return nil, err
}
s := &server{
chanDB: chanDB,
cc: cc,
@ -405,10 +414,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer),
peerDisconnectedListeners: make(map[string][]chan<- struct{}),
globalFeatures: lnwire.NewFeatureVector(
globalFeatures, lnwire.GlobalFeatures,
),
quit: make(chan struct{}),
featureMgr: featureMgr,
quit: make(chan struct{}),
}
s.witnessBeacon = &preimageBeacon{
@ -594,7 +601,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
LastUpdate: time.Now(),
Addresses: selfAddrs,
Alias: nodeAlias.String(),
Features: s.globalFeatures,
Features: s.featureMgr.Get(feature.SetNodeAnn),
Color: color,
}
copy(selfNode.PubKeyBytes[:], privKey.PubKey().SerializeCompressed())
@ -1965,7 +1972,7 @@ func (s *server) initTorController() error {
Addresses: newNodeAnn.Addresses,
Alias: newNodeAnn.Alias.String(),
Features: lnwire.NewFeatureVector(
newNodeAnn.Features, lnwire.GlobalFeatures,
newNodeAnn.Features, lnwire.Features,
),
Color: newNodeAnn.RGBColor,
AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(),
@ -2728,14 +2735,10 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
ChainNet: activeNetParams.Net,
}
// With the brontide connection established, we'll now craft the local
// feature vector to advertise to the remote node.
localFeatures := lnwire.NewRawFeatureVector()
// 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)
// With the brontide connection established, we'll now craft the feature
// vectors to advertise to the remote node.
initFeatures := s.featureMgr.Get(feature.SetInit)
legacyFeatures := s.featureMgr.Get(feature.SetLegacyGlobal)
// 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
@ -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
// closed when the htlc is outstanding and a new block comes in.
p, err := newPeer(
conn, connReq, s, peerAddr, inbound, localFeatures,
cfg.ChanEnableTimeout,
conn, connReq, s, peerAddr, inbound, initFeatures,
legacyFeatures, cfg.ChanEnableTimeout,
defaultOutgoingCltvRejectDelta,
)
if err != nil {