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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 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,244✔
109

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

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

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

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

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

8,392✔
170
                // If this is not our commitment, the above ToRemoteKey will be
8,392✔
171
                // ours, and we blank out the local commitment tweak to
8,392✔
172
                // indicate that the key should not be tweaked when signing.
8,392✔
173
                if whoseCommit.IsRemote() {
13,438✔
174
                        keyRing.LocalCommitKeyTweak = nil
5,046✔
175
                }
5,046✔
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,244✔
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,230✔
201
        return w.OutputScript
39,230✔
202
}
39,230✔
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,163✔
209
        return w.WitnessScript
8,163✔
210
}
8,163✔
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,918✔
231

6,918✔
232
        switch {
6,918✔
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.
UNCOV
246
        case initiator && chanType.HasLeaseExpiration():
×
UNCOV
247
                toLocalRedeemScript, err := input.LeaseCommitScriptToSelf(
×
UNCOV
248
                        selfKey, revokeKey, csvDelay, leaseExpiry,
×
UNCOV
249
                )
×
UNCOV
250
                if err != nil {
×
251
                        return nil, err
×
252
                }
×
253

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

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

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

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

281
                return &WitnessScriptDesc{
6,652✔
282
                        OutputScript:  toLocalScriptHash,
6,652✔
283
                        WitnessScript: toLocalRedeemScript,
6,652✔
284
                }, nil
6,652✔
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,862✔
296

6,862✔
297
        switch {
6,862✔
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.
UNCOV
301
        case chanType.HasLeaseExpiration() && !initiator:
×
UNCOV
302
                script, err := input.LeaseCommitScriptToRemoteConfirmed(
×
UNCOV
303
                        remoteKey, leaseExpiry,
×
UNCOV
304
                )
×
UNCOV
305
                if err != nil {
×
306
                        return nil, 0, err
×
307
                }
×
308

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

UNCOV
314
                return &WitnessScriptDesc{
×
UNCOV
315
                        OutputScript:  p2wsh,
×
UNCOV
316
                        WitnessScript: script,
×
UNCOV
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,318✔
351
                // Otherwise the to_remote will be a simple p2wkh.
6,318✔
352
                p2wkh, err := input.CommitScriptUnencumbered(remoteKey)
6,318✔
353
                if err != nil {
6,318✔
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,318✔
360
                        OutputScript:  p2wkh,
6,318✔
361
                        WitnessScript: p2wkh,
6,318✔
362
                }, 0, nil
6,318✔
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,173✔
369
        if chanType.HasAnchors() {
4,504✔
370
                return txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
331✔
371
        }
331✔
372

373
        return txscript.SigHashAll
3,842✔
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,095✔
397
        if chanType.HasAnchors() {
8,506✔
398
                return 1
411✔
399
        }
411✔
400

401
        return 0
7,684✔
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,094✔
425

8,094✔
426
        switch {
8,094✔
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.
UNCOV
436
        case initiator && chanType.HasLeaseExpiration():
×
UNCOV
437
                witnessScript, err := input.LeaseSecondLevelHtlcScript(
×
UNCOV
438
                        revocationKey, delayKey, csvDelay, leaseExpiry,
×
UNCOV
439
                )
×
UNCOV
440
                if err != nil {
×
441
                        return nil, err
×
442
                }
×
443

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

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

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

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

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

474
// CommitWeight returns the base commitment weight before adding HTLCs.
475
func CommitWeight(chanType channeldb.ChannelType) lntypes.WeightUnit {
19,497✔
476
        switch {
19,497✔
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,131✔
485
                return input.CommitWeight
17,131✔
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 {
103,168✔
493

103,168✔
494
        switch {
103,168✔
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:
101,724✔
504
                return feePerKw.FeeForWeight(input.HtlcTimeoutWeight)
101,724✔
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 {
113,168✔
512

113,168✔
513
        switch {
113,168✔
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:
110,682✔
523
                return feePerKw.FeeForWeight(input.HtlcSuccessWeight)
110,682✔
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,071✔
698

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

704
        numHTLCs := int64(0)
4,071✔
705
        for _, htlc := range filteredHTLCView.Updates.Local {
14,681✔
706
                if HtlcIsDust(
10,610✔
707
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
10,610✔
708
                        htlc.Amount.ToSatoshis(), dustLimit,
10,610✔
709
                ) {
17,242✔
710

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

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

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

725
                numHTLCs++
3,919✔
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,071✔
733
                lntypes.WeightUnit(input.HTLCWeight*numHTLCs)
4,071✔
734

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

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

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

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

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

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

4,071✔
760
        // Before we create the commitment transaction below, we'll try to see
4,071✔
761
        // if there're any aux leaves that need to be a part of the tapscript
4,071✔
762
        // tree. We'll only do this if we have a custom blob defined though.
4,071✔
763
        auxResult, err := fn.MapOptionZ(
4,071✔
764
                cb.auxLeafStore,
4,071✔
765
                func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] {
8,075✔
766
                        return auxLeavesFromView(
4,004✔
767
                                s, cb.chanState, prevCommit.customBlob,
4,004✔
768
                                originalHtlcView, whoseCommit, ourBalance,
4,004✔
769
                                theirBalance, *keyRing,
4,004✔
770
                        )
4,004✔
771
                },
4,004✔
772
        ).Unpack()
773
        if err != nil {
4,071✔
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,071✔
782
        if cb.chanState.ChanType.HasLeaseExpiration() {
4,071✔
UNCOV
783
                leaseExpiry = cb.chanState.ThawHeight
×
UNCOV
784
        }
×
785
        if whoseCommit.IsLocal() {
6,094✔
786
                commitTx, err = CreateCommitTx(
2,023✔
787
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,023✔
788
                        &cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
2,023✔
789
                        ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
2,023✔
790
                        numHTLCs, cb.chanState.IsInitiator, leaseExpiry,
2,023✔
791
                        auxResult.AuxLeaves,
2,023✔
792
                )
2,023✔
793
        } else {
4,071✔
794
                commitTx, err = CreateCommitTx(
2,048✔
795
                        cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
2,048✔
796
                        &cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
2,048✔
797
                        theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
2,048✔
798
                        numHTLCs, !cb.chanState.IsInitiator, leaseExpiry,
2,048✔
799
                        auxResult.AuxLeaves,
2,048✔
800
                )
2,048✔
801
        }
2,048✔
802
        if err != nil {
4,071✔
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,071✔
809
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,071✔
810
                        return leaves.IncomingHtlcLeaves
×
811
                },
×
812
        )(auxResult.AuxLeaves)
813
        outgoingAuxLeaves := fn.MapOption(
4,071✔
814
                func(leaves CommitAuxLeaves) input.HtlcAuxLeaves {
4,071✔
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,071✔
829
        htlcIndexes := make([]input.HtlcIndex, len(commitTx.TxOut))
4,071✔
830
        for _, htlc := range filteredHTLCView.Updates.Local {
14,681✔
831
                if HtlcIsDust(
10,610✔
832
                        cb.chanState.ChanType, false, whoseCommit, feePerKw,
10,610✔
833
                        htlc.Amount.ToSatoshis(), dustLimit,
10,610✔
834
                ) {
17,242✔
835

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

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

845
                err := addHTLC(
3,978✔
846
                        commitTx, whoseCommit, false, htlc, keyRing,
3,978✔
847
                        cb.chanState.ChanType, auxLeaf,
3,978✔
848
                )
3,978✔
849
                if err != nil {
3,978✔
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,978✔
856
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
3,978✔
857
        }
858
        for _, htlc := range filteredHTLCView.Updates.Remote {
14,622✔
859
                if HtlcIsDust(
10,551✔
860
                        cb.chanState.ChanType, true, whoseCommit, feePerKw,
10,551✔
861
                        htlc.Amount.ToSatoshis(), dustLimit,
10,551✔
862
                ) {
17,183✔
863

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

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

873
                err := addHTLC(
3,919✔
874
                        commitTx, whoseCommit, true, htlc, keyRing,
3,919✔
875
                        cb.chanState.ChanType, auxLeaf,
3,919✔
876
                )
3,919✔
877
                if err != nil {
3,919✔
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,919✔
884
                htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint
3,919✔
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,071✔
891
        if err != nil {
4,071✔
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,071✔
901
        err = commitSort(commitTx, cltvs, htlcIndexes)
4,071✔
902
        if err != nil {
4,071✔
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,071✔
910
        if err := blockchain.CheckTransactionSanity(uTx); err != nil {
4,071✔
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,071✔
917
        for _, txOut := range commitTx.TxOut {
20,614✔
918
                totalOut += btcutil.Amount(txOut.Value)
16,543✔
919
        }
16,543✔
920
        if totalOut+commitFee > cb.chanState.Capacity {
4,071✔
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,071✔
928
                txn:          commitTx,
4,071✔
929
                fee:          commitFee,
4,071✔
930
                ourBalance:   ourBalance,
4,071✔
931
                theirBalance: theirBalance,
4,071✔
932
                cltvs:        cltvs,
4,071✔
933
        }, nil
4,071✔
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,875✔
949

4,875✔
950
        // First, we create the script for the delayed "pay-to-self" output.
4,875✔
951
        // This output has 2 main redemption clauses: either we can redeem the
4,875✔
952
        // output after a relative block delay, or the remote node can claim
4,875✔
953
        // the funds with the revocation key if we broadcast a revoked
4,875✔
954
        // commitment transaction.
4,875✔
955
        localAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf {
4,875✔
956
                return l.LocalAuxLeaf
×
957
        })(auxLeaves)
×
958
        toLocalScript, err := CommitScriptToSelf(
4,875✔
959
                chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
4,875✔
960
                uint32(localChanCfg.CsvDelay), leaseExpiry,
4,875✔
961
                fn.FlattenOption(localAuxLeaf),
4,875✔
962
        )
4,875✔
963
        if err != nil {
4,875✔
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,875✔
969
                return l.RemoteAuxLeaf
×
970
        })(auxLeaves)
×
971
        toRemoteScript, _, err := CommitScriptToRemote(
4,875✔
972
                chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
4,875✔
973
                fn.FlattenOption(remoteAuxLeaf),
4,875✔
974
        )
4,875✔
975
        if err != nil {
4,875✔
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,875✔
983
        commitTx.AddTxIn(&fundingOutput)
4,875✔
984

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

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

1002
        // If this channel type has anchors, we'll also add those.
1003
        if chanType.HasAnchors() {
5,272✔
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,875✔
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) {
147✔
1039

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

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

1050
        // To start with, we'll add the anchor and/or commitment fee to the
1051
        // balance of the initiator.
1052
        if isInitiator {
225✔
1053
                ourBalance += initiatorDelta
78✔
1054
        } else {
147✔
1055
                theirBalance += initiatorDelta
69✔
1056
        }
69✔
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 {
294✔
1062
                if isInitiator {
225✔
1063
                        return lntypes.Local
78✔
1064
                }
78✔
1065

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

147✔
1070
        // Based on the payer computed above, we'll subtract the closing fee.
147✔
1071
        switch payer {
147✔
1072
        case lntypes.Local:
71✔
1073
                ourBalance -= coopCloseFee
71✔
1074
        case lntypes.Remote:
76✔
1075
                theirBalance -= coopCloseFee
76✔
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 {
148✔
1082
                return 0, 0, fmt.Errorf("initiator cannot afford proposed " +
1✔
1083
                        "coop close fee")
1✔
1084
        }
1✔
1085

1086
        return ourBalance, theirBalance, nil
146✔
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,255✔
1095

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

8,255✔
1101
        // Choose scripts based on channel type.
8,255✔
1102
        confirmedHtlcSpends := false
8,255✔
1103
        if chanType.HasAnchors() {
8,490✔
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,255✔
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,115✔
1115
                witnessScript, err = input.ReceiverHTLCScript(
2,115✔
1116
                        timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
2,115✔
1117
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,115✔
1118
                )
2,115✔
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,947✔
1124
                witnessScript, err = input.SenderHTLCScript(
1,947✔
1125
                        keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
1,947✔
1126
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,947✔
1127
                )
1,947✔
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,962✔
1133
                witnessScript, err = input.SenderHTLCScript(
1,962✔
1134
                        keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
1,962✔
1135
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
1,962✔
1136
                )
1,962✔
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,231✔
1142
                witnessScript, err = input.ReceiverHTLCScript(
2,231✔
1143
                        timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
2,231✔
1144
                        keyRing.RevocationKey, rHash[:], confirmedHtlcSpends,
2,231✔
1145
                )
2,231✔
1146
        }
1147
        if err != nil {
8,255✔
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,255✔
1154
        if err != nil {
8,255✔
1155
                return nil, err
×
1156
        }
×
1157

1158
        return &WitnessScriptDesc{
8,255✔
1159
                OutputScript:  htlcP2WSH,
8,255✔
1160
                WitnessScript: witnessScript,
8,255✔
1161
        }, nil
8,255✔
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,456✔
1229

8,456✔
1230
        if !chanType.IsTaproot() {
16,711✔
1231
                return genSegwitV0HtlcScript(
8,255✔
1232
                        chanType, isIncoming, whoseCommit, timeout, rHash,
8,255✔
1233
                        keyRing,
8,255✔
1234
                )
8,255✔
1235
        }
8,255✔
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,897✔
1253

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

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

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

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

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

3,919✔
1276
                paymentDesc.ourWitnessScript = scriptInfo.WitnessScriptToSign()
3,919✔
1277
        } else {
7,897✔
1278
                paymentDesc.theirPkScript = pkScript
3,978✔
1279

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

1284
        return nil
7,897✔
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,958✔
1294

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

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

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

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

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

1,958✔
1318
        var leaseExpiry uint32
1,958✔
1319
        if chanState.ChanType.HasLeaseExpiration() {
1,958✔
UNCOV
1320
                leaseExpiry = chanState.ThawHeight
×
UNCOV
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,958✔
1326
                leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] {
3,872✔
1327
                        return a.FetchLeavesFromCommit(
1,914✔
1328
                                NewAuxChanState(chanState), chanCommit,
1,914✔
1329
                                *keyRing, lntypes.Remote,
1,914✔
1330
                        )
1,914✔
1331
                },
1,914✔
1332
        ).Unpack()
1333
        if err != nil {
1,958✔
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,958✔
1346
                func(l CommitAuxLeaves) input.AuxTapLeaf {
1,958✔
1347
                        return l.LocalAuxLeaf
×
1348
                },
×
1349
        )(auxResult.AuxLeaves)
1350
        theirScript, err := CommitScriptToSelf(
1,958✔
1351
                chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
1,958✔
1352
                keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf,
1,958✔
1353
        )
1,958✔
1354
        if err != nil {
1,958✔
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,958✔
1361
                func(l CommitAuxLeaves) input.AuxTapLeaf {
1,958✔
1362
                        return l.RemoteAuxLeaf
×
1363
                },
×
1364
        )(auxResult.AuxLeaves)
1365
        ourScript, _, err := CommitScriptToRemote(
1,958✔
1366
                chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
1,958✔
1367
                leaseExpiry, remoteAuxLeaf,
1,958✔
1368
        )
1,958✔
1369
        if err != nil {
1,958✔
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,581✔
1375
                switch {
7,623✔
1376
                case bytes.Equal(txOut.PkScript, ourScript.PkScript()):
1,763✔
1377
                        ourIndex = uint32(i)
1,763✔
1378
                case bytes.Equal(txOut.PkScript, theirScript.PkScript()):
1,762✔
1379
                        theirIndex = uint32(i)
1,762✔
1380
                }
1381
        }
1382

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