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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 hits per line

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

51.75
/funding/commitment_type_negotiation.go
1
package funding
2

3
import (
4
        "errors"
5

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

10
var (
11
        // errUnsupportedCommitmentType is an error returned when a specific
12
        // channel commitment type is being explicitly negotiated but either
13
        // peer of the channel does not support it.
14
        errUnsupportedChannelType = errors.New("requested channel type " +
15
                "not supported")
16
)
17

18
// negotiateCommitmentType negotiates the commitment type of a newly opened
19
// channel. If a desiredChanType is provided, explicit negotiation for said type
20
// will be attempted if the set of both local and remote features support it.
21
// Otherwise, implicit negotiation will be attempted.
22
//
23
// The returned ChannelType is nil when implicit negotiation is used. An error
24
// is only returned if desiredChanType is not supported.
25
func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
26
        remote *lnwire.FeatureVector) (*lnwire.ChannelType,
27
        lnwallet.CommitmentType, error) {
127✔
28

127✔
29
        // BOLT#2 specifies we MUST use explicit negotiation if both peers
127✔
30
        // signal for it.
127✔
31
        explicitNegotiation := hasFeatures(
127✔
32
                local, remote, lnwire.ExplicitChannelTypeOptional,
127✔
33
        )
127✔
34

127✔
35
        chanTypeRequested := desiredChanType != nil
127✔
36

127✔
37
        switch {
127✔
38
        case explicitNegotiation && chanTypeRequested:
23✔
39
                commitType, err := explicitNegotiateCommitmentType(
23✔
40
                        *desiredChanType, local, remote,
23✔
41
                )
23✔
42

23✔
43
                return desiredChanType, commitType, err
23✔
44

45
        // We don't have a specific channel type requested, so we select a
46
        // default type as if implicit negotiation were used, and then we
47
        // explicitly signal that default type.
48
        case explicitNegotiation && !chanTypeRequested:
3✔
49
                defaultChanType, commitType := implicitNegotiateCommitmentType(
3✔
50
                        local, remote,
3✔
51
                )
3✔
52

3✔
53
                return defaultChanType, commitType, nil
3✔
54

55
        // A specific channel type was requested, but we can't explicitly signal
56
        // it. So if implicit negotiation wouldn't select the desired channel
57
        // type, we must return an error.
58
        case !explicitNegotiation && chanTypeRequested:
2✔
59
                implicitChanType, commitType := implicitNegotiateCommitmentType(
2✔
60
                        local, remote,
2✔
61
                )
2✔
62

2✔
63
                expected := lnwire.RawFeatureVector(*desiredChanType)
2✔
64
                actual := lnwire.RawFeatureVector(*implicitChanType)
2✔
65
                if !expected.Equals(&actual) {
2✔
66
                        return nil, 0, errUnsupportedChannelType
×
67
                }
×
68

69
                return nil, commitType, nil
2✔
70

71
        default: // !explicitNegotiation && !chanTypeRequested
99✔
72
                _, commitType := implicitNegotiateCommitmentType(local, remote)
99✔
73

99✔
74
                return nil, commitType, nil
99✔
75
        }
76
}
77

78
// explicitNegotiateCommitmentType attempts to explicitly negotiate for a
79
// specific channel type. Since the channel type is comprised of a set of even
80
// feature bits, we also make sure each feature is supported by both peers. An
81
// error is returned if either peer does not support said channel type.
82
func explicitNegotiateCommitmentType(channelType lnwire.ChannelType, local,
83
        remote *lnwire.FeatureVector) (lnwallet.CommitmentType, error) {
23✔
84

23✔
85
        channelFeatures := lnwire.RawFeatureVector(channelType)
23✔
86

23✔
87
        switch {
23✔
88
        // Lease script enforcement + anchors zero fee + static remote key +
89
        // zero conf + scid alias features only.
90
        case channelFeatures.OnlyContains(
91
                lnwire.ZeroConfRequired,
92
                lnwire.ScidAliasRequired,
93
                lnwire.ScriptEnforcedLeaseRequired,
94
                lnwire.AnchorsZeroFeeHtlcTxRequired,
95
                lnwire.StaticRemoteKeyRequired,
96
        ):
×
97
                if !hasFeatures(
×
98
                        local, remote,
×
99
                        lnwire.ZeroConfOptional,
×
100
                        lnwire.ScriptEnforcedLeaseOptional,
×
101
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
×
102
                        lnwire.StaticRemoteKeyOptional,
×
103
                ) {
×
104

×
105
                        return 0, errUnsupportedChannelType
×
106
                }
×
107
                return lnwallet.CommitmentTypeScriptEnforcedLease, nil
×
108

109
        // Anchors zero fee + static remote key + zero conf + scid alias
110
        // features only.
111
        case channelFeatures.OnlyContains(
112
                lnwire.ZeroConfRequired,
113
                lnwire.ScidAliasRequired,
114
                lnwire.AnchorsZeroFeeHtlcTxRequired,
115
                lnwire.StaticRemoteKeyRequired,
116
        ):
×
117
                if !hasFeatures(
×
118
                        local, remote,
×
119
                        lnwire.ZeroConfOptional,
×
120
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
×
121
                        lnwire.StaticRemoteKeyOptional,
×
122
                ) {
×
123

×
124
                        return 0, errUnsupportedChannelType
×
125
                }
×
126
                return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
×
127

128
        // Lease script enforcement + anchors zero fee + static remote key +
129
        // zero conf features only.
130
        case channelFeatures.OnlyContains(
131
                lnwire.ZeroConfRequired,
132
                lnwire.ScriptEnforcedLeaseRequired,
133
                lnwire.AnchorsZeroFeeHtlcTxRequired,
134
                lnwire.StaticRemoteKeyRequired,
135
        ):
2✔
136
                if !hasFeatures(
2✔
137
                        local, remote,
2✔
138
                        lnwire.ZeroConfOptional,
2✔
139
                        lnwire.ScriptEnforcedLeaseOptional,
2✔
140
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
2✔
141
                        lnwire.StaticRemoteKeyOptional,
2✔
142
                ) {
2✔
143

×
144
                        return 0, errUnsupportedChannelType
×
145
                }
×
146
                return lnwallet.CommitmentTypeScriptEnforcedLease, nil
2✔
147

148
        // Anchors zero fee + static remote key + zero conf features only.
149
        case channelFeatures.OnlyContains(
150
                lnwire.ZeroConfRequired,
151
                lnwire.AnchorsZeroFeeHtlcTxRequired,
152
                lnwire.StaticRemoteKeyRequired,
153
        ):
4✔
154
                if !hasFeatures(
4✔
155
                        local, remote,
4✔
156
                        lnwire.ZeroConfOptional,
4✔
157
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
4✔
158
                        lnwire.StaticRemoteKeyOptional,
4✔
159
                ) {
4✔
160

×
161
                        return 0, errUnsupportedChannelType
×
162
                }
×
163
                return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
4✔
164

165
        // Lease script enforcement + anchors zero fee + static remote key +
166
        // option-scid-alias features only.
167
        case channelFeatures.OnlyContains(
168
                lnwire.ScidAliasRequired,
169
                lnwire.ScriptEnforcedLeaseRequired,
170
                lnwire.AnchorsZeroFeeHtlcTxRequired,
171
                lnwire.StaticRemoteKeyRequired,
172
        ):
2✔
173
                if !hasFeatures(
2✔
174
                        local, remote,
2✔
175
                        lnwire.ScidAliasOptional,
2✔
176
                        lnwire.ScriptEnforcedLeaseOptional,
2✔
177
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
2✔
178
                        lnwire.StaticRemoteKeyOptional,
2✔
179
                ) {
2✔
180

×
181
                        return 0, errUnsupportedChannelType
×
182
                }
×
183
                return lnwallet.CommitmentTypeScriptEnforcedLease, nil
2✔
184

185
        // Anchors zero fee + static remote key + option-scid-alias features
186
        // only.
187
        case channelFeatures.OnlyContains(
188
                lnwire.ScidAliasRequired,
189
                lnwire.AnchorsZeroFeeHtlcTxRequired,
190
                lnwire.StaticRemoteKeyRequired,
191
        ):
2✔
192
                if !hasFeatures(
2✔
193
                        local, remote,
2✔
194
                        lnwire.ScidAliasOptional,
2✔
195
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
2✔
196
                        lnwire.StaticRemoteKeyOptional,
2✔
197
                ) {
2✔
198

×
199
                        return 0, errUnsupportedChannelType
×
200
                }
×
201
                return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
2✔
202

203
        // Lease script enforcement + anchors zero fee + static remote key
204
        // features only.
205
        case channelFeatures.OnlyContains(
206
                lnwire.ScriptEnforcedLeaseRequired,
207
                lnwire.AnchorsZeroFeeHtlcTxRequired,
208
                lnwire.StaticRemoteKeyRequired,
209
        ):
×
210
                if !hasFeatures(
×
211
                        local, remote,
×
212
                        lnwire.ScriptEnforcedLeaseOptional,
×
213
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
×
214
                        lnwire.StaticRemoteKeyOptional,
×
215
                ) {
×
216

×
217
                        return 0, errUnsupportedChannelType
×
218
                }
×
219
                return lnwallet.CommitmentTypeScriptEnforcedLease, nil
×
220

221
        // Anchors zero fee + static remote key features only.
222
        case channelFeatures.OnlyContains(
223
                lnwire.AnchorsZeroFeeHtlcTxRequired,
224
                lnwire.StaticRemoteKeyRequired,
225
        ):
5✔
226
                if !hasFeatures(
5✔
227
                        local, remote,
5✔
228
                        lnwire.AnchorsZeroFeeHtlcTxOptional,
5✔
229
                        lnwire.StaticRemoteKeyOptional,
5✔
230
                ) {
7✔
231

2✔
232
                        return 0, errUnsupportedChannelType
2✔
233
                }
2✔
234
                return lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx, nil
3✔
235

236
        // Static remote key feature only.
237
        case channelFeatures.OnlyContains(lnwire.StaticRemoteKeyRequired):
2✔
238
                if !hasFeatures(local, remote, lnwire.StaticRemoteKeyOptional) {
2✔
239
                        return 0, errUnsupportedChannelType
×
240
                }
×
241
                return lnwallet.CommitmentTypeTweakless, nil
2✔
242

243
        // Simple taproot channels only.
244
        case channelFeatures.OnlyContains(
245
                lnwire.SimpleTaprootChannelsRequiredStaging,
246
        ):
2✔
247

2✔
248
                if !hasFeatures(
2✔
249
                        local, remote,
2✔
250
                        lnwire.SimpleTaprootChannelsOptionalStaging,
2✔
251
                ) {
2✔
252

×
253
                        return 0, errUnsupportedChannelType
×
254
                }
×
255

256
                return lnwallet.CommitmentTypeSimpleTaproot, nil
2✔
257

258
        // Simple taproot channels with scid only.
259
        case channelFeatures.OnlyContains(
260
                lnwire.SimpleTaprootChannelsRequiredStaging,
261
                lnwire.ScidAliasRequired,
262
        ):
×
263

×
264
                if !hasFeatures(
×
265
                        local, remote,
×
266
                        lnwire.SimpleTaprootChannelsOptionalStaging,
×
267
                        lnwire.ScidAliasOptional,
×
268
                ) {
×
269

×
270
                        return 0, errUnsupportedChannelType
×
271
                }
×
272

273
                return lnwallet.CommitmentTypeSimpleTaproot, nil
×
274

275
        // Simple taproot channels with zero conf only.
276
        case channelFeatures.OnlyContains(
277
                lnwire.SimpleTaprootChannelsRequiredStaging,
278
                lnwire.ZeroConfRequired,
279
        ):
2✔
280

2✔
281
                if !hasFeatures(
2✔
282
                        local, remote,
2✔
283
                        lnwire.SimpleTaprootChannelsOptionalStaging,
2✔
284
                        lnwire.ZeroConfOptional,
2✔
285
                ) {
2✔
286

×
287
                        return 0, errUnsupportedChannelType
×
288
                }
×
289

290
                return lnwallet.CommitmentTypeSimpleTaproot, nil
2✔
291

292
        // Simple taproot channels with scid and zero conf.
293
        case channelFeatures.OnlyContains(
294
                lnwire.SimpleTaprootChannelsRequiredStaging,
295
                lnwire.ZeroConfRequired,
296
                lnwire.ScidAliasRequired,
297
        ):
×
298

×
299
                if !hasFeatures(
×
300
                        local, remote,
×
301
                        lnwire.SimpleTaprootChannelsOptionalStaging,
×
302
                        lnwire.ZeroConfOptional,
×
303
                ) {
×
304

×
305
                        return 0, errUnsupportedChannelType
×
306
                }
×
307

308
                return lnwallet.CommitmentTypeSimpleTaproot, nil
×
309

310
        // Simple taproot channels overlay only.
311
        case channelFeatures.OnlyContains(
312
                lnwire.SimpleTaprootOverlayChansRequired,
313
        ):
×
314

×
315
                if !hasFeatures(
×
316
                        local, remote,
×
317
                        lnwire.SimpleTaprootOverlayChansOptional,
×
318
                ) {
×
319

×
320
                        return 0, errUnsupportedChannelType
×
321
                }
×
322

323
                return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
×
324

325
        // Simple taproot overlay channels with scid only.
326
        case channelFeatures.OnlyContains(
327
                lnwire.SimpleTaprootOverlayChansRequired,
328
                lnwire.ScidAliasRequired,
329
        ):
×
330

×
331
                if !hasFeatures(
×
332
                        local, remote,
×
333
                        lnwire.SimpleTaprootOverlayChansOptional,
×
334
                        lnwire.ScidAliasOptional,
×
335
                ) {
×
336

×
337
                        return 0, errUnsupportedChannelType
×
338
                }
×
339

340
                return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
×
341

342
        // Simple taproot overlay channels with zero conf only.
343
        case channelFeatures.OnlyContains(
344
                lnwire.SimpleTaprootOverlayChansRequired,
345
                lnwire.ZeroConfRequired,
346
        ):
×
347

×
348
                if !hasFeatures(
×
349
                        local, remote,
×
350
                        lnwire.SimpleTaprootOverlayChansOptional,
×
351
                        lnwire.ZeroConfOptional,
×
352
                ) {
×
353

×
354
                        return 0, errUnsupportedChannelType
×
355
                }
×
356

357
                return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
×
358

359
        // Simple taproot overlay channels with scid and zero conf.
360
        case channelFeatures.OnlyContains(
361
                lnwire.SimpleTaprootOverlayChansRequired,
362
                lnwire.ZeroConfRequired,
363
                lnwire.ScidAliasRequired,
364
        ):
×
365

×
366
                if !hasFeatures(
×
367
                        local, remote,
×
368
                        lnwire.SimpleTaprootOverlayChansOptional,
×
369
                        lnwire.ZeroConfOptional,
×
370
                        lnwire.ScidAliasOptional,
×
371
                ) {
×
372

×
373
                        return 0, errUnsupportedChannelType
×
374
                }
×
375

376
                return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
×
377

378
        // No features, use legacy commitment type.
379
        case channelFeatures.IsEmpty():
2✔
380
                return lnwallet.CommitmentTypeLegacy, nil
2✔
381

382
        default:
×
383
                return 0, errUnsupportedChannelType
×
384
        }
385
}
386

387
// implicitNegotiateCommitmentType negotiates the commitment type of a channel
388
// implicitly by choosing the latest type supported by the local and remote
389
// features.
390
func implicitNegotiateCommitmentType(local,
391
        remote *lnwire.FeatureVector) (*lnwire.ChannelType,
392
        lnwallet.CommitmentType) {
104✔
393

104✔
394
        // If both peers are signalling support for anchor commitments with
104✔
395
        // zero-fee HTLC transactions, we'll use this type.
104✔
396
        if hasFeatures(local, remote, lnwire.AnchorsZeroFeeHtlcTxOptional) {
109✔
397
                chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
5✔
398
                        lnwire.AnchorsZeroFeeHtlcTxRequired,
5✔
399
                        lnwire.StaticRemoteKeyRequired,
5✔
400
                ))
5✔
401

5✔
402
                return &chanType, lnwallet.CommitmentTypeAnchorsZeroFeeHtlcTx
5✔
403
        }
5✔
404

405
        // Since we don't want to support the "legacy" anchor type, we will fall
406
        // back to static remote key if the nodes don't support the zero fee
407
        // HTLC tx anchor type.
408
        //
409
        // If both nodes are signaling the proper feature bit for tweakless
410
        // commitments, we'll use that.
411
        if hasFeatures(local, remote, lnwire.StaticRemoteKeyOptional) {
101✔
412
                chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
2✔
413
                        lnwire.StaticRemoteKeyRequired,
2✔
414
                ))
2✔
415

2✔
416
                return &chanType, lnwallet.CommitmentTypeTweakless
2✔
417
        }
2✔
418

419
        // Otherwise we'll fall back to the legacy type.
420
        chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector())
97✔
421
        return &chanType, lnwallet.CommitmentTypeLegacy
97✔
422
}
423

424
// hasFeatures determines whether a set of features is supported by both the set
425
// of local and remote features.
426
func hasFeatures(local, remote *lnwire.FeatureVector,
427
        features ...lnwire.FeatureBit) bool {
454✔
428

454✔
429
        for _, feature := range features {
937✔
430
                if !local.HasFeature(feature) || !remote.HasFeature(feature) {
879✔
431
                        return false
396✔
432
                }
396✔
433
        }
434
        return true
58✔
435
}
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