• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 21485572389

29 Jan 2026 04:09PM UTC coverage: 65.247% (+0.2%) from 65.074%
21485572389

Pull #10089

github

web-flow
Merge 22d34d15e into 19b2ad797
Pull Request #10089: Onion message forwarding

1152 of 1448 new or added lines in 23 files covered. (79.56%)

4109 existing lines in 29 files now uncovered.

139515 of 213825 relevant lines covered (65.25%)

20529.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.19
/feature/manager.go
1
package feature
2

3
import (
4
        "errors"
5
        "fmt"
6

7
        "github.com/lightningnetwork/lnd/lnwire"
8
)
9

10
var (
11
        // ErrUnknownSet is returned if a proposed feature vector contains a
12
        // set that is unknown to LND.
13
        ErrUnknownSet = errors.New("unknown feature bit set")
14

15
        // ErrFeatureConfigured is returned if an attempt is made to unset a
16
        // feature that was configured at startup.
17
        ErrFeatureConfigured = errors.New("can't unset configured feature")
18
)
19

20
// Config houses any runtime modifications to the default set descriptors. For
21
// our purposes, this typically means disabling certain features to test legacy
22
// protocol interoperability or functionality.
23
type Config struct {
24
        // NoTLVOnion unsets any optional or required TLVOnionPaylod bits from
25
        // all feature sets.
26
        NoTLVOnion bool
27

28
        // NoStaticRemoteKey unsets any optional or required StaticRemoteKey
29
        // bits from all feature sets.
30
        NoStaticRemoteKey bool
31

32
        // NoAnchors unsets any bits signaling support for anchor outputs.
33
        NoAnchors bool
34

35
        // NoWumbo unsets any bits signalling support for wumbo channels.
36
        NoWumbo bool
37

38
        // NoTaprootChans unsets any bits signaling support for taproot
39
        // channels.
40
        NoTaprootChans bool
41

42
        // NoScriptEnforcementLease unsets any bits signaling support for script
43
        // enforced leases.
44
        NoScriptEnforcementLease bool
45

46
        // NoKeysend unsets any bits signaling support for accepting keysend
47
        // payments.
48
        NoKeysend bool
49

50
        // NoOptionScidAlias unsets any bits signalling support for
51
        // option_scid_alias. This also implicitly disables zero-conf channels.
52
        NoOptionScidAlias bool
53

54
        // NoZeroConf unsets any bits signalling support for zero-conf
55
        // channels. This should be used instead of NoOptionScidAlias to still
56
        // keep option-scid-alias support.
57
        NoZeroConf bool
58

59
        // NoAnySegwit unsets any bits that signal support for using other
60
        // segwit witness versions for co-op closes.
61
        NoAnySegwit bool
62

63
        // NoRouteBlinding unsets route blinding feature bits.
64
        NoRouteBlinding bool
65

66
        // NoQuiescence unsets quiescence feature bits.
67
        NoQuiescence bool
68

69
        // NoTaprootOverlay unsets the taproot overlay channel feature bits.
70
        NoTaprootOverlay bool
71

72
        // NoExperimentalAccountability unsets any bits that signal support for
73
        // forwarding experimental accountability.
74
        NoExperimentalAccountability bool
75

76
        // NoRbfCoopClose unsets any bits that signal support for using RBF for
77
        // coop close.
78
        NoRbfCoopClose bool
79

80
        // NoOnionMessages unsets any bits that signal support for onion
81
        // messaging.
82
        NoOnionMessages bool
83

84
        // CustomFeatures is a set of custom features to advertise in each
85
        // set.
86
        CustomFeatures map[Set][]lnwire.FeatureBit
87
}
88

89
// Manager is responsible for generating feature vectors for different requested
90
// feature sets.
91
type Manager struct {
92
        // fsets is a static map of feature set to raw feature vectors. Requests
93
        // are fulfilled by cloning these internal feature vectors.
94
        fsets map[Set]*lnwire.RawFeatureVector
95

96
        // configFeatures is a set of custom features that were "hard set" in
97
        // lnd's config that cannot be updated at runtime (as is the case with
98
        // our "standard" features that are defined in LND).
99
        configFeatures map[Set]*lnwire.FeatureVector
100
}
101

102
// NewManager creates a new feature Manager, applying any custom modifications
103
// to its feature sets before returning.
104
func NewManager(cfg Config) (*Manager, error) {
2✔
105
        return newManager(cfg, defaultSetDesc)
2✔
106
}
2✔
107

108
// newManager creates a new feature Manager, applying any custom modifications
109
// to its feature sets before returning. This method accepts the setDesc as its
110
// own parameter so that it can be unit tested.
111
func newManager(cfg Config, desc setDesc) (*Manager, error) {
18✔
112
        // First build the default feature vector for all known sets.
18✔
113
        fsets := make(map[Set]*lnwire.RawFeatureVector)
18✔
114
        for bit, sets := range desc {
57✔
115
                for set := range sets {
99✔
116
                        // Fetch the feature vector for this set, allocating a
60✔
117
                        // new one if it doesn't exist.
60✔
118
                        fv, ok := fsets[set]
60✔
119
                        if !ok {
94✔
120
                                fv = lnwire.NewRawFeatureVector()
34✔
121
                        }
34✔
122

123
                        // Set the configured bit on the feature vector,
124
                        // ensuring that we don't set two feature bits for the
125
                        // same pair.
126
                        err := fv.SafeSet(bit)
60✔
127
                        if err != nil {
60✔
128
                                return nil, fmt.Errorf("unable to set "+
×
129
                                        "%v in %v: %v", bit, set, err)
×
130
                        }
×
131

132
                        // Write the updated feature vector under its set.
133
                        fsets[set] = fv
60✔
134
                }
135
        }
136

137
        // Now, remove any features as directed by the config.
138
        configFeatures := make(map[Set]*lnwire.FeatureVector)
18✔
139
        for set, raw := range fsets {
52✔
140
                if cfg.NoTLVOnion {
40✔
141
                        raw.Unset(lnwire.TLVOnionPayloadOptional)
6✔
142
                        raw.Unset(lnwire.TLVOnionPayloadRequired)
6✔
143
                        raw.Unset(lnwire.PaymentAddrOptional)
6✔
144
                        raw.Unset(lnwire.PaymentAddrRequired)
6✔
145
                        raw.Unset(lnwire.MPPOptional)
6✔
146
                        raw.Unset(lnwire.MPPRequired)
6✔
147
                        raw.Unset(lnwire.RouteBlindingOptional)
6✔
148
                        raw.Unset(lnwire.RouteBlindingRequired)
6✔
149
                        raw.Unset(lnwire.Bolt11BlindedPathsOptional)
6✔
150
                        raw.Unset(lnwire.Bolt11BlindedPathsRequired)
6✔
151
                        raw.Unset(lnwire.AMPOptional)
6✔
152
                        raw.Unset(lnwire.AMPRequired)
6✔
153
                        raw.Unset(lnwire.KeysendOptional)
6✔
154
                        raw.Unset(lnwire.KeysendRequired)
6✔
155
                }
6✔
156
                if cfg.NoStaticRemoteKey {
40✔
157
                        raw.Unset(lnwire.StaticRemoteKeyOptional)
6✔
158
                        raw.Unset(lnwire.StaticRemoteKeyRequired)
6✔
159
                }
6✔
160
                if cfg.NoAnchors {
38✔
161
                        raw.Unset(lnwire.AnchorsZeroFeeHtlcTxOptional)
4✔
162
                        raw.Unset(lnwire.AnchorsZeroFeeHtlcTxRequired)
4✔
163

4✔
164
                        // If anchors are disabled, then we also need to
4✔
165
                        // disable all other features that depend on it as
4✔
166
                        // well, as otherwise we may create an invalid feature
4✔
167
                        // bit set.
4✔
168
                        for bit, depFeatures := range deps {
32✔
169
                                for depFeature := range depFeatures {
62✔
170
                                        switch {
34✔
171
                                        case depFeature == lnwire.AnchorsZeroFeeHtlcTxRequired:
×
172
                                                fallthrough
×
173
                                        case depFeature == lnwire.AnchorsZeroFeeHtlcTxOptional:
6✔
174
                                                raw.Unset(bit)
6✔
175
                                        }
176
                                }
177
                        }
178
                }
179
                if cfg.NoWumbo {
36✔
180
                        raw.Unset(lnwire.WumboChannelsOptional)
2✔
181
                        raw.Unset(lnwire.WumboChannelsRequired)
2✔
182
                }
2✔
183
                if cfg.NoScriptEnforcementLease {
36✔
184
                        raw.Unset(lnwire.ScriptEnforcedLeaseOptional)
2✔
185
                        raw.Unset(lnwire.ScriptEnforcedLeaseRequired)
2✔
186
                }
2✔
187
                if cfg.NoKeysend {
34✔
188
                        raw.Unset(lnwire.KeysendOptional)
×
189
                        raw.Unset(lnwire.KeysendRequired)
×
190
                }
×
191
                if cfg.NoOptionScidAlias {
36✔
192
                        raw.Unset(lnwire.ScidAliasOptional)
2✔
193
                        raw.Unset(lnwire.ScidAliasRequired)
2✔
194
                }
2✔
195
                if cfg.NoZeroConf {
36✔
196
                        raw.Unset(lnwire.ZeroConfOptional)
2✔
197
                        raw.Unset(lnwire.ZeroConfRequired)
2✔
198
                }
2✔
199
                if cfg.NoAnySegwit {
36✔
200
                        raw.Unset(lnwire.ShutdownAnySegwitOptional)
2✔
201
                        raw.Unset(lnwire.ShutdownAnySegwitRequired)
2✔
202
                }
2✔
203
                if cfg.NoTaprootChans {
36✔
204
                        raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging)
2✔
205
                        raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging)
2✔
206
                }
2✔
207
                if cfg.NoRouteBlinding {
36✔
208
                        raw.Unset(lnwire.RouteBlindingOptional)
2✔
209
                        raw.Unset(lnwire.RouteBlindingRequired)
2✔
210
                        raw.Unset(lnwire.Bolt11BlindedPathsOptional)
2✔
211
                        raw.Unset(lnwire.Bolt11BlindedPathsRequired)
2✔
212
                }
2✔
213
                if cfg.NoQuiescence {
34✔
214
                        raw.Unset(lnwire.QuiescenceOptional)
×
215
                }
×
216
                if cfg.NoTaprootOverlay {
36✔
217
                        raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
2✔
218
                        raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
2✔
219
                }
2✔
220
                if cfg.NoExperimentalAccountability {
36✔
221
                        raw.Unset(lnwire.ExperimentalAccountabilityOptional)
2✔
222
                        raw.Unset(lnwire.ExperimentalAccountabilityRequired)
2✔
223
                }
2✔
224
                if cfg.NoRbfCoopClose {
36✔
225
                        raw.Unset(lnwire.RbfCoopCloseOptionalStaging)
2✔
226
                        raw.Unset(lnwire.RbfCoopCloseOptional)
2✔
227
                }
2✔
228
                if cfg.NoOnionMessages {
34✔
NEW
229
                        raw.Unset(lnwire.OnionMessagesOptional)
×
NEW
230
                        raw.Unset(lnwire.OnionMessagesRequired)
×
NEW
231
                }
×
232

233
                for _, custom := range cfg.CustomFeatures[set] {
38✔
234
                        if custom > set.Maximum() {
4✔
235
                                return nil, fmt.Errorf("feature bit: %v "+
×
236
                                        "exceeds set: %v maximum: %v", custom,
×
237
                                        set, set.Maximum())
×
238
                        }
×
239

240
                        if raw.IsSet(custom) {
4✔
241
                                return nil, fmt.Errorf("feature bit: %v "+
×
242
                                        "already set", custom)
×
243
                        }
×
244

245
                        if err := raw.SafeSet(custom); err != nil {
4✔
246
                                return nil, fmt.Errorf("%w: could not set "+
×
247
                                        "feature: %d", err, custom)
×
248
                        }
×
249
                }
250

251
                // Track custom features separately so that we can check that
252
                // they aren't unset in subsequent updates. If there is no
253
                // entry for the set, the vector will just be empty.
254
                configFeatures[set] = lnwire.NewFeatureVector(
34✔
255
                        lnwire.NewRawFeatureVector(cfg.CustomFeatures[set]...),
34✔
256
                        lnwire.Features,
34✔
257
                )
34✔
258

34✔
259
                // Ensure that all of our feature sets properly set any
34✔
260
                // dependent features.
34✔
261
                fv := lnwire.NewFeatureVector(raw, lnwire.Features)
34✔
262
                err := ValidateDeps(fv)
34✔
263
                if err != nil {
34✔
264
                        return nil, fmt.Errorf("invalid feature set %v: %w",
×
265
                                set, err)
×
266
                }
×
267
        }
268

269
        return &Manager{
18✔
270
                fsets:          fsets,
18✔
271
                configFeatures: configFeatures,
18✔
272
        }, nil
18✔
273
}
274

275
// GetRaw returns a raw feature vector for the passed set. If no set is known,
276
// an empty raw feature vector is returned.
277
func (m *Manager) GetRaw(set Set) *lnwire.RawFeatureVector {
68✔
278
        if fv, ok := m.fsets[set]; ok {
114✔
279
                return fv.Clone()
46✔
280
        }
46✔
281

282
        return lnwire.NewRawFeatureVector()
22✔
283
}
284

285
// setRaw sets a new raw feature vector for the given set.
286
func (m *Manager) setRaw(set Set, raw *lnwire.RawFeatureVector) {
8✔
287
        m.fsets[set] = raw
8✔
288
}
8✔
289

290
// Get returns a feature vector for the passed set. If no set is known, an empty
291
// feature vector is returned.
292
func (m *Manager) Get(set Set) *lnwire.FeatureVector {
34✔
293
        raw := m.GetRaw(set)
34✔
294
        return lnwire.NewFeatureVector(raw, lnwire.Features)
34✔
295
}
34✔
296

297
// ListSets returns a list of the feature sets that our node supports.
298
func (m *Manager) ListSets() []Set {
2✔
299
        var sets []Set
2✔
300

2✔
301
        for set := range m.fsets {
4✔
302
                sets = append(sets, set)
2✔
303
        }
2✔
304

305
        return sets
2✔
306
}
307

308
// UpdateFeatureSets accepts a map of new feature vectors for each of the
309
// manager's known sets, validates that the update can be applied and modifies
310
// the feature manager's internal state. If a set is not included in the update
311
// map, it is left unchanged. The feature vectors provided are expected to
312
// include the current set of features, updated with desired bits added/removed.
313
func (m *Manager) UpdateFeatureSets(
314
        updates map[Set]*lnwire.RawFeatureVector) error {
9✔
315

9✔
316
        for set, newFeatures := range updates {
21✔
317
                if !set.valid() {
13✔
318
                        return fmt.Errorf("%w: set: %d", ErrUnknownSet, set)
1✔
319
                }
1✔
320

321
                if err := newFeatures.ValidatePairs(); err != nil {
13✔
322
                        return err
2✔
323
                }
2✔
324

325
                if err := m.Get(set).ValidateUpdate(
9✔
326
                        newFeatures, set.Maximum(),
9✔
327
                ); err != nil {
11✔
328
                        return err
2✔
329
                }
2✔
330

331
                // If any features were configured for this set, ensure that
332
                // they are still set in the new feature vector.
333
                if cfgFeat, haveCfgFeat := m.configFeatures[set]; haveCfgFeat {
16✔
334
                        for feature := range cfgFeat.Features() {
8✔
335
                                if !newFeatures.IsSet(feature) {
2✔
336
                                        return fmt.Errorf("%w: can't unset: "+
1✔
337
                                                "%d", ErrFeatureConfigured,
1✔
338
                                                feature)
1✔
339
                                }
1✔
340
                        }
341
                }
342

343
                fv := lnwire.NewFeatureVector(newFeatures, lnwire.Features)
8✔
344
                if err := ValidateDeps(fv); err != nil {
8✔
345
                        return err
×
346
                }
×
347
        }
348

349
        // Only update the current feature sets once every proposed set has
350
        // passed validation so that we don't partially update any sets then
351
        // fail out on a later set's validation.
352
        for set, features := range updates {
13✔
353
                m.setRaw(set, features.Clone())
8✔
354
        }
8✔
355

356
        return nil
5✔
357
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc