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

lightningnetwork / lnd / 12199391122

06 Dec 2024 01:10PM UTC coverage: 49.807% (-9.1%) from 58.933%
12199391122

push

github

web-flow
Merge pull request #9337 from Guayaba221/patch-1

chore: fix typo in ruby.md

100137 of 201051 relevant lines covered (49.81%)

2.07 hits per line

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

75.19
/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"
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 {
4✔
64
        switch c {
4✔
65
        case CommitmentTypeTweakless,
66
                CommitmentTypeAnchorsZeroFeeHtlcTx,
67
                CommitmentTypeScriptEnforcedLease,
68
                CommitmentTypeSimpleTaproot,
69
                CommitmentTypeSimpleTaprootOverlay:
4✔
70

4✔
71
                return true
4✔
72

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

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

4✔
86
                return true
4✔
87

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

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

99
// String returns the name of the CommitmentType.
100
func (c CommitmentType) String() string {
4✔
101
        switch c {
4✔
102
        case CommitmentTypeLegacy:
4✔
103
                return "legacy"
4✔
104
        case CommitmentTypeTweakless:
4✔
105
                return "tweakless"
4✔
106
        case CommitmentTypeAnchorsZeroFeeHtlcTx:
4✔
107
                return "anchors-zero-fee-second-level"
4✔
108
        case CommitmentTypeScriptEnforcedLease:
4✔
109
                return "script-enforced-lease"
4✔
110
        case CommitmentTypeSimpleTaproot:
4✔
111
                return "simple-taproot"
4✔
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 {
4✔
184
        return *c.ChannelConfig
4✔
185
}
4✔
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) {
4✔
276

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

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

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

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

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

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

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

4✔
330
                if capacity == localFundingAmt {
8✔
331
                        // If we're initiating a single funder workflow, then
4✔
332
                        // we pay all the initial fees within the commitment
4✔
333
                        // transaction. We also deduct our balance by the
4✔
334
                        // amount pushed as part of the initial state.
4✔
335
                        ourBalance = capacityMSat - feeMSat - req.PushMSat
4✔
336
                        theirBalance = req.PushMSat
4✔
337
                } else {
4✔
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
4✔
346

4✔
347
                // If we, the initiator don't have enough funds to actually pay
4✔
348
                // the fees, then we'll exit with an error.
4✔
349
                if int64(ourBalance) < 0 {
8✔
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 {
4✔
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 {
4✔
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
4✔
383

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

396
                switch a := req.ChanFunder.(type) {
4✔
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.
400
                case chanfunding.ConditionalPublishAssembler:
4✔
401
                        if !a.ShouldPublishFundingTx() {
8✔
402
                                chanType |= channeldb.NoFundingTxBit
4✔
403
                        }
4✔
404

405
                // Normal funding flow, the assembler creates a TX from the
406
                // internal wallet.
407
                case chanfunding.FundingTxAssembler:
4✔
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:
4✔
414
                        chanType |= channeldb.NoFundingTxBit
4✔
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() {
8✔
426
                chanType |= channeldb.AnchorOutputsBit
4✔
427
                chanType |= channeldb.ZeroHtlcTxFeeBit
4✔
428
        }
4✔
429

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

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

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

450
        if req.OptionScidAlias {
8✔
451
                chanType |= channeldb.ScidAliasChanBit
4✔
452
        }
4✔
453

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

458
        taprootOverlay := req.CommitType == CommitmentTypeSimpleTaprootOverlay
4✔
459
        switch {
4✔
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{
4✔
471
                ourContribution: &ChannelContribution{
4✔
472
                        FundingAmount: ourBalance.ToSatoshis(),
4✔
473
                        ChannelConfig: &channeldb.ChannelConfig{},
4✔
474
                },
4✔
475
                theirContribution: &ChannelContribution{
4✔
476
                        FundingAmount: theirBalance.ToSatoshis(),
4✔
477
                        ChannelConfig: &channeldb.ChannelConfig{},
4✔
478
                },
4✔
479
                partialState: &channeldb.OpenChannel{
4✔
480
                        ChanType:     chanType,
4✔
481
                        ChainHash:    *chainHash,
4✔
482
                        IsPending:    true,
4✔
483
                        IsInitiator:  initiator,
4✔
484
                        ChannelFlags: req.Flags,
4✔
485
                        Capacity:     capacity,
4✔
486
                        LocalCommitment: channeldb.ChannelCommitment{
4✔
487
                                LocalBalance:  ourBalance,
4✔
488
                                RemoteBalance: theirBalance,
4✔
489
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
4✔
490
                                CommitFee:     commitFee,
4✔
491
                        },
4✔
492
                        RemoteCommitment: channeldb.ChannelCommitment{
4✔
493
                                LocalBalance:  ourBalance,
4✔
494
                                RemoteBalance: theirBalance,
4✔
495
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
4✔
496
                                CommitFee:     commitFee,
4✔
497
                        },
4✔
498
                        ThawHeight:           thawHeight,
4✔
499
                        Db:                   wallet.Cfg.Database,
4✔
500
                        InitialLocalBalance:  ourBalance,
4✔
501
                        InitialRemoteBalance: theirBalance,
4✔
502
                        Memo:                 req.Memo,
4✔
503
                        TapscriptRoot:        req.TapscriptRoot,
4✔
504
                },
4✔
505
                pushMSat:      req.PushMSat,
4✔
506
                pendingChanID: req.PendingChanID,
4✔
507
                reservationID: id,
4✔
508
                wallet:        wallet,
4✔
509
                chanFunder:    req.ChanFunder,
4✔
510
                state:         WaitingToSend,
4✔
511
        }, nil
4✔
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) {
4✔
524
        r.Lock()
4✔
525
        defer r.Unlock()
4✔
526

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

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

4✔
535
        return r.state
4✔
536
}
4✔
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) {
4✔
544
        r.Lock()
4✔
545
        defer r.Unlock()
4✔
546

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

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

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

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

4✔
565
        return r.partialState.ChanType.IsTaproot()
4✔
566
}
4✔
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 {
4✔
579

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

4✔
583
        // First, verify the sanity of the channel constraints.
4✔
584
        err := VerifyConstraints(
4✔
585
                bounds, commitParams, maxLocalCSVDelay, r.partialState.Capacity,
4✔
586
        )
4✔
587
        if err != nil {
4✔
588
                return err
×
589
        }
×
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 {
4✔
594
                r.ourContribution.DustLimit = bounds.ChanReserve
×
595
        }
×
596

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

4✔
603
        return nil
4✔
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 {
4✔
611
        ourDustLimit := r.ourContribution.DustLimit
4✔
612
        ourRequiredReserve := r.ourContribution.ChanReserve
4✔
613
        theirDustLimit := r.theirContribution.DustLimit
4✔
614
        theirRequiredReserve := r.theirContribution.ChanReserve
4✔
615

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

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

628
        return minChanReserve >= maxDustLimit
4✔
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 {
4✔
638
        r.RLock()
4✔
639
        defer r.RUnlock()
4✔
640

4✔
641
        return r.ourContribution
4✔
642
}
4✔
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 {
4✔
652
        errChan := make(chan error, 1)
4✔
653

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

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

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

670
// IsCannedShim returns true if there is a canned shim funding intent mapped to
671
// this reservation.
672
func (r *ChannelReservation) IsCannedShim() bool {
4✔
673
        _, ok := r.fundingIntent.(*chanfunding.ShimIntent)
4✔
674
        return ok
4✔
675
}
4✔
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(
681
        auxFundingDesc fn.Option[AuxFundingDesc]) error {
4✔
682

4✔
683
        errChan := make(chan error, 1)
4✔
684

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

4✔
691
        return <-errChan
4✔
692
}
4✔
693

694
// RemoteCanceled informs the PSBT funding state machine that the remote peer
695
// has canceled the pending reservation, likely due to a timeout.
696
func (r *ChannelReservation) RemoteCanceled() {
4✔
697
        psbtIntent, ok := r.fundingIntent.(*chanfunding.PsbtIntent)
4✔
698
        if !ok {
4✔
699
                return
×
700
        }
×
701
        psbtIntent.RemoteCanceled()
4✔
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 {
4✔
709
        errChan := make(chan error, 1)
4✔
710

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

4✔
717
        return <-errChan
4✔
718
}
4✔
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 {
×
727
        r.RLock()
×
728
        defer r.RUnlock()
×
729
        return r.theirContribution
×
730
}
×
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) {
4✔
742

4✔
743
        r.RLock()
4✔
744
        defer r.RUnlock()
4✔
745
        return r.ourFundingInputScripts, r.ourCommitmentSig
4✔
746
}
4✔
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) {
4✔
763

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

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

4✔
776
        return <-completeChan, <-errChan
4✔
777
}
4✔
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) {
4✔
792

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

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

4✔
805
        return <-completeChan, <-errChan
4✔
806
}
4✔
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 {
×
830
        r.RLock()
×
831
        defer r.RUnlock()
×
832
        return r.fundingTx
×
833
}
×
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 {
4✔
842
        r.RLock()
4✔
843
        defer r.RUnlock()
4✔
844
        return &r.partialState.FundingOutpoint
4✔
845
}
4✔
846

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

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

855
// Capacity returns the channel capacity for this reservation.
856
func (r *ChannelReservation) Capacity() btcutil.Amount {
4✔
857
        r.RLock()
4✔
858
        defer r.RUnlock()
4✔
859
        return r.partialState.Capacity
4✔
860
}
4✔
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 {
4✔
866
        if !r.partialState.ChanType.HasLeaseExpiration() {
8✔
867
                return 0
4✔
868
        }
4✔
869
        return r.partialState.ThawHeight
4✔
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 {
4✔
878
        errChan := make(chan error, 1)
4✔
879
        r.wallet.msgChan <- &fundingReserveCancelMsg{
4✔
880
                pendingFundingID: r.reservationID,
4✔
881
                err:              errChan,
4✔
882
        }
4✔
883

4✔
884
        return <-errChan
4✔
885
}
4✔
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 {
4✔
928

4✔
929
        // Fail if the csv delay for our funds exceeds our maximum.
4✔
930
        if commitParams.CsvDelay > maxLocalCSVDelay {
4✔
931
                return ErrCsvDelayTooLarge(
×
932
                        commitParams.CsvDelay, maxLocalCSVDelay,
×
933
                )
×
934
        }
×
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 {
4✔
939
                return ErrChanReserveTooSmall(
×
940
                        bounds.ChanReserve, commitParams.DustLimit,
×
941
                )
×
942
        }
×
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)
4✔
947
        if commitParams.DustLimit < maxWitnessLimit ||
4✔
948
                commitParams.DustLimit > 3*maxWitnessLimit {
4✔
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
4✔
956
        if bounds.ChanReserve > maxChanReserve {
4✔
957
                return ErrChanReserveTooLarge(
×
958
                        bounds.ChanReserve, maxChanReserve,
×
959
                )
×
960
        }
×
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 {
4✔
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) {
4✔
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
4✔
983
        if bounds.MaxAcceptedHtlcs < minNumHtlc {
4✔
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 {
4✔
992
                return ErrMaxValueInFlightTooSmall(
×
993
                        bounds.MaxPendingAmount, minNumHtlc*bounds.MinHTLC,
×
994
                )
×
995
        }
×
996

997
        return nil
4✔
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