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

lightningnetwork / lnd / 25455608158

06 May 2026 07:10PM UTC coverage: 62.23% (+0.002%) from 62.228%
25455608158

Pull #10686

github

web-flow
Merge c5cd7ffca into 6fd5b7bb2
Pull Request #10686: chainreg: use getnetworkinfo to count outbound peers (bitcoind backend)

7 of 37 new or added lines in 1 file covered. (18.92%)

31329 existing lines in 480 files now uncovered.

143794 of 231070 relevant lines covered (62.23%)

19066.44 hits per line

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

88.19
/lnwallet/commitment.go
1
package lnwallet
2

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

7
        "github.com/btcsuite/btcd/blockchain"
8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/btcsuite/btcd/chaincfg/chainhash"
11
        "github.com/btcsuite/btcd/txscript"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/channeldb"
14
        "github.com/lightningnetwork/lnd/fn/v2"
15
        "github.com/lightningnetwork/lnd/input"
16
        "github.com/lightningnetwork/lnd/lntypes"
17
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
18
        "github.com/lightningnetwork/lnd/lnwire"
19
)
20

21
// AnchorSize is the constant anchor output size.
22
const AnchorSize = btcutil.Amount(330)
23

24
// DefaultAnchorsCommitMaxFeeRateSatPerVByte is the default max fee rate in
25
// sat/vbyte the initiator will use for anchor channels. This should be enough
26
// to ensure propagation before anchoring down the commitment transaction.
27
const DefaultAnchorsCommitMaxFeeRateSatPerVByte = 10
28

29
// CommitmentKeyRing holds all derived keys needed to construct commitment and
30
// HTLC transactions. The keys are derived differently depending whether the
31
// commitment transaction is ours or the remote peer's. Private keys associated
32
// with each key may belong to the commitment owner or the "other party" which
33
// is referred to in the field comments, regardless of which is local and which
34
// is remote.
35
type CommitmentKeyRing struct {
36
        // CommitPoint is the "per commitment point" used to derive the tweak
37
        // for each base point.
38
        CommitPoint *btcec.PublicKey
39

40
        // LocalCommitKeyTweak is the tweak used to derive the local public key
41
        // from the local payment base point or the local private key from the
42
        // base point secret. This may be included in a SignDescriptor to
43
        // generate signatures for the local payment key.
44
        //
45
        // NOTE: This will always refer to "our" local key, regardless of
46
        // whether this is our commit or not.
47
        LocalCommitKeyTweak []byte
48

49
        // TODO(roasbeef): need delay tweak as well?
50

51
        // LocalHtlcKeyTweak is the tweak used to derive the local HTLC key
52
        // from the local HTLC base point. This value is needed in order to
53
        // derive the final key used within the HTLC scripts in the commitment
54
        // transaction.
55
        //
56
        // NOTE: This will always refer to "our" local HTLC key, regardless of
57
        // whether this is our commit or not.
58
        LocalHtlcKeyTweak []byte
59

60
        // LocalHtlcKey is the key that will be used in any clause paying to
61
        // our node of any HTLC scripts within the commitment transaction for
62
        // this key ring set.
63
        //
64
        // NOTE: This will always refer to "our" local HTLC key, regardless of
65
        // whether this is our commit or not.
66
        LocalHtlcKey *btcec.PublicKey
67

68
        // RemoteHtlcKey is the key that will be used in clauses within the
69
        // HTLC script that send money to the remote party.
70
        //
71
        // NOTE: This will always refer to "their" remote HTLC key, regardless
72
        // of whether this is our commit or not.
73
        RemoteHtlcKey *btcec.PublicKey
74

75
        // ToLocalKey is the commitment transaction owner's key which is
76
        // included in HTLC success and timeout transaction scripts. This is
77
        // the public key used for the to_local output of the commitment
78
        // transaction.
79
        //
80
        // NOTE: Who's key this is depends on the current perspective. If this
81
        // is our commitment this will be our key.
82
        ToLocalKey *btcec.PublicKey
83

84
        // ToRemoteKey is the non-owner's payment key in the commitment tx.
85
        // This is the key used to generate the to_remote output within the
86
        // commitment transaction.
87
        //
88
        // NOTE: Who's key this is depends on the current perspective. If this
89
        // is our commitment this will be their key.
90
        ToRemoteKey *btcec.PublicKey
91

92
        // RevocationKey is the key that can be used by the other party to
93
        // redeem outputs from a revoked commitment transaction if it were to
94
        // be published.
95
        //
96
        // NOTE: Who can sign for this key depends on the current perspective.
97
        // If this is our commitment, it means the remote node can sign for
98
        // this key in case of a breach.
99
        RevocationKey *btcec.PublicKey
100
}
101

102
// DeriveCommitmentKeys generates a new commitment key set using the base points
103
// and commitment point. The keys are derived differently depending on the type
104
// of channel, and whether the commitment transaction is ours or the remote
105
// peer's.
106
func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
107
        whoseCommit lntypes.ChannelParty, chanType channeldb.ChannelType,
108
        localChanCfg, remoteChanCfg *channeldb.ChannelConfig) *CommitmentKeyRing {
12,204✔
109

12,204✔
110
        tweaklessCommit := chanType.IsTweakless()
12,204✔
111

12,204✔
112
        // Depending on if this is our commit or not, we'll choose the correct
12,204✔
113
        // base point.
12,204✔
114
        localBasePoint := localChanCfg.PaymentBasePoint
12,204✔
115
        if whoseCommit.IsLocal() {
17,205✔
116
                localBasePoint = localChanCfg.DelayBasePoint
5,001✔
117
        }
5,001✔
118

119
        // First, we'll derive all the keys that don't depend on the context of
120
        // whose commitment transaction this is.
121
        keyRing := &CommitmentKeyRing{
12,204✔
122
                CommitPoint: commitPoint,
12,204✔
123

12,204✔
124
                LocalCommitKeyTweak: input.SingleTweakBytes(
12,204✔
125
                        commitPoint, localBasePoint.PubKey,
12,204✔
126
                ),
12,204✔
127
                LocalHtlcKeyTweak: input.SingleTweakBytes(
12,204✔
128
                        commitPoint, localChanCfg.HtlcBasePoint.PubKey,
12,204✔
129
                ),
12,204✔
130
                LocalHtlcKey: input.TweakPubKey(
12,204✔
131
                        localChanCfg.HtlcBasePoint.PubKey, commitPoint,
12,204✔
132
                ),
12,204✔
133
                RemoteHtlcKey: input.TweakPubKey(
12,204✔
134
                        remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
12,204✔
135
                ),
12,204✔
136
        }
12,204✔
137

12,204✔
138
        // We'll now compute the to_local, to_remote, and revocation key based
12,204✔
139
        // on the current commitment point. All keys are tweaked each state in
12,204✔
140
        // order to ensure the keys from each state are unlinkable. To create
12,204✔
141
        // the revocation key, we take the opposite party's revocation base
12,204✔
142
        // point and combine that with the current commitment point.
12,204✔
143
        var (
12,204✔
144
                toLocalBasePoint    *btcec.PublicKey
12,204✔
145
                toRemoteBasePoint   *btcec.PublicKey
12,204✔
146
                revocationBasePoint *btcec.PublicKey
12,204✔
147
        )
12,204✔
148
        if whoseCommit.IsLocal() {
17,205✔
149
                toLocalBasePoint = localChanCfg.DelayBasePoint.PubKey
5,001✔
150
                toRemoteBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
5,001✔
151
                revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
5,001✔
152
        } else {
12,208✔
153
                toLocalBasePoint = remoteChanCfg.DelayBasePoint.PubKey
7,207✔
154
                toRemoteBasePoint = localChanCfg.PaymentBasePoint.PubKey
7,207✔
155
                revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
7,207✔
156
        }
7,207✔
157

158
        // With the base points assigned, we can now derive the actual keys
159
        // using the base point, and the current commitment tweak.
160
        keyRing.ToLocalKey = input.TweakPubKey(toLocalBasePoint, commitPoint)
12,204✔
161
        keyRing.RevocationKey = input.DeriveRevocationPubkey(
12,204✔
162
                revocationBasePoint, commitPoint,
12,204✔
163
        )
12,204✔
164

12,204✔
165
        // If this commitment should omit the tweak for the remote point, then
12,204✔
166
        // we'll use that directly, and ignore the commitPoint tweak.
12,204✔
167
        if tweaklessCommit {
22,400✔
168
                keyRing.ToRemoteKey = toRemoteBasePoint
10,196✔
169

10,196✔
170
                // If this is not our commitment, the above ToRemoteKey will be
10,196✔
171
                // ours, and we blank out the local commitment tweak to
10,196✔
172
                // indicate that the key should not be tweaked when signing.
10,196✔
173
                if whoseCommit.IsRemote() {
16,243✔
174
                        keyRing.LocalCommitKeyTweak = nil
6,047✔
175
                }
6,047✔
176
        } else {
2,012✔
177
                keyRing.ToRemoteKey = input.TweakPubKey(
2,012✔
178
                        toRemoteBasePoint, commitPoint,
2,012✔
179
                )
2,012✔
180
        }
2,012✔
181

182
        return keyRing
12,204✔
183
}
184

185
// WitnessScriptDesc holds the output script and the witness script for p2wsh
186
// outputs.
187
type WitnessScriptDesc struct {
188
        // OutputScript is the output's PkScript.
189
        OutputScript []byte
190

191
        // WitnessScript is the full script required to properly redeem the
192
        // output. This field should be set to the full script if a p2wsh
193
        // output is being signed. For p2wkh it should be set equal to the
194
        // PkScript.
195
        WitnessScript []byte
196
}
197

198
// PkScript is the public key script that commits to the final
199
// contract.
200
func (w *WitnessScriptDesc) PkScript() []byte {
40,272✔
201
        return w.OutputScript
40,272✔
202
}
40,272✔
203

204
// WitnessScriptToSign returns the witness script that we'll use when signing
205
// for the remote party, and also verifying signatures on our transactions. As
206
// an example, when we create an outgoing HTLC for the remote party, we want to
207
// sign their success path.
208
func (w *WitnessScriptDesc) WitnessScriptToSign() []byte {
8,175✔
209
        return w.WitnessScript
8,175✔
210
}
8,175✔
211

212
// WitnessScriptForPath returns the witness script for the given spending path.
213
// An error is returned if the path is unknown. This is useful as when
214
// constructing a control block for a given path, one also needs witness script
215
// being signed.
216
func (w *WitnessScriptDesc) WitnessScriptForPath(
217
        _ input.ScriptPath) ([]byte, error) {
406✔
218

406✔
219
        return w.WitnessScript, nil
406✔
220
}
406✔
221

222
// CommitScriptToSelf constructs the public key script for the output on the
223
// commitment transaction paying to the "owner" of said commitment transaction.
224
// The `initiator` argument should correspond to the owner of the commitment
225
// transaction which we are generating the to_local script for. If the other
226
// party learns of the preimage to the revocation hash, then they can claim all
227
// the settled funds in the channel, plus the unsettled funds.
228
func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
229
        selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32,
230
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) {
7,784✔
231

7,784✔
232
        switch {
7,784✔
233
        // For taproot scripts, we'll need to make a slightly modified script
234
        // where a NUMS key is used to force a script path reveal of either the
235
        // revocation or the CSV timeout.
236
        //
237
        // Our "redeem" script here is just the taproot witness program.
238
        case chanType.IsTaproot():
624✔
239
                // Determine script options based on channel type.
624✔
240
                var scriptOpts []input.TaprootScriptOpt
624✔
241
                if chanType.IsTaprootFinal() {
656✔
242
                        scriptOpts = append(scriptOpts, input.WithProdScripts())
32✔
243
                }
32✔
244

245
                return input.NewLocalCommitScriptTree(
624✔
246
                        csvDelay, selfKey, revokeKey, auxLeaf, scriptOpts...,
624✔
247
                )
624✔
248

249
        // If we are the initiator of a leased channel, then we have an
250
        // additional CLTV requirement in addition to the usual CSV
251
        // requirement.
252
        case initiator && chanType.HasLeaseExpiration():
4✔
253
                toLocalRedeemScript, err := input.LeaseCommitScriptToSelf(
4✔
254
                        selfKey, revokeKey, csvDelay, leaseExpiry,
4✔
255
                )
4✔
256
                if err != nil {
4✔
257
                        return nil, err
×
258
                }
×
259

260
                toLocalScriptHash, err := input.WitnessScriptHash(
4✔
261
                        toLocalRedeemScript,
4✔
262
                )
4✔
263
                if err != nil {
4✔
264
                        return nil, err
×
265
                }
×
266

267
                return &WitnessScriptDesc{
4✔
268
                        OutputScript:  toLocalScriptHash,
4✔
269
                        WitnessScript: toLocalRedeemScript,
4✔
270
                }, nil
4✔
271

272
        default:
7,164✔
273
                toLocalRedeemScript, err := input.CommitScriptToSelf(
7,164✔
274
                        csvDelay, selfKey, revokeKey,
7,164✔
275
                )
7,164✔
276
                if err != nil {
7,164✔
277
                        return nil, err
×
278
                }
×
279

280
                toLocalScriptHash, err := input.WitnessScriptHash(
7,164✔
281
                        toLocalRedeemScript,
7,164✔
282
                )
7,164✔
283
                if err != nil {
7,164✔
284
                        return nil, err
×
285
                }
×
286

287
                return &WitnessScriptDesc{
7,164✔
288
                        OutputScript:  toLocalScriptHash,
7,164✔
289
                        WitnessScript: toLocalRedeemScript,
7,164✔
290
                }, nil
7,164✔
291
        }
292
}
293

294
// CommitScriptToRemote derives the appropriate to_remote script based on the
295
// channel's commitment type. The `initiator` argument should correspond to the
296
// owner of the commitment transaction which we are generating the to_remote
297
// script for. The second return value is the CSV delay of the output script,
298
// what must be satisfied in order to spend the output.
299
func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
300
        remoteKey *btcec.PublicKey, leaseExpiry uint32,
301
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, uint32, error) {
7,724✔
302

7,724✔
303
        switch {
7,724✔
304
        // If we are not the initiator of a leased channel, then the remote
305
        // party has an additional CLTV requirement in addition to the 1 block
306
        // CSV requirement.
307
        case chanType.HasLeaseExpiration() && !initiator:
4✔
308
                script, err := input.LeaseCommitScriptToRemoteConfirmed(
4✔
309
                        remoteKey, leaseExpiry,
4✔
310
                )
4✔
311
                if err != nil {
4✔
312
                        return nil, 0, err
×
313
                }
×
314

315
                p2wsh, err := input.WitnessScriptHash(script)
4✔
316
                if err != nil {
4✔
317
                        return nil, 0, err
×
318
                }
×
319

320
                return &WitnessScriptDesc{
4✔
321
                        OutputScript:  p2wsh,
4✔
322
                        WitnessScript: script,
4✔
323
                }, 1, nil
4✔
324

325
        // For taproot channels, we'll use a slightly different format, where
326
        // we use a NUMS key to force the remote party to take a script path,
327
        // with the sole tap leaf enforcing the 1 CSV delay.
328
        case chanType.IsTaproot():
617✔
329
                // Determine script options based on channel type.
617✔
330
                var scriptOpts []input.TaprootScriptOpt
617✔
331
                if chanType.IsTaprootFinal() {
646✔
332
                        scriptOpts = append(scriptOpts, input.WithProdScripts())
29✔
333
                }
29✔
334

335
                toRemoteScriptTree, err := input.NewRemoteCommitScriptTree(
617✔
336
                        remoteKey, auxLeaf, scriptOpts...,
617✔
337
                )
617✔
338
                if err != nil {
617✔
339
                        return nil, 0, err
×
340
                }
×
341

342
                return toRemoteScriptTree, 1, nil
617✔
343

344
        // If this channel type has anchors, we derive the delayed to_remote
345
        // script.
346
        case chanType.HasAnchors():
286✔
347
                script, err := input.CommitScriptToRemoteConfirmed(remoteKey)
286✔
348
                if err != nil {
286✔
349
                        return nil, 0, err
×
350
                }
×
351

352
                p2wsh, err := input.WitnessScriptHash(script)
286✔
353
                if err != nil {
286✔
354
                        return nil, 0, err
×
355
                }
×
356

357
                return &WitnessScriptDesc{
286✔
358
                        OutputScript:  p2wsh,
286✔
359
                        WitnessScript: script,
286✔
360
                }, 1, nil
286✔
361

362
        default:
6,829✔
363
                // Otherwise the to_remote will be a simple p2wkh.
6,829✔
364
                p2wkh, err := input.CommitScriptUnencumbered(remoteKey)
6,829✔
365
                if err != nil {
6,829✔
366
                        return nil, 0, err
×
367
                }
×
368

369
                // Since this is a regular P2WKH, the WitnessScipt and PkScript
370
                // should both be set to the script hash.
371
                return &WitnessScriptDesc{
6,829✔
372
                        OutputScript:  p2wkh,
6,829✔
373
                        WitnessScript: p2wkh,
6,829✔
374
                }, 0, nil
6,829✔
375
        }
376
}
377

378
// HtlcSigHashType returns the sighash type to use for HTLC success and timeout
379
// transactions given the channel type.
380
func HtlcSigHashType(chanType channeldb.ChannelType) txscript.SigHashType {
4,541✔
381
        if chanType.HasAnchors() {
5,079✔
382
                return txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
538✔
383
        }
538✔
384

385
        return txscript.SigHashAll
4,007✔
386
}
387

388
// HtlcSignDetails converts the passed parameters to a SignDetails valid for
389
// this channel type. For non-anchor channels this will return nil.
390
func HtlcSignDetails(chanType channeldb.ChannelType, signDesc input.SignDescriptor,
391
        sigHash txscript.SigHashType, peerSig input.Signature) *input.SignDetails {
107✔
392

107✔
393
        // Non-anchor channels don't need sign details, as the HTLC second
107✔
394
        // level cannot be altered.
107✔
395
        if !chanType.HasAnchors() {
152✔
396
                return nil
45✔
397
        }
45✔
398

399
        return &input.SignDetails{
66✔
400
                SignDesc:    signDesc,
66✔
401
                SigHashType: sigHash,
66✔
402
                PeerSig:     peerSig,
66✔
403
        }
66✔
404
}
405

406
// HtlcSecondLevelInputSequence dictates the sequence number we must use on the
407
// input to a second level HTLC transaction.
408
func HtlcSecondLevelInputSequence(chanType channeldb.ChannelType) uint32 {
8,198✔
409
        if chanType.HasAnchors() {
8,703✔
410
                return 1
505✔
411
        }
505✔
412

413
        return 0
7,697✔
414
}
415

416
// sweepSigHash returns the sign descriptor to use when signing a sweep
417
// transaction. For taproot channels, we'll use this to always sweep with
418
// sighash default.
419
func sweepSigHash(chanType channeldb.ChannelType) txscript.SigHashType {
454✔
420
        if chanType.IsTaproot() {
506✔
421
                return txscript.SigHashDefault
52✔
422
        }
52✔
423

424
        return txscript.SigHashAll
406✔
425
}
426

427
// SecondLevelHtlcScript derives the appropriate second level HTLC script based
428
// on the channel's commitment type. It is the uniform script that's used as the
429
// output for the second-level HTLC transactions. The second level transaction
430
// act as a sort of covenant, ensuring that a 2-of-2 multi-sig output can only
431
// be spent in a particular way, and to a particular output. The `initiator`
432
// argument should correspond to the owner of the commitment transaction which
433
// we are generating the to_local script for.
434
func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool,
435
        revocationKey, delayKey *btcec.PublicKey, csvDelay, leaseExpiry uint32,
436
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) {
8,189✔
437

8,189✔
438
        switch {
8,189✔
439
        // For taproot channels, the pkScript is a segwit v1 p2tr output.
440
        case chanType.IsTaproot():
236✔
441
                // Determine script options based on channel type.
236✔
442
                var scriptOpts []input.TaprootScriptOpt
236✔
443
                if chanType.IsTaprootFinal() {
267✔
444
                        scriptOpts = append(scriptOpts, input.WithProdScripts())
31✔
445
                }
31✔
446

447
                return input.TaprootSecondLevelScriptTree(
236✔
448
                        revocationKey, delayKey, csvDelay, auxLeaf,
236✔
449
                        scriptOpts...,
236✔
450
                )
236✔
451

452
        // If we are the initiator of a leased channel, then we have an
453
        // additional CLTV requirement in addition to the usual CSV
454
        // requirement.
455
        case initiator && chanType.HasLeaseExpiration():
4✔
456
                witnessScript, err := input.LeaseSecondLevelHtlcScript(
4✔
457
                        revocationKey, delayKey, csvDelay, leaseExpiry,
4✔
458
                )
4✔
459
                if err != nil {
4✔
460
                        return nil, err
×
461
                }
×
462

463
                pkScript, err := input.WitnessScriptHash(witnessScript)
4✔
464
                if err != nil {
4✔
465
                        return nil, err
×
466
                }
×
467

468
                return &WitnessScriptDesc{
4✔
469
                        OutputScript:  pkScript,
4✔
470
                        WitnessScript: witnessScript,
4✔
471
                }, nil
4✔
472

473
        default:
7,957✔
474
                witnessScript, err := input.SecondLevelHtlcScript(
7,957✔
475
                        revocationKey, delayKey, csvDelay,
7,957✔
476
                )
7,957✔
477
                if err != nil {
7,957✔
478
                        return nil, err
×
479
                }
×
480

481
                pkScript, err := input.WitnessScriptHash(witnessScript)
7,957✔
482
                if err != nil {
7,957✔
483
                        return nil, err
×
484
                }
×
485

486
                return &WitnessScriptDesc{
7,957✔
487
                        OutputScript:  pkScript,
7,957✔
488
                        WitnessScript: witnessScript,
7,957✔
489
                }, nil
7,957✔
490
        }
491
}
492

493
// CommitWeight returns the base commitment weight before adding HTLCs.
494
func CommitWeight(chanType channeldb.ChannelType) lntypes.WeightUnit {
20,893✔
495
        switch {
20,893✔
496
        case chanType.IsTaproot():
1,273✔
497
                return input.TaprootCommitWeight
1,273✔
498

499
        // If this commitment has anchors, it will be slightly heavier.
500
        case chanType.HasAnchors():
1,848✔
501
                return input.AnchorCommitWeight
1,848✔
502

503
        default:
17,780✔
504
                return input.CommitWeight
17,780✔
505
        }
506
}
507

508
// HtlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
509
// transaction based on the current fee rate.
510
func HtlcTimeoutFee(chanType channeldb.ChannelType,
511
        feePerKw chainfee.SatPerKWeight) btcutil.Amount {
95,616✔
512

95,616✔
513
        switch {
95,616✔
514
        // For zero-fee HTLC channels, this will always be zero, regardless of
515
        // feerate.
516
        case chanType.ZeroHtlcTxFee() || chanType.IsTaproot():
1,293✔
517
                return 0
1,293✔
518

UNCOV
519
        case chanType.HasAnchors():
588✔
UNCOV
520
                return feePerKw.FeeForWeight(input.HtlcTimeoutWeightConfirmed)
588✔
521

522
        default:
93,739✔
523
                return feePerKw.FeeForWeight(input.HtlcTimeoutWeight)
93,739✔
524
        }
525
}
526

527
// HtlcSuccessFee returns the fee in satoshis required for an HTLC success
528
// transaction based on the current fee rate.
529
func HtlcSuccessFee(chanType channeldb.ChannelType,
530
        feePerKw chainfee.SatPerKWeight) btcutil.Amount {
105,478✔
531

105,478✔
532
        switch {
105,478✔
533
        // For zero-fee HTLC channels, this will always be zero, regardless of
534
        // feerate.
535
        case chanType.ZeroHtlcTxFee() || chanType.IsTaproot():
2,071✔
536
                return 0
2,071✔
537

UNCOV
538
        case chanType.HasAnchors():
1,179✔
UNCOV
539
                return feePerKw.FeeForWeight(input.HtlcSuccessWeightConfirmed)
1,179✔
540

541
        default:
102,232✔
542
                return feePerKw.FeeForWeight(input.HtlcSuccessWeight)
102,232✔
543
        }
544
}
545

546
// CommitScriptAnchors return the scripts to use for the local and remote
547
// anchor.
548
func CommitScriptAnchors(chanType channeldb.ChannelType,
549
        localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
550
        keyRing *CommitmentKeyRing) (
551
        input.ScriptDescriptor, input.ScriptDescriptor, error) {
651✔
552

651✔
553
        var (
651✔
554
                anchorScript func(
651✔
555
                        key *btcec.PublicKey) (input.ScriptDescriptor, error)
651✔
556

651✔
557
                keySelector func(*channeldb.ChannelConfig,
651✔
558
                        bool) *btcec.PublicKey
651✔
559
        )
651✔
560

651✔
561
        switch {
651✔
562
        // For taproot channels, the anchor is slightly different: the top
563
        // level key is now the (relative) local delay and remote public key,
564
        // since these are fully revealed once the commitment hits the chain.
565
        case chanType.IsTaproot():
398✔
566
                anchorScript = func(
398✔
567
                        key *btcec.PublicKey) (input.ScriptDescriptor, error) {
1,190✔
568

792✔
569
                        return input.NewAnchorScriptTree(key)
792✔
570
                }
792✔
571

572
                keySelector = func(cfg *channeldb.ChannelConfig,
398✔
573
                        local bool) *btcec.PublicKey {
1,190✔
574

792✔
575
                        if local {
1,190✔
576
                                return keyRing.ToLocalKey
398✔
577
                        }
398✔
578

579
                        return keyRing.ToRemoteKey
398✔
580
                }
581

582
        // For normal channels we'll use the multi-sig keys since those are
583
        // revealed when the channel closes
584
        default:
257✔
585
                // For normal channels, we'll create a p2wsh script based on
257✔
586
                // the target key.
257✔
587
                anchorScript = func(
257✔
588
                        key *btcec.PublicKey) (input.ScriptDescriptor, error) {
767✔
589

510✔
590
                        script, err := input.CommitScriptAnchor(key)
510✔
591
                        if err != nil {
510✔
592
                                return nil, err
×
593
                        }
×
594

595
                        scriptHash, err := input.WitnessScriptHash(script)
510✔
596
                        if err != nil {
510✔
597
                                return nil, err
×
598
                        }
×
599

600
                        return &WitnessScriptDesc{
510✔
601
                                OutputScript:  scriptHash,
510✔
602
                                WitnessScript: script,
510✔
603
                        }, nil
510✔
604
                }
605

606
                // For the existing channels, we'll always select the multi-sig
607
                // key from the party's channel config.
608
                keySelector = func(cfg *channeldb.ChannelConfig,
257✔
609
                        _ bool) *btcec.PublicKey {
767✔
610

510✔
611
                        return cfg.MultiSigKey.PubKey
510✔
612
                }
510✔
613
        }
614

615
        // Get the script used for the anchor output spendable by the local
616
        // node.
617
        localAnchor, err := anchorScript(keySelector(localChanCfg, true))
651✔
618
        if err != nil {
651✔
619
                return nil, nil, err
×
620
        }
×
621

622
        // And the anchor spendable by the remote node.
623
        remoteAnchor, err := anchorScript(keySelector(remoteChanCfg, false))
651✔
624
        if err != nil {
651✔
625
                return nil, nil, err
×
626
        }
×
627

628
        return localAnchor, remoteAnchor, nil
651✔
629
}
630

631
// CommitmentBuilder is a type that wraps the type of channel we are dealing
632
// with, and abstracts the various ways of constructing commitment
633
// transactions.
634
type CommitmentBuilder struct {
635
        // chanState is the underlying channel's state struct, used to
636
        // determine the type of channel we are dealing with, and relevant
637
        // parameters.
638
        chanState *channeldb.OpenChannel
639

640
        // obfuscator is a 48-bit state hint that's used to obfuscate the
641
        // current state number on the commitment transactions.
642
        obfuscator [StateHintSize]byte
643

644
        // auxLeafStore is an interface that allows us to fetch auxiliary
645
        // tapscript leaves for the commitment output.
646
        auxLeafStore fn.Option[AuxLeafStore]
647
}
648

649
// NewCommitmentBuilder creates a new CommitmentBuilder from chanState.
650
func NewCommitmentBuilder(chanState *channeldb.OpenChannel,
651
        leafStore fn.Option[AuxLeafStore]) *CommitmentBuilder {
1,079✔
652

1,079✔
653
        // The anchor channel type MUST be tweakless.
1,079✔
654
        if chanState.ChanType.HasAnchors() && !chanState.ChanType.IsTweakless() {
1,079✔
655
                panic("invalid channel type combination")
×
656
        }
657

658
        return &CommitmentBuilder{
1,079✔
659
                chanState:    chanState,
1,079✔
660
                obfuscator:   createStateHintObfuscator(chanState),
1,079✔
661
                auxLeafStore: leafStore,
1,079✔
662
        }
1,079✔
663
}
664

665
// createStateHintObfuscator derives and assigns the state hint obfuscator for
666
// the channel, which is used to encode the commitment height in the sequence
667
// number of commitment transaction inputs.
668
func createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte {
1,392✔
669
        if state.IsInitiator {
2,223✔
670
                return DeriveStateHintObfuscator(
831✔
671
                        state.LocalChanCfg.PaymentBasePoint.PubKey,
831✔
672
                        state.RemoteChanCfg.PaymentBasePoint.PubKey,
831✔
673
                )
831✔
674
        }
831✔
675

676
        return DeriveStateHintObfuscator(
565✔
677
                state.RemoteChanCfg.PaymentBasePoint.PubKey,
565✔
678
                state.LocalChanCfg.PaymentBasePoint.PubKey,
565✔
679
        )
565✔
680
}
681

682
// unsignedCommitmentTx is the final commitment created from evaluating an HTLC
683
// view at a given height, along with some meta data.
684
type unsignedCommitmentTx struct {
685
        // txn is the final, unsigned commitment transaction for this view.
686
        txn *wire.MsgTx
687

688
        // fee is the total fee of the commitment transaction.
689
        fee btcutil.Amount
690

691
        // ourBalance is our balance on this commitment *after* subtracting
692
        // commitment fees and anchor outputs. This can be different than the
693
        // balances before creating the commitment transaction as one party must
694
        // pay the commitment fee.
695
        ourBalance lnwire.MilliSatoshi
696

697
        // theirBalance is their balance of this commitment *after* subtracting
698
        // commitment fees and anchor outputs. This can be different than the
699
        // balances before creating the commitment transaction as one party must
700
        // pay the commitment fee.
701
        theirBalance lnwire.MilliSatoshi
702

703
        // cltvs is a sorted list of CLTV deltas for each HTLC on the commitment
704
        // transaction. Any non-htlc outputs will have a CLTV delay of zero.
705
        cltvs []uint32
706
}
707

708
// createUnsignedCommitmentTx generates the unsigned commitment transaction for
709
// a commitment view and returns it as part of the unsignedCommitmentTx. The
710
// passed in balances should be balances *before* subtracting any commitment
711
// fees, but after anchor outputs.
712
func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
713
        theirBalance lnwire.MilliSatoshi, whoseCommit lntypes.ChannelParty,
714
        feePerKw chainfee.SatPerKWeight, height uint64, originalHtlcView,
715
        filteredHTLCView *HtlcView, keyRing *CommitmentKeyRing,
716
        prevCommit *commitment) (*unsignedCommitmentTx, error) {
4,432✔
717

4,432✔
718
        dustLimit := cb.chanState.LocalChanCfg.DustLimit
4,432✔
719
        if whoseCommit.IsRemote() {
6,662✔
720
                dustLimit = cb.chanState.RemoteChanCfg.DustLimit
2,230✔
721
        }
2,230✔
722

723
        numHTLCs := int64(0)
4,432✔
724
        for _, htlc := range filteredHTLCView.Updates.Local {
13,695✔
725
                if HtlcIsDust(
9,263✔
726
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
9,263✔
727
                        htlc.Amount.ToSatoshis(), dustLimit,
9,263✔
728
                ) {
14,506✔
729

5,243✔
730
                        continue
5,243✔
731
                }
732

733
                numHTLCs++
4,024✔
734
        }
735
        for _, htlc := range filteredHTLCView.Updates.Remote {
13,636✔
736
                if HtlcIsDust(
9,204✔
737
                        cb.chanState.ChanType, true, whoseCommit, feePerKw,
9,204✔
738
                        htlc.Amount.ToSatoshis(), dustLimit,
9,204✔
739
                ) {
14,447✔
740

5,243✔
741
                        continue
5,243✔
742
                }
743

744
                numHTLCs++
3,965✔
745
        }
746

747
        // Next, we'll calculate the fee for the commitment transaction based
748
        // on its total weight. Once we have the total weight, we'll multiply
749
        // by the current fee-per-kw, then divide by 1000 to get the proper
750
        // fee.
751
        totalCommitWeight := CommitWeight(cb.chanState.ChanType) +
4,432✔
752
                lntypes.WeightUnit(input.HTLCWeight*numHTLCs)
4,432✔
753

4,432✔
754
        // With the weight known, we can now calculate the commitment fee,
4,432✔
755
        // ensuring that we account for any dust outputs trimmed above.
4,432✔
756
        commitFee := feePerKw.FeeForWeight(totalCommitWeight)
4,432✔
757
        commitFeeMSat := lnwire.NewMSatFromSatoshis(commitFee)
4,432✔
758

4,432✔
759
        // Currently, within the protocol, the initiator always pays the fees.
4,432✔
760
        // So we'll subtract the fee amount from the balance of the current
4,432✔
761
        // initiator. If the initiator is unable to pay the fee fully, then
4,432✔
762
        // their entire output is consumed.
4,432✔
763
        switch {
4,432✔
764
        case cb.chanState.IsInitiator && commitFee > ourBalance.ToSatoshis():
×
765
                ourBalance = 0
×
766

767
        case cb.chanState.IsInitiator:
2,218✔
768
                ourBalance -= commitFeeMSat
2,218✔
769

770
        case !cb.chanState.IsInitiator && commitFee > theirBalance.ToSatoshis():
×
771
                theirBalance = 0
×
772

773
        case !cb.chanState.IsInitiator:
2,218✔
774
                theirBalance -= commitFeeMSat
2,218✔
775
        }
776

777
        var commitTx *wire.MsgTx
4,432✔
778

4,432✔
779
        // Before we create the commitment transaction below, we'll try to see
4,432✔
780
        // if there're any aux leaves that need to be a part of the tapscript
4,432✔
781
        // tree. We'll only do this if we have a custom blob defined though.
4,432✔
782
        auxResult, err := fn.MapOptionZ(
4,432✔
783
                cb.auxLeafStore,
4,432✔
784
                func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
8,790✔
UNCOV
785
                        return auxLeavesFromView(
4,358✔
UNCOV
786
                                s, cb.chanState, prevCommit.customBlob,
4,358✔
UNCOV
787
                                originalHtlcView, whoseCommit, ourBalance,
4,358✔
UNCOV
788
                                theirBalance, *keyRing,
4,358✔
UNCOV
789
                        )
4,358✔
UNCOV
790
                },
4,358✔
791
        ).Unpack()
792
        if err != nil {
4,432✔
793
                return nil, fmt.Errorf("unable to fetch aux leaves: %w", err)
×
794
        }
×
795

796
        // Depending on whether the transaction is ours or not, we call
797
        // CreateCommitTx with parameters matching the perspective, to generate
798
        // a new commitment transaction with all the latest unsettled/un-timed
799
        // out HTLCs.
800
        var leaseExpiry uint32
4,432✔
801
        if cb.chanState.ChanType.HasLeaseExpiration() {
4,436✔
802
                leaseExpiry = cb.chanState.ThawHeight
4✔
803
        }
4✔
804
        if whoseCommit.IsLocal() {
6,638✔
805
                commitTx, err = CreateCommitTx(
2,206✔
806
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,206✔
807
                        &cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
2,206✔
808
                        ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
2,206✔
809
                        numHTLCs, cb.chanState.IsInitiator, leaseExpiry,
2,206✔
810
                        auxResult.AuxLeaves,
2,206✔
811
                )
2,206✔
812
        } else {
4,436✔
813
                commitTx, err = CreateCommitTx(
2,230✔
814
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,230✔
815
                        &cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
2,230✔
816
                        theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
2,230✔
817
                        numHTLCs, !cb.chanState.IsInitiator, leaseExpiry,
2,230✔
818
                        auxResult.AuxLeaves,
2,230✔
819
                )
2,230✔
820
        }
2,230✔
821
        if err != nil {
4,432✔
822
                return nil, err
×
823
        }
×
824

825
        // Similarly, we'll now attempt to extract the set of aux leaves for
826
        // the set of incoming and outgoing HTLCs.
827
        incomingAuxLeaves := fn.MapOption(
4,432✔
828
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,432✔
829
                        return leaves.IncomingHtlcLeaves
×
830
                },
×
831
        )(auxResult.AuxLeaves)
832
        outgoingAuxLeaves := fn.MapOption(
4,432✔
833
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,432✔
834
                        return leaves.OutgoingHtlcLeaves
×
835
                },
×
836
        )(auxResult.AuxLeaves)
837

838
        // We'll now add all the HTLC outputs to the commitment transaction.
839
        // Each output includes an off-chain 2-of-2 covenant clause, so we'll
840
        // need the objective local/remote keys for this particular commitment
841
        // as well. For any non-dust HTLCs that are manifested on the commitment
842
        // transaction, we'll also record its CLTV which is required to sort the
843
        // commitment transaction below. The slice is initially sized to the
844
        // number of existing outputs, since any outputs already added are
845
        // commitment outputs and should correspond to zero values for the
846
        // purposes of sorting.
847
        cltvs := make([]uint32, len(commitTx.TxOut))
4,432✔
848
        htlcIndexes := make([]input.HtlcIndex, len(commitTx.TxOut))
4,432✔
849
        for _, htlc := range filteredHTLCView.Updates.Local {
13,695✔
850
                if HtlcIsDust(
9,263✔
851
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
9,263✔
852
                        htlc.Amount.ToSatoshis(), dustLimit,
9,263✔
853
                ) {
14,506✔
854

5,243✔
855
                        continue
5,243✔
856
                }
857

858
                auxLeaf := fn.FlatMapOption(
4,024✔
859
                        func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf {
4,024✔
860
                                return leaves[htlc.HtlcIndex].AuxTapLeaf
×
861
                        },
×
862
                )(outgoingAuxLeaves)
863

864
                err := addHTLC(
4,024✔
865
                        commitTx, whoseCommit, false, htlc, keyRing,
4,024✔
866
                        cb.chanState.ChanType, auxLeaf,
4,024✔
867
                )
4,024✔
868
                if err != nil {
4,024✔
869
                        return nil, err
×
870
                }
×
871

872
                // We want to add the CLTV and HTLC index to their respective
873
                // slices, even if we already pre-allocated them.
874
                cltvs = append(cltvs, htlc.Timeout)               //nolint
4,024✔
875
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
4,024✔
876
        }
877
        for _, htlc := range filteredHTLCView.Updates.Remote {
13,636✔
878
                if HtlcIsDust(
9,204✔
879
                        cb.chanState.ChanType, true, whoseCommit, feePerKw,
9,204✔
880
                        htlc.Amount.ToSatoshis(), dustLimit,
9,204✔
881
                ) {
14,447✔
882

5,243✔
883
                        continue
5,243✔
884
                }
885

886
                auxLeaf := fn.FlatMapOption(
3,965✔
887
                        func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf {
3,965✔
888
                                return leaves[htlc.HtlcIndex].AuxTapLeaf
×
889
                        },
×
890
                )(incomingAuxLeaves)
891

892
                err := addHTLC(
3,965✔
893
                        commitTx, whoseCommit, true, htlc, keyRing,
3,965✔
894
                        cb.chanState.ChanType, auxLeaf,
3,965✔
895
                )
3,965✔
896
                if err != nil {
3,965✔
897
                        return nil, err
×
898
                }
×
899

900
                // We want to add the CLTV and HTLC index to their respective
901
                // slices, even if we already pre-allocated them.
902
                cltvs = append(cltvs, htlc.Timeout)               //nolint
3,965✔
903
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
3,965✔
904
        }
905

906
        // Set the state hint of the commitment transaction to facilitate
907
        // quickly recovering the necessary penalty state in the case of an
908
        // uncooperative broadcast.
909
        err = SetStateNumHint(commitTx, height, cb.obfuscator)
4,432✔
910
        if err != nil {
4,432✔
911
                return nil, err
×
912
        }
×
913

914
        // Sort the transactions according to the agreed upon canonical
915
        // ordering (which might be customized for custom channel types, but
916
        // deterministic and both parties will arrive at the same result). This
917
        // lets us skip sending the entire transaction over, instead we'll just
918
        // send signatures.
919
        commitSort := auxResult.CommitSortFunc.UnwrapOr(DefaultCommitSort)
4,432✔
920
        err = commitSort(commitTx, cltvs, htlcIndexes)
4,432✔
921
        if err != nil {
4,432✔
922
                return nil, fmt.Errorf("unable to sort commitment "+
×
923
                        "transaction: %w", err)
×
924
        }
×
925

926
        // Next, we'll ensure that we don't accidentally create a commitment
927
        // transaction which would be invalid by consensus.
928
        uTx := btcutil.NewTx(commitTx)
4,432✔
929
        if err := blockchain.CheckTransactionSanity(uTx); err != nil {
4,432✔
930
                return nil, err
×
931
        }
×
932

933
        // Finally, we'll assert that were not attempting to draw more out of
934
        // the channel that was originally placed within it.
935
        var totalOut btcutil.Amount
4,432✔
936
        for _, txOut := range commitTx.TxOut {
22,169✔
937
                totalOut += btcutil.Amount(txOut.Value)
17,737✔
938
        }
17,737✔
939
        if totalOut+commitFee > cb.chanState.Capacity {
4,432✔
940
                return nil, fmt.Errorf("height=%v, for ChannelPoint(%v) "+
×
941
                        "attempts to consume %v while channel capacity is %v",
×
942
                        height, cb.chanState.FundingOutpoint,
×
943
                        totalOut+commitFee, cb.chanState.Capacity)
×
944
        }
×
945

946
        return &unsignedCommitmentTx{
4,432✔
947
                txn:          commitTx,
4,432✔
948
                fee:          commitFee,
4,432✔
949
                ourBalance:   ourBalance,
4,432✔
950
                theirBalance: theirBalance,
4,432✔
951
                cltvs:        cltvs,
4,432✔
952
        }, nil
4,432✔
953
}
954

955
// CreateCommitTx creates a commitment transaction, spending from specified
956
// funding output. The commitment transaction contains two outputs: one local
957
// output paying to the "owner" of the commitment transaction which can be
958
// spent after a relative block delay or revocation event, and a remote output
959
// paying the counterparty within the channel, which can be spent immediately
960
// or after a delay depending on the commitment type. The `initiator` argument
961
// should correspond to the owner of the commitment transaction we are creating.
962
func CreateCommitTx(chanType channeldb.ChannelType,
963
        fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
964
        localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
965
        amountToLocal, amountToRemote btcutil.Amount,
966
        numHTLCs int64, initiator bool, leaseExpiry uint32,
967
        auxLeaves fn.Option[CommitAuxLeaves]) (*wire.MsgTx, error) {
5,510✔
968

5,510✔
969
        // First, we create the script for the delayed "pay-to-self" output.
5,510✔
970
        // This output has 2 main redemption clauses: either we can redeem the
5,510✔
971
        // output after a relative block delay, or the remote node can claim
5,510✔
972
        // the funds with the revocation key if we broadcast a revoked
5,510✔
973
        // commitment transaction.
5,510✔
974
        localAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf {
5,510✔
975
                return l.LocalAuxLeaf
×
976
        })(auxLeaves)
×
977
        toLocalScript, err := CommitScriptToSelf(
5,510✔
978
                chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
5,510✔
979
                uint32(localChanCfg.CsvDelay), leaseExpiry,
5,510✔
980
                fn.FlattenOption(localAuxLeaf),
5,510✔
981
        )
5,510✔
982
        if err != nil {
5,510✔
983
                return nil, err
×
984
        }
×
985

986
        // Next, we create the script paying to the remote.
987
        remoteAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf {
5,510✔
988
                return l.RemoteAuxLeaf
×
989
        })(auxLeaves)
×
990
        toRemoteScript, _, err := CommitScriptToRemote(
5,510✔
991
                chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
5,510✔
992
                fn.FlattenOption(remoteAuxLeaf),
5,510✔
993
        )
5,510✔
994
        if err != nil {
5,510✔
995
                return nil, err
×
996
        }
×
997

998
        // Now that both output scripts have been created, we can finally create
999
        // the transaction itself. We use a transaction version of 2 since CSV
1000
        // will fail unless the tx version is >= 2.
1001
        commitTx := wire.NewMsgTx(2)
5,510✔
1002
        commitTx.AddTxIn(&fundingOutput)
5,510✔
1003

5,510✔
1004
        // Avoid creating dust outputs within the commitment transaction.
5,510✔
1005
        localOutput := amountToLocal >= localChanCfg.DustLimit
5,510✔
1006
        if localOutput {
10,944✔
1007
                commitTx.AddTxOut(&wire.TxOut{
5,434✔
1008
                        PkScript: toLocalScript.PkScript(),
5,434✔
1009
                        Value:    int64(amountToLocal),
5,434✔
1010
                })
5,434✔
1011
        }
5,434✔
1012

1013
        remoteOutput := amountToRemote >= localChanCfg.DustLimit
5,510✔
1014
        if remoteOutput {
10,946✔
1015
                commitTx.AddTxOut(&wire.TxOut{
5,436✔
1016
                        PkScript: toRemoteScript.PkScript(),
5,436✔
1017
                        Value:    int64(amountToRemote),
5,436✔
1018
                })
5,436✔
1019
        }
5,436✔
1020

1021
        // If this channel type has anchors, we'll also add those.
1022
        if chanType.HasAnchors() {
6,123✔
1023
                localAnchor, remoteAnchor, err := CommitScriptAnchors(
613✔
1024
                        chanType, localChanCfg, remoteChanCfg, keyRing,
613✔
1025
                )
613✔
1026
                if err != nil {
613✔
1027
                        return nil, err
×
1028
                }
×
1029

1030
                // Add local anchor output only if we have a commitment output
1031
                // or there are HTLCs.
1032
                if localOutput || numHTLCs > 0 {
1,210✔
1033
                        commitTx.AddTxOut(&wire.TxOut{
597✔
1034
                                PkScript: localAnchor.PkScript(),
597✔
1035
                                Value:    int64(AnchorSize),
597✔
1036
                        })
597✔
1037
                }
597✔
1038

1039
                // Add anchor output to remote only if they have a commitment
1040
                // output or there are HTLCs.
1041
                if remoteOutput || numHTLCs > 0 {
1,210✔
1042
                        commitTx.AddTxOut(&wire.TxOut{
597✔
1043
                                PkScript: remoteAnchor.PkScript(),
597✔
1044
                                Value:    int64(AnchorSize),
597✔
1045
                        })
597✔
1046
                }
597✔
1047
        }
1048

1049
        return commitTx, nil
5,510✔
1050
}
1051

1052
// CoopCloseBalance returns the final balances that should be used to create
1053
// the cooperative close tx, given the channel type and transaction fee.
1054
func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool,
1055
        coopCloseFee, ourBalance, theirBalance, commitFee btcutil.Amount,
1056
        feePayer fn.Option[lntypes.ChannelParty],
1057
) (btcutil.Amount, btcutil.Amount, error) {
154✔
1058

154✔
1059
        // We'll make sure we account for the complete balance by adding the
154✔
1060
        // current dangling commitment fee to the balance of the initiator.
154✔
1061
        initiatorDelta := commitFee
154✔
1062

154✔
1063
        // Since the initiator's balance also is stored after subtracting the
154✔
1064
        // anchor values, add that back in case this was an anchor commitment.
154✔
1065
        if chanType.HasAnchors() {
244✔
1066
                initiatorDelta += 2 * AnchorSize
90✔
1067
        }
90✔
1068

1069
        // To start with, we'll add the anchor and/or commitment fee to the
1070
        // balance of the initiator.
1071
        if isInitiator {
239✔
1072
                ourBalance += initiatorDelta
85✔
1073
        } else {
158✔
1074
                theirBalance += initiatorDelta
73✔
1075
        }
73✔
1076

1077
        // With the initiator's balance credited, we'll now subtract the closing
1078
        // fee from the closing party. By default, the initiator pays the full
1079
        // amount, but this can be overridden by the feePayer option.
1080
        defaultPayer := func() lntypes.ChannelParty {
308✔
1081
                if isInitiator {
239✔
1082
                        return lntypes.Local
85✔
1083
                }
85✔
1084

1085
                return lntypes.Remote
73✔
1086
        }()
1087
        payer := feePayer.UnwrapOr(defaultPayer)
154✔
1088

154✔
1089
        // Based on the payer computed above, we'll subtract the closing fee.
154✔
1090
        switch payer {
154✔
1091
        case lntypes.Local:
84✔
1092
                ourBalance -= coopCloseFee
84✔
1093
        case lntypes.Remote:
74✔
1094
                theirBalance -= coopCloseFee
74✔
1095
        }
1096

1097
        // During fee negotiation it should always be verified that the
1098
        // initiator can pay the proposed fee, but we do a sanity check just to
1099
        // be sure here.
1100
        if ourBalance < 0 || theirBalance < 0 {
155✔
UNCOV
1101
                return 0, 0, fmt.Errorf("initiator cannot afford proposed " +
1✔
UNCOV
1102
                        "coop close fee")
1✔
UNCOV
1103
        }
1✔
1104

1105
        return ourBalance, theirBalance, nil
153✔
1106
}
1107

1108
// genSegwitV0HtlcScript generates the HTLC scripts for a normal segwit v0
1109
// channel.
1110
func genSegwitV0HtlcScript(chanType channeldb.ChannelType,
1111
        isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32,
1112
        rHash [32]byte, keyRing *CommitmentKeyRing,
1113
) (*WitnessScriptDesc, error) {
8,268✔
1114

8,268✔
1115
        var (
8,268✔
1116
                witnessScript []byte
8,268✔
1117
                err           error
8,268✔
1118
        )
8,268✔
1119

8,268✔
1120
        // Choose scripts based on channel type.
8,268✔
1121
        confirmedHtlcSpends := false
8,268✔
1122
        if chanType.HasAnchors() {
8,507✔
1123
                confirmedHtlcSpends = true
239✔
1124
        }
239✔
1125

1126
        // Generate the proper redeem scripts for the HTLC output modified by
1127
        // two-bits denoting if this is an incoming HTLC, and if the HTLC is
1128
        // being applied to their commitment transaction or ours.
1129
        switch {
8,268✔
1130
        // The HTLC is paying to us, and being applied to our commitment
1131
        // transaction. So we need to use the receiver's version of the HTLC
1132
        // script.
1133
        case isIncoming && whoseCommit.IsLocal():
2,121✔
1134
                witnessScript, err = input.ReceiverHTLCScript(
2,121✔
1135
                        timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
2,121✔
1136
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,121✔
1137
                )
2,121✔
1138

1139
        // We're being paid via an HTLC by the remote party, and the HTLC is
1140
        // being added to their commitment transaction, so we use the sender's
1141
        // version of the HTLC script.
1142
        case isIncoming && whoseCommit.IsRemote():
1,953✔
1143
                witnessScript, err = input.SenderHTLCScript(
1,953✔
1144
                        keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
1,953✔
1145
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,953✔
1146
                )
1,953✔
1147

1148
        // We're sending an HTLC which is being added to our commitment
1149
        // transaction. Therefore, we need to use the sender's version of the
1150
        // HTLC script.
1151
        case !isIncoming && whoseCommit.IsLocal():
1,968✔
1152
                witnessScript, err = input.SenderHTLCScript(
1,968✔
1153
                        keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
1,968✔
1154
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,968✔
1155
                )
1,968✔
1156

1157
        // Finally, we're paying the remote party via an HTLC, which is being
1158
        // added to their commitment transaction. Therefore, we use the
1159
        // receiver's version of the HTLC script.
1160
        case !isIncoming && whoseCommit.IsRemote():
2,238✔
1161
                witnessScript, err = input.ReceiverHTLCScript(
2,238✔
1162
                        timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
2,238✔
1163
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,238✔
1164
                )
2,238✔
1165
        }
1166
        if err != nil {
8,268✔
1167
                return nil, err
×
1168
        }
×
1169

1170
        // Now that we have the redeem scripts, create the P2WSH public key
1171
        // script for the output itself.
1172
        htlcP2WSH, err := input.WitnessScriptHash(witnessScript)
8,268✔
1173
        if err != nil {
8,268✔
1174
                return nil, err
×
1175
        }
×
1176

1177
        return &WitnessScriptDesc{
8,268✔
1178
                OutputScript:  htlcP2WSH,
8,268✔
1179
                WitnessScript: witnessScript,
8,268✔
1180
        }, nil
8,268✔
1181
}
1182

1183
// GenTaprootHtlcScript generates the HTLC scripts for a taproot+musig2
1184
// channel.
1185
func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty,
1186
        timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing,
1187
        auxLeaf input.AuxTapLeaf,
1188
        opts ...input.TaprootScriptOpt) (*input.HtlcScriptTree, error) {
302✔
1189

302✔
1190
        var (
302✔
1191
                htlcScriptTree *input.HtlcScriptTree
302✔
1192
                err            error
302✔
1193
        )
302✔
1194

302✔
1195
        // Generate the proper redeem scripts for the HTLC output modified by
302✔
1196
        // two-bits denoting if this is an incoming HTLC, and if the HTLC is
302✔
1197
        // being applied to their commitment transaction or ours.
302✔
1198
        switch {
302✔
1199
        // The HTLC is paying to us, and being applied to our commitment
1200
        // transaction. So we need to use the receiver's version of HTLC the
1201
        // script.
1202
        case isIncoming && whoseCommit.IsLocal():
77✔
1203
                htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
77✔
1204
                        timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
77✔
1205
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
77✔
1206
                        opts...,
77✔
1207
                )
77✔
1208

1209
        // We're being paid via an HTLC by the remote party, and the HTLC is
1210
        // being added to their commitment transaction, so we use the sender's
1211
        // version of the HTLC script.
1212
        case isIncoming && whoseCommit.IsRemote():
81✔
1213
                htlcScriptTree, err = input.SenderHTLCScriptTaproot(
81✔
1214
                        keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
81✔
1215
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
81✔
1216
                        opts...,
81✔
1217
                )
81✔
1218

1219
        // We're sending an HTLC which is being added to our commitment
1220
        // transaction. Therefore, we need to use the sender's version of the
1221
        // HTLC script.
1222
        case !isIncoming && whoseCommit.IsLocal():
82✔
1223
                htlcScriptTree, err = input.SenderHTLCScriptTaproot(
82✔
1224
                        keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
82✔
1225
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
82✔
1226
                        opts...,
82✔
1227
                )
82✔
1228

1229
        // Finally, we're paying the remote party via an HTLC, which is being
1230
        // added to their commitment transaction. Therefore, we use the
1231
        // receiver's version of the HTLC script.
1232
        case !isIncoming && whoseCommit.IsRemote():
74✔
1233
                htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
74✔
1234
                        timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
74✔
1235
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
74✔
1236
                        opts...,
74✔
1237
                )
74✔
1238
        }
1239

1240
        return htlcScriptTree, err
302✔
1241
}
1242

1243
// genHtlcScript generates the proper P2WSH public key scripts for the HTLC
1244
// output modified by two-bits denoting if this is an incoming HTLC, and if the
1245
// HTLC is being applied to their commitment transaction or ours. A script
1246
// multiplexer for the various spending paths is returned. The script path that
1247
// we need to sign for the remote party (2nd level HTLCs) is also returned
1248
// along side the multiplexer.
1249
func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool,
1250
        whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte,
1251
        keyRing *CommitmentKeyRing,
1252
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) {
8,566✔
1253

8,566✔
1254
        if !chanType.IsTaproot() {
16,834✔
1255
                return genSegwitV0HtlcScript(
8,268✔
1256
                        chanType, isIncoming, whoseCommit, timeout, rHash,
8,268✔
1257
                        keyRing,
8,268✔
1258
                )
8,268✔
1259
        }
8,268✔
1260

1261
        // Determine script options based on channel type.
1262
        var scriptOpts []input.TaprootScriptOpt
302✔
1263
        if chanType.IsTaprootFinal() {
333✔
1264
                scriptOpts = append(scriptOpts, input.WithProdScripts())
31✔
1265
        }
31✔
1266

1267
        return GenTaprootHtlcScript(
302✔
1268
                isIncoming, whoseCommit, timeout, rHash, keyRing, auxLeaf,
302✔
1269
                scriptOpts...,
302✔
1270
        )
302✔
1271
}
1272

1273
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
1274
// full scripts will be generated for the HTLC output depending on if the HTLC
1275
// is incoming and if it's being applied to our commitment transaction or that
1276
// of the remote node's. Additionally, in order to be able to efficiently
1277
// locate the added HTLC on the commitment transaction from the
1278
// paymentDescriptor that generated it, the generated script is stored within
1279
// the descriptor itself.
1280
func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty,
1281
        isIncoming bool, paymentDesc *paymentDescriptor,
1282
        keyRing *CommitmentKeyRing, chanType channeldb.ChannelType,
1283
        auxLeaf input.AuxTapLeaf) error {
7,985✔
1284

7,985✔
1285
        timeout := paymentDesc.Timeout
7,985✔
1286
        rHash := paymentDesc.RHash
7,985✔
1287

7,985✔
1288
        scriptInfo, err := genHtlcScript(
7,985✔
1289
                chanType, isIncoming, whoseCommit, timeout, rHash, keyRing,
7,985✔
1290
                auxLeaf,
7,985✔
1291
        )
7,985✔
1292
        if err != nil {
7,985✔
1293
                return err
×
1294
        }
×
1295

1296
        pkScript := scriptInfo.PkScript()
7,985✔
1297

7,985✔
1298
        // Add the new HTLC outputs to the respective commitment transactions.
7,985✔
1299
        amountPending := int64(paymentDesc.Amount.ToSatoshis())
7,985✔
1300
        commitTx.AddTxOut(wire.NewTxOut(amountPending, pkScript))
7,985✔
1301

7,985✔
1302
        // Store the pkScript of this particular paymentDescriptor so we can
7,985✔
1303
        // quickly locate it within the commitment transaction later.
7,985✔
1304
        if whoseCommit.IsLocal() {
11,950✔
1305
                paymentDesc.ourPkScript = pkScript
3,965✔
1306

3,965✔
1307
                paymentDesc.ourWitnessScript = scriptInfo.WitnessScriptToSign()
3,965✔
1308
        } else {
7,989✔
1309
                paymentDesc.theirPkScript = pkScript
4,024✔
1310

4,024✔
1311
                //nolint:ll
4,024✔
1312
                paymentDesc.theirWitnessScript = scriptInfo.WitnessScriptToSign()
4,024✔
1313
        }
4,024✔
1314

1315
        return nil
7,985✔
1316
}
1317

1318
// findOutputIndexesFromRemote finds the index of our and their outputs from
1319
// the remote commitment transaction. It derives the key ring to compute the
1320
// output scripts and compares them against the outputs inside the commitment
1321
// to find the match.
1322
func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
1323
        chanState *channeldb.OpenChannel,
1324
        leafStore fn.Option[AuxLeafStore]) (uint32, uint32, error) {
2,137✔
1325

2,137✔
1326
        // Init the output indexes as empty.
2,137✔
1327
        ourIndex := uint32(channeldb.OutputIndexEmpty)
2,137✔
1328
        theirIndex := uint32(channeldb.OutputIndexEmpty)
2,137✔
1329

2,137✔
1330
        chanCommit := chanState.RemoteCommitment
2,137✔
1331
        _, commitmentPoint := btcec.PrivKeyFromBytes(revocationPreimage[:])
2,137✔
1332

2,137✔
1333
        // With the commitment point generated, we can now derive the king ring
2,137✔
1334
        // which will be used to generate the output scripts.
2,137✔
1335
        keyRing := DeriveCommitmentKeys(
2,137✔
1336
                commitmentPoint, lntypes.Remote, chanState.ChanType,
2,137✔
1337
                &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
2,137✔
1338
        )
2,137✔
1339

2,137✔
1340
        // Since it's remote commitment chain, we'd used the mirrored values.
2,137✔
1341
        //
2,137✔
1342
        // We use the remote's channel config for the csv delay.
2,137✔
1343
        theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
2,137✔
1344

2,137✔
1345
        // If we are the initiator of this channel, then it's be false from the
2,137✔
1346
        // remote's PoV.
2,137✔
1347
        isRemoteInitiator := !chanState.IsInitiator
2,137✔
1348

2,137✔
1349
        var leaseExpiry uint32
2,137✔
1350
        if chanState.ChanType.HasLeaseExpiration() {
2,141✔
1351
                leaseExpiry = chanState.ThawHeight
4✔
1352
        }
4✔
1353

1354
        // If we have a custom blob, then we'll attempt to fetch the aux leaves
1355
        // for this state.
1356
        auxResult, err := fn.MapOptionZ(
2,137✔
1357
                leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] {
4,224✔
UNCOV
1358
                        return a.FetchLeavesFromCommit(
2,087✔
UNCOV
1359
                                NewAuxChanState(chanState), chanCommit,
2,087✔
UNCOV
1360
                                *keyRing, lntypes.Remote,
2,087✔
UNCOV
1361
                        )
2,087✔
UNCOV
1362
                },
2,087✔
1363
        ).Unpack()
1364
        if err != nil {
2,137✔
1365
                return ourIndex, theirIndex, fmt.Errorf("unable to fetch aux "+
×
1366
                        "leaves: %w", err)
×
1367
        }
×
1368

1369
        // Map the scripts from our PoV. When facing a local commitment, the
1370
        // to_local output belongs to us and the to_remote output belongs to
1371
        // them. When facing a remote commitment, the to_local output belongs to
1372
        // them and the to_remote output belongs to us.
1373

1374
        // Compute the to_local script. From our PoV, when facing a remote
1375
        // commitment, the to_local output belongs to them.
1376
        localAuxLeaf := fn.FlatMapOption(
2,137✔
1377
                func(l CommitAuxLeaves) input.AuxTapLeaf {
2,137✔
1378
                        return l.LocalAuxLeaf
×
1379
                },
×
1380
        )(auxResult.AuxLeaves)
1381
        theirScript, err := CommitScriptToSelf(
2,137✔
1382
                chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
2,137✔
1383
                keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf,
2,137✔
1384
        )
2,137✔
1385
        if err != nil {
2,137✔
1386
                return ourIndex, theirIndex, err
×
1387
        }
×
1388

1389
        // Compute the to_remote script. From our PoV, when facing a remote
1390
        // commitment, the to_remote output belongs to us.
1391
        remoteAuxLeaf := fn.FlatMapOption(
2,137✔
1392
                func(l CommitAuxLeaves) input.AuxTapLeaf {
2,137✔
1393
                        return l.RemoteAuxLeaf
×
1394
                },
×
1395
        )(auxResult.AuxLeaves)
1396
        ourScript, _, err := CommitScriptToRemote(
2,137✔
1397
                chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
2,137✔
1398
                leaseExpiry, remoteAuxLeaf,
2,137✔
1399
        )
2,137✔
1400
        if err != nil {
2,137✔
1401
                return ourIndex, theirIndex, err
×
1402
        }
×
1403

1404
        // Now compare the scripts to find our/their output index.
1405
        for i, txOut := range chanCommit.CommitTx.TxOut {
10,278✔
1406
                switch {
8,141✔
1407
                case bytes.Equal(txOut.PkScript, ourScript.PkScript()):
1,939✔
1408
                        ourIndex = uint32(i)
1,939✔
1409
                case bytes.Equal(txOut.PkScript, theirScript.PkScript()):
1,938✔
1410
                        theirIndex = uint32(i)
1,938✔
1411
                }
1412
        }
1413

1414
        return ourIndex, theirIndex, nil
2,137✔
1415
}
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