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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 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
        "net"
6
        "sync"
7

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

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

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

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

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

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

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

55
// HasStaticRemoteKey returns whether the commitment type supports remote
56
// outputs backed by static keys.
57
func (c CommitmentType) HasStaticRemoteKey() bool {
58
        switch c {
59
        case CommitmentTypeTweakless,
60
                CommitmentTypeAnchorsZeroFeeHtlcTx,
61
                CommitmentTypeScriptEnforcedLease,
62
                CommitmentTypeSimpleTaproot:
63
                return true
150✔
64
        default:
150✔
65
                return false
66
        }
67
}
68

69
// HasAnchors returns whether the commitment type supports anchor outputs.
36✔
70
func (c CommitmentType) HasAnchors() bool {
36✔
71
        switch c {
36✔
72
        case CommitmentTypeAnchorsZeroFeeHtlcTx,
73
                CommitmentTypeScriptEnforcedLease,
114✔
74
                CommitmentTypeSimpleTaproot:
114✔
75
                return true
76
        default:
77
                return false
78
        }
79
}
660✔
80

660✔
81
// IsTaproot returns true if the channel type is a taproot channel.
82
func (c CommitmentType) IsTaproot() bool {
83
        return c == CommitmentTypeSimpleTaproot
84
}
56✔
85

56✔
86
// String returns the name of the CommitmentType.
56✔
87
func (c CommitmentType) String() string {
88
        switch c {
604✔
89
        case CommitmentTypeLegacy:
604✔
90
                return "legacy"
91
        case CommitmentTypeTweakless:
92
                return "tweakless"
93
        case CommitmentTypeAnchorsZeroFeeHtlcTx:
94
                return "anchors-zero-fee-second-level"
591✔
95
        case CommitmentTypeScriptEnforcedLease:
591✔
96
                return "script-enforced-lease"
591✔
97
        case CommitmentTypeSimpleTaproot:
591✔
98
                return "simple-taproot"
99
        default:
100
                return "invalid"
×
UNCOV
101
        }
×
UNCOV
102
}
×
UNCOV
103

×
UNCOV
104
// ReservationState is a type that represents the current state of a channel
×
UNCOV
105
// reservation within the funding workflow.
×
UNCOV
106
type ReservationState int
×
UNCOV
107

×
UNCOV
108
const (
×
UNCOV
109
        // WaitingToSend is the state either the funder/fundee is in after
×
UNCOV
110
        // creating a reservation, but hasn't sent a message yet.
×
UNCOV
111
        WaitingToSend ReservationState = iota
×
UNCOV
112

×
UNCOV
113
        // SentOpenChannel is the state the funder is in after sending the
×
UNCOV
114
        // OpenChannel message.
×
UNCOV
115
        SentOpenChannel
×
116

117
        // SentAcceptChannel is the state the fundee is in after sending the
118
        // AcceptChannel message.
119
        SentAcceptChannel
120

121
        // SentFundingCreated is the state the funder is in after sending the
122
        // FundingCreated message.
123
        SentFundingCreated
124
)
125

126
// ChannelContribution is the primary constituent of the funding workflow
127
// within lnwallet. Each side first exchanges their respective contributions
128
// along with channel specific parameters like the min fee/KB. Once
129
// contributions have been exchanged, each side will then produce signatures
130
// for all their inputs to the funding transactions, and finally a signature
131
// for the other party's version of the commitment transaction.
132
type ChannelContribution struct {
133
        // FundingOutpoint is the amount of funds contributed to the funding
134
        // transaction.
135
        FundingAmount btcutil.Amount
136

137
        // Inputs to the funding transaction.
138
        Inputs []*wire.TxIn
139

140
        // ChangeOutputs are the Outputs to be used in the case that the total
141
        // value of the funding inputs is greater than the total potential
142
        // channel capacity.
143
        ChangeOutputs []*wire.TxOut
144

145
        // FirstCommitmentPoint is the first commitment point that will be used
146
        // to create the revocation key in the first commitment transaction we
147
        // send to the remote party.
148
        FirstCommitmentPoint *btcec.PublicKey
149

150
        // ChannelConfig is the concrete contribution that this node is
151
        // offering to the channel. This includes all the various constraints
152
        // such as the min HTLC, and also all the keys which will be used for
153
        // the duration of the channel.
154
        *channeldb.ChannelConfig
155

156
        // UpfrontShutdown is an optional address to which the channel should be
157
        // paid out to on cooperative close.
158
        UpfrontShutdown lnwire.DeliveryAddress
159

160
        // LocalNonce is populated if the channel type is a simple taproot
161
        // channel. This stores the public (and secret) nonce that will be used
162
        // to generate commitments for the local party.
163
        LocalNonce *musig2.Nonces
164
}
165

166
// toChanConfig returns the raw channel configuration generated by a node's
167
// contribution to the channel.
168
func (c *ChannelContribution) toChanConfig() channeldb.ChannelConfig {
169
        return *c.ChannelConfig
170
}
171

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

208
        // fundingTx is the funding transaction for this pending channel.
209
        fundingTx *wire.MsgTx
210

211
        // In order of sorted inputs. Sorting is done in accordance
212
        // to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
213
        ourFundingInputScripts   []*input.Script
214
        theirFundingInputScripts []*input.Script
215

216
        // Our signature for their version of the commitment transaction.
217
        ourCommitmentSig   input.Signature
218
        theirCommitmentSig input.Signature
219

220
        ourContribution   *ChannelContribution
221
        theirContribution *ChannelContribution
222

223
        partialState *channeldb.OpenChannel
224
        nodeAddr     net.Addr
225

226
        // The ID of this reservation, used to uniquely track the reservation
227
        // throughout its lifetime.
228
        reservationID uint64
229

230
        // pendingChanID is the pending channel ID for this channel as
231
        // identified within the wire protocol.
232
        pendingChanID [32]byte
233

234
        // pushMSat the amount of milli-satoshis that should be pushed to the
235
        // responder of a single funding channel as part of the initial
236
        // commitment state.
237
        pushMSat lnwire.MilliSatoshi
238

239
        wallet     *LightningWallet
240
        chanFunder chanfunding.Assembler
241

242
        fundingIntent chanfunding.Intent
243

244
        // nextRevocationKeyLoc stores the key locator information for this
245
        // channel.
246
        nextRevocationKeyLoc keychain.KeyLocator
247

248
        musigSessions *MusigPairSession
249

250
        state ReservationState
251
}
252

253
// NewChannelReservation creates a new channel reservation. This function is
254
// used only internally by lnwallet. In order to concurrent safety, the
255
// creation of all channel reservations should be carried out via the
256
// lnwallet.InitChannelReservation interface.
257
func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
258
        wallet *LightningWallet, id uint64, chainHash *chainhash.Hash,
259
        thawHeight uint32, req *InitFundingReserveMsg) (*ChannelReservation,
260
        error) {
261

262
        var (
263
                ourBalance   lnwire.MilliSatoshi
264
                theirBalance lnwire.MilliSatoshi
265
                initiator    bool
266
        )
267

268
        // Based on the channel type, we determine the initial commit weight
269
        // and fee.
270
        commitWeight := input.CommitWeight
271
        if req.CommitType.IsTaproot() {
272
                commitWeight = input.TaprootCommitWeight
273
        } else if req.CommitType.HasAnchors() {
274
                commitWeight = input.AnchorCommitWeight
275
        }
154✔
276
        commitFee := req.CommitFeePerKw.FeeForWeight(
154✔
277
                lntypes.WeightUnit(commitWeight),
154✔
278
        )
154✔
279

154✔
280
        localFundingMSat := lnwire.NewMSatFromSatoshis(localFundingAmt)
154✔
281
        // TODO(halseth): make method take remote funding amount directly
154✔
282
        // instead of inferring it from capacity and local amt.
154✔
283
        capacityMSat := lnwire.NewMSatFromSatoshis(capacity)
154✔
284

154✔
285
        // The total fee paid by the initiator will be the commitment fee in
154✔
286
        // addition to the two anchor outputs.
166✔
287
        feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
12✔
288
        if req.CommitType.HasAnchors() {
158✔
289
                feeMSat += 2 * lnwire.NewMSatFromSatoshis(AnchorSize)
4✔
290
        }
4✔
291

154✔
292
        // Used to cut down on verbosity.
154✔
293
        defaultDust := DustLimitUnknownWitness()
154✔
294

154✔
295
        // If we're the responder to a single-funder reservation, then we have
154✔
296
        // no initial balance in the channel unless the remote party is pushing
154✔
297
        // some funds to us within the first commitment state.
154✔
298
        if localFundingAmt == 0 {
154✔
299
                ourBalance = req.PushMSat
154✔
300
                theirBalance = capacityMSat - feeMSat - req.PushMSat
154✔
301
                initiator = false
154✔
302

154✔
303
                // If the responder doesn't have enough funds to actually pay
170✔
304
                // the fees, then we'll bail our early.
16✔
305
                if int64(theirBalance) < 0 {
16✔
306
                        return nil, ErrFunderBalanceDust(
307
                                int64(commitFee), int64(theirBalance.ToSatoshis()),
308
                                int64(2*defaultDust),
154✔
309
                        )
154✔
310
                }
154✔
311
        } else {
154✔
312
                // TODO(roasbeef): need to rework fee structure in general and
154✔
313
                // also when we "unlock" dual funder within the daemon
216✔
314

62✔
315
                if capacity == localFundingAmt {
62✔
316
                        // If we're initiating a single funder workflow, then
62✔
317
                        // we pay all the initial fees within the commitment
62✔
318
                        // transaction. We also deduct our balance by the
62✔
319
                        // amount pushed as part of the initial state.
62✔
320
                        ourBalance = capacityMSat - feeMSat - req.PushMSat
62✔
UNCOV
321
                        theirBalance = req.PushMSat
×
UNCOV
322
                } else {
×
323
                        // Otherwise, this is a dual funder workflow where both
×
324
                        // slides split the amount funded and the commitment
×
325
                        // fee.
×
326
                        ourBalance = localFundingMSat - (feeMSat / 2)
92✔
327
                        theirBalance = capacityMSat - localFundingMSat - (feeMSat / 2) + req.PushMSat
92✔
328
                }
92✔
329

92✔
330
                initiator = true
184✔
331

92✔
332
                // If we, the initiator don't have enough funds to actually pay
92✔
333
                // the fees, then we'll exit with an error.
92✔
334
                if int64(ourBalance) < 0 {
92✔
335
                        return nil, ErrFunderBalanceDust(
92✔
336
                                int64(commitFee), int64(ourBalance),
92✔
337
                                int64(2*defaultDust),
92✔
UNCOV
338
                        )
×
UNCOV
339
                }
×
UNCOV
340
        }
×
UNCOV
341

×
UNCOV
342
        // If we're the initiator and our starting balance within the channel
×
UNCOV
343
        // after we take account of fees is below 2x the dust limit, then we'll
×
344
        // reject this channel creation request.
345
        //
92✔
346
        // TODO(roasbeef): reject if 30% goes to fees? dust channel
92✔
347
        if initiator && ourBalance.ToSatoshis() <= 2*defaultDust {
92✔
348
                return nil, ErrFunderBalanceDust(
92✔
349
                        int64(commitFee),
96✔
350
                        int64(ourBalance.ToSatoshis()),
4✔
351
                        int64(2*defaultDust),
4✔
352
                )
4✔
353
        }
4✔
354

4✔
355
        // Similarly we ensure their balance is reasonable if we are not the
356
        // initiator.
357
        if !initiator && theirBalance.ToSatoshis() <= 2*defaultDust {
358
                return nil, ErrFunderBalanceDust(
359
                        int64(commitFee),
360
                        int64(theirBalance.ToSatoshis()),
361
                        int64(2*defaultDust),
362
                )
150✔
363
        }
×
UNCOV
364

×
UNCOV
365
        // Next we'll set the channel type based on what we can ascertain about
×
UNCOV
366
        // the balances/push amount within the channel.
×
UNCOV
367
        var chanType channeldb.ChannelType
×
UNCOV
368

×
369
        // If either of the balances are zero at this point, or we have a
370
        // non-zero push amt (there's no pushing for dual funder), then this is
371
        // a single-funder channel.
372
        if ourBalance == 0 || theirBalance == 0 || req.PushMSat != 0 {
150✔
UNCOV
373
                // Both the tweakless type and the anchor type is tweakless,
×
UNCOV
374
                // hence set the bit.
×
UNCOV
375
                if req.CommitType.HasStaticRemoteKey() {
×
UNCOV
376
                        chanType |= channeldb.SingleFunderTweaklessBit
×
UNCOV
377
                } else {
×
UNCOV
378
                        chanType |= channeldb.SingleFunderBit
×
379
                }
380

381
                switch a := req.ChanFunder.(type) {
382
                // The first channels of a batch shouldn't publish the batch TX
150✔
383
                // to avoid problems if some of the funding flows can't be
150✔
384
                // completed. Only the last channel of a batch should publish.
150✔
385
                case chanfunding.ConditionalPublishAssembler:
150✔
386
                        if !a.ShouldPublishFundingTx() {
150✔
387
                                chanType |= channeldb.NoFundingTxBit
300✔
388
                        }
150✔
389

150✔
390
                // Normal funding flow, the assembler creates a TX from the
186✔
391
                // internal wallet.
36✔
392
                case chanfunding.FundingTxAssembler:
150✔
393
                        // Do nothing, a FundingTxAssembler has the transaction.
114✔
394

114✔
395
                // If this intent isn't one that's able to provide us with a
396
                // funding transaction, then we'll set the chanType bit to
150✔
397
                // signal that we don't have access to one.
398
                default:
399
                        chanType |= channeldb.NoFundingTxBit
UNCOV
400
                }
×
401
        } else {
×
402
                // Otherwise, this is a dual funder channel, and no side is
×
403
                // technically the "initiator"
×
404
                initiator = false
405
                chanType |= channeldb.DualFunderBit
406
        }
407

141✔
408
        // We are adding anchor outputs to our commitment. We only support this
409
        // in combination with zero-fee second-levels HTLCs.
410
        if req.CommitType.HasAnchors() {
411
                chanType |= channeldb.AnchorOutputsBit
412
                chanType |= channeldb.ZeroHtlcTxFeeBit
413
        }
9✔
414

9✔
415
        // Set the appropriate LeaseExpiration/Frozen bit based on the
UNCOV
416
        // reservation parameters.
×
UNCOV
417
        if req.CommitType == CommitmentTypeScriptEnforcedLease {
×
UNCOV
418
                if thawHeight == 0 {
×
419
                        return nil, errors.New("missing absolute expiration " +
×
420
                                "for script enforced lease commitment type")
×
421
                }
×
422
                chanType |= channeldb.LeaseExpirationBit
423
        } else if thawHeight > 0 {
424
                chanType |= channeldb.FrozenBit
425
        }
166✔
426

16✔
427
        if req.CommitType == CommitmentTypeSimpleTaproot {
16✔
428
                chanType |= channeldb.SimpleTaprootFeatureBit
16✔
429
        }
430

431
        if req.ZeroConf {
432
                chanType |= channeldb.ZeroConfBit
150✔
UNCOV
433
        }
×
UNCOV
434

×
UNCOV
435
        if req.OptionScidAlias {
×
UNCOV
436
                chanType |= channeldb.ScidAliasChanBit
×
UNCOV
437
        }
×
438

158✔
439
        if req.ScidAliasFeature {
8✔
440
                chanType |= channeldb.ScidAliasFeatureBit
8✔
441
        }
442

162✔
443
        if req.TapscriptRoot.IsSome() {
12✔
444
                chanType |= channeldb.TapscriptRootBit
12✔
445
        }
446

154✔
447
        return &ChannelReservation{
4✔
448
                ourContribution: &ChannelContribution{
4✔
449
                        FundingAmount: ourBalance.ToSatoshis(),
450
                        ChannelConfig: &channeldb.ChannelConfig{},
150✔
UNCOV
451
                },
×
UNCOV
452
                theirContribution: &ChannelContribution{
×
453
                        FundingAmount: theirBalance.ToSatoshis(),
454
                        ChannelConfig: &channeldb.ChannelConfig{},
156✔
455
                },
6✔
456
                partialState: &channeldb.OpenChannel{
6✔
457
                        ChanType:     chanType,
458
                        ChainHash:    *chainHash,
150✔
459
                        IsPending:    true,
150✔
UNCOV
460
                        IsInitiator:  initiator,
×
UNCOV
461
                        ChannelFlags: req.Flags,
×
UNCOV
462
                        Capacity:     capacity,
×
UNCOV
463
                        LocalCommitment: channeldb.ChannelCommitment{
×
UNCOV
464
                                LocalBalance:  ourBalance,
×
465
                                RemoteBalance: theirBalance,
UNCOV
466
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
×
UNCOV
467
                                CommitFee:     commitFee,
×
468
                        },
469
                        RemoteCommitment: channeldb.ChannelCommitment{
470
                                LocalBalance:  ourBalance,
150✔
471
                                RemoteBalance: theirBalance,
150✔
472
                                FeePerKw:      btcutil.Amount(req.CommitFeePerKw),
150✔
473
                                CommitFee:     commitFee,
150✔
474
                        },
150✔
475
                        ThawHeight:           thawHeight,
150✔
476
                        Db:                   wallet.Cfg.Database,
150✔
477
                        InitialLocalBalance:  ourBalance,
150✔
478
                        InitialRemoteBalance: theirBalance,
150✔
479
                        Memo:                 req.Memo,
150✔
480
                        TapscriptRoot:        req.TapscriptRoot,
150✔
481
                },
150✔
482
                pushMSat:      req.PushMSat,
150✔
483
                pendingChanID: req.PendingChanID,
150✔
484
                reservationID: id,
150✔
485
                wallet:        wallet,
150✔
486
                chanFunder:    req.ChanFunder,
150✔
487
                state:         WaitingToSend,
150✔
488
        }, nil
150✔
489
}
150✔
490

150✔
491
// AddAlias stores the first alias for zero-conf channels.
150✔
492
func (r *ChannelReservation) AddAlias(scid lnwire.ShortChannelID) {
150✔
493
        r.Lock()
150✔
494
        defer r.Unlock()
150✔
495

150✔
496
        r.partialState.ShortChannelID = scid
150✔
497
}
150✔
498

150✔
499
// SetState sets the ReservationState.
150✔
500
func (r *ChannelReservation) SetState(state ReservationState) {
150✔
501
        r.Lock()
150✔
502
        defer r.Unlock()
150✔
503

150✔
504
        r.state = state
150✔
505
}
150✔
506

150✔
507
// State returns the current ReservationState.
150✔
508
func (r *ChannelReservation) State() ReservationState {
150✔
509
        r.RLock()
150✔
510
        defer r.RUnlock()
150✔
511

150✔
512
        return r.state
513
}
514

515
// SetNumConfsRequired sets the number of confirmations that are required for
4✔
516
// the ultimate funding transaction before the channel can be considered open.
4✔
517
// This is distinct from the main reservation workflow as it allows
4✔
518
// implementations a bit more flexibility w.r.t to if the responder of the
4✔
519
// initiator sets decides the number of confirmations needed.
4✔
520
func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) {
4✔
521
        r.Lock()
522
        defer r.Unlock()
523

123✔
524
        r.partialState.NumConfsRequired = numConfs
123✔
525
}
123✔
526

123✔
527
// IsZeroConf returns if the reservation's underlying partial channel state is
123✔
528
// a zero-conf channel.
123✔
529
func (r *ChannelReservation) IsZeroConf() bool {
530
        r.RLock()
531
        defer r.RUnlock()
86✔
532

86✔
533
        return r.partialState.IsZeroConf()
86✔
534
}
86✔
535

86✔
536
// IsTaproot returns if the reservation's underlying partial channel state is a
86✔
537
// taproot channel.
538
func (r *ChannelReservation) IsTaproot() bool {
539
        r.RLock()
540
        defer r.RUnlock()
541

542
        return r.partialState.ChanType.IsTaproot()
543
}
108✔
544

108✔
545
// CommitConstraints takes the constraints that the remote party specifies for
108✔
546
// the type of commitments that we can generate for them. These constraints
108✔
547
// include several parameters that serve as flow control restricting the amount
108✔
548
// of satoshis that can be transferred in a single commitment. This function
108✔
549
// will also attempt to verify the constraints for sanity, returning an error
550
// if the parameters are seemed unsound.
551
func (r *ChannelReservation) CommitConstraints(
552
        bounds *channeldb.ChannelStateBounds,
106✔
553
        commitParams *channeldb.CommitmentParams,
106✔
554
        maxLocalCSVDelay uint16,
106✔
555
        responder bool) error {
106✔
556

106✔
557
        r.Lock()
106✔
558
        defer r.Unlock()
559

560
        // First, verify the sanity of the channel constraints.
561
        err := VerifyConstraints(
185✔
562
                bounds, commitParams, maxLocalCSVDelay, r.partialState.Capacity,
185✔
563
        )
185✔
564
        if err != nil {
185✔
565
                return err
185✔
566
        }
185✔
567

568
        // Our dust limit should always be less than or equal to our proposed
569
        // channel reserve.
570
        if responder && r.ourContribution.DustLimit > bounds.ChanReserve {
571
                r.ourContribution.DustLimit = bounds.ChanReserve
572
        }
573

574
        r.ourContribution.ChanReserve = bounds.ChanReserve
575
        r.ourContribution.MaxPendingAmount = bounds.MaxPendingAmount
576
        r.ourContribution.MinHTLC = bounds.MinHTLC
577
        r.ourContribution.MaxAcceptedHtlcs = bounds.MaxAcceptedHtlcs
578
        r.ourContribution.CsvDelay = commitParams.CsvDelay
108✔
579

108✔
580
        return nil
108✔
581
}
108✔
582

108✔
583
// validateReserveBounds checks that both ChannelReserve values are above both
108✔
584
// DustLimit values. This not only avoids stuck channels, but is also mandated
108✔
585
// by BOLT#02 even if it's not explicit. This returns true if the bounds are
108✔
586
// valid. This function should be called with the lock held.
108✔
587
func (r *ChannelReservation) validateReserveBounds() bool {
109✔
588
        ourDustLimit := r.ourContribution.DustLimit
1✔
589
        ourRequiredReserve := r.ourContribution.ChanReserve
1✔
590
        theirDustLimit := r.theirContribution.DustLimit
591
        theirRequiredReserve := r.theirContribution.ChanReserve
592

593
        // We take the smaller of the two ChannelReserves and compare it
107✔
UNCOV
594
        // against the larger of the two DustLimits.
×
UNCOV
595
        minChanReserve := ourRequiredReserve
×
596
        if minChanReserve > theirRequiredReserve {
597
                minChanReserve = theirRequiredReserve
107✔
598
        }
107✔
599

107✔
600
        maxDustLimit := ourDustLimit
107✔
601
        if maxDustLimit < theirDustLimit {
107✔
602
                maxDustLimit = theirDustLimit
107✔
603
        }
107✔
604

605
        return minChanReserve >= maxDustLimit
606
}
607

608
// OurContribution returns the wallet's fully populated contribution to the
609
// pending payment channel. See 'ChannelContribution' for further details
610
// regarding the contents of a contribution.
101✔
611
//
101✔
612
// NOTE: This SHOULD NOT be modified.
101✔
613
// TODO(roasbeef): make copy?
101✔
614
func (r *ChannelReservation) OurContribution() *ChannelContribution {
101✔
615
        r.RLock()
101✔
616
        defer r.RUnlock()
101✔
617

101✔
618
        return r.ourContribution
101✔
619
}
102✔
620

1✔
621
// ProcessContribution verifies the counterparty's contribution to the pending
1✔
622
// payment channel. As a result of this incoming message, lnwallet is able to
623
// build the funding transaction, and both commitment transactions. Once this
101✔
624
// message has been processed, all signatures to inputs to the funding
101✔
UNCOV
625
// transaction belonging to the wallet are available. Additionally, the wallet
×
UNCOV
626
// will generate a signature to the counterparty's version of the commitment
×
627
// transaction.
628
func (r *ChannelReservation) ProcessContribution(theirContribution *ChannelContribution) error {
101✔
629
        errChan := make(chan error, 1)
630

631
        r.wallet.msgChan <- &addContributionMsg{
632
                pendingFundingID: r.reservationID,
633
                contribution:     theirContribution,
634
                err:              errChan,
635
        }
636

637
        return <-errChan
186✔
638
}
186✔
639

186✔
640
// IsPsbt returns true if there is a PSBT funding intent mapped to this
186✔
641
// reservation.
186✔
642
func (r *ChannelReservation) IsPsbt() bool {
186✔
643
        _, ok := r.fundingIntent.(*chanfunding.PsbtIntent)
644
        return ok
645
}
646

647
// IsCannedShim returns true if there is a canned shim funding intent mapped to
648
// this reservation.
649
func (r *ChannelReservation) IsCannedShim() bool {
650
        _, ok := r.fundingIntent.(*chanfunding.ShimIntent)
651
        return ok
45✔
652
}
45✔
653

45✔
654
// ProcessPsbt continues a previously paused funding flow that involves PSBT to
45✔
655
// construct the funding transaction. This method can be called once the PSBT
45✔
656
// is finalized and the signed transaction is available.
45✔
657
func (r *ChannelReservation) ProcessPsbt(
45✔
658
        auxFundingDesc fn.Option[AuxFundingDesc]) error {
45✔
659

45✔
660
        errChan := make(chan error, 1)
45✔
661

45✔
662
        r.wallet.msgChan <- &continueContributionMsg{
663
                auxFundingDesc:   auxFundingDesc,
664
                pendingFundingID: r.reservationID,
665
                err:              errChan,
63✔
666
        }
63✔
667

63✔
668
        return <-errChan
63✔
669
}
670

671
// RemoteCanceled informs the PSBT funding state machine that the remote peer
672
// has canceled the pending reservation, likely due to a timeout.
58✔
673
func (r *ChannelReservation) RemoteCanceled() {
58✔
674
        psbtIntent, ok := r.fundingIntent.(*chanfunding.PsbtIntent)
58✔
675
        if !ok {
58✔
676
                return
677
        }
678
        psbtIntent.RemoteCanceled()
679
}
680

UNCOV
681
// ProcessSingleContribution verifies, and records the initiator's contribution
×
UNCOV
682
// to this pending single funder channel. Internally, no further action is
×
UNCOV
683
// taken other than recording the initiator's contribution to the single funder
×
UNCOV
684
// channel.
×
UNCOV
685
func (r *ChannelReservation) ProcessSingleContribution(theirContribution *ChannelContribution) error {
×
UNCOV
686
        errChan := make(chan error, 1)
×
UNCOV
687

×
UNCOV
688
        r.wallet.msgChan <- &addSingleContributionMsg{
×
UNCOV
689
                pendingFundingID: r.reservationID,
×
UNCOV
690
                contribution:     theirContribution,
×
UNCOV
691
                err:              errChan,
×
UNCOV
692
        }
×
693

694
        return <-errChan
695
}
UNCOV
696

×
UNCOV
697
// TheirContribution returns the counterparty's pending contribution to the
×
UNCOV
698
// payment channel. See 'ChannelContribution' for further details regarding the
×
UNCOV
699
// contents of a contribution. This attribute will ONLY be available after a
×
UNCOV
700
// call to .ProcessContribution().
×
UNCOV
701
//
×
702
// NOTE: This SHOULD NOT be modified.
703
func (r *ChannelReservation) TheirContribution() *ChannelContribution {
704
        r.RLock()
705
        defer r.RUnlock()
706
        return r.theirContribution
707
}
708

62✔
709
// OurSignatures retrieves the wallet's signatures to all inputs to the funding
62✔
710
// transaction belonging to itself, and also a signature for the counterparty's
62✔
711
// version of the commitment transaction. The signatures for the wallet's
62✔
712
// inputs to the funding transaction are returned in sorted order according to
62✔
713
// BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
62✔
714
//
62✔
715
// NOTE: These signatures will only be populated after a call to
62✔
716
// .ProcessContribution()
62✔
717
func (r *ChannelReservation) OurSignatures() ([]*input.Script,
62✔
718
        input.Signature) {
62✔
719

720
        r.RLock()
721
        defer r.RUnlock()
722
        return r.ourFundingInputScripts, r.ourCommitmentSig
723
}
724

725
// CompleteReservation finalizes the pending channel reservation, transitioning
726
// from a pending payment channel, to an open payment channel. All passed
54✔
727
// signatures to the counterparty's inputs to the funding transaction will be
54✔
728
// fully verified. Signatures are expected to be passed in sorted order
54✔
729
// according to BIP-69:
54✔
730
// https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
54✔
731
// Additionally, verification is performed in order to ensure that the
732
// counterparty supplied a valid signature to our version of the commitment
733
// transaction.  Once this method returns, callers should broadcast the
734
// created funding transaction, then call .WaitForChannelOpen() which will
735
// block until the funding transaction obtains the configured number of
736
// confirmations. Once the method unblocks, a LightningChannel instance is
737
// returned, marking the channel available for updates.
738
func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*input.Script,
739
        commitmentSig input.Signature) (*channeldb.OpenChannel, error) {
740

741
        // TODO(roasbeef): add flag for watch or not?
88✔
742
        errChan := make(chan error, 1)
88✔
743
        completeChan := make(chan *channeldb.OpenChannel, 1)
88✔
744

88✔
745
        r.wallet.msgChan <- &addCounterPartySigsMsg{
88✔
746
                pendingFundingID:         r.reservationID,
88✔
747
                theirFundingInputScripts: fundingInputScripts,
748
                theirCommitmentSig:       commitmentSig,
749
                completeChan:             completeChan,
750
                err:                      errChan,
751
        }
752

753
        return <-completeChan, <-errChan
754
}
755

756
// CompleteReservationSingle finalizes the pending single funder channel
757
// reservation. Using the funding outpoint of the constructed funding
758
// transaction, and the initiator's signature for our version of the commitment
759
// transaction, we are able to verify the correctness of our commitment
760
// transaction as crafted by the initiator. Once this method returns, our
761
// signature for the initiator's version of the commitment transaction is
762
// available via the .OurSignatures() method. As this method should only be
43✔
763
// called as a response to a single funder channel, only a commitment signature
43✔
764
// will be populated.
43✔
765
func (r *ChannelReservation) CompleteReservationSingle(
43✔
766
        fundingPoint *wire.OutPoint, commitSig input.Signature,
43✔
767
        auxFundingDesc fn.Option[AuxFundingDesc]) (*channeldb.OpenChannel,
43✔
768
        error) {
43✔
769

43✔
770
        errChan := make(chan error, 1)
43✔
771
        completeChan := make(chan *channeldb.OpenChannel, 1)
43✔
772

43✔
773
        r.wallet.msgChan <- &addSingleFunderSigsMsg{
43✔
774
                pendingFundingID:   r.reservationID,
43✔
775
                fundingOutpoint:    fundingPoint,
43✔
776
                theirCommitmentSig: commitSig,
43✔
777
                completeChan:       completeChan,
43✔
778
                auxFundingDesc:     auxFundingDesc,
779
                err:                errChan,
780
        }
781

782
        return <-completeChan, <-errChan
783
}
784

785
// TheirSignatures returns the counterparty's signatures to all inputs to the
786
// funding transaction belonging to them, as well as their signature for the
787
// wallet's version of the commitment transaction. This methods is provided for
788
// additional verification, such as needed by tests.
789
//
790
// NOTE: These attributes will be unpopulated before a call to
791
// .CompleteReservation().
43✔
792
func (r *ChannelReservation) TheirSignatures() ([]*input.Script,
43✔
793
        input.Signature) {
43✔
794

43✔
795
        r.RLock()
43✔
796
        defer r.RUnlock()
43✔
797
        return r.theirFundingInputScripts, r.theirCommitmentSig
43✔
798
}
43✔
799

43✔
800
// FinalFundingTx returns the finalized, fully signed funding transaction for
43✔
801
// this reservation.
43✔
802
//
43✔
803
// NOTE: If this reservation was created as the non-initiator to a single
43✔
804
// funding workflow, then the full funding transaction will not be available.
43✔
805
// Instead we will only have the final outpoint of the funding transaction.
43✔
806
func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx {
43✔
807
        r.RLock()
808
        defer r.RUnlock()
809
        return r.fundingTx
810
}
811

812
// FundingOutpoint returns the outpoint of the funding transaction.
813
//
814
// NOTE: The pointer returned will only be set once the .ProcessContribution()
815
// method is called in the case of the initiator of a single funder workflow,
UNCOV
816
// and after the .CompleteReservationSingle() method is called in the case of
×
UNCOV
817
// a responder to a single funder workflow.
×
UNCOV
818
func (r *ChannelReservation) FundingOutpoint() *wire.OutPoint {
×
UNCOV
819
        r.RLock()
×
UNCOV
820
        defer r.RUnlock()
×
UNCOV
821
        return &r.partialState.FundingOutpoint
×
822
}
823

824
// SetOurUpfrontShutdown sets the upfront shutdown address on our contribution.
825
func (r *ChannelReservation) SetOurUpfrontShutdown(shutdown lnwire.DeliveryAddress) {
826
        r.Lock()
827
        defer r.Unlock()
828

829
        r.ourContribution.UpfrontShutdown = shutdown
28✔
830
}
28✔
831

28✔
832
// Capacity returns the channel capacity for this reservation.
28✔
833
func (r *ChannelReservation) Capacity() btcutil.Amount {
28✔
834
        r.RLock()
835
        defer r.RUnlock()
836
        return r.partialState.Capacity
837
}
838

839
// LeaseExpiry returns the absolute expiration height for a leased channel using
840
// the script enforced commitment type. A zero value is returned when the
841
// channel is not using a script enforced lease commitment type.
91✔
842
func (r *ChannelReservation) LeaseExpiry() uint32 {
91✔
843
        if !r.partialState.ChanType.HasLeaseExpiration() {
91✔
844
                return 0
91✔
845
        }
91✔
846
        return r.partialState.ThawHeight
847
}
848

102✔
849
// Cancel abandons this channel reservation. This method should be called in
102✔
850
// the scenario that communications with the counterparty break down. Upon
102✔
851
// cancellation, all resources previously reserved for this pending payment
102✔
852
// channel are returned to the free pool, allowing subsequent reservations to
102✔
853
// utilize the now freed resources.
102✔
854
func (r *ChannelReservation) Cancel() error {
855
        errChan := make(chan error, 1)
856
        r.wallet.msgChan <- &fundingReserveCancelMsg{
56✔
857
                pendingFundingID: r.reservationID,
56✔
858
                err:              errChan,
56✔
859
        }
56✔
860

56✔
861
        return <-errChan
862
}
863

864
// ChanState the current open channel state.
865
func (r *ChannelReservation) ChanState() *channeldb.OpenChannel {
3✔
866
        r.RLock()
6✔
867
        defer r.RUnlock()
3✔
868

3✔
869
        return r.partialState
×
870
}
871

872
// CommitmentKeyRings returns the local+remote key ring used for the very first
873
// commitment transaction both parties.
874
//
875
//nolint:lll
876
func (r *ChannelReservation) CommitmentKeyRings() lntypes.Dual[CommitmentKeyRing] {
877
        r.RLock()
54✔
878
        defer r.RUnlock()
54✔
879

54✔
880
        chanType := r.partialState.ChanType
54✔
881
        ourChanCfg := r.ourContribution.ChannelConfig
54✔
882
        theirChanCfg := r.theirContribution.ChannelConfig
54✔
883

54✔
884
        localKeys := DeriveCommitmentKeys(
54✔
885
                r.ourContribution.FirstCommitmentPoint, lntypes.Local, chanType,
54✔
886
                ourChanCfg, theirChanCfg,
887
        )
888

×
889
        remoteKeys := DeriveCommitmentKeys(
×
890
                r.theirContribution.FirstCommitmentPoint, lntypes.Remote,
×
891
                chanType, ourChanCfg, theirChanCfg,
×
892
        )
×
893

×
894
        return lntypes.Dual[CommitmentKeyRing]{
895
                Local:  *localKeys,
896
                Remote: *remoteKeys,
897
        }
898
}
UNCOV
899

×
UNCOV
900
// VerifyConstraints is a helper function that can be used to check the sanity
×
UNCOV
901
// of various channel constraints.
×
UNCOV
902
func VerifyConstraints(bounds *channeldb.ChannelStateBounds,
×
UNCOV
903
        commitParams *channeldb.CommitmentParams, maxLocalCSVDelay uint16,
×
UNCOV
904
        channelCapacity btcutil.Amount) error {
×
UNCOV
905

×
UNCOV
906
        // Fail if the csv delay for our funds exceeds our maximum.
×
UNCOV
907
        if commitParams.CsvDelay > maxLocalCSVDelay {
×
UNCOV
908
                return ErrCsvDelayTooLarge(
×
UNCOV
909
                        commitParams.CsvDelay, maxLocalCSVDelay,
×
UNCOV
910
                )
×
UNCOV
911
        }
×
UNCOV
912

×
UNCOV
913
        // The channel reserve should always be greater or equal to the dust
×
UNCOV
914
        // limit. The reservation request should be denied if otherwise.
×
UNCOV
915
        if commitParams.DustLimit > bounds.ChanReserve {
×
UNCOV
916
                return ErrChanReserveTooSmall(
×
UNCOV
917
                        bounds.ChanReserve, commitParams.DustLimit,
×
UNCOV
918
                )
×
UNCOV
919
        }
×
UNCOV
920

×
UNCOV
921
        // Validate against the maximum-sized witness script dust limit, and
×
922
        // also ensure that the DustLimit is not too large.
923
        maxWitnessLimit := DustLimitForSize(input.UnknownWitnessSize)
924
        if commitParams.DustLimit < maxWitnessLimit ||
925
                commitParams.DustLimit > 3*maxWitnessLimit {
926

927
                return ErrInvalidDustLimit(commitParams.DustLimit)
164✔
928
        }
164✔
929

164✔
930
        // Fail if we consider the channel reserve to be too large.  We
165✔
931
        // currently fail if it is greater than 20% of the channel capacity.
1✔
932
        maxChanReserve := channelCapacity / 5
1✔
933
        if bounds.ChanReserve > maxChanReserve {
1✔
934
                return ErrChanReserveTooLarge(
1✔
935
                        bounds.ChanReserve, maxChanReserve,
936
                )
937
        }
938

164✔
939
        // Fail if the minimum HTLC value is too large. If this is too large,
1✔
940
        // the channel won't be useful for sending small payments. This limit
1✔
941
        // is currently set to maxValueInFlight, effectively letting the remote
1✔
942
        // setting this as large as it wants.
1✔
943
        if bounds.MinHTLC > bounds.MaxPendingAmount {
944
                return ErrMinHtlcTooLarge(
945
                        bounds.MinHTLC, bounds.MaxPendingAmount,
946
                )
162✔
947
        }
162✔
948

162✔
UNCOV
949
        // Fail if maxHtlcs is above the maximum allowed number of 483.  This
×
UNCOV
950
        // number is specified in BOLT-02.
×
UNCOV
951
        if bounds.MaxAcceptedHtlcs > uint16(input.MaxHTLCNumber/2) {
×
952
                return ErrMaxHtlcNumTooLarge(
953
                        bounds.MaxAcceptedHtlcs, uint16(input.MaxHTLCNumber/2),
954
                )
955
        }
162✔
956

163✔
957
        // Fail if we consider maxHtlcs too small. If this is too small we
1✔
958
        // cannot offer many HTLCs to the remote.
1✔
959
        const minNumHtlc = 5
1✔
960
        if bounds.MaxAcceptedHtlcs < minNumHtlc {
1✔
961
                return ErrMaxHtlcNumTooSmall(
962
                        bounds.MaxAcceptedHtlcs, minNumHtlc,
963
                )
964
        }
965

966
        // Fail if we consider maxValueInFlight too small. We currently require
161✔
UNCOV
967
        // the remote to at least allow minNumHtlc * minHtlc in flight.
×
UNCOV
968
        if bounds.MaxPendingAmount < minNumHtlc*bounds.MinHTLC {
×
969
                return ErrMaxValueInFlightTooSmall(
×
970
                        bounds.MaxPendingAmount, minNumHtlc*bounds.MinHTLC,
×
971
                )
972
        }
973

974
        return nil
161✔
UNCOV
975
}
×
UNCOV
976

×
UNCOV
977
// OpenChannelDetails wraps the finalized fully confirmed channel which
×
UNCOV
978
// resulted from a ChannelReservation instance with details concerning exactly
×
979
// _where_ in the chain the channel was ultimately opened.
980
type OpenChannelDetails struct {
981
        // Channel is the active channel created by an instance of a
982
        // ChannelReservation and the required funding workflow.
161✔
983
        Channel *LightningChannel
161✔
UNCOV
984

×
UNCOV
985
        // ConfirmationHeight is the block height within the chain that
×
UNCOV
986
        // included the channel.
×
UNCOV
987
        ConfirmationHeight uint32
×
988

989
        // TransactionIndex is the index within the confirming block that the
990
        // transaction resides.
991
        TransactionIndex uint32
161✔
UNCOV
992
}
×
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