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

lightningnetwork / lnd / 13157733617

05 Feb 2025 12:49PM UTC coverage: 57.712% (-1.1%) from 58.82%
13157733617

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19472 existing lines in 252 files now uncovered.

103634 of 179570 relevant lines covered (57.71%)

24840.31 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