Merge pull request #7568 from carlaKC/7094-updatefeaturemanager

Add config options for custom feature bits
This commit is contained in:
Oliver Gugger 2023-05-04 18:35:23 +02:00 committed by GitHub
commit 728452636a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 844 additions and 145 deletions

View file

@ -76,7 +76,7 @@ var updateNodeAnnouncementCommand = cli.Command{
},
cli.Int64SliceFlag{
Name: "feature_bit_remove",
Usage: "a feature bit that needs to be disabled" +
Usage: "a feature bit that needs to be disabled. " +
"Can be set multiple times in the same command",
},
},

View file

@ -203,11 +203,14 @@ type Config struct {
// notification for when it reconnects.
NotifyWhenOffline func(peerPubKey [33]byte) <-chan struct{}
// SelfNodeAnnouncement is a function that fetches our own current node
// announcement, for use when determining whether we should update our
// peers about our presence on the network. If the refresh is true, a
// new and updated announcement will be returned.
SelfNodeAnnouncement func(refresh bool) (lnwire.NodeAnnouncement, error)
// FetchSelfAnnouncement retrieves our current node announcement, for
// use when determining whether we should update our peers about our
// presence in the network.
FetchSelfAnnouncement func() lnwire.NodeAnnouncement
// UpdateSelfAnnouncement produces a new announcement for our node with
// an updated timestamp which can be broadcast to our peers.
UpdateSelfAnnouncement func() (lnwire.NodeAnnouncement, error)
// ProofMatureDelta the number of confirmations which is needed before
// exchange the channel announcement proofs.
@ -1660,12 +1663,7 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error {
}
// We'll also check that our NodeAnnouncement is not too old.
currentNodeAnn, err := d.cfg.SelfNodeAnnouncement(false)
if err != nil {
return fmt.Errorf("unable to get current node announment: %v",
err)
}
currentNodeAnn := d.cfg.FetchSelfAnnouncement()
timestamp := time.Unix(int64(currentNodeAnn.Timestamp), 0)
timeElapsed := now.Sub(timestamp)
@ -1673,7 +1671,7 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error {
// node announcement, refresh it and resend it.
nodeAnnStr := ""
if timeElapsed >= d.cfg.RebroadcastInterval {
newNodeAnn, err := d.cfg.SelfNodeAnnouncement(true)
newNodeAnn, err := d.cfg.UpdateSelfAnnouncement()
if err != nil {
return fmt.Errorf("unable to get refreshed node "+
"announcement: %v", err)

View file

@ -775,7 +775,14 @@ func createTestCtx(t *testing.T, startHeight uint32) (*testCtx, error) {
c := make(chan struct{})
return c
},
SelfNodeAnnouncement: func(bool) (lnwire.NodeAnnouncement, error) {
FetchSelfAnnouncement: func() lnwire.NodeAnnouncement {
return lnwire.NodeAnnouncement{
Timestamp: testTimestamp,
}
},
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement,
error) {
return lnwire.NodeAnnouncement{
Timestamp: testTimestamp,
}, nil
@ -1446,28 +1453,30 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
return lnwire.ShortChannelID{}, fmt.Errorf("no peer alias")
}
//nolint:lll
gossiper := New(Config{
Notifier: ctx.gossiper.cfg.Notifier,
Broadcast: ctx.gossiper.cfg.Broadcast,
NotifyWhenOnline: ctx.gossiper.reliableSender.cfg.NotifyWhenOnline,
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
SelfNodeAnnouncement: ctx.gossiper.cfg.SelfNodeAnnouncement,
Router: ctx.gossiper.cfg.Router,
TrickleDelay: trickleDelay,
RetransmitTicker: ticker.NewForce(retransmitDelay),
RebroadcastInterval: rebroadcastInterval,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: ctx.gossiper.cfg.WaitingProofStore,
MessageStore: ctx.gossiper.cfg.MessageStore,
RotateTicker: ticker.NewForce(DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.NewForce(DefaultHistoricalSyncInterval),
NumActiveSyncers: 3,
MinimumBatchSize: 10,
SubBatchDelay: time.Second * 5,
IsAlias: isAlias,
SignAliasUpdate: signAliasUpdate,
FindBaseByAlias: findBaseByAlias,
GetAlias: getAlias,
Notifier: ctx.gossiper.cfg.Notifier,
Broadcast: ctx.gossiper.cfg.Broadcast,
NotifyWhenOnline: ctx.gossiper.reliableSender.cfg.NotifyWhenOnline,
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
FetchSelfAnnouncement: ctx.gossiper.cfg.FetchSelfAnnouncement,
UpdateSelfAnnouncement: ctx.gossiper.cfg.UpdateSelfAnnouncement,
Router: ctx.gossiper.cfg.Router,
TrickleDelay: trickleDelay,
RetransmitTicker: ticker.NewForce(retransmitDelay),
RebroadcastInterval: rebroadcastInterval,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: ctx.gossiper.cfg.WaitingProofStore,
MessageStore: ctx.gossiper.cfg.MessageStore,
RotateTicker: ticker.NewForce(DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.NewForce(DefaultHistoricalSyncInterval),
NumActiveSyncers: 3,
MinimumBatchSize: 10,
SubBatchDelay: time.Second * 5,
IsAlias: isAlias,
SignAliasUpdate: signAliasUpdate,
FindBaseByAlias: findBaseByAlias,
GetAlias: getAlias,
}, &keychain.KeyDescriptor{
PubKey: ctx.gossiper.selfKey,
KeyLocator: ctx.gossiper.selfKeyLoc,

View file

@ -18,14 +18,25 @@
* [SendOutputs](https://github.com/lightningnetwork/lnd/pull/7631) now adheres
to the anchor channel reserve requirement.
* The [UpdateNodeAnnouncement](https://github.com/lightningnetwork/lnd/pull/7568)
API can no longer be used to set/unset protocol features that are defined by
LND.
Custom node announcement feature bits can also be specified in config using
the `dev` build tag and `--protocol.custom-nodeann`, `--protocol.custom-init`
and `--protocol.custom-invoice` flags to set feature bits for various feature
"sets", as defined in [BOLT 9](https://github.com/lightning/bolts/blob/master/09-features.md).
## Misc
* [Generate default macaroons
independently](https://github.com/lightningnetwork/lnd/pull/7592) on wallet
unlock or create.
# Contributors (Alphabetical Order)
* Carla Kirk-Cohen
* Daniel McNally
* Elle Mouton
* hieblmi

View file

@ -1,11 +1,22 @@
package feature
import (
"errors"
"fmt"
"github.com/lightningnetwork/lnd/lnwire"
)
var (
// ErrUnknownSet is returned if a proposed feature vector contains a
// set that is unknown to LND.
ErrUnknownSet = errors.New("unknown feature bit set")
// ErrFeatureConfigured is returned if an attempt is made to unset a
// feature that was configured at startup.
ErrFeatureConfigured = errors.New("can't unset configured feature")
)
// 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.
@ -44,6 +55,10 @@ type Config struct {
// NoAnySegwit unsets any bits that signal support for using other
// segwit witness versions for co-op closes.
NoAnySegwit bool
// CustomFeatures is a set of custom features to advertise in each
// set.
CustomFeatures map[Set][]lnwire.FeatureBit
}
// Manager is responsible for generating feature vectors for different requested
@ -52,6 +67,11 @@ type Manager struct {
// fsets is a static map of feature set to raw feature vectors. Requests
// are fulfilled by cloning these internal feature vectors.
fsets map[Set]*lnwire.RawFeatureVector
// configFeatures is a set of custom features that were "hard set" in
// lnd's config that cannot be updated at runtime (as is the case with
// our "standard" features that are defined in LND).
configFeatures map[Set]*lnwire.FeatureVector
}
// NewManager creates a new feature Manager, applying any custom modifications
@ -90,6 +110,7 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
}
// Now, remove any features as directed by the config.
configFeatures := make(map[Set]*lnwire.FeatureVector)
for set, raw := range fsets {
if cfg.NoTLVOnion {
raw.Unset(lnwire.TLVOnionPayloadOptional)
@ -151,6 +172,32 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.ShutdownAnySegwitRequired)
}
for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() {
return nil, fmt.Errorf("feature bit: %v "+
"exceeds set: %v maximum: %v", custom,
set, set.Maximum())
}
if raw.IsSet(custom) {
return nil, fmt.Errorf("feature bit: %v "+
"already set", custom)
}
if err := raw.SafeSet(custom); err != nil {
return nil, fmt.Errorf("%w: could not set "+
"feature: %d", err, custom)
}
}
// Track custom features separately so that we can check that
// they aren't unset in subsequent updates. If there is no
// entry for the set, the vector will just be empty.
configFeatures[set] = lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(cfg.CustomFeatures[set]...),
lnwire.Features,
)
// Ensure that all of our feature sets properly set any
// dependent features.
fv := lnwire.NewFeatureVector(raw, lnwire.Features)
@ -162,7 +209,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
}
return &Manager{
fsets: fsets,
fsets: fsets,
configFeatures: configFeatures,
}, nil
}
@ -176,8 +224,8 @@ func (m *Manager) GetRaw(set Set) *lnwire.RawFeatureVector {
return lnwire.NewRawFeatureVector()
}
// SetRaw sets a new raw feature vector for the given set.
func (m *Manager) SetRaw(set Set, raw *lnwire.RawFeatureVector) {
// setRaw sets a new raw feature vector for the given set.
func (m *Manager) setRaw(set Set, raw *lnwire.RawFeatureVector) {
m.fsets[set] = raw
}
@ -198,3 +246,54 @@ func (m *Manager) ListSets() []Set {
return sets
}
// UpdateFeatureSets accepts a map of new feature vectors for each of the
// manager's known sets, validates that the update can be applied and modifies
// the feature manager's internal state. If a set is not included in the update
// map, it is left unchanged. The feature vectors provided are expected to
// include the current set of features, updated with desired bits added/removed.
func (m *Manager) UpdateFeatureSets(
updates map[Set]*lnwire.RawFeatureVector) error {
for set, newFeatures := range updates {
if !set.valid() {
return fmt.Errorf("%w: set: %d", ErrUnknownSet, set)
}
if err := newFeatures.ValidatePairs(); err != nil {
return err
}
if err := m.Get(set).ValidateUpdate(
newFeatures, set.Maximum(),
); err != nil {
return err
}
// If any features were configured for this set, ensure that
// they are still set in the new feature vector.
if cfgFeat, haveCfgFeat := m.configFeatures[set]; haveCfgFeat {
for feature := range cfgFeat.Features() {
if !newFeatures.IsSet(feature) {
return fmt.Errorf("%w: can't unset: "+
"%d", ErrFeatureConfigured,
feature)
}
}
}
fv := lnwire.NewFeatureVector(newFeatures, lnwire.Features)
if err := ValidateDeps(fv); err != nil {
return err
}
}
// Only update the current feature sets once every proposed set has
// passed validation so that we don't partially update any sets then
// fail out on a later set's validation.
for set, features := range updates {
m.setRaw(set, features.Clone())
}
return nil
}

View file

@ -135,3 +135,157 @@ func testManager(t *testing.T, test managerTest) {
assertSet(lnwire.StaticRemoteKeyOptional)
}
}
// TestUpdateFeatureSets tests validation of the update of various features in
// each of our sets, asserting that the feature set is not partially modified
// if one set in incorrectly specified.
func TestUpdateFeatureSets(t *testing.T) {
t.Parallel()
// Use a reduced set description to make reasoning about our sets
// easier.
setDesc := setDesc{
lnwire.DataLossProtectRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.GossipQueriesOptional: {
SetNodeAnn: {}, // N
},
}
testCases := []struct {
name string
features map[Set]*lnwire.RawFeatureVector
config Config
err error
}{
{
name: "unknown set",
features: map[Set]*lnwire.RawFeatureVector{
setSentinel + 1: lnwire.NewRawFeatureVector(),
},
err: ErrUnknownSet,
},
{
name: "invalid pairwise feature",
features: map[Set]*lnwire.RawFeatureVector{
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1000),
lnwire.FeatureBit(1001),
),
},
err: lnwire.ErrFeaturePairExists,
},
{
name: "error in one set",
features: map[Set]*lnwire.RawFeatureVector{
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1000),
lnwire.FeatureBit(1001),
),
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
},
err: lnwire.ErrFeaturePairExists,
},
{
name: "update existing sets ok",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.FeatureBit(1001),
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
lnwire.FeatureBit(1000),
),
},
},
{
name: "update new, valid set ok",
features: map[Set]*lnwire.RawFeatureVector{
SetInvoice: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1001),
),
},
},
{
name: "missing configured feature",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
),
},
config: Config{
CustomFeatures: map[Set][]lnwire.FeatureBit{
SetInit: {
lnwire.FeatureBit(333),
},
},
},
err: ErrFeatureConfigured,
},
{
name: "valid",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
lnwire.FeatureBit(500),
),
SetInvoice: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(333),
),
},
config: Config{
CustomFeatures: map[Set][]lnwire.FeatureBit{
SetInvoice: {
lnwire.FeatureBit(333),
},
},
},
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
featureMgr, err := newManager(testCase.config, setDesc)
require.NoError(t, err)
err = featureMgr.UpdateFeatureSets(testCase.features)
require.ErrorIs(t, err, testCase.err)
// Compare the feature manager's sets to the updated
// set if no error was hit, otherwise assert that it
// is unchanged.
expected := testCase.features
actual := featureMgr
if err != nil {
originalMgr, err := newManager(
testCase.config, setDesc,
)
require.NoError(t, err)
expected = originalMgr.fsets
}
for set, expectedFeatures := range expected {
actualSet := actual.GetRaw(set)
require.True(t,
actualSet.Equals(expectedFeatures))
}
})
}
}

View file

@ -1,5 +1,11 @@
package feature
import (
"math"
"github.com/lightningnetwork/lnd/lnwire"
)
// 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.
@ -26,8 +32,19 @@ const (
// SetInvoiceAmp identifies the features that should be advertised on
// AMP invoices generated by the daemon.
SetInvoiceAmp
// setSentinel is used to mark the end of our known sets. This enum
// member must *always* be the last item in the iota list to ensure
// that validation works as expected.
setSentinel
)
// valid returns a boolean indicating whether a set value is one of our
// predefined feature sets.
func (s Set) valid() bool {
return s < setSentinel
}
// String returns a human-readable description of a Set.
func (s Set) String() string {
switch s {
@ -45,3 +62,22 @@ func (s Set) String() string {
return "SetUnknown"
}
}
// Maximum returns the maximum allowable value for a feature bit in the context
// of a set. The maximum feature value we can express differs by set context
// because the amount of space available varies between protocol messages. In
// practice this should never be a problem (reasonably one would never hit
// these high ranges), but we enforce these maximums for the sake of sane
// validation.
func (s Set) Maximum() lnwire.FeatureBit {
switch s {
case SetInvoice, SetInvoiceAmp:
return lnwire.MaxBolt11Feature
// The space available in other sets is > math.MaxUint16, so we just
// return the maximum value our expression of a feature bit allows so
// that any value will pass.
default:
return math.MaxUint16
}
}

View file

@ -373,7 +373,8 @@ type Config struct {
msg []byte, doubleHash bool) (*ecdsa.Signature, error)
// CurrentNodeAnnouncement should return the latest, fully signed node
// announcement from the backing Lightning Network node.
// announcement from the backing Lightning Network node with a fresh
// timestamp.
CurrentNodeAnnouncement func() (lnwire.NodeAnnouncement, error)
// SendAnnouncement is used by the FundingManager to send announcement

View file

@ -531,4 +531,8 @@ var allTestCases = []*lntest.TestCase{
Name: "htlc timeout resolver extract preimage local",
TestFunc: testHtlcTimeoutResolverExtractPreimageLocal,
},
{
Name: "custom features",
TestFunc: testCustomFeatures,
},
}

View file

@ -14,6 +14,7 @@ import (
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/node"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
@ -491,10 +492,29 @@ func testUpdateNodeAnnouncement(ht *lntest.HarnessTest) {
)
}
// This feature bit is used to test that our endpoint sets/unsets
// feature bits properly. If the current FeatureBit is set by default
// update this one for another one unset by default at random.
featureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ
// Test that we can't modify a feature bit that is known to LND.
reservedFeatureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ
_, known := lnwire.Features[lnwire.FeatureBit(reservedFeatureBit)]
require.True(ht, known, "feature bit should be known to lnd")
nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
FeatureUpdates: []*peersrpc.UpdateFeatureAction{
{
Action: peersrpc.UpdateAction_ADD,
FeatureBit: reservedFeatureBit,
},
},
}
// Node announcement should fail because we're not allowed to alter
// "known" feature bits.
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
// We also set an unknown feature bit (which we should be able to
// set/unset) and test that we can update it accordingly.
featureBit := lnrpc.FeatureBit(999)
_, known = lnwire.Features[lnwire.FeatureBit(featureBit)]
require.False(ht, known, "feature bit should be unknown to lnd")
featureIdx := uint32(featureBit)
_, ok := resp.Features[featureIdx]
require.False(ht, ok, "unexpected feature bit enabled by default")
@ -566,7 +586,7 @@ func testUpdateNodeAnnouncement(ht *lntest.HarnessTest) {
},
}
nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
Alias: newAlias,
Color: newColor,
AddressUpdates: updateAddressActions,

View file

@ -0,0 +1,96 @@
package itest
import (
"fmt"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/stretchr/testify/require"
)
// testCustomFeatures tests advertisement of custom features in various bolt 9
// sets. For completeness, it also asserts that features aren't set in places
// where they aren't intended to be.
func testCustomFeatures(ht *lntest.HarnessTest) {
var (
// Odd custom features so that we don't need to worry about
// issues connecting to peers.
customInit uint32 = 101
customNodeAnn uint32 = 103
customInvoice uint32 = 105
)
// Restart Alice with custom feature bits configured.
extraArgs := []string{
fmt.Sprintf("--protocol.custom-init=%v", customInit),
fmt.Sprintf("--protocol.custom-nodeann=%v", customNodeAnn),
fmt.Sprintf("--protocol.custom-invoice=%v", customInvoice),
}
ht.RestartNodeWithExtraArgs(ht.Alice, extraArgs)
// Connect nodes and open a channel so that Alice will be included
// in Bob's graph.
ht.ConnectNodes(ht.Alice, ht.Bob)
chanPoint := ht.OpenChannel(
ht.Alice, ht.Bob, lntest.OpenChannelParams{Amt: 1000000},
)
// Check that Alice's custom feature bit was sent to Bob in her init
// message.
peers := ht.Bob.RPC.ListPeers()
require.Len(ht, peers.Peers, 1)
require.Equal(ht, peers.Peers[0].PubKey, ht.Alice.PubKeyStr)
_, customInitSet := peers.Peers[0].Features[customInit]
require.True(ht, customInitSet)
assertFeatureNotInSet(
ht, []uint32{customNodeAnn, customInvoice},
peers.Peers[0].Features,
)
// Assert that Alice's custom feature bit is contained in the node
// announcement sent to Bob.
updates := ht.AssertNumNodeAnns(ht.Bob, ht.Alice.PubKeyStr, 1)
features := updates[len(updates)-1].Features
_, customFeature := features[customNodeAnn]
require.True(ht, customFeature)
assertFeatureNotInSet(
ht, []uint32{customInit, customInvoice}, features,
)
// Assert that Alice's custom feature bit is included in invoices.
invoice := ht.Alice.RPC.AddInvoice(&lnrpc.Invoice{})
payReq := ht.Alice.RPC.DecodePayReq(invoice.PaymentRequest)
_, customInvoiceSet := payReq.Features[customInvoice]
require.True(ht, customInvoiceSet)
assertFeatureNotInSet(
ht, []uint32{customInit, customNodeAnn}, payReq.Features,
)
// Check that Alice can't unset configured features via the node
// announcement update API. This is only checked for node announcement
// because it is the only set that can be updated via the API.
nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
FeatureUpdates: []*peersrpc.UpdateFeatureAction{
{
Action: peersrpc.UpdateAction_ADD,
FeatureBit: lnrpc.FeatureBit(customNodeAnn),
},
},
}
ht.Alice.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
ht.CloseChannel(ht.Alice, chanPoint)
}
// assertFeatureNotInSet checks that the features provided aren't contained in
// a feature map.
func assertFeatureNotInSet(ht *lntest.HarnessTest, features []uint32,
advertised map[uint32]*lnrpc.Feature) {
for _, feature := range features {
_, featureSet := advertised[feature]
require.False(ht, featureSet)
}
}

View file

@ -3,6 +3,11 @@
package lncfg
import (
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
)
// ExperimentalProtocol is a sub-config that houses any experimental protocol
// features that also require a build-tag to activate.
type ExperimentalProtocol struct {
@ -13,3 +18,8 @@ type ExperimentalProtocol struct {
func (p ExperimentalProtocol) CustomMessageOverrides() []uint16 {
return nil
}
// CustomFeatures returns a custom set of feature bits to advertise.
func (p ExperimentalProtocol) CustomFeatures() map[feature.Set][]lnwire.FeatureBit {
return map[feature.Set][]lnwire.FeatureBit{}
}

View file

@ -3,12 +3,23 @@
package lncfg
import (
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
)
// ExperimentalProtocol is a sub-config that houses any experimental protocol
// features that also require a build-tag to activate.
//
//nolint:lll
type ExperimentalProtocol struct {
CustomMessage []uint16 `long:"custom-message" description:"allows the custom message apis to send and report messages with the protocol number provided that fall outside of the custom message number range."`
CustomInit []uint16 `long:"custom-init" description:"custom feature bits to advertise in the node's init message"`
CustomNodeAnn []uint16 `long:"custom-nodeann" description:"custom feature bits to advertise in the node's announcement message"`
CustomInvoice []uint16 `long:"custom-invoice" description:"custom feature bits to advertise in the node's invoices"`
}
// CustomMessageOverrides returns the set of protocol messages that we override
@ -16,3 +27,25 @@ type ExperimentalProtocol struct {
func (p ExperimentalProtocol) CustomMessageOverrides() []uint16 {
return p.CustomMessage
}
// CustomFeatures returns a custom set of feature bits to advertise.
//
//nolint:lll
func (p ExperimentalProtocol) CustomFeatures() map[feature.Set][]lnwire.FeatureBit {
customFeatures := make(map[feature.Set][]lnwire.FeatureBit)
setFeatures := func(set feature.Set, bits []uint16) {
for _, customFeature := range bits {
customFeatures[set] = append(
customFeatures[set],
lnwire.FeatureBit(customFeature),
)
}
}
setFeatures(feature.SetInit, p.CustomInit)
setFeatures(feature.SetNodeAnn, p.CustomNodeAnn)
setFeatures(feature.SetInvoice, p.CustomInvoice)
return customFeatures
}

View file

@ -18,12 +18,15 @@ import (
type Config struct {
// GetNodeAnnouncement is used to send our retrieve the current
// node announcement information.
GetNodeAnnouncement func() (lnwire.NodeAnnouncement, error)
GetNodeAnnouncement func() lnwire.NodeAnnouncement
// ParseAddr parses an address from its string format to a net.Addr.
ParseAddr func(addr string) (net.Addr, error)
// UpdateNodeAnnouncement updates our node announcement applying the
// given NodeAnnModifiers and broadcasts the new version to the network.
UpdateNodeAnnouncement func(...netann.NodeAnnModifier) error
// UpdateNodeAnnouncement updates and broadcasts our node announcement,
// setting the feature vector provided and applying the
// NodeAnnModifiers. If no feature updates are required, a nil feature
// vector should be provided.
UpdateNodeAnnouncement func(features *lnwire.RawFeatureVector,
mods ...netann.NodeAnnModifier) error
}

View file

@ -313,26 +313,23 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context,
resp := &NodeAnnouncementUpdateResponse{}
nodeModifiers := make([]netann.NodeAnnModifier, 0)
currentNodeAnn, err := s.cfg.GetNodeAnnouncement()
if err != nil {
return nil, fmt.Errorf("unable to get current node "+
"announcement: %v", err)
}
currentNodeAnn := s.cfg.GetNodeAnnouncement()
if len(req.FeatureUpdates) > 0 {
features, ops, err := s.updateFeatures(
currentNodeAnn.Features,
req.FeatureUpdates,
nodeAnnFeatures := currentNodeAnn.Features
featureUpdates := len(req.FeatureUpdates) > 0
if featureUpdates {
var (
ops *lnrpc.Op
err error
)
nodeAnnFeatures, ops, err = s.updateFeatures(
nodeAnnFeatures, req.FeatureUpdates,
)
if err != nil {
return nil, fmt.Errorf("error trying to update node "+
"features: %w", err)
}
resp.Ops = append(resp.Ops, ops)
nodeModifiers = append(
nodeModifiers,
netann.NodeAnnSetFeatures(features),
)
}
if req.Color != "" {
@ -390,12 +387,14 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context,
)
}
if len(nodeModifiers) == 0 {
return nil, fmt.Errorf("unable detect any new values to " +
if len(nodeModifiers) == 0 && !featureUpdates {
return nil, fmt.Errorf("unable to detect any new values to " +
"update the node announcement")
}
if err := s.cfg.UpdateNodeAnnouncement(nodeModifiers...); err != nil {
if err := s.cfg.UpdateNodeAnnouncement(
nodeAnnFeatures, nodeModifiers...,
); err != nil {
return nil, err
}

View file

@ -3,6 +3,7 @@ package lnwire
import (
"encoding/binary"
"errors"
"fmt"
"io"
)
@ -10,6 +11,15 @@ 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")
// ErrFeatureStandard is returned when attempts to modify LND's known
// set of features are made.
ErrFeatureStandard = errors.New("feature is used in standard " +
"protocol set")
// ErrFeatureBitMaximum is returned when a feature bit exceeds the
// maximum allowable value.
ErrFeatureBitMaximum = errors.New("feature bit exceeds allowed maximum")
)
// FeatureBit represents a feature that can be enabled in either a local or
@ -221,17 +231,18 @@ const (
// TODO: Decide on actual feature bit value.
ScriptEnforcedLeaseOptional FeatureBit = 2023
// maxAllowedSize is a maximum allowed size of feature vector.
// MaxBolt11Feature is the maximum feature bit value allowed in bolt 11
// invoices.
//
// NOTE: Within the protocol, the maximum allowed message size is 65535
// bytes for all messages. Accounting for the overhead within the feature
// message to signal the type of message, that leaves us with 65533 bytes
// for the init message itself. Next, we reserve 4 bytes to encode the
// lengths of both the local and global feature vectors, so 65529 bytes
// for the local and global features. Knocking off one byte for the sake
// of the calculation, that leads us to 32764 bytes for each feature
// vector, or 131056 different features.
maxAllowedSize = 32764
// The base 32 encoded tagged fields in invoices are limited to 10 bits
// to express the length of the field's data.
//nolint:lll
// See: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md#tagged-fields
//
// With a maximum length field of 1023 (2^10 -1) and 5 bit encoding,
// the highest feature bit that can be expressed is:
// 1023 * 5 - 1 = 5114.
MaxBolt11Feature = 5114
)
// IsRequired returns true if the feature bit is even, and false otherwise.
@ -344,6 +355,67 @@ func (fv *RawFeatureVector) Merge(other *RawFeatureVector) error {
return nil
}
// ValidateUpdate checks whether a feature vector can safely be updated to the
// new feature vector provided, checking that it does not alter any of the
// "standard" features that are defined by LND. The new feature vector should
// be inclusive of all features in the original vector that it still wants to
// advertise, setting and unsetting updates as desired. Features in the vector
// are also checked against a maximum inclusive value, as feature vectors in
// different contexts have different maximum values.
func (fv *RawFeatureVector) ValidateUpdate(other *RawFeatureVector,
maximumValue FeatureBit) error {
// Run through the new set of features and check that we're not adding
// any feature bits that are defined but not set in LND.
for feature := range other.features {
if fv.IsSet(feature) {
continue
}
if feature > maximumValue {
return fmt.Errorf("can't set feature bit %d: %w %v",
feature, ErrFeatureBitMaximum,
maximumValue)
}
if name, known := Features[feature]; known {
return fmt.Errorf("can't set feature "+
"bit %d (%v): %w", feature, name,
ErrFeatureStandard)
}
}
// Check that the new feature vector for this set does not unset any
// features that are standard in LND by comparing the features in our
// current set to the omitted values in the new set.
for feature := range fv.features {
if other.IsSet(feature) {
continue
}
if name, known := Features[feature]; known {
return fmt.Errorf("can't unset feature "+
"bit %d (%v): %w", feature, name,
ErrFeatureStandard)
}
}
return nil
}
// ValidatePairs checks each feature bit in a raw vector to ensure that the
// opposing bit is not set, validating that the vector has either the optional
// or required bit set, not both.
func (fv *RawFeatureVector) ValidatePairs() error {
for feature := range fv.features {
if _, ok := fv.features[feature^1]; ok {
return ErrFeaturePairExists
}
}
return nil
}
// Clone makes a copy of a feature vector.
func (fv *RawFeatureVector) Clone() *RawFeatureVector {
newFeatures := NewRawFeatureVector()

View file

@ -2,6 +2,7 @@ package lnwire
import (
"bytes"
"math"
"reflect"
"sort"
"testing"
@ -396,3 +397,134 @@ func TestIsEmptyFeatureVector(t *testing.T) {
fv.Unset(StaticRemoteKeyOptional)
require.True(t, fv.IsEmpty())
}
// TestValidatePairs tests that feature vectors can only set the required or
// optional feature bit in a pair, not both.
func TestValidatePairs(t *testing.T) {
t.Parallel()
rfv := NewRawFeatureVector(
StaticRemoteKeyOptional,
StaticRemoteKeyRequired,
)
require.Equal(t, ErrFeaturePairExists, rfv.ValidatePairs())
rfv = NewRawFeatureVector(
StaticRemoteKeyOptional,
PaymentAddrRequired,
)
require.Nil(t, rfv.ValidatePairs())
}
// TestValidateUpdate tests validation of an update to a feature vector.
func TestValidateUpdate(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
currentFeatures []FeatureBit
newFeatures []FeatureBit
maximumValue FeatureBit
err error
}{
{
name: "defined feature bit set, can include",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: nil,
},
{
name: "defined feature bit not already set",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
PaymentAddrRequired,
},
err: ErrFeatureStandard,
},
{
name: "known feature missing",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
PaymentAddrRequired,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: ErrFeatureStandard,
},
{
name: "can set unknown feature",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
FeatureBit(1001),
},
err: nil,
},
{
name: "can unset unknown feature",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
FeatureBit(1001),
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: nil,
},
{
name: "at allowed maximum",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
100,
},
maximumValue: 100,
err: nil,
},
{
name: "above allowed maximum",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
101,
},
maximumValue: 100,
err: ErrFeatureBitMaximum,
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
currentFV := NewRawFeatureVector(
testCase.currentFeatures...,
)
newFV := NewRawFeatureVector(testCase.newFeatures...)
// Set maximum value if not populated in the test case.
maximumValue := testCase.maximumValue
if testCase.maximumValue == 0 {
maximumValue = math.MaxUint16
}
err := currentFV.ValidateUpdate(newFV, maximumValue)
require.ErrorIs(t, err, testCase.err)
})
}
}

View file

@ -168,15 +168,18 @@ func (h *HostAnnouncer) hostWatcher() {
// NodeAnnUpdater describes a function that's able to update our current node
// announcement on disk. It returns the updated node announcement given a set
// of updates to be applied to the current node announcement.
type NodeAnnUpdater func(refresh bool, modifier ...NodeAnnModifier,
type NodeAnnUpdater func(modifier ...NodeAnnModifier,
) (lnwire.NodeAnnouncement, error)
// IPAnnouncer is a factory function that generates a new function that uses
// the passed annUpdater function to to announce new IP changes for a given
// host.
func IPAnnouncer(annUpdater NodeAnnUpdater) func([]net.Addr, map[string]struct{}) error {
func IPAnnouncer(annUpdater NodeAnnUpdater) func([]net.Addr,
map[string]struct{}) error {
return func(newAddrs []net.Addr, oldAddrs map[string]struct{}) error {
_, err := annUpdater(true, func(currentNodeAnn *lnwire.NodeAnnouncement) {
_, err := annUpdater(nil, func(
currentNodeAnn *lnwire.NodeAnnouncement) {
// To ensure we don't duplicate any addresses, we'll
// filter out the same of addresses we should no longer
// advertise.

View file

@ -60,18 +60,11 @@ func NodeAnnSetTimestamp(nodeAnn *lnwire.NodeAnnouncement) {
nodeAnn.Timestamp = newTimestamp
}
// SignNodeAnnouncement applies the given modifies to the passed
// lnwire.NodeAnnouncement, then signs the resulting announcement. The provided
// update should be the most recent, valid update, otherwise the timestamp may
// not monotonically increase from the prior.
// SignNodeAnnouncement signs the lnwire.NodeAnnouncement provided, which
// should be the most recent, valid update, otherwise the timestamp may not
// monotonically increase from the prior.
func SignNodeAnnouncement(signer lnwallet.MessageSigner,
keyLoc keychain.KeyLocator, nodeAnn *lnwire.NodeAnnouncement,
mods ...NodeAnnModifier) error {
// Apply the requested changes to the node announcement.
for _, modifier := range mods {
modifier(nodeAnn)
}
keyLoc keychain.KeyLocator, nodeAnn *lnwire.NodeAnnouncement) error {
// Create the DER-encoded ECDSA signature over the message digest.
sig, err := SignAnnouncement(signer, keyLoc, nodeAnn)

View file

@ -263,8 +263,8 @@ type Config struct {
// GenNodeAnnouncement is used to send our node announcement to the remote
// on startup.
GenNodeAnnouncement func(bool,
...netann.NodeAnnModifier) (lnwire.NodeAnnouncement, error)
GenNodeAnnouncement func(...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error)
// PrunePersistentPeerConnection is used to remove all internal state
// related to this peer in the server.
@ -1036,7 +1036,7 @@ func (p *Brontide) maybeSendNodeAnn(channels []*channeldb.OpenChannel) {
return
}
ourNodeAnn, err := p.cfg.GenNodeAnnouncement(false)
ourNodeAnn, err := p.cfg.GenNodeAnnouncement()
if err != nil {
p.log.Debugf("Unable to retrieve node announcement: %v", err)
return

View file

@ -762,10 +762,6 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
return s.featureMgr.Get(feature.SetInvoiceAmp)
}
getNodeAnnouncement := func() (lnwire.NodeAnnouncement, error) {
return s.genNodeAnnouncement(false)
}
parseAddr := func(addr string) (net.Addr, error) {
return parseAddr(addr, r.cfg.net)
}
@ -786,7 +782,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
routerBackend, s.nodeSigner, s.graphDB, s.chanStateDB,
s.sweeper, tower, s.towerClient, s.anchorTowerClient,
r.cfg.net.ResolveTCPAddr, genInvoiceFeatures,
genAmpInvoiceFeatures, getNodeAnnouncement,
genAmpInvoiceFeatures, s.getNodeAnnouncement,
s.updateAndBrodcastSelfNode, parseAddr, rpcsLog,
s.aliasMgr.GetPeerAlias,
)
@ -2910,11 +2906,8 @@ func (r *rpcServer) GetInfo(_ context.Context,
// Check if external IP addresses were provided to lnd and use them
// to set the URIs.
nodeAnn, err := r.server.genNodeAnnouncement(false)
if err != nil {
return nil, fmt.Errorf("unable to retrieve current fully signed "+
"node announcement: %v", err)
}
nodeAnn := r.server.getNodeAnnouncement()
addrs := nodeAnn.Addresses
uris := make([]string, len(addrs))
for i, addr := range addrs {

116
server.go
View file

@ -530,6 +530,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout,
)
//nolint:lll
featureMgr, err := feature.NewManager(feature.Config{
NoTLVOnion: cfg.ProtocolOptions.LegacyOnion(),
NoStaticRemoteKey: cfg.ProtocolOptions.NoStaticRemoteKey(),
@ -540,6 +541,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
NoOptionScidAlias: !cfg.ProtocolOptions.ScidAlias(),
NoZeroConf: !cfg.ProtocolOptions.ZeroConf(),
NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(),
CustomFeatures: cfg.ProtocolOptions.ExperimentalProtocol.CustomFeatures(),
})
if err != nil {
return nil, err
@ -993,15 +995,18 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
}
s.authGossiper = discovery.New(discovery.Config{
Router: s.chanRouter,
Notifier: s.cc.ChainNotifier,
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
Broadcast: s.BroadcastMessage,
ChanSeries: chanSeries,
NotifyWhenOnline: s.NotifyWhenOnline,
NotifyWhenOffline: s.NotifyWhenOffline,
SelfNodeAnnouncement: func(refresh bool) (lnwire.NodeAnnouncement, error) {
return s.genNodeAnnouncement(refresh)
Router: s.chanRouter,
Notifier: s.cc.ChainNotifier,
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
Broadcast: s.BroadcastMessage,
ChanSeries: chanSeries,
NotifyWhenOnline: s.NotifyWhenOnline,
NotifyWhenOffline: s.NotifyWhenOffline,
FetchSelfAnnouncement: s.getNodeAnnouncement,
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement,
error) {
return s.genNodeAnnouncement(nil)
},
ProofMatureDelta: 0,
TrickleDelay: time.Millisecond * time.Duration(cfg.TrickleDelay),
@ -1271,6 +1276,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
return ourPolicy, err
}
//nolint:lll
s.fundingMgr, err = funding.NewFundingManager(funding.Config{
NoWumboChans: !cfg.ProtocolOptions.Wumbo(),
IDKey: nodeKeyDesc.PubKey,
@ -1283,8 +1289,10 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
Notifier: cc.ChainNotifier,
FeeEstimator: cc.FeeEstimator,
SignMessage: cc.MsgSigner.SignMessage,
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement, error) {
return s.genNodeAnnouncement(true)
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement,
error) {
return s.genNodeAnnouncement(nil)
},
SendAnnouncement: s.authGossiper.ProcessLocalAnnouncement,
NotifyWhenOnline: s.NotifyWhenOnline,
@ -1625,8 +1633,15 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
cfg.net.ResolveTCPAddr,
)
},
AdvertisedIPs: advertisedIPs,
AnnounceNewIPs: netann.IPAnnouncer(s.genNodeAnnouncement),
AdvertisedIPs: advertisedIPs,
AnnounceNewIPs: netann.IPAnnouncer(
func(modifier ...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error) {
return s.genNodeAnnouncement(
nil, modifier...,
)
}),
})
}
@ -2476,12 +2491,8 @@ out:
// throughout the network. We'll only include addresses
// that have a different IP from the previous one, as
// the previous IP is no longer valid.
currentNodeAnn, err := s.genNodeAnnouncement(false)
if err != nil {
srvrLog.Debugf("Unable to retrieve current "+
"node announcement: %v", err)
continue
}
currentNodeAnn := s.getNodeAnnouncement()
for _, addr := range currentNodeAnn.Addresses {
host, _, err := net.SplitHostPort(addr.String())
if err != nil {
@ -2503,7 +2514,7 @@ out:
// announcement with the updated addresses and broadcast
// it to our peers.
newNodeAnn, err := s.genNodeAnnouncement(
true, netann.NodeAnnSetAddrs(newAddrs),
nil, netann.NodeAnnSetAddrs(newAddrs),
)
if err != nil {
srvrLog.Debugf("Unable to generate new node "+
@ -2892,7 +2903,7 @@ func (s *server) createNewHiddenService() error {
// Now that the onion service has been created, we'll add the onion
// address it can be reached at to our list of advertised addresses.
newNodeAnn, err := s.genNodeAnnouncement(
true, func(currentAnn *lnwire.NodeAnnouncement) {
nil, func(currentAnn *lnwire.NodeAnnouncement) {
currentAnn.Addresses = append(currentAnn.Addresses, addr)
},
)
@ -2942,30 +2953,54 @@ func (s *server) findChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) (
return nil, fmt.Errorf("unable to find channel")
}
// getNodeAnnouncement fetches the current, fully signed node announcement.
func (s *server) getNodeAnnouncement() lnwire.NodeAnnouncement {
s.mu.Lock()
defer s.mu.Unlock()
return *s.currentNodeAnn
}
// genNodeAnnouncement generates and returns the current fully signed node
// announcement. If refresh is true, then the time stamp of the announcement
// will be updated in order to ensure it propagates through the network.
func (s *server) genNodeAnnouncement(refresh bool,
// announcement. The time stamp of the announcement will be updated in order
// to ensure it propagates through the network.
func (s *server) genNodeAnnouncement(features *lnwire.RawFeatureVector,
modifiers ...netann.NodeAnnModifier) (lnwire.NodeAnnouncement, error) {
s.mu.Lock()
defer s.mu.Unlock()
// If we don't need to refresh the announcement, then we can return a
// copy of our cached version.
if !refresh {
return *s.currentNodeAnn, nil
// First, try to update our feature manager with the updated set of
// features.
if features != nil {
proposedFeatures := map[feature.Set]*lnwire.RawFeatureVector{
feature.SetNodeAnn: features,
}
err := s.featureMgr.UpdateFeatureSets(proposedFeatures)
if err != nil {
return lnwire.NodeAnnouncement{}, err
}
// If we could successfully update our feature manager, add
// an update modifier to include these new features to our
// set.
modifiers = append(
modifiers, netann.NodeAnnSetFeatures(features),
)
}
// Always update the timestamp when refreshing to ensure the update
// propagates.
modifiers = append(modifiers, netann.NodeAnnSetTimestamp)
// Otherwise, we'll sign a new update after applying all of the passed
// modifiers.
// Apply the requested changes to the node announcement.
for _, modifier := range modifiers {
modifier(s.currentNodeAnn)
}
// Sign a new update after applying all of the passed modifiers.
err := netann.SignNodeAnnouncement(
s.nodeSigner, s.identityKeyLoc, s.currentNodeAnn,
modifiers...,
)
if err != nil {
return lnwire.NodeAnnouncement{}, err
@ -2978,10 +3013,10 @@ func (s *server) genNodeAnnouncement(refresh bool,
// applying the giving modifiers and updating the time stamp
// to ensure it propagates through the network. Then it brodcasts
// it to the network.
func (s *server) updateAndBrodcastSelfNode(
func (s *server) updateAndBrodcastSelfNode(features *lnwire.RawFeatureVector,
modifiers ...netann.NodeAnnModifier) error {
newNodeAnn, err := s.genNodeAnnouncement(true, modifiers...)
newNodeAnn, err := s.genNodeAnnouncement(features, modifiers...)
if err != nil {
return fmt.Errorf("unable to generate new node "+
"announcement: %v", err)
@ -2999,9 +3034,7 @@ func (s *server) updateAndBrodcastSelfNode(
selfNode.LastUpdate = time.Unix(int64(newNodeAnn.Timestamp), 0)
selfNode.Addresses = newNodeAnn.Addresses
selfNode.Alias = newNodeAnn.Alias.String()
selfNode.Features = lnwire.NewFeatureVector(
newNodeAnn.Features, lnwire.Features,
)
selfNode.Features = s.featureMgr.Get(feature.SetNodeAnn)
selfNode.Color = newNodeAnn.RGBColor
selfNode.AuthSigBytes = newNodeAnn.Signature.ToSignatureBytes()
@ -3011,11 +3044,6 @@ func (s *server) updateAndBrodcastSelfNode(
return fmt.Errorf("can't set self node: %v", err)
}
// Update the feature bits for the SetNodeAnn in case they changed.
s.featureMgr.SetRaw(
feature.SetNodeAnn, selfNode.Features.RawFeatureVector,
)
// Finally, propagate it to the nodes in the network.
err = s.BroadcastMessage(nil, &newNodeAnn)
if err != nil {
@ -3821,7 +3849,11 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
TowerClient: s.towerClient,
AnchorTowerClient: s.anchorTowerClient,
DisconnectPeer: s.DisconnectPeer,
GenNodeAnnouncement: s.genNodeAnnouncement,
GenNodeAnnouncement: func(...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error) {
return s.genNodeAnnouncement(nil)
},
PongBuf: s.pongBuf,

View file

@ -118,8 +118,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
tcpResolver lncfg.TCPResolver,
genInvoiceFeatures func() *lnwire.FeatureVector,
genAmpInvoiceFeatures func() *lnwire.FeatureVector,
getNodeAnnouncement func() (lnwire.NodeAnnouncement, error),
updateNodeAnnouncement func(modifiers ...netann.NodeAnnModifier) error,
getNodeAnnouncement func() lnwire.NodeAnnouncement,
updateNodeAnnouncement func(features *lnwire.RawFeatureVector,
modifiers ...netann.NodeAnnModifier) error,
parseAddr func(addr string) (net.Addr, error),
rpcLogger btclog.Logger,
getAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)) error {