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

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 hits per line

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

73.08
/lnwallet/reservation.go
1
package lnwallet
2

3
import (
4
        "errors"
5
        "fmt"
6
        "net"
7
        "sync"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
11
        "github.com/btcsuite/btcd/btcutil"
12
        "github.com/btcsuite/btcd/chaincfg/chainhash"
13
        "github.com/btcsuite/btcd/wire"
14
        "github.com/lightningnetwork/lnd/channeldb"
15
        "github.com/lightningnetwork/lnd/fn/v2"
16
        "github.com/lightningnetwork/lnd/input"
17
        "github.com/lightningnetwork/lnd/keychain"
18
        "github.com/lightningnetwork/lnd/lntypes"
19
        "github.com/lightningnetwork/lnd/lnwallet/chanfunding"
20
        "github.com/lightningnetwork/lnd/lnwire"
21
)
22

23
// CommitmentType is an enum indicating the commitment type we should use for
24
// the channel we are opening.
25
type CommitmentType int
26

27
const (
28
        // CommitmentTypeLegacy is the legacy commitment format with a tweaked
29
        // to_remote key.
30
        CommitmentTypeLegacy CommitmentType = iota
31

32
        // CommitmentTypeTweakless is a newer commitment format where the
33
        // to_remote key is static.
34
        CommitmentTypeTweakless
35

36
        // CommitmentTypeAnchorsZeroFeeHtlcTx is a commitment type that is an
37
        // extension of the outdated CommitmentTypeAnchors, which in addition
38
        // requires second-level HTLC transactions to be signed using a
39
        // zero-fee.
40
        CommitmentTypeAnchorsZeroFeeHtlcTx
41

42
        // CommitmentTypeScriptEnforcedLease is a commitment type that builds
43
        // upon CommitmentTypeTweakless and CommitmentTypeAnchorsZeroFeeHtlcTx,
44
        // which in addition requires a CLTV clause to spend outputs paying to
45
        // the channel initiator. This is intended for use on leased channels to
46
        // guarantee that the channel initiator has no incentives to close a
47
        // leased channel before its maturity date.
48
        CommitmentTypeScriptEnforcedLease
49

50
        // CommitmentTypeSimpleTaproot is the base commitment type for the
51
        // channels that use a musig2 funding output and the tapscript tree
52
        // where relevant for the commitment transaction pk scripts.
53
        CommitmentTypeSimpleTaproot
54

55
        // CommitmentTypeSimpleTaprootOverlay builds on the existing
56
        // CommitmentTypeSimpleTaproot type but layers on a special overlay
57
        // protocol.
58
        CommitmentTypeSimpleTaprootOverlay
59
)
60

61
// HasStaticRemoteKey returns whether the commitment type supports remote
62
// outputs backed by static keys.
63
func (c CommitmentType) HasStaticRemoteKey() bool {
151✔
64
        switch c {
151✔
65
        case CommitmentTypeTweakless,
66
                CommitmentTypeAnchorsZeroFeeHtlcTx,
67
                CommitmentTypeScriptEnforcedLease,
68
                CommitmentTypeSimpleTaproot,
69
                CommitmentTypeSimpleTaprootOverlay:
36✔
70

36✔
71
                return true
36✔
72

73
        default:
115✔
74
                return false
115✔
75
        }
76
}
77

78
// HasAnchors returns whether the commitment type supports anchor outputs.
79
func (c CommitmentType) HasAnchors() bool {
664✔
80
        switch c {
664✔
81
        case CommitmentTypeAnchorsZeroFeeHtlcTx,
82
                CommitmentTypeScriptEnforcedLease,
83
                CommitmentTypeSimpleTaproot,
84
                CommitmentTypeSimpleTaprootOverlay:
56✔
85

56✔
86
                return true
56✔
87

88
        default:
608✔
89
                return false
608✔
90
        }
91
}
92

93
// IsTaproot returns true if the channel type is a taproot channel.
94
func (c CommitmentType) IsTaproot() bool {
595✔
95
        return c == CommitmentTypeSimpleTaproot ||
595✔
96
                c == CommitmentTypeSimpleTaprootOverlay
595✔
97
}
595✔
98

99
// String returns the name of the CommitmentType.
UNCOV
100
func (c CommitmentType) String() string {
×
UNCOV
101
        switch c {
×
UNCOV
102
        case CommitmentTypeLegacy:
×
UNCOV
103
                return "legacy"
×
UNCOV
104
        case CommitmentTypeTweakless:
×
UNCOV
105
                return "tweakless"
×
UNCOV
106
        case CommitmentTypeAnchorsZeroFeeHtlcTx:
×
UNCOV
107
                return "anchors-zero-fee-second-level"
×
UNCOV
108
        case CommitmentTypeScriptEnforcedLease:
×
UNCOV
109
                return "script-enforced-lease"
×
UNCOV
110
        case CommitmentTypeSimpleTaproot:
×
UNCOV
111
                return "simple-taproot"
×
112
        case CommitmentTypeSimpleTaprootOverlay:
×
113
                return "simple-taproot-overlay"
×
114
        default:
×
115
                return "invalid"
×
116
        }
117
}
118

119
// ReservationState is a type that represents the current state of a channel
120
// reservation within the funding workflow.
121
type ReservationState int
122

123
const (
124
        // WaitingToSend is the state either the funder/fundee is in after
125
        // creating a reservation, but hasn't sent a message yet.
126
        WaitingToSend ReservationState = iota
127

128
        // SentOpenChannel is the state the funder is in after sending the
129
        // OpenChannel message.
130
        SentOpenChannel
131

132
        // SentAcceptChannel is the state the fundee is in after sending the
133
        // AcceptChannel message.
134
        SentAcceptChannel
135

136
        // SentFundingCreated is the state the funder is in after sending the
137
        // FundingCreated message.
138
        SentFundingCreated
139
)
140

141
// ChannelContribution is the primary constituent of the funding workflow
142
// within lnwallet. Each side first exchanges their respective contributions
143
// along with channel specific parameters like the min fee/KB. Once
144
// contributions have been exchanged, each side will then produce signatures
145
// for all their inputs to the funding transactions, and finally a signature
146
// for the other party's version of the commitment transaction.
147
type ChannelContribution struct {
148
        // FundingOutpoint is the amount of funds contributed to the funding
149
        // transaction.
150
        FundingAmount btcutil.Amount
151

152
        // Inputs to the funding transaction.
153
        Inputs []*wire.TxIn
154

155
        // ChangeOutputs are the Outputs to be used in the case that the total
156
        // value of the funding inputs is greater than the total potential
157
        // channel capacity.
158
        ChangeOutputs []*wire.TxOut
159

160
        // FirstCommitmentPoint is the first commitment point that will be used
161
        // to create the revocation key in the first commitment transaction we
162
        // send to the remote party.
163
        FirstCommitmentPoint *btcec.PublicKey
164

165
        // ChannelConfig is the concrete contribution that this node is
166
        // offering to the channel. This includes all the various constraints
167
        // such as the min HTLC, and also all the keys which will be used for
168
        // the duration of the channel.
169
        *channeldb.ChannelConfig
170

171
        // UpfrontShutdown is an optional address to which the channel should be
172
        // paid out to on cooperative close.
173
        UpfrontShutdown lnwire.DeliveryAddress
174

175
        // LocalNonce is populated if the channel type is a simple taproot
176
        // channel. This stores the public (and secret) nonce that will be used
177
        // to generate commitments for the local party.
178
        LocalNonce *musig2.Nonces
179
}
180

181
// toChanConfig returns the raw channel configuration generated by a node's
182
// contribution to the channel.
183
func (c *ChannelContribution) toChanConfig() channeldb.ChannelConfig {
172✔
184
        return *c.ChannelConfig
172✔
185
}
172✔
186

187
// ChannelReservation represents an intent to open a lightning payment channel
188
// with a counterparty. The funding processes from reservation to channel opening
189
// is a 3-step process. In order to allow for full concurrency during the
190
// reservation workflow, resources consumed by a contribution are "locked"
191
// themselves. This prevents a number of race conditions such as two funding
192
// transactions double-spending the same input. A reservation can also be
193
// canceled, which removes the resources from limbo, allowing another
194
// reservation to claim them.
195
//
196
// The reservation workflow consists of the following three steps:
197
//  1. lnwallet.InitChannelReservation
198
//     * One requests the wallet to allocate the necessary resources for a
199
//     channel reservation. These resources are put in limbo for the lifetime
200
//     of a reservation.
201
//     * Once completed the reservation will have the wallet's contribution
202
//     accessible via the .OurContribution() method. This contribution
203
//     contains the necessary items to allow the remote party to build both
204
//     the funding, and commitment transactions.
205
//  2. ChannelReservation.ProcessContribution/ChannelReservation.ProcessSingleContribution
206
//     * The counterparty presents their contribution to the payment channel.
207
//     This allows us to build the funding, and commitment transactions
208
//     ourselves.
209
//     * We're now able to sign our inputs to the funding transactions, and
210
//     the counterparty's version of the commitment transaction.
211
//     * All signatures crafted by us, are now available via .OurSignatures().
212
//  3. ChannelReservation.CompleteReservation/ChannelReservation.CompleteReservationSingle
213
//     * The final step in the workflow. The counterparty presents the
214
//     signatures for all their inputs to the funding transaction, as well
215
//     as a signature to our version of the commitment transaction.
216
//     * We then verify the validity of all signatures before considering the
217
//     channel "open".
218
type ChannelReservation struct {
219
        // This mutex MUST be held when either reading or modifying any of the
220
        // fields below.
221
        sync.RWMutex
222

223
        // fundingTx is the funding transaction for this pending channel.
224
        fundingTx *wire.MsgTx
225

226
        // In order of sorted inputs. Sorting is done in accordance
227
        // to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
228
        ourFundingInputScripts   []*input.Script
229
        theirFundingInputScripts []*input.Script
230

231
        // Our signature for their version of the commitment transaction.
232
        ourCommitmentSig   input.Signature
233
        theirCommitmentSig input.Signature
234

235
        ourContribution   *ChannelContribution
236
        theirContribution *ChannelContribution
237

238
        partialState *channeldb.OpenChannel
239
        nodeAddr     net.Addr
240

241
        // The ID of this reservation, used to uniquely track the reservation
242
        // throughout its lifetime.
243
        reservationID uint64
244

245
        // pendingChanID is the pending channel ID for this channel as
246
        // identified within the wire protocol.
247
        pendingChanID [32]byte
248

249
        // pushMSat the amount of milli-satoshis that should be pushed to the
250
        // responder of a single funding channel as part of the initial
251
        // commitment state.
252
        pushMSat lnwire.MilliSatoshi
253

254
        wallet     *LightningWallet
255
        chanFunder chanfunding.Assembler
256

257
        fundingIntent chanfunding.Intent
258

259
        // nextRevocationKeyLoc stores the key locator information for this
260
        // channel.
261
        nextRevocationKeyLoc keychain.KeyLocator
262

263
        musigSessions *MusigPairSession
264

265
        state ReservationState
266
}
267

268
// NewChannelReservation creates a new channel reservation. This function is
269
// used only internally by lnwallet. In order to concurrent safety, the
270
// creation of all channel reservations should be carried out via the
271
// lnwallet.InitChannelReservation interface.
272
func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
273
        wallet *LightningWallet, id uint64, chainHash *chainhash.Hash,
274
        thawHeight uint32, req *InitFundingReserveMsg) (*ChannelReservation,
275
        error) {
155✔
276

155✔
277
        var (
155✔
278
                ourBalance   lnwire.MilliSatoshi
155✔
279
                theirBalance lnwire.MilliSatoshi
155✔
280
                initiator    bool
155✔
281
        )
155✔
282

155✔
283
        // Based on the channel type, we determine the initial commit weight
155✔
284
        // and fee.
155✔
285
        commitWeight := input.CommitWeight
155✔
286
        if req.CommitType.IsTaproot() {
167✔
287
                commitWeight = input.TaprootCommitWeight
12✔
288
        } else if req.CommitType.HasAnchors() {
159✔
289
                commitWeight = input.AnchorCommitWeight
4✔
290
        }
4✔
291
        commitFee := req.CommitFeePerKw.FeeForWeight(
155✔
292
                lntypes.WeightUnit(commitWeight),
155✔
293
        )
155✔
294

155✔
295
        localFundingMSat := lnwire.NewMSatFromSatoshis(localFundingAmt)
155✔
296
        // TODO(halseth): make method take remote funding amount directly
155✔
297
        // instead of inferring it from capacity and local amt.
155✔
298
        capacityMSat := lnwire.NewMSatFromSatoshis(capacity)
155✔
299

155✔
300
        // The total fee paid by the initiator will be the commitment fee in
155✔
301
        // addition to the two anchor outputs.
155✔
302
        feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
155✔
303
        if req.CommitType.HasAnchors() {
171✔
304
                feeMSat += 2 * lnwire.NewMSatFromSatoshis(AnchorSize)
16✔
305
        }
16✔
306

307
        // Used to cut down on verbosity.
308
        defaultDust := DustLimitUnknownWitness()
155✔
309

155✔
310
        // If we're the responder to a single-funder reservation, then we have
155✔
311
        // no initial balance in the channel unless the remote party is pushing
155✔
312
        // some funds to us within the first commitment state.
155✔
313
        if localFundingAmt == 0 {
218✔
314
                ourBalance = req.PushMSat
63✔
315
                theirBalance = capacityMSat - feeMSat - req.PushMSat
63✔
316
                initiator = false
63✔
317

63✔
318
                // If the responder doesn't have enough funds to actually pay
63✔
319
                // the fees, then we'll bail our early.
63✔
320
                if int64(theirBalance) < 0 {
63✔
321
                        return nil, ErrFunderBalanceDust(
×
322
                                int64(commitFee), int64(theirBalance.ToSatoshis()),
×
323
                                int64(2*defaultDust),
×
324
                        )
×
325
                }
×
326
        } else {
92✔
327
                // TODO(roasbeef): need to rework fee structure in general and
92✔
328
                // also when we "unlock" dual funder within the daemon
92✔
329

92✔
330
                if capacity == localFundingAmt {
184✔
331
                        // If we're initiating a single funder workflow, then
92✔
332
                        // we pay all the initial fees within the commitment
92✔
333
                        // transaction. We also deduct our balance by the
92✔
334
                        // amount pushed as part of the initial state.
92✔
335
                        ourBalance = capacityMSat - feeMSat - req.PushMSat
92✔
336
                        theirBalance = req.PushMSat
92✔
337
                } else {
92✔
338
                        // Otherwise, this is a dual funder workflow where both
×
339
                        // slides split the amount funded and the commitment
×
340
                        // fee.
×
341
                        ourBalance = localFundingMSat - (feeMSat / 2)
×
342
                        theirBalance = capacityMSat - localFundingMSat - (feeMSat / 2) + req.PushMSat
×
343
                }
×
344

345
                initiator = true
92✔
346

92✔
347
                // If we, the initiator don't have enough funds to actually pay
92✔
348
                // the fees, then we'll exit with an error.
92✔
349
                if int64(ourBalance) < 0 {
96✔
350
                        return nil, ErrFunderBalanceDust(
4✔
351
                                int64(commitFee), int64(ourBalance),
4✔
352
                                int64(2*defaultDust),
4✔
353
                        )
4✔
354
                }
4✔
355
        }
356

357
        // If we're the initiator and our starting balance within the channel
358
        // after we take account of fees is below 2x the dust limit, then we'll
359
        // reject this channel creation request.
360
        //
361
        // TODO(roasbeef): reject if 30% goes to fees? dust channel
362
        if initiator && ourBalance.ToSatoshis() <= 2*defaultDust {
151✔
363
                return nil, ErrFunderBalanceDust(
×
364
                        int64(commitFee),
×
365
                        int64(ourBalance.ToSatoshis()),
×
366
                        int64(2*defaultDust),
×
367
                )
×
368
        }
×
369

370
        // Similarly we ensure their balance is reasonable if we are not the
371
        // initiator.
372
        if !initiator && theirBalance.ToSatoshis() <= 2*defaultDust {
151✔
373
                return nil, ErrFunderBalanceDust(
×
374
                        int64(commitFee),
×
375
                        int64(theirBalance.ToSatoshis()),
×
376
                        int64(2*defaultDust),
×
377
                )
×
378
        }
×
379

380
        // Next we'll set the channel type based on what we can ascertain about
381
        // the balances/push amount within the channel.
382
        var chanType channeldb.ChannelType
151✔
383

151✔
384
        // If either of the balances are zero at this point, or we have a
151✔
385
        // non-zero push amt (there's no pushing for dual funder), then this is
151✔
386
        // a single-funder channel.
151✔
387
        if ourBalance == 0 || theirBalance == 0 || req.PushMSat != 0 {
302✔
388
                // Both the tweakless type and the anchor type is tweakless,
151✔
389
                // hence set the bit.
151✔
390
                if req.CommitType.HasStaticRemoteKey() {
187✔
391
                        chanType |= channeldb.SingleFunderTweaklessBit
36✔
392
                } else {
151✔
393
                        chanType |= channeldb.SingleFunderBit
115✔
394
                }
115✔
395

396
                switch a := req.ChanFunder.(type) {
151✔
397
                // The first channels of a batch shouldn't publish the batch TX
398
                // to avoid problems if some of the funding flows can't be
399
                // completed. Only the last channel of a batch should publish.
UNCOV
400
                case chanfunding.ConditionalPublishAssembler:
×
UNCOV
401
                        if !a.ShouldPublishFundingTx() {
×
UNCOV
402
                                chanType |= channeldb.NoFundingTxBit
×
UNCOV
403
                        }
×
404

405
                // Normal funding flow, the assembler creates a TX from the
406
                // internal wallet.
407
                case chanfunding.FundingTxAssembler:
142✔
408
                        // Do nothing, a FundingTxAssembler has the transaction.
409

410
                // If this intent isn't one that's able to provide us with a
411
                // funding transaction, then we'll set the chanType bit to
412
                // signal that we don't have access to one.
413
                default:
9✔
414
                        chanType |= channeldb.NoFundingTxBit
9✔
415
                }
416
        } else {
×
417
                // Otherwise, this is a dual funder channel, and no side is
×
418
                // technically the "initiator"
×
419
                initiator = false
×
420
                chanType |= channeldb.DualFunderBit
×
421
        }
×
422

423
        // We are adding anchor outputs to our commitment. We only support this
424
        // in combination with zero-fee second-levels HTLCs.
425
        if req.CommitType.HasAnchors() {
167✔
426
                chanType |= channeldb.AnchorOutputsBit
16✔
427
                chanType |= channeldb.ZeroHtlcTxFeeBit
16✔
428
        }
16✔
429

430
        // Set the appropriate LeaseExpiration/Frozen bit based on the
431
        // reservation parameters.
432
        if req.CommitType == CommitmentTypeScriptEnforcedLease {
151✔
UNCOV
433
                if thawHeight == 0 {
×
434
                        return nil, errors.New("missing absolute expiration " +
×
435
                                "for script enforced lease commitment type")
×
436
                }
×
UNCOV
437
                chanType |= channeldb.LeaseExpirationBit
×
438
        } else if thawHeight > 0 {
159✔
439
                chanType |= channeldb.FrozenBit
8✔
440
        }
8✔
441

442
        if req.CommitType.IsTaproot() {
163✔
443
                chanType |= channeldb.SimpleTaprootFeatureBit
12✔
444
        }
12✔
445

446
        if req.ZeroConf {
155✔
447
                chanType |= channeldb.ZeroConfBit
4✔
448
        }
4✔
449

450
        if req.OptionScidAlias {
151✔
UNCOV
451
                chanType |= channeldb.ScidAliasChanBit
×
UNCOV
452
        }
×
453

454
        if req.ScidAliasFeature {
157✔
455
                chanType |= channeldb.ScidAliasFeatureBit
6✔
456
        }
6✔
457

458
        taprootOverlay := req.CommitType == CommitmentTypeSimpleTaprootOverlay
151✔
459
        switch {
151✔
460
        case taprootOverlay && req.TapscriptRoot.IsNone():
×
461
                fallthrough
×
462
        case !taprootOverlay && req.TapscriptRoot.IsSome():
×
463
                return nil, fmt.Errorf("taproot overlay chans must be set " +
×
464
                        "with tapscript root")
×
465

466
        case taprootOverlay && req.TapscriptRoot.IsSome():
×
467
                chanType |= channeldb.TapscriptRootBit
×
468
        }
469

470
        return &ChannelReservation{
151✔
471
                ourContribution: &ChannelContribution{
151✔
472
                        FundingAmount: ourBalance.ToSatoshis(),
151✔
473
                        ChannelConfig: &channeldb.ChannelConfig{},
151✔
474
                },
151✔
475
                theirContribution: &ChannelContribution{
151✔
476
                        FundingAmount: theirBalance.ToSatoshis(),
151✔
477
                        ChannelConfig: &channeldb.ChannelConfig{},
151✔
478
                },
151✔
479
                partialState: &channeldb.OpenChannel{
151✔
480
                        ChanType:     chanType,
151✔
481
                        ChainHash:    *chainHash,
151✔
482
                        IsPending:    true,
151✔
483
                        IsInitiator:  initiator,
151✔
484
                        ChannelFlags: req.Flags,
151✔
485
                        Capacity:     capacity,
151✔
486
                        LocalCommitment: channeldb.ChannelCommitment{
151✔
487
                                LocalBalance:  ourBalance,
151✔
488
                                RemoteBalance: theirBalance,
151✔
489
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
151✔
490
                                CommitFee:     commitFee,
151✔
491
                        },
151✔
492
                        RemoteCommitment: channeldb.ChannelCommitment{
151✔
493
                                LocalBalance:  ourBalance,
151✔
494
                                RemoteBalance: theirBalance,
151✔
495
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
151✔
496
                                CommitFee:     commitFee,
151✔
497
                        },
151✔
498
                        ThawHeight:           thawHeight,
151✔
499
                        Db:                   wallet.Cfg.Database,
151✔
500
                        InitialLocalBalance:  ourBalance,
151✔
501
                        InitialRemoteBalance: theirBalance,
151✔
502
                        Memo:                 req.Memo,
151✔
503
                        TapscriptRoot:        req.TapscriptRoot,
151✔
504
                },
151✔
505
                pushMSat:      req.PushMSat,
151✔
506
                pendingChanID: req.PendingChanID,
151✔
507
                reservationID: id,
151✔
508
                wallet:        wallet,
151✔
509
                chanFunder:    req.ChanFunder,
151✔
510
                state:         WaitingToSend,
151✔
511
        }, nil
151✔
512
}
513

514
// AddAlias stores the first alias for zero-conf channels.
515
func (r *ChannelReservation) AddAlias(scid lnwire.ShortChannelID) {
4✔
516
        r.Lock()
4✔
517
        defer r.Unlock()
4✔
518

4✔
519
        r.partialState.ShortChannelID = scid
4✔
520
}
4✔
521

522
// SetState sets the ReservationState.
523
func (r *ChannelReservation) SetState(state ReservationState) {
123✔
524
        r.Lock()
123✔
525
        defer r.Unlock()
123✔
526

123✔
527
        r.state = state
123✔
528
}
123✔
529

530
// State returns the current ReservationState.
531
func (r *ChannelReservation) State() ReservationState {
86✔
532
        r.RLock()
86✔
533
        defer r.RUnlock()
86✔
534

86✔
535
        return r.state
86✔
536
}
86✔
537

538
// SetNumConfsRequired sets the number of confirmations that are required for
539
// the ultimate funding transaction before the channel can be considered open.
540
// This is distinct from the main reservation workflow as it allows
541
// implementations a bit more flexibility w.r.t to if the responder of the
542
// initiator sets decides the number of confirmations needed.
543
func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) {
109✔
544
        r.Lock()
109✔
545
        defer r.Unlock()
109✔
546

109✔
547
        r.partialState.NumConfsRequired = numConfs
109✔
548
}
109✔
549

550
// IsZeroConf returns if the reservation's underlying partial channel state is
551
// a zero-conf channel.
552
func (r *ChannelReservation) IsZeroConf() bool {
107✔
553
        r.RLock()
107✔
554
        defer r.RUnlock()
107✔
555

107✔
556
        return r.partialState.IsZeroConf()
107✔
557
}
107✔
558

559
// IsTaproot returns if the reservation's underlying partial channel state is a
560
// taproot channel.
561
func (r *ChannelReservation) IsTaproot() bool {
185✔
562
        r.RLock()
185✔
563
        defer r.RUnlock()
185✔
564

185✔
565
        return r.partialState.ChanType.IsTaproot()
185✔
566
}
185✔
567

568
// CommitConstraints takes the constraints that the remote party specifies for
569
// the type of commitments that we can generate for them. These constraints
570
// include several parameters that serve as flow control restricting the amount
571
// of satoshis that can be transferred in a single commitment. This function
572
// will also attempt to verify the constraints for sanity, returning an error
573
// if the parameters are seemed unsound.
574
func (r *ChannelReservation) CommitConstraints(
575
        bounds *channeldb.ChannelStateBounds,
576
        commitParams *channeldb.CommitmentParams,
577
        maxLocalCSVDelay uint16,
578
        responder bool) error {
109✔
579

109✔
580
        r.Lock()
109✔
581
        defer r.Unlock()
109✔
582

109✔
583
        // First, verify the sanity of the channel constraints.
109✔
584
        err := VerifyConstraints(
109✔
585
                bounds, commitParams, maxLocalCSVDelay, r.partialState.Capacity,
109✔
586
        )
109✔
587
        if err != nil {
111✔
588
                return err
2✔
589
        }
2✔
590

591
        // Our dust limit should always be less than or equal to our proposed
592
        // channel reserve.
593
        if responder && r.ourContribution.DustLimit > bounds.ChanReserve {
107✔
594
                r.ourContribution.DustLimit = bounds.ChanReserve
×
595
        }
×
596

597
        r.ourContribution.ChanReserve = bounds.ChanReserve
107✔
598
        r.ourContribution.MaxPendingAmount = bounds.MaxPendingAmount
107✔
599
        r.ourContribution.MinHTLC = bounds.MinHTLC
107✔
600
        r.ourContribution.MaxAcceptedHtlcs = bounds.MaxAcceptedHtlcs
107✔
601
        r.ourContribution.CsvDelay = commitParams.CsvDelay
107✔
602

107✔
603
        return nil
107✔
604
}
605

606
// validateReserveBounds checks that both ChannelReserve values are above both
607
// DustLimit values. This not only avoids stuck channels, but is also mandated
608
// by BOLT#02 even if it's not explicit. This returns true if the bounds are
609
// valid. This function should be called with the lock held.
610
func (r *ChannelReservation) validateReserveBounds() bool {
101✔
611
        ourDustLimit := r.ourContribution.DustLimit
101✔
612
        ourRequiredReserve := r.ourContribution.ChanReserve
101✔
613
        theirDustLimit := r.theirContribution.DustLimit
101✔
614
        theirRequiredReserve := r.theirContribution.ChanReserve
101✔
615

101✔
616
        // We take the smaller of the two ChannelReserves and compare it
101✔
617
        // against the larger of the two DustLimits.
101✔
618
        minChanReserve := ourRequiredReserve
101✔
619
        if minChanReserve > theirRequiredReserve {
102✔
620
                minChanReserve = theirRequiredReserve
1✔
621
        }
1✔
622

623
        maxDustLimit := ourDustLimit
101✔
624
        if maxDustLimit < theirDustLimit {
101✔
625
                maxDustLimit = theirDustLimit
×
626
        }
×
627

628
        return minChanReserve >= maxDustLimit
101✔
629
}
630

631
// OurContribution returns the wallet's fully populated contribution to the
632
// pending payment channel. See 'ChannelContribution' for further details
633
// regarding the contents of a contribution.
634
//
635
// NOTE: This SHOULD NOT be modified.
636
// TODO(roasbeef): make copy?
637
func (r *ChannelReservation) OurContribution() *ChannelContribution {
186✔
638
        r.RLock()
186✔
639
        defer r.RUnlock()
186✔
640

186✔
641
        return r.ourContribution
186✔
642
}
186✔
643

644
// ProcessContribution verifies the counterparty's contribution to the pending
645
// payment channel. As a result of this incoming message, lnwallet is able to
646
// build the funding transaction, and both commitment transactions. Once this
647
// message has been processed, all signatures to inputs to the funding
648
// transaction belonging to the wallet are available. Additionally, the wallet
649
// will generate a signature to the counterparty's version of the commitment
650
// transaction.
651
func (r *ChannelReservation) ProcessContribution(theirContribution *ChannelContribution) error {
45✔
652
        errChan := make(chan error, 1)
45✔
653

45✔
654
        r.wallet.msgChan <- &addContributionMsg{
45✔
655
                pendingFundingID: r.reservationID,
45✔
656
                contribution:     theirContribution,
45✔
657
                err:              errChan,
45✔
658
        }
45✔
659

45✔
660
        return <-errChan
45✔
661
}
45✔
662

663
// IsPsbt returns true if there is a PSBT funding intent mapped to this
664
// reservation.
665
func (r *ChannelReservation) IsPsbt() bool {
64✔
666
        _, ok := r.fundingIntent.(*chanfunding.PsbtIntent)
64✔
667
        return ok
64✔
668
}
64✔
669

670
// IsCannedShim returns true if there is a canned shim funding intent mapped to
671
// this reservation.
672
func (r *ChannelReservation) IsCannedShim() bool {
59✔
673
        _, ok := r.fundingIntent.(*chanfunding.ShimIntent)
59✔
674
        return ok
59✔
675
}
59✔
676

677
// ProcessPsbt continues a previously paused funding flow that involves PSBT to
678
// construct the funding transaction. This method can be called once the PSBT
679
// is finalized and the signed transaction is available.
680
func (r *ChannelReservation) ProcessPsbt(
UNCOV
681
        auxFundingDesc fn.Option[AuxFundingDesc]) error {
×
UNCOV
682

×
UNCOV
683
        errChan := make(chan error, 1)
×
UNCOV
684

×
UNCOV
685
        r.wallet.msgChan <- &continueContributionMsg{
×
UNCOV
686
                auxFundingDesc:   auxFundingDesc,
×
UNCOV
687
                pendingFundingID: r.reservationID,
×
UNCOV
688
                err:              errChan,
×
UNCOV
689
        }
×
UNCOV
690

×
UNCOV
691
        return <-errChan
×
UNCOV
692
}
×
693

694
// RemoteCanceled informs the PSBT funding state machine that the remote peer
695
// has canceled the pending reservation, likely due to a timeout.
UNCOV
696
func (r *ChannelReservation) RemoteCanceled() {
×
UNCOV
697
        psbtIntent, ok := r.fundingIntent.(*chanfunding.PsbtIntent)
×
UNCOV
698
        if !ok {
×
699
                return
×
700
        }
×
UNCOV
701
        psbtIntent.RemoteCanceled()
×
702
}
703

704
// ProcessSingleContribution verifies, and records the initiator's contribution
705
// to this pending single funder channel. Internally, no further action is
706
// taken other than recording the initiator's contribution to the single funder
707
// channel.
708
func (r *ChannelReservation) ProcessSingleContribution(theirContribution *ChannelContribution) error {
62✔
709
        errChan := make(chan error, 1)
62✔
710

62✔
711
        r.wallet.msgChan <- &addSingleContributionMsg{
62✔
712
                pendingFundingID: r.reservationID,
62✔
713
                contribution:     theirContribution,
62✔
714
                err:              errChan,
62✔
715
        }
62✔
716

62✔
717
        return <-errChan
62✔
718
}
62✔
719

720
// TheirContribution returns the counterparty's pending contribution to the
721
// payment channel. See 'ChannelContribution' for further details regarding the
722
// contents of a contribution. This attribute will ONLY be available after a
723
// call to .ProcessContribution().
724
//
725
// NOTE: This SHOULD NOT be modified.
726
func (r *ChannelReservation) TheirContribution() *ChannelContribution {
54✔
727
        r.RLock()
54✔
728
        defer r.RUnlock()
54✔
729
        return r.theirContribution
54✔
730
}
54✔
731

732
// OurSignatures retrieves the wallet's signatures to all inputs to the funding
733
// transaction belonging to itself, and also a signature for the counterparty's
734
// version of the commitment transaction. The signatures for the wallet's
735
// inputs to the funding transaction are returned in sorted order according to
736
// BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
737
//
738
// NOTE: These signatures will only be populated after a call to
739
// .ProcessContribution()
740
func (r *ChannelReservation) OurSignatures() ([]*input.Script,
741
        input.Signature) {
88✔
742

88✔
743
        r.RLock()
88✔
744
        defer r.RUnlock()
88✔
745
        return r.ourFundingInputScripts, r.ourCommitmentSig
88✔
746
}
88✔
747

748
// CompleteReservation finalizes the pending channel reservation, transitioning
749
// from a pending payment channel, to an open payment channel. All passed
750
// signatures to the counterparty's inputs to the funding transaction will be
751
// fully verified. Signatures are expected to be passed in sorted order
752
// according to BIP-69:
753
// https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
754
// Additionally, verification is performed in order to ensure that the
755
// counterparty supplied a valid signature to our version of the commitment
756
// transaction.  Once this method returns, callers should broadcast the
757
// created funding transaction, then call .WaitForChannelOpen() which will
758
// block until the funding transaction obtains the configured number of
759
// confirmations. Once the method unblocks, a LightningChannel instance is
760
// returned, marking the channel available for updates.
761
func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*input.Script,
762
        commitmentSig input.Signature) (*channeldb.OpenChannel, error) {
43✔
763

43✔
764
        // TODO(roasbeef): add flag for watch or not?
43✔
765
        errChan := make(chan error, 1)
43✔
766
        completeChan := make(chan *channeldb.OpenChannel, 1)
43✔
767

43✔
768
        r.wallet.msgChan <- &addCounterPartySigsMsg{
43✔
769
                pendingFundingID:         r.reservationID,
43✔
770
                theirFundingInputScripts: fundingInputScripts,
43✔
771
                theirCommitmentSig:       commitmentSig,
43✔
772
                completeChan:             completeChan,
43✔
773
                err:                      errChan,
43✔
774
        }
43✔
775

43✔
776
        return <-completeChan, <-errChan
43✔
777
}
43✔
778

779
// CompleteReservationSingle finalizes the pending single funder channel
780
// reservation. Using the funding outpoint of the constructed funding
781
// transaction, and the initiator's signature for our version of the commitment
782
// transaction, we are able to verify the correctness of our commitment
783
// transaction as crafted by the initiator. Once this method returns, our
784
// signature for the initiator's version of the commitment transaction is
785
// available via the .OurSignatures() method. As this method should only be
786
// called as a response to a single funder channel, only a commitment signature
787
// will be populated.
788
func (r *ChannelReservation) CompleteReservationSingle(
789
        fundingPoint *wire.OutPoint, commitSig input.Signature,
790
        auxFundingDesc fn.Option[AuxFundingDesc]) (*channeldb.OpenChannel,
791
        error) {
43✔
792

43✔
793
        errChan := make(chan error, 1)
43✔
794
        completeChan := make(chan *channeldb.OpenChannel, 1)
43✔
795

43✔
796
        r.wallet.msgChan <- &addSingleFunderSigsMsg{
43✔
797
                pendingFundingID:   r.reservationID,
43✔
798
                fundingOutpoint:    fundingPoint,
43✔
799
                theirCommitmentSig: commitSig,
43✔
800
                completeChan:       completeChan,
43✔
801
                auxFundingDesc:     auxFundingDesc,
43✔
802
                err:                errChan,
43✔
803
        }
43✔
804

43✔
805
        return <-completeChan, <-errChan
43✔
806
}
43✔
807

808
// TheirSignatures returns the counterparty's signatures to all inputs to the
809
// funding transaction belonging to them, as well as their signature for the
810
// wallet's version of the commitment transaction. This methods is provided for
811
// additional verification, such as needed by tests.
812
//
813
// NOTE: These attributes will be unpopulated before a call to
814
// .CompleteReservation().
815
func (r *ChannelReservation) TheirSignatures() ([]*input.Script,
816
        input.Signature) {
×
817

×
818
        r.RLock()
×
819
        defer r.RUnlock()
×
820
        return r.theirFundingInputScripts, r.theirCommitmentSig
×
821
}
×
822

823
// FinalFundingTx returns the finalized, fully signed funding transaction for
824
// this reservation.
825
//
826
// NOTE: If this reservation was created as the non-initiator to a single
827
// funding workflow, then the full funding transaction will not be available.
828
// Instead we will only have the final outpoint of the funding transaction.
829
func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx {
28✔
830
        r.RLock()
28✔
831
        defer r.RUnlock()
28✔
832
        return r.fundingTx
28✔
833
}
28✔
834

835
// FundingOutpoint returns the outpoint of the funding transaction.
836
//
837
// NOTE: The pointer returned will only be set once the .ProcessContribution()
838
// method is called in the case of the initiator of a single funder workflow,
839
// and after the .CompleteReservationSingle() method is called in the case of
840
// a responder to a single funder workflow.
841
func (r *ChannelReservation) FundingOutpoint() *wire.OutPoint {
91✔
842
        r.RLock()
91✔
843
        defer r.RUnlock()
91✔
844
        return &r.partialState.FundingOutpoint
91✔
845
}
91✔
846

847
// SetOurUpfrontShutdown sets the upfront shutdown address on our contribution.
848
func (r *ChannelReservation) SetOurUpfrontShutdown(shutdown lnwire.DeliveryAddress) {
102✔
849
        r.Lock()
102✔
850
        defer r.Unlock()
102✔
851

102✔
852
        r.ourContribution.UpfrontShutdown = shutdown
102✔
853
}
102✔
854

855
// Capacity returns the channel capacity for this reservation.
856
func (r *ChannelReservation) Capacity() btcutil.Amount {
56✔
857
        r.RLock()
56✔
858
        defer r.RUnlock()
56✔
859
        return r.partialState.Capacity
56✔
860
}
56✔
861

862
// LeaseExpiry returns the absolute expiration height for a leased channel using
863
// the script enforced commitment type. A zero value is returned when the
864
// channel is not using a script enforced lease commitment type.
865
func (r *ChannelReservation) LeaseExpiry() uint32 {
3✔
866
        if !r.partialState.ChanType.HasLeaseExpiration() {
6✔
867
                return 0
3✔
868
        }
3✔
UNCOV
869
        return r.partialState.ThawHeight
×
870
}
871

872
// Cancel abandons this channel reservation. This method should be called in
873
// the scenario that communications with the counterparty break down. Upon
874
// cancellation, all resources previously reserved for this pending payment
875
// channel are returned to the free pool, allowing subsequent reservations to
876
// utilize the now freed resources.
877
func (r *ChannelReservation) Cancel() error {
54✔
878
        errChan := make(chan error, 1)
54✔
879
        r.wallet.msgChan <- &fundingReserveCancelMsg{
54✔
880
                pendingFundingID: r.reservationID,
54✔
881
                err:              errChan,
54✔
882
        }
54✔
883

54✔
884
        return <-errChan
54✔
885
}
54✔
886

887
// ChanState the current open channel state.
888
func (r *ChannelReservation) ChanState() *channeldb.OpenChannel {
×
889
        r.RLock()
×
890
        defer r.RUnlock()
×
891

×
892
        return r.partialState
×
893
}
×
894

895
// CommitmentKeyRings returns the local+remote key ring used for the very first
896
// commitment transaction both parties.
897
//
898
//nolint:ll
899
func (r *ChannelReservation) CommitmentKeyRings() lntypes.Dual[CommitmentKeyRing] {
×
900
        r.RLock()
×
901
        defer r.RUnlock()
×
902

×
903
        chanType := r.partialState.ChanType
×
904
        ourChanCfg := r.ourContribution.ChannelConfig
×
905
        theirChanCfg := r.theirContribution.ChannelConfig
×
906

×
907
        localKeys := DeriveCommitmentKeys(
×
908
                r.ourContribution.FirstCommitmentPoint, lntypes.Local, chanType,
×
909
                ourChanCfg, theirChanCfg,
×
910
        )
×
911

×
912
        remoteKeys := DeriveCommitmentKeys(
×
913
                r.theirContribution.FirstCommitmentPoint, lntypes.Remote,
×
914
                chanType, ourChanCfg, theirChanCfg,
×
915
        )
×
916

×
917
        return lntypes.Dual[CommitmentKeyRing]{
×
918
                Local:  *localKeys,
×
919
                Remote: *remoteKeys,
×
920
        }
×
921
}
×
922

923
// VerifyConstraints is a helper function that can be used to check the sanity
924
// of various channel constraints.
925
func VerifyConstraints(bounds *channeldb.ChannelStateBounds,
926
        commitParams *channeldb.CommitmentParams, maxLocalCSVDelay uint16,
927
        channelCapacity btcutil.Amount) error {
165✔
928

165✔
929
        // Fail if the csv delay for our funds exceeds our maximum.
165✔
930
        if commitParams.CsvDelay > maxLocalCSVDelay {
167✔
931
                return ErrCsvDelayTooLarge(
2✔
932
                        commitParams.CsvDelay, maxLocalCSVDelay,
2✔
933
                )
2✔
934
        }
2✔
935

936
        // The channel reserve should always be greater or equal to the dust
937
        // limit. The reservation request should be denied if otherwise.
938
        if commitParams.DustLimit > bounds.ChanReserve {
164✔
939
                return ErrChanReserveTooSmall(
1✔
940
                        bounds.ChanReserve, commitParams.DustLimit,
1✔
941
                )
1✔
942
        }
1✔
943

944
        // Validate against the maximum-sized witness script dust limit, and
945
        // also ensure that the DustLimit is not too large.
946
        maxWitnessLimit := DustLimitForSize(input.UnknownWitnessSize)
162✔
947
        if commitParams.DustLimit < maxWitnessLimit ||
162✔
948
                commitParams.DustLimit > 3*maxWitnessLimit {
162✔
949

×
950
                return ErrInvalidDustLimit(commitParams.DustLimit)
×
951
        }
×
952

953
        // Fail if we consider the channel reserve to be too large.  We
954
        // currently fail if it is greater than 20% of the channel capacity.
955
        maxChanReserve := channelCapacity / 5
162✔
956
        if bounds.ChanReserve > maxChanReserve {
163✔
957
                return ErrChanReserveTooLarge(
1✔
958
                        bounds.ChanReserve, maxChanReserve,
1✔
959
                )
1✔
960
        }
1✔
961

962
        // Fail if the minimum HTLC value is too large. If this is too large,
963
        // the channel won't be useful for sending small payments. This limit
964
        // is currently set to maxValueInFlight, effectively letting the remote
965
        // setting this as large as it wants.
966
        if bounds.MinHTLC > bounds.MaxPendingAmount {
161✔
967
                return ErrMinHtlcTooLarge(
×
968
                        bounds.MinHTLC, bounds.MaxPendingAmount,
×
969
                )
×
970
        }
×
971

972
        // Fail if maxHtlcs is above the maximum allowed number of 483.  This
973
        // number is specified in BOLT-02.
974
        if bounds.MaxAcceptedHtlcs > uint16(input.MaxHTLCNumber/2) {
161✔
975
                return ErrMaxHtlcNumTooLarge(
×
976
                        bounds.MaxAcceptedHtlcs, uint16(input.MaxHTLCNumber/2),
×
977
                )
×
978
        }
×
979

980
        // Fail if we consider maxHtlcs too small. If this is too small we
981
        // cannot offer many HTLCs to the remote.
982
        const minNumHtlc = 5
161✔
983
        if bounds.MaxAcceptedHtlcs < minNumHtlc {
161✔
984
                return ErrMaxHtlcNumTooSmall(
×
985
                        bounds.MaxAcceptedHtlcs, minNumHtlc,
×
986
                )
×
987
        }
×
988

989
        // Fail if we consider maxValueInFlight too small. We currently require
990
        // the remote to at least allow minNumHtlc * minHtlc in flight.
991
        if bounds.MaxPendingAmount < minNumHtlc*bounds.MinHTLC {
161✔
992
                return ErrMaxValueInFlightTooSmall(
×
993
                        bounds.MaxPendingAmount, minNumHtlc*bounds.MinHTLC,
×
994
                )
×
995
        }
×
996

997
        return nil
161✔
998
}
999

1000
// OpenChannelDetails wraps the finalized fully confirmed channel which
1001
// resulted from a ChannelReservation instance with details concerning exactly
1002
// _where_ in the chain the channel was ultimately opened.
1003
type OpenChannelDetails struct {
1004
        // Channel is the active channel created by an instance of a
1005
        // ChannelReservation and the required funding workflow.
1006
        Channel *LightningChannel
1007

1008
        // ConfirmationHeight is the block height within the chain that
1009
        // included the channel.
1010
        ConfirmationHeight uint32
1011

1012
        // TransactionIndex is the index within the confirming block that the
1013
        // transaction resides.
1014
        TransactionIndex uint32
1015
}
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