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

lightningnetwork / lnd / 12284350326

11 Dec 2024 08:27PM UTC coverage: 57.485% (+7.9%) from 49.54%
12284350326

Pull #9348

github

ziggie1984
github: update goveralls tool
Pull Request #9348: github: update goveralls tool

101901 of 177264 relevant lines covered (57.49%)

24841.21 hits per line

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

82.87
/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 {
10,223✔
109

10,223✔
110
        tweaklessCommit := chanType.IsTweakless()
10,223✔
111

10,223✔
112
        // Depending on if this is our commit or not, we'll choose the correct
10,223✔
113
        // base point.
10,223✔
114
        localBasePoint := localChanCfg.PaymentBasePoint
10,223✔
115
        if whoseCommit.IsLocal() {
14,335✔
116
                localBasePoint = localChanCfg.DelayBasePoint
4,112✔
117
        }
4,112✔
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{
10,223✔
122
                CommitPoint: commitPoint,
10,223✔
123

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

10,223✔
138
        // We'll now compute the to_local, to_remote, and revocation key based
10,223✔
139
        // on the current commitment point. All keys are tweaked each state in
10,223✔
140
        // order to ensure the keys from each state are unlinkable. To create
10,223✔
141
        // the revocation key, we take the opposite party's revocation base
10,223✔
142
        // point and combine that with the current commitment point.
10,223✔
143
        var (
10,223✔
144
                toLocalBasePoint    *btcec.PublicKey
10,223✔
145
                toRemoteBasePoint   *btcec.PublicKey
10,223✔
146
                revocationBasePoint *btcec.PublicKey
10,223✔
147
        )
10,223✔
148
        if whoseCommit.IsLocal() {
14,335✔
149
                toLocalBasePoint = localChanCfg.DelayBasePoint.PubKey
4,112✔
150
                toRemoteBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
4,112✔
151
                revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
4,112✔
152
        } else {
10,223✔
153
                toLocalBasePoint = remoteChanCfg.DelayBasePoint.PubKey
6,111✔
154
                toRemoteBasePoint = localChanCfg.PaymentBasePoint.PubKey
6,111✔
155
                revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
6,111✔
156
        }
6,111✔
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)
10,223✔
161
        keyRing.RevocationKey = input.DeriveRevocationPubkey(
10,223✔
162
                revocationBasePoint, commitPoint,
10,223✔
163
        )
10,223✔
164

10,223✔
165
        // If this commitment should omit the tweak for the remote point, then
10,223✔
166
        // we'll use that directly, and ignore the commitPoint tweak.
10,223✔
167
        if tweaklessCommit {
18,594✔
168
                keyRing.ToRemoteKey = toRemoteBasePoint
8,371✔
169

8,371✔
170
                // If this is not our commitment, the above ToRemoteKey will be
8,371✔
171
                // ours, and we blank out the local commitment tweak to
8,371✔
172
                // indicate that the key should not be tweaked when signing.
8,371✔
173
                if whoseCommit.IsRemote() {
13,403✔
174
                        keyRing.LocalCommitKeyTweak = nil
5,032✔
175
                }
5,032✔
176
        } else {
1,852✔
177
                keyRing.ToRemoteKey = input.TweakPubKey(
1,852✔
178
                        toRemoteBasePoint, commitPoint,
1,852✔
179
                )
1,852✔
180
        }
1,852✔
181

182
        return keyRing
10,223✔
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 {
39,277✔
201
        return w.OutputScript
39,277✔
202
}
39,277✔
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,195✔
209
        return w.WitnessScript
8,195✔
210
}
8,195✔
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) {
296✔
218

296✔
219
        return w.WitnessScript, nil
296✔
220
}
296✔
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) {
6,897✔
231

6,897✔
232
        switch {
6,897✔
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():
266✔
239
                return input.NewLocalCommitScriptTree(
266✔
240
                        csvDelay, selfKey, revokeKey, auxLeaf,
266✔
241
                )
266✔
242

243
        // If we are the initiator of a leased channel, then we have an
244
        // additional CLTV requirement in addition to the usual CSV
245
        // requirement.
246
        case initiator && chanType.HasLeaseExpiration():
×
247
                toLocalRedeemScript, err := input.LeaseCommitScriptToSelf(
×
248
                        selfKey, revokeKey, csvDelay, leaseExpiry,
×
249
                )
×
250
                if err != nil {
×
251
                        return nil, err
×
252
                }
×
253

254
                toLocalScriptHash, err := input.WitnessScriptHash(
×
255
                        toLocalRedeemScript,
×
256
                )
×
257
                if err != nil {
×
258
                        return nil, err
×
259
                }
×
260

261
                return &WitnessScriptDesc{
×
262
                        OutputScript:  toLocalScriptHash,
×
263
                        WitnessScript: toLocalRedeemScript,
×
264
                }, nil
×
265

266
        default:
6,631✔
267
                toLocalRedeemScript, err := input.CommitScriptToSelf(
6,631✔
268
                        csvDelay, selfKey, revokeKey,
6,631✔
269
                )
6,631✔
270
                if err != nil {
6,631✔
271
                        return nil, err
×
272
                }
×
273

274
                toLocalScriptHash, err := input.WitnessScriptHash(
6,631✔
275
                        toLocalRedeemScript,
6,631✔
276
                )
6,631✔
277
                if err != nil {
6,631✔
278
                        return nil, err
×
279
                }
×
280

281
                return &WitnessScriptDesc{
6,631✔
282
                        OutputScript:  toLocalScriptHash,
6,631✔
283
                        WitnessScript: toLocalRedeemScript,
6,631✔
284
                }, nil
6,631✔
285
        }
286
}
287

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

6,841✔
297
        switch {
6,841✔
298
        // If we are not the initiator of a leased channel, then the remote
299
        // party has an additional CLTV requirement in addition to the 1 block
300
        // CSV requirement.
301
        case chanType.HasLeaseExpiration() && !initiator:
×
302
                script, err := input.LeaseCommitScriptToRemoteConfirmed(
×
303
                        remoteKey, leaseExpiry,
×
304
                )
×
305
                if err != nil {
×
306
                        return nil, 0, err
×
307
                }
×
308

309
                p2wsh, err := input.WitnessScriptHash(script)
×
310
                if err != nil {
×
311
                        return nil, 0, err
×
312
                }
×
313

314
                return &WitnessScriptDesc{
×
315
                        OutputScript:  p2wsh,
×
316
                        WitnessScript: script,
×
317
                }, 1, nil
×
318

319
        // For taproot channels, we'll use a slightly different format, where
320
        // we use a NUMS key to force the remote party to take a script path,
321
        // with the sole tap leaf enforcing the 1 CSV delay.
322
        case chanType.IsTaproot():
262✔
323
                toRemoteScriptTree, err := input.NewRemoteCommitScriptTree(
262✔
324
                        remoteKey, auxLeaf,
262✔
325
                )
262✔
326
                if err != nil {
262✔
327
                        return nil, 0, err
×
328
                }
×
329

330
                return toRemoteScriptTree, 1, nil
262✔
331

332
        // If this channel type has anchors, we derive the delayed to_remote
333
        // script.
334
        case chanType.HasAnchors():
282✔
335
                script, err := input.CommitScriptToRemoteConfirmed(remoteKey)
282✔
336
                if err != nil {
282✔
337
                        return nil, 0, err
×
338
                }
×
339

340
                p2wsh, err := input.WitnessScriptHash(script)
282✔
341
                if err != nil {
282✔
342
                        return nil, 0, err
×
343
                }
×
344

345
                return &WitnessScriptDesc{
282✔
346
                        OutputScript:  p2wsh,
282✔
347
                        WitnessScript: script,
282✔
348
                }, 1, nil
282✔
349

350
        default:
6,297✔
351
                // Otherwise the to_remote will be a simple p2wkh.
6,297✔
352
                p2wkh, err := input.CommitScriptUnencumbered(remoteKey)
6,297✔
353
                if err != nil {
6,297✔
354
                        return nil, 0, err
×
355
                }
×
356

357
                // Since this is a regular P2WKH, the WitnessScipt and PkScript
358
                // should both be set to the script hash.
359
                return &WitnessScriptDesc{
6,297✔
360
                        OutputScript:  p2wkh,
6,297✔
361
                        WitnessScript: p2wkh,
6,297✔
362
                }, 0, nil
6,297✔
363
        }
364
}
365

366
// HtlcSigHashType returns the sighash type to use for HTLC success and timeout
367
// transactions given the channel type.
368
func HtlcSigHashType(chanType channeldb.ChannelType) txscript.SigHashType {
4,159✔
369
        if chanType.HasAnchors() {
4,490✔
370
                return txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
331✔
371
        }
331✔
372

373
        return txscript.SigHashAll
3,828✔
374
}
375

376
// HtlcSignDetails converts the passed parameters to a SignDetails valid for
377
// this channel type. For non-anchor channels this will return nil.
378
func HtlcSignDetails(chanType channeldb.ChannelType, signDesc input.SignDescriptor,
379
        sigHash txscript.SigHashType, peerSig input.Signature) *input.SignDetails {
96✔
380

96✔
381
        // Non-anchor channels don't need sign details, as the HTLC second
96✔
382
        // level cannot be altered.
96✔
383
        if !chanType.HasAnchors() {
137✔
384
                return nil
41✔
385
        }
41✔
386

387
        return &input.SignDetails{
55✔
388
                SignDesc:    signDesc,
55✔
389
                SigHashType: sigHash,
55✔
390
                PeerSig:     peerSig,
55✔
391
        }
55✔
392
}
393

394
// HtlcSecondLevelInputSequence dictates the sequence number we must use on the
395
// input to a second level HTLC transaction.
396
func HtlcSecondLevelInputSequence(chanType channeldb.ChannelType) uint32 {
8,127✔
397
        if chanType.HasAnchors() {
8,538✔
398
                return 1
411✔
399
        }
411✔
400

401
        return 0
7,716✔
402
}
403

404
// sweepSigHash returns the sign descriptor to use when signing a sweep
405
// transaction. For taproot channels, we'll use this to always sweep with
406
// sighash default.
407
func sweepSigHash(chanType channeldb.ChannelType) txscript.SigHashType {
324✔
408
        if chanType.IsTaproot() {
352✔
409
                return txscript.SigHashDefault
28✔
410
        }
28✔
411

412
        return txscript.SigHashAll
296✔
413
}
414

415
// SecondLevelHtlcScript derives the appropriate second level HTLC script based
416
// on the channel's commitment type. It is the uniform script that's used as the
417
// output for the second-level HTLC transactions. The second level transaction
418
// act as a sort of covenant, ensuring that a 2-of-2 multi-sig output can only
419
// be spent in a particular way, and to a particular output. The `initiator`
420
// argument should correspond to the owner of the commitment transaction which
421
// we are generating the to_local script for.
422
func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool,
423
        revocationKey, delayKey *btcec.PublicKey, csvDelay, leaseExpiry uint32,
424
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) {
8,126✔
425

8,126✔
426
        switch {
8,126✔
427
        // For taproot channels, the pkScript is a segwit v1 p2tr output.
428
        case chanType.IsTaproot():
149✔
429
                return input.TaprootSecondLevelScriptTree(
149✔
430
                        revocationKey, delayKey, csvDelay, auxLeaf,
149✔
431
                )
149✔
432

433
        // If we are the initiator of a leased channel, then we have an
434
        // additional CLTV requirement in addition to the usual CSV
435
        // requirement.
436
        case initiator && chanType.HasLeaseExpiration():
×
437
                witnessScript, err := input.LeaseSecondLevelHtlcScript(
×
438
                        revocationKey, delayKey, csvDelay, leaseExpiry,
×
439
                )
×
440
                if err != nil {
×
441
                        return nil, err
×
442
                }
×
443

444
                pkScript, err := input.WitnessScriptHash(witnessScript)
×
445
                if err != nil {
×
446
                        return nil, err
×
447
                }
×
448

449
                return &WitnessScriptDesc{
×
450
                        OutputScript:  pkScript,
×
451
                        WitnessScript: witnessScript,
×
452
                }, nil
×
453

454
        default:
7,977✔
455
                witnessScript, err := input.SecondLevelHtlcScript(
7,977✔
456
                        revocationKey, delayKey, csvDelay,
7,977✔
457
                )
7,977✔
458
                if err != nil {
7,977✔
459
                        return nil, err
×
460
                }
×
461

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

467
                return &WitnessScriptDesc{
7,977✔
468
                        OutputScript:  pkScript,
7,977✔
469
                        WitnessScript: witnessScript,
7,977✔
470
                }, nil
7,977✔
471
        }
472
}
473

474
// CommitWeight returns the base commitment weight before adding HTLCs.
475
func CommitWeight(chanType channeldb.ChannelType) lntypes.WeightUnit {
19,455✔
476
        switch {
19,455✔
477
        case chanType.IsTaproot():
522✔
478
                return input.TaprootCommitWeight
522✔
479

480
        // If this commitment has anchors, it will be slightly heavier.
481
        case chanType.HasAnchors():
1,844✔
482
                return input.AnchorCommitWeight
1,844✔
483

484
        default:
17,089✔
485
                return input.CommitWeight
17,089✔
486
        }
487
}
488

489
// HtlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
490
// transaction based on the current fee rate.
491
func HtlcTimeoutFee(chanType channeldb.ChannelType,
492
        feePerKw chainfee.SatPerKWeight) btcutil.Amount {
101,815✔
493

101,815✔
494
        switch {
101,815✔
495
        // For zero-fee HTLC channels, this will always be zero, regardless of
496
        // feerate.
497
        case chanType.ZeroHtlcTxFee() || chanType.IsTaproot():
856✔
498
                return 0
856✔
499

500
        case chanType.HasAnchors():
588✔
501
                return feePerKw.FeeForWeight(input.HtlcTimeoutWeightConfirmed)
588✔
502

503
        default:
100,371✔
504
                return feePerKw.FeeForWeight(input.HtlcTimeoutWeight)
100,371✔
505
        }
506
}
507

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

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

519
        case chanType.HasAnchors():
1,179✔
520
                return feePerKw.FeeForWeight(input.HtlcSuccessWeightConfirmed)
1,179✔
521

522
        default:
108,903✔
523
                return feePerKw.FeeForWeight(input.HtlcSuccessWeight)
108,903✔
524
        }
525
}
526

527
// CommitScriptAnchors return the scripts to use for the local and remote
528
// anchor.
529
func CommitScriptAnchors(chanType channeldb.ChannelType,
530
        localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
531
        keyRing *CommitmentKeyRing) (
532
        input.ScriptDescriptor, input.ScriptDescriptor, error) {
432✔
533

432✔
534
        var (
432✔
535
                anchorScript func(
432✔
536
                        key *btcec.PublicKey) (input.ScriptDescriptor, error)
432✔
537

432✔
538
                keySelector func(*channeldb.ChannelConfig,
432✔
539
                        bool) *btcec.PublicKey
432✔
540
        )
432✔
541

432✔
542
        switch {
432✔
543
        // For taproot channels, the anchor is slightly different: the top
544
        // level key is now the (relative) local delay and remote public key,
545
        // since these are fully revealed once the commitment hits the chain.
546
        case chanType.IsTaproot():
179✔
547
                anchorScript = func(
179✔
548
                        key *btcec.PublicKey) (input.ScriptDescriptor, error) {
537✔
549

358✔
550
                        return input.NewAnchorScriptTree(key)
358✔
551
                }
358✔
552

553
                keySelector = func(cfg *channeldb.ChannelConfig,
179✔
554
                        local bool) *btcec.PublicKey {
537✔
555

358✔
556
                        if local {
537✔
557
                                return keyRing.ToLocalKey
179✔
558
                        }
179✔
559

560
                        return keyRing.ToRemoteKey
179✔
561
                }
562

563
        // For normal channels we'll use the multi-sig keys since those are
564
        // revealed when the channel closes
565
        default:
253✔
566
                // For normal channels, we'll create a p2wsh script based on
253✔
567
                // the target key.
253✔
568
                anchorScript = func(
253✔
569
                        key *btcec.PublicKey) (input.ScriptDescriptor, error) {
759✔
570

506✔
571
                        script, err := input.CommitScriptAnchor(key)
506✔
572
                        if err != nil {
506✔
573
                                return nil, err
×
574
                        }
×
575

576
                        scriptHash, err := input.WitnessScriptHash(script)
506✔
577
                        if err != nil {
506✔
578
                                return nil, err
×
579
                        }
×
580

581
                        return &WitnessScriptDesc{
506✔
582
                                OutputScript:  scriptHash,
506✔
583
                                WitnessScript: script,
506✔
584
                        }, nil
506✔
585
                }
586

587
                // For the existing channels, we'll always select the multi-sig
588
                // key from the party's channel config.
589
                keySelector = func(cfg *channeldb.ChannelConfig,
253✔
590
                        _ bool) *btcec.PublicKey {
759✔
591

506✔
592
                        return cfg.MultiSigKey.PubKey
506✔
593
                }
506✔
594
        }
595

596
        // Get the script used for the anchor output spendable by the local
597
        // node.
598
        localAnchor, err := anchorScript(keySelector(localChanCfg, true))
432✔
599
        if err != nil {
432✔
600
                return nil, nil, err
×
601
        }
×
602

603
        // And the anchor spendable by the remote node.
604
        remoteAnchor, err := anchorScript(keySelector(remoteChanCfg, false))
432✔
605
        if err != nil {
432✔
606
                return nil, nil, err
×
607
        }
×
608

609
        return localAnchor, remoteAnchor, nil
432✔
610
}
611

612
// CommitmentBuilder is a type that wraps the type of channel we are dealing
613
// with, and abstracts the various ways of constructing commitment
614
// transactions.
615
type CommitmentBuilder struct {
616
        // chanState is the underlying channel's state struct, used to
617
        // determine the type of channel we are dealing with, and relevant
618
        // parameters.
619
        chanState *channeldb.OpenChannel
620

621
        // obfuscator is a 48-bit state hint that's used to obfuscate the
622
        // current state number on the commitment transactions.
623
        obfuscator [StateHintSize]byte
624

625
        // auxLeafStore is an interface that allows us to fetch auxiliary
626
        // tapscript leaves for the commitment output.
627
        auxLeafStore fn.Option[AuxLeafStore]
628
}
629

630
// NewCommitmentBuilder creates a new CommitmentBuilder from chanState.
631
func NewCommitmentBuilder(chanState *channeldb.OpenChannel,
632
        leafStore fn.Option[AuxLeafStore]) *CommitmentBuilder {
808✔
633

808✔
634
        // The anchor channel type MUST be tweakless.
808✔
635
        if chanState.ChanType.HasAnchors() && !chanState.ChanType.IsTweakless() {
808✔
636
                panic("invalid channel type combination")
×
637
        }
638

639
        return &CommitmentBuilder{
808✔
640
                chanState:    chanState,
808✔
641
                obfuscator:   createStateHintObfuscator(chanState),
808✔
642
                auxLeafStore: leafStore,
808✔
643
        }
808✔
644
}
645

646
// createStateHintObfuscator derives and assigns the state hint obfuscator for
647
// the channel, which is used to encode the commitment height in the sequence
648
// number of commitment transaction inputs.
649
func createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte {
988✔
650
        if state.IsInitiator {
1,551✔
651
                return DeriveStateHintObfuscator(
563✔
652
                        state.LocalChanCfg.PaymentBasePoint.PubKey,
563✔
653
                        state.RemoteChanCfg.PaymentBasePoint.PubKey,
563✔
654
                )
563✔
655
        }
563✔
656

657
        return DeriveStateHintObfuscator(
425✔
658
                state.RemoteChanCfg.PaymentBasePoint.PubKey,
425✔
659
                state.LocalChanCfg.PaymentBasePoint.PubKey,
425✔
660
        )
425✔
661
}
662

663
// unsignedCommitmentTx is the final commitment created from evaluating an HTLC
664
// view at a given height, along with some meta data.
665
type unsignedCommitmentTx struct {
666
        // txn is the final, unsigned commitment transaction for this view.
667
        txn *wire.MsgTx
668

669
        // fee is the total fee of the commitment transaction.
670
        fee btcutil.Amount
671

672
        // ourBalance is our balance on this commitment *after* subtracting
673
        // commitment fees and anchor outputs. This can be different than the
674
        // balances before creating the commitment transaction as one party must
675
        // pay the commitment fee.
676
        ourBalance lnwire.MilliSatoshi
677

678
        // theirBalance is their balance of this commitment *after* subtracting
679
        // commitment fees and anchor outputs. This can be different than the
680
        // balances before creating the commitment transaction as one party must
681
        // pay the commitment fee.
682
        theirBalance lnwire.MilliSatoshi
683

684
        // cltvs is a sorted list of CLTV deltas for each HTLC on the commitment
685
        // transaction. Any non-htlc outputs will have a CLTV delay of zero.
686
        cltvs []uint32
687
}
688

689
// createUnsignedCommitmentTx generates the unsigned commitment transaction for
690
// a commitment view and returns it as part of the unsignedCommitmentTx. The
691
// passed in balances should be balances *before* subtracting any commitment
692
// fees, but after anchor outputs.
693
func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
694
        theirBalance lnwire.MilliSatoshi, whoseCommit lntypes.ChannelParty,
695
        feePerKw chainfee.SatPerKWeight, height uint64, originalHtlcView,
696
        filteredHTLCView *HtlcView, keyRing *CommitmentKeyRing,
697
        prevCommit *commitment) (*unsignedCommitmentTx, error) {
4,057✔
698

4,057✔
699
        dustLimit := cb.chanState.LocalChanCfg.DustLimit
4,057✔
700
        if whoseCommit.IsRemote() {
6,098✔
701
                dustLimit = cb.chanState.RemoteChanCfg.DustLimit
2,041✔
702
        }
2,041✔
703

704
        numHTLCs := int64(0)
4,057✔
705
        for _, htlc := range filteredHTLCView.Updates.Local {
14,391✔
706
                if HtlcIsDust(
10,334✔
707
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
10,334✔
708
                        htlc.Amount.ToSatoshis(), dustLimit,
10,334✔
709
                ) {
16,674✔
710

6,340✔
711
                        continue
6,340✔
712
                }
713

714
                numHTLCs++
3,994✔
715
        }
716
        for _, htlc := range filteredHTLCView.Updates.Remote {
14,332✔
717
                if HtlcIsDust(
10,275✔
718
                        cb.chanState.ChanType, true, whoseCommit, feePerKw,
10,275✔
719
                        htlc.Amount.ToSatoshis(), dustLimit,
10,275✔
720
                ) {
16,615✔
721

6,340✔
722
                        continue
6,340✔
723
                }
724

725
                numHTLCs++
3,935✔
726
        }
727

728
        // Next, we'll calculate the fee for the commitment transaction based
729
        // on its total weight. Once we have the total weight, we'll multiply
730
        // by the current fee-per-kw, then divide by 1000 to get the proper
731
        // fee.
732
        totalCommitWeight := CommitWeight(cb.chanState.ChanType) +
4,057✔
733
                lntypes.WeightUnit(input.HTLCWeight*numHTLCs)
4,057✔
734

4,057✔
735
        // With the weight known, we can now calculate the commitment fee,
4,057✔
736
        // ensuring that we account for any dust outputs trimmed above.
4,057✔
737
        commitFee := feePerKw.FeeForWeight(totalCommitWeight)
4,057✔
738
        commitFeeMSat := lnwire.NewMSatFromSatoshis(commitFee)
4,057✔
739

4,057✔
740
        // Currently, within the protocol, the initiator always pays the fees.
4,057✔
741
        // So we'll subtract the fee amount from the balance of the current
4,057✔
742
        // initiator. If the initiator is unable to pay the fee fully, then
4,057✔
743
        // their entire output is consumed.
4,057✔
744
        switch {
4,057✔
745
        case cb.chanState.IsInitiator && commitFee > ourBalance.ToSatoshis():
×
746
                ourBalance = 0
×
747

748
        case cb.chanState.IsInitiator:
2,028✔
749
                ourBalance -= commitFeeMSat
2,028✔
750

751
        case !cb.chanState.IsInitiator && commitFee > theirBalance.ToSatoshis():
×
752
                theirBalance = 0
×
753

754
        case !cb.chanState.IsInitiator:
2,029✔
755
                theirBalance -= commitFeeMSat
2,029✔
756
        }
757

758
        var commitTx *wire.MsgTx
4,057✔
759

4,057✔
760
        // Before we create the commitment transaction below, we'll try to see
4,057✔
761
        // if there're any aux leaves that need to be a part of the tapscript
4,057✔
762
        // tree. We'll only do this if we have a custom blob defined though.
4,057✔
763
        auxResult, err := fn.MapOptionZ(
4,057✔
764
                cb.auxLeafStore,
4,057✔
765
                func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
8,047✔
766
                        return auxLeavesFromView(
3,990✔
767
                                s, cb.chanState, prevCommit.customBlob,
3,990✔
768
                                originalHtlcView, whoseCommit, ourBalance,
3,990✔
769
                                theirBalance, *keyRing,
3,990✔
770
                        )
3,990✔
771
                },
3,990✔
772
        ).Unpack()
773
        if err != nil {
4,057✔
774
                return nil, fmt.Errorf("unable to fetch aux leaves: %w", err)
×
775
        }
×
776

777
        // Depending on whether the transaction is ours or not, we call
778
        // CreateCommitTx with parameters matching the perspective, to generate
779
        // a new commitment transaction with all the latest unsettled/un-timed
780
        // out HTLCs.
781
        var leaseExpiry uint32
4,057✔
782
        if cb.chanState.ChanType.HasLeaseExpiration() {
4,057✔
783
                leaseExpiry = cb.chanState.ThawHeight
×
784
        }
×
785
        if whoseCommit.IsLocal() {
6,073✔
786
                commitTx, err = CreateCommitTx(
2,016✔
787
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,016✔
788
                        &cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
2,016✔
789
                        ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
2,016✔
790
                        numHTLCs, cb.chanState.IsInitiator, leaseExpiry,
2,016✔
791
                        auxResult.AuxLeaves,
2,016✔
792
                )
2,016✔
793
        } else {
4,057✔
794
                commitTx, err = CreateCommitTx(
2,041✔
795
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,041✔
796
                        &cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
2,041✔
797
                        theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
2,041✔
798
                        numHTLCs, !cb.chanState.IsInitiator, leaseExpiry,
2,041✔
799
                        auxResult.AuxLeaves,
2,041✔
800
                )
2,041✔
801
        }
2,041✔
802
        if err != nil {
4,057✔
803
                return nil, err
×
804
        }
×
805

806
        // Similarly, we'll now attempt to extract the set of aux leaves for
807
        // the set of incoming and outgoing HTLCs.
808
        incomingAuxLeaves := fn.MapOption(
4,057✔
809
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,057✔
810
                        return leaves.IncomingHtlcLeaves
×
811
                },
×
812
        )(auxResult.AuxLeaves)
813
        outgoingAuxLeaves := fn.MapOption(
4,057✔
814
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,057✔
815
                        return leaves.OutgoingHtlcLeaves
×
816
                },
×
817
        )(auxResult.AuxLeaves)
818

819
        // We'll now add all the HTLC outputs to the commitment transaction.
820
        // Each output includes an off-chain 2-of-2 covenant clause, so we'll
821
        // need the objective local/remote keys for this particular commitment
822
        // as well. For any non-dust HTLCs that are manifested on the commitment
823
        // transaction, we'll also record its CLTV which is required to sort the
824
        // commitment transaction below. The slice is initially sized to the
825
        // number of existing outputs, since any outputs already added are
826
        // commitment outputs and should correspond to zero values for the
827
        // purposes of sorting.
828
        cltvs := make([]uint32, len(commitTx.TxOut))
4,057✔
829
        htlcIndexes := make([]input.HtlcIndex, len(commitTx.TxOut))
4,057✔
830
        for _, htlc := range filteredHTLCView.Updates.Local {
14,391✔
831
                if HtlcIsDust(
10,334✔
832
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
10,334✔
833
                        htlc.Amount.ToSatoshis(), dustLimit,
10,334✔
834
                ) {
16,674✔
835

6,340✔
836
                        continue
6,340✔
837
                }
838

839
                auxLeaf := fn.FlatMapOption(
3,994✔
840
                        func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf {
3,994✔
841
                                return leaves[htlc.HtlcIndex].AuxTapLeaf
×
842
                        },
×
843
                )(outgoingAuxLeaves)
844

845
                err := addHTLC(
3,994✔
846
                        commitTx, whoseCommit, false, htlc, keyRing,
3,994✔
847
                        cb.chanState.ChanType, auxLeaf,
3,994✔
848
                )
3,994✔
849
                if err != nil {
3,994✔
850
                        return nil, err
×
851
                }
×
852

853
                // We want to add the CLTV and HTLC index to their respective
854
                // slices, even if we already pre-allocated them.
855
                cltvs = append(cltvs, htlc.Timeout)               //nolint
3,994✔
856
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
3,994✔
857
        }
858
        for _, htlc := range filteredHTLCView.Updates.Remote {
14,332✔
859
                if HtlcIsDust(
10,275✔
860
                        cb.chanState.ChanType, true, whoseCommit, feePerKw,
10,275✔
861
                        htlc.Amount.ToSatoshis(), dustLimit,
10,275✔
862
                ) {
16,615✔
863

6,340✔
864
                        continue
6,340✔
865
                }
866

867
                auxLeaf := fn.FlatMapOption(
3,935✔
868
                        func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf {
3,935✔
869
                                return leaves[htlc.HtlcIndex].AuxTapLeaf
×
870
                        },
×
871
                )(incomingAuxLeaves)
872

873
                err := addHTLC(
3,935✔
874
                        commitTx, whoseCommit, true, htlc, keyRing,
3,935✔
875
                        cb.chanState.ChanType, auxLeaf,
3,935✔
876
                )
3,935✔
877
                if err != nil {
3,935✔
878
                        return nil, err
×
879
                }
×
880

881
                // We want to add the CLTV and HTLC index to their respective
882
                // slices, even if we already pre-allocated them.
883
                cltvs = append(cltvs, htlc.Timeout)               //nolint
3,935✔
884
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
3,935✔
885
        }
886

887
        // Set the state hint of the commitment transaction to facilitate
888
        // quickly recovering the necessary penalty state in the case of an
889
        // uncooperative broadcast.
890
        err = SetStateNumHint(commitTx, height, cb.obfuscator)
4,057✔
891
        if err != nil {
4,057✔
892
                return nil, err
×
893
        }
×
894

895
        // Sort the transactions according to the agreed upon canonical
896
        // ordering (which might be customized for custom channel types, but
897
        // deterministic and both parties will arrive at the same result). This
898
        // lets us skip sending the entire transaction over, instead we'll just
899
        // send signatures.
900
        commitSort := auxResult.CommitSortFunc.UnwrapOr(DefaultCommitSort)
4,057✔
901
        err = commitSort(commitTx, cltvs, htlcIndexes)
4,057✔
902
        if err != nil {
4,057✔
903
                return nil, fmt.Errorf("unable to sort commitment "+
×
904
                        "transaction: %w", err)
×
905
        }
×
906

907
        // Next, we'll ensure that we don't accidentally create a commitment
908
        // transaction which would be invalid by consensus.
909
        uTx := btcutil.NewTx(commitTx)
4,057✔
910
        if err := blockchain.CheckTransactionSanity(uTx); err != nil {
4,057✔
911
                return nil, err
×
912
        }
×
913

914
        // Finally, we'll assert that were not attempting to draw more out of
915
        // the channel that was originally placed within it.
916
        var totalOut btcutil.Amount
4,057✔
917
        for _, txOut := range commitTx.TxOut {
20,604✔
918
                totalOut += btcutil.Amount(txOut.Value)
16,547✔
919
        }
16,547✔
920
        if totalOut+commitFee > cb.chanState.Capacity {
4,057✔
921
                return nil, fmt.Errorf("height=%v, for ChannelPoint(%v) "+
×
922
                        "attempts to consume %v while channel capacity is %v",
×
923
                        height, cb.chanState.FundingOutpoint,
×
924
                        totalOut+commitFee, cb.chanState.Capacity)
×
925
        }
×
926

927
        return &unsignedCommitmentTx{
4,057✔
928
                txn:          commitTx,
4,057✔
929
                fee:          commitFee,
4,057✔
930
                ourBalance:   ourBalance,
4,057✔
931
                theirBalance: theirBalance,
4,057✔
932
                cltvs:        cltvs,
4,057✔
933
        }, nil
4,057✔
934
}
935

936
// CreateCommitTx creates a commitment transaction, spending from specified
937
// funding output. The commitment transaction contains two outputs: one local
938
// output paying to the "owner" of the commitment transaction which can be
939
// spent after a relative block delay or revocation event, and a remote output
940
// paying the counterparty within the channel, which can be spent immediately
941
// or after a delay depending on the commitment type. The `initiator` argument
942
// should correspond to the owner of the commitment transaction we are creating.
943
func CreateCommitTx(chanType channeldb.ChannelType,
944
        fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
945
        localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
946
        amountToLocal, amountToRemote btcutil.Amount,
947
        numHTLCs int64, initiator bool, leaseExpiry uint32,
948
        auxLeaves fn.Option[CommitAuxLeaves]) (*wire.MsgTx, error) {
4,861✔
949

4,861✔
950
        // First, we create the script for the delayed "pay-to-self" output.
4,861✔
951
        // This output has 2 main redemption clauses: either we can redeem the
4,861✔
952
        // output after a relative block delay, or the remote node can claim
4,861✔
953
        // the funds with the revocation key if we broadcast a revoked
4,861✔
954
        // commitment transaction.
4,861✔
955
        localAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf {
4,861✔
956
                return l.LocalAuxLeaf
×
957
        })(auxLeaves)
×
958
        toLocalScript, err := CommitScriptToSelf(
4,861✔
959
                chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
4,861✔
960
                uint32(localChanCfg.CsvDelay), leaseExpiry,
4,861✔
961
                fn.FlattenOption(localAuxLeaf),
4,861✔
962
        )
4,861✔
963
        if err != nil {
4,861✔
964
                return nil, err
×
965
        }
×
966

967
        // Next, we create the script paying to the remote.
968
        remoteAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf {
4,861✔
969
                return l.RemoteAuxLeaf
×
970
        })(auxLeaves)
×
971
        toRemoteScript, _, err := CommitScriptToRemote(
4,861✔
972
                chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
4,861✔
973
                fn.FlattenOption(remoteAuxLeaf),
4,861✔
974
        )
4,861✔
975
        if err != nil {
4,861✔
976
                return nil, err
×
977
        }
×
978

979
        // Now that both output scripts have been created, we can finally create
980
        // the transaction itself. We use a transaction version of 2 since CSV
981
        // will fail unless the tx version is >= 2.
982
        commitTx := wire.NewMsgTx(2)
4,861✔
983
        commitTx.AddTxIn(&fundingOutput)
4,861✔
984

4,861✔
985
        // Avoid creating dust outputs within the commitment transaction.
4,861✔
986
        localOutput := amountToLocal >= localChanCfg.DustLimit
4,861✔
987
        if localOutput {
9,650✔
988
                commitTx.AddTxOut(&wire.TxOut{
4,789✔
989
                        PkScript: toLocalScript.PkScript(),
4,789✔
990
                        Value:    int64(amountToLocal),
4,789✔
991
                })
4,789✔
992
        }
4,789✔
993

994
        remoteOutput := amountToRemote >= localChanCfg.DustLimit
4,861✔
995
        if remoteOutput {
9,652✔
996
                commitTx.AddTxOut(&wire.TxOut{
4,791✔
997
                        PkScript: toRemoteScript.PkScript(),
4,791✔
998
                        Value:    int64(amountToRemote),
4,791✔
999
                })
4,791✔
1000
        }
4,791✔
1001

1002
        // If this channel type has anchors, we'll also add those.
1003
        if chanType.HasAnchors() {
5,258✔
1004
                localAnchor, remoteAnchor, err := CommitScriptAnchors(
397✔
1005
                        chanType, localChanCfg, remoteChanCfg, keyRing,
397✔
1006
                )
397✔
1007
                if err != nil {
397✔
1008
                        return nil, err
×
1009
                }
×
1010

1011
                // Add local anchor output only if we have a commitment output
1012
                // or there are HTLCs.
1013
                if localOutput || numHTLCs > 0 {
778✔
1014
                        commitTx.AddTxOut(&wire.TxOut{
381✔
1015
                                PkScript: localAnchor.PkScript(),
381✔
1016
                                Value:    int64(AnchorSize),
381✔
1017
                        })
381✔
1018
                }
381✔
1019

1020
                // Add anchor output to remote only if they have a commitment
1021
                // output or there are HTLCs.
1022
                if remoteOutput || numHTLCs > 0 {
778✔
1023
                        commitTx.AddTxOut(&wire.TxOut{
381✔
1024
                                PkScript: remoteAnchor.PkScript(),
381✔
1025
                                Value:    int64(AnchorSize),
381✔
1026
                        })
381✔
1027
                }
381✔
1028
        }
1029

1030
        return commitTx, nil
4,861✔
1031
}
1032

1033
// CoopCloseBalance returns the final balances that should be used to create
1034
// the cooperative close tx, given the channel type and transaction fee.
1035
func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool,
1036
        coopCloseFee, ourBalance, theirBalance, commitFee btcutil.Amount,
1037
        feePayer fn.Option[lntypes.ChannelParty],
1038
) (btcutil.Amount, btcutil.Amount, error) {
146✔
1039

146✔
1040
        // We'll make sure we account for the complete balance by adding the
146✔
1041
        // current dangling commitment fee to the balance of the initiator.
146✔
1042
        initiatorDelta := commitFee
146✔
1043

146✔
1044
        // Since the initiator's balance also is stored after subtracting the
146✔
1045
        // anchor values, add that back in case this was an anchor commitment.
146✔
1046
        if chanType.HasAnchors() {
229✔
1047
                initiatorDelta += 2 * AnchorSize
83✔
1048
        }
83✔
1049

1050
        // To start with, we'll add the anchor and/or commitment fee to the
1051
        // balance of the initiator.
1052
        if isInitiator {
221✔
1053
                ourBalance += initiatorDelta
75✔
1054
        } else {
146✔
1055
                theirBalance += initiatorDelta
71✔
1056
        }
71✔
1057

1058
        // With the initiator's balance credited, we'll now subtract the closing
1059
        // fee from the closing party. By default, the initiator pays the full
1060
        // amount, but this can be overridden by the feePayer option.
1061
        defaultPayer := func() lntypes.ChannelParty {
292✔
1062
                if isInitiator {
221✔
1063
                        return lntypes.Local
75✔
1064
                }
75✔
1065

1066
                return lntypes.Remote
71✔
1067
        }()
1068
        payer := feePayer.UnwrapOr(defaultPayer)
146✔
1069

146✔
1070
        // Based on the payer computed above, we'll subtract the closing fee.
146✔
1071
        switch payer {
146✔
1072
        case lntypes.Local:
75✔
1073
                ourBalance -= coopCloseFee
75✔
1074
        case lntypes.Remote:
71✔
1075
                theirBalance -= coopCloseFee
71✔
1076
        }
1077

1078
        // During fee negotiation it should always be verified that the
1079
        // initiator can pay the proposed fee, but we do a sanity check just to
1080
        // be sure here.
1081
        if ourBalance < 0 || theirBalance < 0 {
147✔
1082
                return 0, 0, fmt.Errorf("initiator cannot afford proposed " +
1✔
1083
                        "coop close fee")
1✔
1084
        }
1✔
1085

1086
        return ourBalance, theirBalance, nil
145✔
1087
}
1088

1089
// genSegwitV0HtlcScript generates the HTLC scripts for a normal segwit v0
1090
// channel.
1091
func genSegwitV0HtlcScript(chanType channeldb.ChannelType,
1092
        isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32,
1093
        rHash [32]byte, keyRing *CommitmentKeyRing,
1094
) (*WitnessScriptDesc, error) {
8,287✔
1095

8,287✔
1096
        var (
8,287✔
1097
                witnessScript []byte
8,287✔
1098
                err           error
8,287✔
1099
        )
8,287✔
1100

8,287✔
1101
        // Choose scripts based on channel type.
8,287✔
1102
        confirmedHtlcSpends := false
8,287✔
1103
        if chanType.HasAnchors() {
8,522✔
1104
                confirmedHtlcSpends = true
235✔
1105
        }
235✔
1106

1107
        // Generate the proper redeem scripts for the HTLC output modified by
1108
        // two-bits denoting if this is an incoming HTLC, and if the HTLC is
1109
        // being applied to their commitment transaction or ours.
1110
        switch {
8,287✔
1111
        // The HTLC is paying to us, and being applied to our commitment
1112
        // transaction. So we need to use the receiver's version of the HTLC
1113
        // script.
1114
        case isIncoming && whoseCommit.IsLocal():
2,123✔
1115
                witnessScript, err = input.ReceiverHTLCScript(
2,123✔
1116
                        timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
2,123✔
1117
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,123✔
1118
                )
2,123✔
1119

1120
        // We're being paid via an HTLC by the remote party, and the HTLC is
1121
        // being added to their commitment transaction, so we use the sender's
1122
        // version of the HTLC script.
1123
        case isIncoming && whoseCommit.IsRemote():
1,955✔
1124
                witnessScript, err = input.SenderHTLCScript(
1,955✔
1125
                        keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
1,955✔
1126
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,955✔
1127
                )
1,955✔
1128

1129
        // We're sending an HTLC which is being added to our commitment
1130
        // transaction. Therefore, we need to use the sender's version of the
1131
        // HTLC script.
1132
        case !isIncoming && whoseCommit.IsLocal():
1,970✔
1133
                witnessScript, err = input.SenderHTLCScript(
1,970✔
1134
                        keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
1,970✔
1135
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,970✔
1136
                )
1,970✔
1137

1138
        // Finally, we're paying the remote party via an HTLC, which is being
1139
        // added to their commitment transaction. Therefore, we use the
1140
        // receiver's version of the HTLC script.
1141
        case !isIncoming && whoseCommit.IsRemote():
2,239✔
1142
                witnessScript, err = input.ReceiverHTLCScript(
2,239✔
1143
                        timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
2,239✔
1144
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,239✔
1145
                )
2,239✔
1146
        }
1147
        if err != nil {
8,287✔
1148
                return nil, err
×
1149
        }
×
1150

1151
        // Now that we have the redeem scripts, create the P2WSH public key
1152
        // script for the output itself.
1153
        htlcP2WSH, err := input.WitnessScriptHash(witnessScript)
8,287✔
1154
        if err != nil {
8,287✔
1155
                return nil, err
×
1156
        }
×
1157

1158
        return &WitnessScriptDesc{
8,287✔
1159
                OutputScript:  htlcP2WSH,
8,287✔
1160
                WitnessScript: witnessScript,
8,287✔
1161
        }, nil
8,287✔
1162
}
1163

1164
// GenTaprootHtlcScript generates the HTLC scripts for a taproot+musig2
1165
// channel.
1166
func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty,
1167
        timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing,
1168
        auxLeaf input.AuxTapLeaf) (*input.HtlcScriptTree, error) {
201✔
1169

201✔
1170
        var (
201✔
1171
                htlcScriptTree *input.HtlcScriptTree
201✔
1172
                err            error
201✔
1173
        )
201✔
1174

201✔
1175
        // Generate the proper redeem scripts for the HTLC output modified by
201✔
1176
        // two-bits denoting if this is an incoming HTLC, and if the HTLC is
201✔
1177
        // being applied to their commitment transaction or ours.
201✔
1178
        switch {
201✔
1179
        // The HTLC is paying to us, and being applied to our commitment
1180
        // transaction. So we need to use the receiver's version of HTLC the
1181
        // script.
1182
        case isIncoming && whoseCommit.IsLocal():
52✔
1183
                htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
52✔
1184
                        timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
52✔
1185
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
52✔
1186
                )
52✔
1187

1188
        // We're being paid via an HTLC by the remote party, and the HTLC is
1189
        // being added to their commitment transaction, so we use the sender's
1190
        // version of the HTLC script.
1191
        case isIncoming && whoseCommit.IsRemote():
44✔
1192
                htlcScriptTree, err = input.SenderHTLCScriptTaproot(
44✔
1193
                        keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
44✔
1194
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
44✔
1195
                )
44✔
1196

1197
        // We're sending an HTLC which is being added to our commitment
1198
        // transaction. Therefore, we need to use the sender's version of the
1199
        // HTLC script.
1200
        case !isIncoming && whoseCommit.IsLocal():
48✔
1201
                htlcScriptTree, err = input.SenderHTLCScriptTaproot(
48✔
1202
                        keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
48✔
1203
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
48✔
1204
                )
48✔
1205

1206
        // Finally, we're paying the remote party via an HTLC, which is being
1207
        // added to their commitment transaction. Therefore, we use the
1208
        // receiver's version of the HTLC script.
1209
        case !isIncoming && whoseCommit.IsRemote():
57✔
1210
                htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
57✔
1211
                        timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
57✔
1212
                        keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf,
57✔
1213
                )
57✔
1214
        }
1215

1216
        return htlcScriptTree, err
201✔
1217
}
1218

1219
// genHtlcScript generates the proper P2WSH public key scripts for the HTLC
1220
// output modified by two-bits denoting if this is an incoming HTLC, and if the
1221
// HTLC is being applied to their commitment transaction or ours. A script
1222
// multiplexer for the various spending paths is returned. The script path that
1223
// we need to sign for the remote party (2nd level HTLCs) is also returned
1224
// along side the multiplexer.
1225
func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool,
1226
        whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte,
1227
        keyRing *CommitmentKeyRing,
1228
        auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) {
8,488✔
1229

8,488✔
1230
        if !chanType.IsTaproot() {
16,775✔
1231
                return genSegwitV0HtlcScript(
8,287✔
1232
                        chanType, isIncoming, whoseCommit, timeout, rHash,
8,287✔
1233
                        keyRing,
8,287✔
1234
                )
8,287✔
1235
        }
8,287✔
1236

1237
        return GenTaprootHtlcScript(
201✔
1238
                isIncoming, whoseCommit, timeout, rHash, keyRing, auxLeaf,
201✔
1239
        )
201✔
1240
}
1241

1242
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
1243
// full scripts will be generated for the HTLC output depending on if the HTLC
1244
// is incoming and if it's being applied to our commitment transaction or that
1245
// of the remote node's. Additionally, in order to be able to efficiently
1246
// locate the added HTLC on the commitment transaction from the
1247
// paymentDescriptor that generated it, the generated script is stored within
1248
// the descriptor itself.
1249
func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty,
1250
        isIncoming bool, paymentDesc *paymentDescriptor,
1251
        keyRing *CommitmentKeyRing, chanType channeldb.ChannelType,
1252
        auxLeaf input.AuxTapLeaf) error {
7,929✔
1253

7,929✔
1254
        timeout := paymentDesc.Timeout
7,929✔
1255
        rHash := paymentDesc.RHash
7,929✔
1256

7,929✔
1257
        scriptInfo, err := genHtlcScript(
7,929✔
1258
                chanType, isIncoming, whoseCommit, timeout, rHash, keyRing,
7,929✔
1259
                auxLeaf,
7,929✔
1260
        )
7,929✔
1261
        if err != nil {
7,929✔
1262
                return err
×
1263
        }
×
1264

1265
        pkScript := scriptInfo.PkScript()
7,929✔
1266

7,929✔
1267
        // Add the new HTLC outputs to the respective commitment transactions.
7,929✔
1268
        amountPending := int64(paymentDesc.Amount.ToSatoshis())
7,929✔
1269
        commitTx.AddTxOut(wire.NewTxOut(amountPending, pkScript))
7,929✔
1270

7,929✔
1271
        // Store the pkScript of this particular paymentDescriptor so we can
7,929✔
1272
        // quickly locate it within the commitment transaction later.
7,929✔
1273
        if whoseCommit.IsLocal() {
11,864✔
1274
                paymentDesc.ourPkScript = pkScript
3,935✔
1275

3,935✔
1276
                paymentDesc.ourWitnessScript = scriptInfo.WitnessScriptToSign()
3,935✔
1277
        } else {
7,929✔
1278
                paymentDesc.theirPkScript = pkScript
3,994✔
1279

3,994✔
1280
                //nolint:ll
3,994✔
1281
                paymentDesc.theirWitnessScript = scriptInfo.WitnessScriptToSign()
3,994✔
1282
        }
3,994✔
1283

1284
        return nil
7,929✔
1285
}
1286

1287
// findOutputIndexesFromRemote finds the index of our and their outputs from
1288
// the remote commitment transaction. It derives the key ring to compute the
1289
// output scripts and compares them against the outputs inside the commitment
1290
// to find the match.
1291
func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
1292
        chanState *channeldb.OpenChannel,
1293
        leafStore fn.Option[AuxLeafStore]) (uint32, uint32, error) {
1,951✔
1294

1,951✔
1295
        // Init the output indexes as empty.
1,951✔
1296
        ourIndex := uint32(channeldb.OutputIndexEmpty)
1,951✔
1297
        theirIndex := uint32(channeldb.OutputIndexEmpty)
1,951✔
1298

1,951✔
1299
        chanCommit := chanState.RemoteCommitment
1,951✔
1300
        _, commitmentPoint := btcec.PrivKeyFromBytes(revocationPreimage[:])
1,951✔
1301

1,951✔
1302
        // With the commitment point generated, we can now derive the king ring
1,951✔
1303
        // which will be used to generate the output scripts.
1,951✔
1304
        keyRing := DeriveCommitmentKeys(
1,951✔
1305
                commitmentPoint, lntypes.Remote, chanState.ChanType,
1,951✔
1306
                &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
1,951✔
1307
        )
1,951✔
1308

1,951✔
1309
        // Since it's remote commitment chain, we'd used the mirrored values.
1,951✔
1310
        //
1,951✔
1311
        // We use the remote's channel config for the csv delay.
1,951✔
1312
        theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
1,951✔
1313

1,951✔
1314
        // If we are the initiator of this channel, then it's be false from the
1,951✔
1315
        // remote's PoV.
1,951✔
1316
        isRemoteInitiator := !chanState.IsInitiator
1,951✔
1317

1,951✔
1318
        var leaseExpiry uint32
1,951✔
1319
        if chanState.ChanType.HasLeaseExpiration() {
1,951✔
1320
                leaseExpiry = chanState.ThawHeight
×
1321
        }
×
1322

1323
        // If we have a custom blob, then we'll attempt to fetch the aux leaves
1324
        // for this state.
1325
        auxResult, err := fn.MapOptionZ(
1,951✔
1326
                leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] {
3,858✔
1327
                        return a.FetchLeavesFromCommit(
1,907✔
1328
                                NewAuxChanState(chanState), chanCommit,
1,907✔
1329
                                *keyRing, lntypes.Remote,
1,907✔
1330
                        )
1,907✔
1331
                },
1,907✔
1332
        ).Unpack()
1333
        if err != nil {
1,951✔
1334
                return ourIndex, theirIndex, fmt.Errorf("unable to fetch aux "+
×
1335
                        "leaves: %w", err)
×
1336
        }
×
1337

1338
        // Map the scripts from our PoV. When facing a local commitment, the
1339
        // to_local output belongs to us and the to_remote output belongs to
1340
        // them. When facing a remote commitment, the to_local output belongs to
1341
        // them and the to_remote output belongs to us.
1342

1343
        // Compute the to_local script. From our PoV, when facing a remote
1344
        // commitment, the to_local output belongs to them.
1345
        localAuxLeaf := fn.FlatMapOption(
1,951✔
1346
                func(l CommitAuxLeaves) input.AuxTapLeaf {
1,951✔
1347
                        return l.LocalAuxLeaf
×
1348
                },
×
1349
        )(auxResult.AuxLeaves)
1350
        theirScript, err := CommitScriptToSelf(
1,951✔
1351
                chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
1,951✔
1352
                keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf,
1,951✔
1353
        )
1,951✔
1354
        if err != nil {
1,951✔
1355
                return ourIndex, theirIndex, err
×
1356
        }
×
1357

1358
        // Compute the to_remote script. From our PoV, when facing a remote
1359
        // commitment, the to_remote output belongs to us.
1360
        remoteAuxLeaf := fn.FlatMapOption(
1,951✔
1361
                func(l CommitAuxLeaves) input.AuxTapLeaf {
1,951✔
1362
                        return l.RemoteAuxLeaf
×
1363
                },
×
1364
        )(auxResult.AuxLeaves)
1365
        ourScript, _, err := CommitScriptToRemote(
1,951✔
1366
                chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
1,951✔
1367
                leaseExpiry, remoteAuxLeaf,
1,951✔
1368
        )
1,951✔
1369
        if err != nil {
1,951✔
1370
                return ourIndex, theirIndex, err
×
1371
        }
×
1372

1373
        // Now compare the scripts to find our/their output index.
1374
        for i, txOut := range chanCommit.CommitTx.TxOut {
9,576✔
1375
                switch {
7,625✔
1376
                case bytes.Equal(txOut.PkScript, ourScript.PkScript()):
1,756✔
1377
                        ourIndex = uint32(i)
1,756✔
1378
                case bytes.Equal(txOut.PkScript, theirScript.PkScript()):
1,755✔
1379
                        theirIndex = uint32(i)
1,755✔
1380
                }
1381
        }
1382

1383
        return ourIndex, theirIndex, nil
1,951✔
1384
}
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