From 16318c5a41fa52d900dc143d26bb082f1effef0e Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 8 Nov 2019 05:31:47 -0800 Subject: [PATCH] multi: merge local+global features from remote peer --- discovery/mock_test.go | 4 +-- fundingmanager.go | 8 +++--- fundingmanager_test.go | 4 +-- htlcswitch/link_test.go | 4 +-- htlcswitch/mock.go | 4 +-- lnpeer/peer.go | 15 +++++----- lnwire/features.go | 11 +++++++ lnwire/init_message.go | 24 ++++++++++------ peer.go | 63 ++++++++++++++++++++--------------------- 9 files changed, 75 insertions(+), 62 deletions(-) diff --git a/discovery/mock_test.go b/discovery/mock_test.go index 57fc319a..f3f707d5 100644 --- a/discovery/mock_test.go +++ b/discovery/mock_test.go @@ -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 } diff --git a/fundingmanager.go b/fundingmanager.go index f33ce498..ca7959d0 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -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 diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 024339d9..09bbe660 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -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) } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 93eb3763..96e2430b 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -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 } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 3dc1160d..c63aa171 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -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 } diff --git a/lnpeer/peer.go b/lnpeer/peer.go index e21e2796..53f57131 100644 --- a/lnpeer/peer.go +++ b/lnpeer/peer.go @@ -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 } diff --git a/lnwire/features.go b/lnwire/features.go index 6843086c..7ae0cac9 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -143,6 +143,17 @@ 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() diff --git a/lnwire/init_message.go b/lnwire/init_message.go index f64f9f8c..0236a71f 100644 --- a/lnwire/init_message.go +++ b/lnwire/init_message.go @@ -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, ) } diff --git a/peer.go b/peer.go index a46a1b7d..a007701b 100644 --- a/peer.go +++ b/peer.go @@ -205,13 +205,9 @@ type peer struct { // 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 @@ -407,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[:]) @@ -2395,54 +2391,55 @@ 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 { +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