commit
b222b6e625
@ -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
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
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)
|
||||
}
|
128
feature/manager_internal_test.go
Normal file
128
feature/manager_internal_test.go
Normal file
@ -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
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
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,
|
||||
|
39
server.go
39
server.go
@ -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,9 +414,7 @@ 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,
|
||||
),
|
||||
featureMgr: featureMgr,
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user