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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

80.66
/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
        // NoExperimentalEndorsement unsets any bits that signal support for
73
        // forwarding experimental endorsement.
74
        NoExperimentalEndorsement bool
75

76
        // CustomFeatures is a set of custom features to advertise in each
77
        // set.
78
        CustomFeatures map[Set][]lnwire.FeatureBit
79
}
80

81
// Manager is responsible for generating feature vectors for different requested
82
// feature sets.
83
type Manager struct {
84
        // fsets is a static map of feature set to raw feature vectors. Requests
85
        // are fulfilled by cloning these internal feature vectors.
86
        fsets map[Set]*lnwire.RawFeatureVector
87

88
        // configFeatures is a set of custom features that were "hard set" in
89
        // lnd's config that cannot be updated at runtime (as is the case with
90
        // our "standard" features that are defined in LND).
91
        configFeatures map[Set]*lnwire.FeatureVector
92
}
93

94
// NewManager creates a new feature Manager, applying any custom modifications
95
// to its feature sets before returning.
96
func NewManager(cfg Config) (*Manager, error) {
3✔
97
        return newManager(cfg, defaultSetDesc)
3✔
98
}
3✔
99

100
// newManager creates a new feature Manager, applying any custom modifications
101
// to its feature sets before returning. This method accepts the setDesc as its
102
// own parameter so that it can be unit tested.
103
func newManager(cfg Config, desc setDesc) (*Manager, error) {
3✔
104
        // First build the default feature vector for all known sets.
3✔
105
        fsets := make(map[Set]*lnwire.RawFeatureVector)
3✔
106
        for bit, sets := range desc {
6✔
107
                for set := range sets {
6✔
108
                        // Fetch the feature vector for this set, allocating a
3✔
109
                        // new one if it doesn't exist.
3✔
110
                        fv, ok := fsets[set]
3✔
111
                        if !ok {
6✔
112
                                fv = lnwire.NewRawFeatureVector()
3✔
113
                        }
3✔
114

115
                        // Set the configured bit on the feature vector,
116
                        // ensuring that we don't set two feature bits for the
117
                        // same pair.
118
                        err := fv.SafeSet(bit)
3✔
119
                        if err != nil {
3✔
120
                                return nil, fmt.Errorf("unable to set "+
×
121
                                        "%v in %v: %v", bit, set, err)
×
122
                        }
×
123

124
                        // Write the updated feature vector under its set.
125
                        fsets[set] = fv
3✔
126
                }
127
        }
128

129
        // Now, remove any features as directed by the config.
130
        configFeatures := make(map[Set]*lnwire.FeatureVector)
3✔
131
        for set, raw := range fsets {
6✔
132
                if cfg.NoTLVOnion {
6✔
133
                        raw.Unset(lnwire.TLVOnionPayloadOptional)
3✔
134
                        raw.Unset(lnwire.TLVOnionPayloadRequired)
3✔
135
                        raw.Unset(lnwire.PaymentAddrOptional)
3✔
136
                        raw.Unset(lnwire.PaymentAddrRequired)
3✔
137
                        raw.Unset(lnwire.MPPOptional)
3✔
138
                        raw.Unset(lnwire.MPPRequired)
3✔
139
                        raw.Unset(lnwire.RouteBlindingOptional)
3✔
140
                        raw.Unset(lnwire.RouteBlindingRequired)
3✔
141
                        raw.Unset(lnwire.Bolt11BlindedPathsOptional)
3✔
142
                        raw.Unset(lnwire.Bolt11BlindedPathsRequired)
3✔
143
                        raw.Unset(lnwire.AMPOptional)
3✔
144
                        raw.Unset(lnwire.AMPRequired)
3✔
145
                        raw.Unset(lnwire.KeysendOptional)
3✔
146
                        raw.Unset(lnwire.KeysendRequired)
3✔
147
                }
3✔
148
                if cfg.NoStaticRemoteKey {
6✔
149
                        raw.Unset(lnwire.StaticRemoteKeyOptional)
3✔
150
                        raw.Unset(lnwire.StaticRemoteKeyRequired)
3✔
151
                }
3✔
152
                if cfg.NoAnchors {
6✔
153
                        raw.Unset(lnwire.AnchorsZeroFeeHtlcTxOptional)
3✔
154
                        raw.Unset(lnwire.AnchorsZeroFeeHtlcTxRequired)
3✔
155

3✔
156
                        // If anchors are disabled, then we also need to
3✔
157
                        // disable all other features that depend on it as
3✔
158
                        // well, as otherwise we may create an invalid feature
3✔
159
                        // bit set.
3✔
160
                        for bit, depFeatures := range deps {
6✔
161
                                for depFeature := range depFeatures {
6✔
162
                                        switch {
3✔
163
                                        case depFeature == lnwire.AnchorsZeroFeeHtlcTxRequired:
×
164
                                                fallthrough
×
165
                                        case depFeature == lnwire.AnchorsZeroFeeHtlcTxOptional:
3✔
166
                                                raw.Unset(bit)
3✔
167
                                        }
168
                                }
169
                        }
170
                }
171
                if cfg.NoWumbo {
6✔
172
                        raw.Unset(lnwire.WumboChannelsOptional)
3✔
173
                        raw.Unset(lnwire.WumboChannelsRequired)
3✔
174
                }
3✔
175
                if cfg.NoScriptEnforcementLease {
6✔
176
                        raw.Unset(lnwire.ScriptEnforcedLeaseOptional)
3✔
177
                        raw.Unset(lnwire.ScriptEnforcedLeaseRequired)
3✔
178
                }
3✔
179
                if cfg.NoKeysend {
3✔
180
                        raw.Unset(lnwire.KeysendOptional)
×
181
                        raw.Unset(lnwire.KeysendRequired)
×
182
                }
×
183
                if cfg.NoOptionScidAlias {
6✔
184
                        raw.Unset(lnwire.ScidAliasOptional)
3✔
185
                        raw.Unset(lnwire.ScidAliasRequired)
3✔
186
                }
3✔
187
                if cfg.NoZeroConf {
6✔
188
                        raw.Unset(lnwire.ZeroConfOptional)
3✔
189
                        raw.Unset(lnwire.ZeroConfRequired)
3✔
190
                }
3✔
191
                if cfg.NoAnySegwit {
6✔
192
                        raw.Unset(lnwire.ShutdownAnySegwitOptional)
3✔
193
                        raw.Unset(lnwire.ShutdownAnySegwitRequired)
3✔
194
                }
3✔
195
                if cfg.NoTaprootChans {
6✔
196
                        raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging)
3✔
197
                        raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging)
3✔
198
                }
3✔
199
                if cfg.NoRouteBlinding {
6✔
200
                        raw.Unset(lnwire.RouteBlindingOptional)
3✔
201
                        raw.Unset(lnwire.RouteBlindingRequired)
3✔
202
                        raw.Unset(lnwire.Bolt11BlindedPathsOptional)
3✔
203
                        raw.Unset(lnwire.Bolt11BlindedPathsRequired)
3✔
204
                }
3✔
205
                if cfg.NoQuiescence {
3✔
206
                        raw.Unset(lnwire.QuiescenceOptional)
×
207
                }
×
208
                if cfg.NoTaprootOverlay {
6✔
209
                        raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
3✔
210
                        raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
3✔
211
                }
3✔
212

213
                if cfg.NoExperimentalEndorsement {
6✔
214
                        raw.Unset(lnwire.ExperimentalEndorsementOptional)
3✔
215
                        raw.Unset(lnwire.ExperimentalEndorsementRequired)
3✔
216
                }
3✔
217

218
                for _, custom := range cfg.CustomFeatures[set] {
6✔
219
                        if custom > set.Maximum() {
3✔
220
                                return nil, fmt.Errorf("feature bit: %v "+
×
221
                                        "exceeds set: %v maximum: %v", custom,
×
222
                                        set, set.Maximum())
×
223
                        }
×
224

225
                        if raw.IsSet(custom) {
3✔
226
                                return nil, fmt.Errorf("feature bit: %v "+
×
227
                                        "already set", custom)
×
228
                        }
×
229

230
                        if err := raw.SafeSet(custom); err != nil {
3✔
231
                                return nil, fmt.Errorf("%w: could not set "+
×
232
                                        "feature: %d", err, custom)
×
233
                        }
×
234
                }
235

236
                // Track custom features separately so that we can check that
237
                // they aren't unset in subsequent updates. If there is no
238
                // entry for the set, the vector will just be empty.
239
                configFeatures[set] = lnwire.NewFeatureVector(
3✔
240
                        lnwire.NewRawFeatureVector(cfg.CustomFeatures[set]...),
3✔
241
                        lnwire.Features,
3✔
242
                )
3✔
243

3✔
244
                // Ensure that all of our feature sets properly set any
3✔
245
                // dependent features.
3✔
246
                fv := lnwire.NewFeatureVector(raw, lnwire.Features)
3✔
247
                err := ValidateDeps(fv)
3✔
248
                if err != nil {
3✔
249
                        return nil, fmt.Errorf("invalid feature set %v: %w",
×
250
                                set, err)
×
251
                }
×
252
        }
253

254
        return &Manager{
3✔
255
                fsets:          fsets,
3✔
256
                configFeatures: configFeatures,
3✔
257
        }, nil
3✔
258
}
259

260
// GetRaw returns a raw feature vector for the passed set. If no set is known,
261
// an empty raw feature vector is returned.
262
func (m *Manager) GetRaw(set Set) *lnwire.RawFeatureVector {
3✔
263
        if fv, ok := m.fsets[set]; ok {
6✔
264
                return fv.Clone()
3✔
265
        }
3✔
266

UNCOV
267
        return lnwire.NewRawFeatureVector()
×
268
}
269

270
// setRaw sets a new raw feature vector for the given set.
271
func (m *Manager) setRaw(set Set, raw *lnwire.RawFeatureVector) {
3✔
272
        m.fsets[set] = raw
3✔
273
}
3✔
274

275
// Get returns a feature vector for the passed set. If no set is known, an empty
276
// feature vector is returned.
277
func (m *Manager) Get(set Set) *lnwire.FeatureVector {
3✔
278
        raw := m.GetRaw(set)
3✔
279
        return lnwire.NewFeatureVector(raw, lnwire.Features)
3✔
280
}
3✔
281

282
// ListSets returns a list of the feature sets that our node supports.
283
func (m *Manager) ListSets() []Set {
3✔
284
        var sets []Set
3✔
285

3✔
286
        for set := range m.fsets {
6✔
287
                sets = append(sets, set)
3✔
288
        }
3✔
289

290
        return sets
3✔
291
}
292

293
// UpdateFeatureSets accepts a map of new feature vectors for each of the
294
// manager's known sets, validates that the update can be applied and modifies
295
// the feature manager's internal state. If a set is not included in the update
296
// map, it is left unchanged. The feature vectors provided are expected to
297
// include the current set of features, updated with desired bits added/removed.
298
func (m *Manager) UpdateFeatureSets(
299
        updates map[Set]*lnwire.RawFeatureVector) error {
3✔
300

3✔
301
        for set, newFeatures := range updates {
6✔
302
                if !set.valid() {
3✔
UNCOV
303
                        return fmt.Errorf("%w: set: %d", ErrUnknownSet, set)
×
UNCOV
304
                }
×
305

306
                if err := newFeatures.ValidatePairs(); err != nil {
3✔
UNCOV
307
                        return err
×
UNCOV
308
                }
×
309

310
                if err := m.Get(set).ValidateUpdate(
3✔
311
                        newFeatures, set.Maximum(),
3✔
312
                ); err != nil {
6✔
313
                        return err
3✔
314
                }
3✔
315

316
                // If any features were configured for this set, ensure that
317
                // they are still set in the new feature vector.
318
                if cfgFeat, haveCfgFeat := m.configFeatures[set]; haveCfgFeat {
6✔
319
                        for feature := range cfgFeat.Features() {
3✔
UNCOV
320
                                if !newFeatures.IsSet(feature) {
×
UNCOV
321
                                        return fmt.Errorf("%w: can't unset: "+
×
UNCOV
322
                                                "%d", ErrFeatureConfigured,
×
UNCOV
323
                                                feature)
×
UNCOV
324
                                }
×
325
                        }
326
                }
327

328
                fv := lnwire.NewFeatureVector(newFeatures, lnwire.Features)
3✔
329
                if err := ValidateDeps(fv); err != nil {
3✔
330
                        return err
×
331
                }
×
332
        }
333

334
        // Only update the current feature sets once every proposed set has
335
        // passed validation so that we don't partially update any sets then
336
        // fail out on a later set's validation.
337
        for set, features := range updates {
6✔
338
                m.setRaw(set, features.Clone())
3✔
339
        }
3✔
340

341
        return nil
3✔
342
}
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

© 2025 Coveralls, Inc