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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

76.59
/lnwallet/wallet.go
1
package lnwallet
2

3
import (
4
        "bytes"
5
        "crypto/sha256"
6
        "errors"
7
        "fmt"
8
        "math"
9
        "net"
10
        "sync"
11
        "sync/atomic"
12

13
        "github.com/btcsuite/btcd/blockchain"
14
        "github.com/btcsuite/btcd/btcec/v2"
15
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
16
        "github.com/btcsuite/btcd/btcutil"
17
        "github.com/btcsuite/btcd/btcutil/psbt"
18
        "github.com/btcsuite/btcd/btcutil/txsort"
19
        "github.com/btcsuite/btcd/chaincfg"
20
        "github.com/btcsuite/btcd/chaincfg/chainhash"
21
        "github.com/btcsuite/btcd/txscript"
22
        "github.com/btcsuite/btcd/wire"
23
        "github.com/btcsuite/btcwallet/wallet"
24
        "github.com/davecgh/go-spew/spew"
25
        "github.com/lightningnetwork/lnd/channeldb"
26
        "github.com/lightningnetwork/lnd/input"
27
        "github.com/lightningnetwork/lnd/keychain"
28
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
29
        "github.com/lightningnetwork/lnd/lnwallet/chanfunding"
30
        "github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
31
        "github.com/lightningnetwork/lnd/lnwire"
32
        "github.com/lightningnetwork/lnd/shachain"
33
)
34

35
const (
36
        // The size of the buffered queue of requests to the wallet from the
37
        // outside word.
38
        msgBufferSize = 100
39

40
        // AnchorChanReservedValue is the amount we'll keep around in the
41
        // wallet in case we have to fee bump anchor channels on force close.
42
        // TODO(halseth): update constant to target a specific commit size at
43
        // set fee rate.
44
        AnchorChanReservedValue = btcutil.Amount(10_000)
45

46
        // MaxAnchorChanReservedValue is the maximum value we'll reserve for
47
        // anchor channel fee bumping. We cap it at 10 times the per-channel
48
        // amount such that nodes with a high number of channels don't have to
49
        // keep around a very large amount for the unlikely scenario that they
50
        // all close at the same time.
51
        MaxAnchorChanReservedValue = 10 * AnchorChanReservedValue
52
)
53

54
var (
55
        // ErrPsbtFundingRequired is the error that is returned during the
56
        // contribution handling process if the process should be paused for
57
        // the construction of a PSBT outside of lnd's wallet.
58
        ErrPsbtFundingRequired = errors.New("PSBT funding required")
59

60
        // ErrReservedValueInvalidated is returned if we try to publish a
61
        // transaction that would take the walletbalance below what we require
62
        // to keep around to fee bump our open anchor channels.
63
        ErrReservedValueInvalidated = errors.New("reserved wallet balance " +
64
                "invalidated: transaction would leave insufficient funds for " +
65
                "fee bumping anchor channel closings (see debug log for details)")
66

67
        // ErrEmptyPendingChanID is returned when an empty value is used for
68
        // the pending channel ID.
69
        ErrEmptyPendingChanID = errors.New("pending channel ID is empty")
70

71
        // ErrDuplicatePendingChanID is returned when an existing pending
72
        // channel ID is registered again.
73
        ErrDuplicatePendingChanID = errors.New("duplicate pending channel ID")
74
)
75

76
// PsbtFundingRequired is a type that implements the error interface and
77
// contains the information needed to construct a PSBT.
78
type PsbtFundingRequired struct {
79
        // Intent is the pending PSBT funding intent that needs to be funded
80
        // if the wrapping error is returned.
81
        Intent *chanfunding.PsbtIntent
82
}
83

84
// Error returns the underlying error.
85
//
86
// NOTE: This method is part of the error interface.
87
func (p *PsbtFundingRequired) Error() string {
×
88
        return ErrPsbtFundingRequired.Error()
×
89
}
×
90

91
// InitFundingReserveMsg is the first message sent to initiate the workflow
92
// required to open a payment channel with a remote peer. The initial required
93
// parameters are configurable across channels. These parameters are to be
94
// chosen depending on the fee climate within the network, and time value of
95
// funds to be locked up within the channel. Upon success a ChannelReservation
96
// will be created in order to track the lifetime of this pending channel.
97
// Outputs selected will be 'locked', making them unavailable, for any other
98
// pending reservations. Therefore, all channels in reservation limbo will be
99
// periodically timed out after an idle period in order to avoid "exhaustion"
100
// attacks.
101
type InitFundingReserveMsg struct {
102
        // ChainHash denotes that chain to be used to ultimately open the
103
        // target channel.
104
        ChainHash *chainhash.Hash
105

106
        // PendingChanID is the pending channel ID for this funding flow as
107
        // used in the wire protocol.
108
        PendingChanID [32]byte
109

110
        // NodeID is the ID of the remote node we would like to open a channel
111
        // with.
112
        NodeID *btcec.PublicKey
113

114
        // NodeAddr is the address port that we used to either establish or
115
        // accept the connection which led to the negotiation of this funding
116
        // workflow.
117
        NodeAddr net.Addr
118

119
        // SubtractFees should be set if we intend to spend exactly
120
        // LocalFundingAmt when opening the channel, subtracting the fees from
121
        // the funding output. This can be used for instance to use all our
122
        // remaining funds to open the channel, since it will take fees into
123
        // account.
124
        SubtractFees bool
125

126
        // LocalFundingAmt is the amount of funds requested from us for this
127
        // channel.
128
        LocalFundingAmt btcutil.Amount
129

130
        // RemoteFundingAmnt is the amount of funds the remote will contribute
131
        // to this channel.
132
        RemoteFundingAmt btcutil.Amount
133

134
        // FundUpToMaxAmt defines if channel funding should try to add as many
135
        // funds to the channel opening as possible up to this amount. If used,
136
        // then MinFundAmt is treated as the minimum amount of funds that must
137
        // be available to open the channel. If set to zero it is ignored.
138
        FundUpToMaxAmt btcutil.Amount
139

140
        // MinFundAmt denotes the minimum channel capacity that has to be
141
        // allocated iff the FundUpToMaxAmt is set.
142
        MinFundAmt btcutil.Amount
143

144
        // Outpoints is a list of client-selected outpoints that should be used
145
        // for funding a channel. If LocalFundingAmt is specified then this
146
        // amount is allocated from the sum of outpoints towards funding. If the
147
        // FundUpToMaxAmt is specified the entirety of selected funds is
148
        // allocated towards channel funding.
149
        Outpoints []wire.OutPoint
150

151
        // RemoteChanReserve is the channel reserve we required for the remote
152
        // peer.
153
        RemoteChanReserve btcutil.Amount
154

155
        // CommitFeePerKw is the starting accepted satoshis/Kw fee for the set
156
        // of initial commitment transactions. In order to ensure timely
157
        // confirmation, it is recommended that this fee should be generous,
158
        // paying some multiple of the accepted base fee rate of the network.
159
        CommitFeePerKw chainfee.SatPerKWeight
160

161
        // FundingFeePerKw is the fee rate in sat/kw to use for the initial
162
        // funding transaction.
163
        FundingFeePerKw chainfee.SatPerKWeight
164

165
        // PushMSat is the number of milli-satoshis that should be pushed over
166
        // the responder as part of the initial channel creation.
167
        PushMSat lnwire.MilliSatoshi
168

169
        // Flags are the channel flags specified by the initiator in the
170
        // open_channel message.
171
        Flags lnwire.FundingFlag
172

173
        // MinConfs indicates the minimum number of confirmations that each
174
        // output selected to fund the channel should satisfy.
175
        MinConfs int32
176

177
        // CommitType indicates what type of commitment type the channel should
178
        // be using, like tweakless or anchors.
179
        CommitType CommitmentType
180

181
        // ChanFunder is an optional channel funder that allows the caller to
182
        // control exactly how the channel funding is carried out. If not
183
        // specified, then the default chanfunding.WalletAssembler will be
184
        // used.
185
        ChanFunder chanfunding.Assembler
186

187
        // AllowUtxoForFunding enables the channel funding workflow to restrict
188
        // the selection of utxos when selecting the inputs for the channel
189
        // opening. This does ONLY apply for the internal wallet backed channel
190
        // opening case.
191
        //
192
        // NOTE: This is very useful when opening channels with unconfirmed
193
        // inputs to make sure stable non-replaceable inputs are used.
194
        AllowUtxoForFunding func(Utxo) bool
195

196
        // ZeroConf is a boolean that is true if a zero-conf channel was
197
        // negotiated.
198
        ZeroConf bool
199

200
        // OptionScidAlias is a boolean that is true if an option-scid-alias
201
        // channel type was explicitly negotiated.
202
        OptionScidAlias bool
203

204
        // ScidAliasFeature is true if the option-scid-alias feature bit was
205
        // negotiated.
206
        ScidAliasFeature bool
207

208
        // Memo is any arbitrary information we wish to store locally about the
209
        // channel that will be useful to our future selves.
210
        Memo []byte
211

212
        // err is a channel in which all errors will be sent across. Will be
213
        // nil if this initial set is successful.
214
        //
215
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
216
        err chan error
217

218
        // resp is channel in which a ChannelReservation with our contributions
219
        // filled in will be sent across this channel in the case of a
220
        // successfully reservation initiation. In the case of an error, this
221
        // will read a nil pointer.
222
        //
223
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
224
        resp chan *ChannelReservation
225
}
226

227
// fundingReserveCancelMsg is a message reserved for cancelling an existing
228
// channel reservation identified by its reservation ID. Cancelling a reservation
229
// frees its locked outputs up, for inclusion within further reservations.
230
type fundingReserveCancelMsg struct {
231
        pendingFundingID uint64
232

233
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
234
        err chan error // Buffered
235
}
236

237
// addContributionMsg represents a message executing the second phase of the
238
// channel reservation workflow. This message carries the counterparty's
239
// "contribution" to the payment channel. In the case that this message is
240
// processed without generating any errors, then channel reservation will then
241
// be able to construct the funding tx, both commitment transactions, and
242
// finally generate signatures for all our inputs to the funding transaction,
243
// and for the remote node's version of the commitment transaction.
244
type addContributionMsg struct {
245
        pendingFundingID uint64
246

247
        // TODO(roasbeef): Should also carry SPV proofs in we're in SPV mode
248
        contribution *ChannelContribution
249

250
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
251
        err chan error
252
}
253

254
// continueContributionMsg represents a message that signals that the
255
// interrupted funding process involving a PSBT can now be continued because the
256
// finalized transaction is now available.
257
type continueContributionMsg struct {
258
        pendingFundingID uint64
259

260
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
261
        err chan error
262
}
263

264
// addSingleContributionMsg represents a message executing the second phase of
265
// a single funder channel reservation workflow. This messages carries the
266
// counterparty's "contribution" to the payment channel. As this message is
267
// sent when on the responding side to a single funder workflow, no further
268
// action apart from storing the provided contribution is carried out.
269
type addSingleContributionMsg struct {
270
        pendingFundingID uint64
271

272
        contribution *ChannelContribution
273

274
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
275
        err chan error
276
}
277

278
// addCounterPartySigsMsg represents the final message required to complete,
279
// and 'open' a payment channel. This message carries the counterparty's
280
// signatures for each of their inputs to the funding transaction, and also a
281
// signature allowing us to spend our version of the commitment transaction.
282
// If we're able to verify all the signatures are valid, the funding transaction
283
// will be broadcast to the network. After the funding transaction gains a
284
// configurable number of confirmations, the channel is officially considered
285
// 'open'.
286
type addCounterPartySigsMsg struct {
287
        pendingFundingID uint64
288

289
        // Should be order of sorted inputs that are theirs. Sorting is done
290
        // in accordance to BIP-69:
291
        // https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
292
        theirFundingInputScripts []*input.Script
293

294
        // This should be 1/2 of the signatures needed to successfully spend our
295
        // version of the commitment transaction.
296
        theirCommitmentSig input.Signature
297

298
        // This channel is used to return the completed channel after the wallet
299
        // has completed all of its stages in the funding process.
300
        completeChan chan *channeldb.OpenChannel
301

302
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
303
        err chan error
304
}
305

306
// addSingleFunderSigsMsg represents the next-to-last message required to
307
// complete a single-funder channel workflow. Once the initiator is able to
308
// construct the funding transaction, they send both the outpoint and a
309
// signature for our version of the commitment transaction. Once this message
310
// is processed we (the responder) are able to construct both commitment
311
// transactions, signing the remote party's version.
312
type addSingleFunderSigsMsg struct {
313
        pendingFundingID uint64
314

315
        // fundingOutpoint is the outpoint of the completed funding
316
        // transaction as assembled by the workflow initiator.
317
        fundingOutpoint *wire.OutPoint
318

319
        // theirCommitmentSig are the 1/2 of the signatures needed to
320
        // successfully spend our version of the commitment transaction.
321
        theirCommitmentSig input.Signature
322

323
        // This channel is used to return the completed channel after the wallet
324
        // has completed all of its stages in the funding process.
325
        completeChan chan *channeldb.OpenChannel
326

327
        // NOTE: In order to avoid deadlocks, this channel MUST be buffered.
328
        err chan error
329
}
330

331
// CheckReservedValueTxReq is the request struct used to call
332
// CheckReservedValueTx with. It contains the transaction to check as well as
333
// an optional explicitly defined index to denote a change output that is not
334
// watched by the wallet.
335
type CheckReservedValueTxReq struct {
336
        // Tx is the transaction to check the outputs for.
337
        Tx *wire.MsgTx
338

339
        // ChangeIndex denotes an optional output index that can be explicitly
340
        // set for a change that is not being watched by the wallet and would
341
        // otherwise not be recognized as a change output.
342
        ChangeIndex *int
343
}
344

345
// LightningWallet is a domain specific, yet general Bitcoin wallet capable of
346
// executing workflow required to interact with the Lightning Network. It is
347
// domain specific in the sense that it understands all the fancy scripts used
348
// within the Lightning Network, channel lifetimes, etc. However, it embeds a
349
// general purpose Bitcoin wallet within it. Therefore, it is also able to
350
// serve as a regular Bitcoin wallet which uses HD keys. The wallet is highly
351
// concurrent internally. All communication, and requests towards the wallet
352
// are dispatched as messages over channels, ensuring thread safety across all
353
// operations. Interaction has been designed independent of any peer-to-peer
354
// communication protocol, allowing the wallet to be self-contained and
355
// embeddable within future projects interacting with the Lightning Network.
356
//
357
// NOTE: At the moment the wallet requires a btcd full node, as it's dependent
358
// on btcd's websockets notifications as event triggers during the lifetime of a
359
// channel. However, once the chainntnfs package is complete, the wallet will
360
// be compatible with multiple RPC/notification services such as Electrum,
361
// Bitcoin Core + ZeroMQ, etc. Eventually, the wallet won't require a full-node
362
// at all, as SPV support is integrated into btcwallet.
363
type LightningWallet struct {
364
        started  int32 // To be used atomically.
365
        shutdown int32 // To be used atomically.
366

367
        nextFundingID uint64 // To be used atomically.
368

369
        // Cfg is the configuration struct that will be used by the wallet to
370
        // access the necessary interfaces and default it needs to carry on its
371
        // duties.
372
        Cfg Config
373

374
        // WalletController is the core wallet, all non Lightning Network
375
        // specific interaction is proxied to the internal wallet.
376
        WalletController
377

378
        // SecretKeyRing is the interface we'll use to derive any keys related
379
        // to our purpose within the network including: multi-sig keys, node
380
        // keys, revocation keys, etc.
381
        keychain.SecretKeyRing
382

383
        // This mutex MUST be held when performing coin selection in order to
384
        // avoid inadvertently creating multiple funding transaction which
385
        // double spend inputs across each other.
386
        coinSelectMtx sync.RWMutex
387

388
        // All messages to the wallet are to be sent across this channel.
389
        msgChan chan interface{}
390

391
        // Incomplete payment channels are stored in the map below. An intent
392
        // to create a payment channel is tracked as a "reservation" within
393
        // limbo. Once the final signatures have been exchanged, a reservation
394
        // is removed from limbo. Each reservation is tracked by a unique
395
        // monotonically integer. All requests concerning the channel MUST
396
        // carry a valid, active funding ID.
397
        fundingLimbo map[uint64]*ChannelReservation
398

399
        // reservationIDs maps a pending channel ID to the reservation ID used
400
        // as key in the fundingLimbo map. Used to easily look up a channel
401
        // reservation given a pending channel ID.
402
        reservationIDs map[[32]byte]uint64
403
        limboMtx       sync.RWMutex
404

405
        // lockedOutPoints is a set of the currently locked outpoint. This
406
        // information is kept in order to provide an easy way to unlock all
407
        // the currently locked outpoints.
408
        lockedOutPoints map[wire.OutPoint]struct{}
409

410
        // fundingIntents houses all the "interception" registered by a caller
411
        // using the RegisterFundingIntent method.
412
        intentMtx      sync.RWMutex
413
        fundingIntents map[[32]byte]chanfunding.Intent
414

415
        quit chan struct{}
416

417
        wg sync.WaitGroup
418

419
        // TODO(roasbeef): handle wallet lock/unlock
420
}
421

422
// NewLightningWallet creates/opens and initializes a LightningWallet instance.
423
// If the wallet has never been created (according to the passed dataDir), first-time
424
// setup is executed.
425
func NewLightningWallet(Cfg Config) (*LightningWallet, error) {
3✔
426

3✔
427
        return &LightningWallet{
3✔
428
                Cfg:              Cfg,
3✔
429
                SecretKeyRing:    Cfg.SecretKeyRing,
3✔
430
                WalletController: Cfg.WalletController,
3✔
431
                msgChan:          make(chan interface{}, msgBufferSize),
3✔
432
                nextFundingID:    0,
3✔
433
                fundingLimbo:     make(map[uint64]*ChannelReservation),
3✔
434
                reservationIDs:   make(map[[32]byte]uint64),
3✔
435
                lockedOutPoints:  make(map[wire.OutPoint]struct{}),
3✔
436
                fundingIntents:   make(map[[32]byte]chanfunding.Intent),
3✔
437
                quit:             make(chan struct{}),
3✔
438
        }, nil
3✔
439
}
3✔
440

441
// Startup establishes a connection to the RPC source, and spins up all
442
// goroutines required to handle incoming messages.
443
func (l *LightningWallet) Startup() error {
3✔
444
        // Already started?
3✔
445
        if atomic.AddInt32(&l.started, 1) != 1 {
3✔
446
                return nil
×
447
        }
×
448

449
        // Start the underlying wallet controller.
450
        if err := l.Start(); err != nil {
3✔
451
                return err
×
452
        }
×
453

454
        if l.Cfg.Rebroadcaster != nil {
6✔
455
                go func() {
6✔
456
                        if err := l.Cfg.Rebroadcaster.Start(); err != nil {
3✔
457
                                walletLog.Errorf("unable to start "+
×
458
                                        "rebroadcaster: %v", err)
×
459
                        }
×
460
                }()
461
        }
462

463
        l.wg.Add(1)
3✔
464
        // TODO(roasbeef): multiple request handlers?
3✔
465
        go l.requestHandler()
3✔
466

3✔
467
        return nil
3✔
468
}
469

470
// Shutdown gracefully stops the wallet, and all active goroutines.
471
func (l *LightningWallet) Shutdown() error {
3✔
472
        if atomic.AddInt32(&l.shutdown, 1) != 1 {
3✔
473
                return nil
×
474
        }
×
475

476
        // Signal the underlying wallet controller to shutdown, waiting until
477
        // all active goroutines have been shutdown.
478
        if err := l.Stop(); err != nil {
3✔
479
                return err
×
480
        }
×
481

482
        if l.Cfg.Rebroadcaster != nil && l.Cfg.Rebroadcaster.Started() {
6✔
483
                l.Cfg.Rebroadcaster.Stop()
3✔
484
        }
3✔
485

486
        close(l.quit)
3✔
487
        l.wg.Wait()
3✔
488
        return nil
3✔
489
}
490

491
// PublishTransaction wraps the wallet controller tx publish method with an
492
// extra rebroadcaster layer if the sub-system is configured.
493
func (l *LightningWallet) PublishTransaction(tx *wire.MsgTx,
494
        label string) error {
3✔
495

3✔
496
        sendTxToWallet := func() error {
6✔
497
                return l.WalletController.PublishTransaction(tx, label)
3✔
498
        }
3✔
499

500
        // If we don't have rebroadcaster then we can exit early (and send only
501
        // to the wallet).
502
        if l.Cfg.Rebroadcaster == nil || !l.Cfg.Rebroadcaster.Started() {
6✔
503
                return sendTxToWallet()
3✔
504
        }
3✔
505

506
        // We pass this into the rebroadcaster first, so the initial attempt
507
        // will succeed if the transaction isn't yet in the mempool. However we
508
        // ignore the error here as this might be resent on start up and the
509
        // transaction already exists.
510
        _ = l.Cfg.Rebroadcaster.Broadcast(tx)
3✔
511

3✔
512
        // Then we pass things into the wallet as normal, which'll add the
3✔
513
        // transaction label on disk.
3✔
514
        if err := sendTxToWallet(); err != nil {
6✔
515
                return err
3✔
516
        }
3✔
517

518
        // TODO(roasbeef): want diff height actually? no context though
519
        _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock()
3✔
520
        if err != nil {
3✔
521
                return err
×
522
        }
×
523

524
        txHash := tx.TxHash()
3✔
525
        go func() {
6✔
526
                const numConfs = 6
3✔
527

3✔
528
                txConf, err := l.Cfg.Notifier.RegisterConfirmationsNtfn(
3✔
529
                        &txHash, tx.TxOut[0].PkScript, numConfs,
3✔
530
                        uint32(bestHeight),
3✔
531
                )
3✔
532
                if err != nil {
3✔
533
                        return
×
534
                }
×
535

536
                select {
3✔
537
                case <-txConf.Confirmed:
3✔
538
                        // TODO(roasbeef): also want to remove from
3✔
539
                        // rebroadcaster if conflict happens...deeper wallet
3✔
540
                        // integration?
3✔
541
                        l.Cfg.Rebroadcaster.MarkAsConfirmed(tx.TxHash())
3✔
542

543
                case <-l.quit:
×
544
                        return
×
545
                }
546
        }()
547

548
        return nil
3✔
549
}
550

551
// ConfirmedBalance returns the current confirmed balance of a wallet account.
552
// This methods wraps the internal WalletController method so we're able to
553
// properly hold the coin select mutex while we compute the balance.
554
func (l *LightningWallet) ConfirmedBalance(confs int32,
555
        account string) (btcutil.Amount, error) {
3✔
556

3✔
557
        l.coinSelectMtx.Lock()
3✔
558
        defer l.coinSelectMtx.Unlock()
3✔
559

3✔
560
        return l.WalletController.ConfirmedBalance(confs, account)
3✔
561
}
3✔
562

563
// ListUnspentWitnessFromDefaultAccount returns all unspent outputs from the
564
// default wallet account which are version 0 witness programs. The 'minConfs'
565
// and 'maxConfs' parameters indicate the minimum and maximum number of
566
// confirmations an output needs in order to be returned by this method. Passing
567
// -1 as 'minConfs' indicates that even unconfirmed outputs should be returned.
568
// Using MaxInt32 as 'maxConfs' implies returning all outputs with at least
569
// 'minConfs'.
570
//
571
// NOTE: This method requires the global coin selection lock to be held.
572
func (l *LightningWallet) ListUnspentWitnessFromDefaultAccount(
573
        minConfs, maxConfs int32) ([]*Utxo, error) {
3✔
574

3✔
575
        return l.WalletController.ListUnspentWitness(
3✔
576
                minConfs, maxConfs, DefaultAccountName,
3✔
577
        )
3✔
578
}
3✔
579

580
// LockedOutpoints returns a list of all currently locked outpoint.
581
func (l *LightningWallet) LockedOutpoints() []*wire.OutPoint {
×
582
        outPoints := make([]*wire.OutPoint, 0, len(l.lockedOutPoints))
×
583
        for outPoint := range l.lockedOutPoints {
×
584
                outPoint := outPoint
×
585

×
586
                outPoints = append(outPoints, &outPoint)
×
587
        }
×
588

589
        return outPoints
×
590
}
591

592
// ResetReservations reset the volatile wallet state which tracks all currently
593
// active reservations.
594
func (l *LightningWallet) ResetReservations() {
×
595
        l.nextFundingID = 0
×
596
        l.fundingLimbo = make(map[uint64]*ChannelReservation)
×
597
        l.reservationIDs = make(map[[32]byte]uint64)
×
598

×
599
        for outpoint := range l.lockedOutPoints {
×
600
                _ = l.ReleaseOutput(chanfunding.LndInternalLockID, outpoint)
×
601
        }
×
602
        l.lockedOutPoints = make(map[wire.OutPoint]struct{})
×
603
}
604

605
// ActiveReservations returns a slice of all the currently active
606
// (non-canceled) reservations.
607
func (l *LightningWallet) ActiveReservations() []*ChannelReservation {
×
608
        reservations := make([]*ChannelReservation, 0, len(l.fundingLimbo))
×
609
        for _, reservation := range l.fundingLimbo {
×
610
                reservations = append(reservations, reservation)
×
611
        }
×
612

613
        return reservations
×
614
}
615

616
// requestHandler is the primary goroutine(s) responsible for handling, and
617
// dispatching replies to all messages.
618
func (l *LightningWallet) requestHandler() {
3✔
619
        defer l.wg.Done()
3✔
620

3✔
621
out:
3✔
622
        for {
6✔
623
                select {
3✔
624
                case m := <-l.msgChan:
3✔
625
                        switch msg := m.(type) {
3✔
626
                        case *InitFundingReserveMsg:
3✔
627
                                l.handleFundingReserveRequest(msg)
3✔
628
                        case *fundingReserveCancelMsg:
3✔
629
                                l.handleFundingCancelRequest(msg)
3✔
630
                        case *addSingleContributionMsg:
3✔
631
                                l.handleSingleContribution(msg)
3✔
632
                        case *addContributionMsg:
3✔
633
                                l.handleContributionMsg(msg)
3✔
634
                        case *continueContributionMsg:
3✔
635
                                l.handleChanPointReady(msg)
3✔
636
                        case *addSingleFunderSigsMsg:
3✔
637
                                l.handleSingleFunderSigs(msg)
3✔
638
                        case *addCounterPartySigsMsg:
3✔
639
                                l.handleFundingCounterPartySigs(msg)
3✔
640
                        }
641
                case <-l.quit:
3✔
642
                        // TODO: do some clean up
3✔
643
                        break out
3✔
644
                }
645
        }
646
}
647

648
// InitChannelReservation kicks off the 3-step workflow required to successfully
649
// open a payment channel with a remote node. As part of the funding
650
// reservation, the inputs selected for the funding transaction are 'locked'.
651
// This ensures that multiple channel reservations aren't double spending the
652
// same inputs in the funding transaction. If reservation initialization is
653
// successful, a ChannelReservation containing our completed contribution is
654
// returned. Our contribution contains all the items necessary to allow the
655
// counterparty to build the funding transaction, and both versions of the
656
// commitment transaction. Otherwise, an error occurred and a nil pointer along
657
// with an error are returned.
658
//
659
// Once a ChannelReservation has been obtained, two additional steps must be
660
// processed before a payment channel can be considered 'open'. The second step
661
// validates, and processes the counterparty's channel contribution. The third,
662
// and final step verifies all signatures for the inputs of the funding
663
// transaction, and that the signature we record for our version of the
664
// commitment transaction is valid.
665
func (l *LightningWallet) InitChannelReservation(
666
        req *InitFundingReserveMsg) (*ChannelReservation, error) {
3✔
667

3✔
668
        req.resp = make(chan *ChannelReservation, 1)
3✔
669
        req.err = make(chan error, 1)
3✔
670

3✔
671
        select {
3✔
672
        case l.msgChan <- req:
3✔
673
        case <-l.quit:
×
674
                return nil, errors.New("wallet shutting down")
×
675
        }
676

677
        return <-req.resp, <-req.err
3✔
678
}
679

680
// RegisterFundingIntent allows a caller to signal to the wallet that if a
681
// pending channel ID of expectedID is found, then it can skip constructing a
682
// new chanfunding.Assembler, and instead use the specified chanfunding.Intent.
683
// As an example, this lets some of the parameters for funding transaction to
684
// be negotiated outside the regular funding protocol.
685
func (l *LightningWallet) RegisterFundingIntent(expectedID [32]byte,
686
        shimIntent chanfunding.Intent) error {
3✔
687

3✔
688
        l.intentMtx.Lock()
3✔
689
        defer l.intentMtx.Unlock()
3✔
690

3✔
691
        // Sanity check the pending channel ID is not empty.
3✔
692
        var zeroID [32]byte
3✔
693
        if expectedID == zeroID {
3✔
694
                return ErrEmptyPendingChanID
×
695
        }
×
696

697
        if _, ok := l.fundingIntents[expectedID]; ok {
6✔
698
                return fmt.Errorf("%w: already has intent registered: %v",
3✔
699
                        ErrDuplicatePendingChanID, expectedID[:])
3✔
700
        }
3✔
701

702
        l.fundingIntents[expectedID] = shimIntent
3✔
703

3✔
704
        return nil
3✔
705
}
706

707
// PsbtFundingVerify looks up a previously registered funding intent by its
708
// pending channel ID and tries to advance the state machine by verifying the
709
// passed PSBT.
710
func (l *LightningWallet) PsbtFundingVerify(pendingChanID [32]byte,
711
        packet *psbt.Packet, skipFinalize bool) error {
3✔
712

3✔
713
        l.intentMtx.Lock()
3✔
714
        defer l.intentMtx.Unlock()
3✔
715

3✔
716
        intent, ok := l.fundingIntents[pendingChanID]
3✔
717
        if !ok {
3✔
718
                return fmt.Errorf("no funding intent found for "+
×
719
                        "pendingChannelID(%x)", pendingChanID[:])
×
720
        }
×
721
        psbtIntent, ok := intent.(*chanfunding.PsbtIntent)
3✔
722
        if !ok {
3✔
723
                return fmt.Errorf("incompatible funding intent")
×
724
        }
×
725

726
        if skipFinalize && psbtIntent.ShouldPublishFundingTX() {
3✔
727
                return fmt.Errorf("cannot set skip_finalize for channel that " +
×
728
                        "did not set no_publish")
×
729
        }
×
730

731
        err := psbtIntent.Verify(packet, skipFinalize)
3✔
732
        if err != nil {
3✔
733
                return fmt.Errorf("error verifying PSBT: %w", err)
×
734
        }
×
735

736
        // Get the channel reservation for that corresponds to this pending
737
        // channel ID.
738
        l.limboMtx.Lock()
3✔
739
        pid, ok := l.reservationIDs[pendingChanID]
3✔
740
        if !ok {
3✔
741
                l.limboMtx.Unlock()
×
742
                return fmt.Errorf("no channel reservation found for "+
×
743
                        "pendingChannelID(%x)", pendingChanID[:])
×
744
        }
×
745

746
        pendingReservation, ok := l.fundingLimbo[pid]
3✔
747
        l.limboMtx.Unlock()
3✔
748

3✔
749
        if !ok {
3✔
750
                return fmt.Errorf("no channel reservation found for "+
×
751
                        "reservation ID %v", pid)
×
752
        }
×
753

754
        // Now the PSBT has been populated and verified, we can again check
755
        // whether the value reserved for anchor fee bumping is respected.
756
        isPublic := pendingReservation.partialState.ChannelFlags&lnwire.FFAnnounceChannel != 0
3✔
757
        hasAnchors := pendingReservation.partialState.ChanType.HasAnchors()
3✔
758
        return l.enforceNewReservedValue(intent, isPublic, hasAnchors)
3✔
759
}
760

761
// PsbtFundingFinalize looks up a previously registered funding intent by its
762
// pending channel ID and tries to advance the state machine by finalizing the
763
// passed PSBT.
764
func (l *LightningWallet) PsbtFundingFinalize(pid [32]byte, packet *psbt.Packet,
765
        rawTx *wire.MsgTx) error {
3✔
766

3✔
767
        l.intentMtx.Lock()
3✔
768
        defer l.intentMtx.Unlock()
3✔
769

3✔
770
        intent, ok := l.fundingIntents[pid]
3✔
771
        if !ok {
3✔
772
                return fmt.Errorf("no funding intent found for "+
×
773
                        "pendingChannelID(%x)", pid[:])
×
774
        }
×
775
        psbtIntent, ok := intent.(*chanfunding.PsbtIntent)
3✔
776
        if !ok {
3✔
777
                return fmt.Errorf("incompatible funding intent")
×
778
        }
×
779

780
        // Either the PSBT or the raw TX must be set.
781
        switch {
3✔
782
        case packet != nil && rawTx == nil:
3✔
783
                err := psbtIntent.Finalize(packet)
3✔
784
                if err != nil {
3✔
785
                        return fmt.Errorf("error finalizing PSBT: %w", err)
×
786
                }
×
787

788
        case rawTx != nil && packet == nil:
3✔
789
                err := psbtIntent.FinalizeRawTX(rawTx)
3✔
790
                if err != nil {
3✔
791
                        return fmt.Errorf("error finalizing raw TX: %w", err)
×
792
                }
×
793

794
        default:
×
795
                return fmt.Errorf("either a PSBT or raw TX must be specified")
×
796
        }
797

798
        return nil
3✔
799
}
800

801
// CancelFundingIntent allows a caller to cancel a previously registered
802
// funding intent. If no intent was found, then an error will be returned.
803
func (l *LightningWallet) CancelFundingIntent(pid [32]byte) error {
3✔
804
        l.intentMtx.Lock()
3✔
805
        defer l.intentMtx.Unlock()
3✔
806

3✔
807
        intent, ok := l.fundingIntents[pid]
3✔
808
        if !ok {
6✔
809
                return fmt.Errorf("no funding intent found for "+
3✔
810
                        "pendingChannelID(%x)", pid[:])
3✔
811
        }
3✔
812

813
        // Give the intent a chance to clean up after itself, removing coin
814
        // locks or similar reserved resources.
815
        intent.Cancel()
3✔
816

3✔
817
        delete(l.fundingIntents, pid)
3✔
818

3✔
819
        return nil
3✔
820
}
821

822
// handleFundingReserveRequest processes a message intending to create, and
823
// validate a funding reservation request.
824
func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg) {
3✔
825

3✔
826
        noFundsCommitted := req.LocalFundingAmt == 0 &&
3✔
827
                req.RemoteFundingAmt == 0 && req.FundUpToMaxAmt == 0
3✔
828

3✔
829
        // It isn't possible to create a channel with zero funds committed.
3✔
830
        if noFundsCommitted {
3✔
831
                err := ErrZeroCapacity()
×
832
                req.err <- err
×
833
                req.resp <- nil
×
834
                return
×
835
        }
×
836

837
        // If the funding request is for a different chain than the one the
838
        // wallet is aware of, then we'll reject the request.
839
        if !bytes.Equal(l.Cfg.NetParams.GenesisHash[:], req.ChainHash[:]) {
3✔
840
                err := ErrChainMismatch(
×
841
                        l.Cfg.NetParams.GenesisHash, req.ChainHash,
×
842
                )
×
843
                req.err <- err
×
844
                req.resp <- nil
×
845
                return
×
846
        }
×
847

848
        // We need to avoid enforcing reserved value in the middle of PSBT
849
        // funding because some of the following steps may add UTXOs funding
850
        // the on-chain wallet.
851
        // The enforcement still happens at the last step - in PsbtFundingVerify
852
        enforceNewReservedValue := true
3✔
853

3✔
854
        // If no chanFunder was provided, then we'll assume the default
3✔
855
        // assembler, which is backed by the wallet's internal coin selection.
3✔
856
        if req.ChanFunder == nil {
6✔
857
                // We use the P2WSH dust limit since it is larger than the
3✔
858
                // P2WPKH dust limit and to avoid threading through two
3✔
859
                // different dust limits.
3✔
860
                cfg := chanfunding.WalletConfig{
3✔
861
                        CoinSource: NewCoinSource(
3✔
862
                                l, req.AllowUtxoForFunding,
3✔
863
                        ),
3✔
864
                        CoinSelectLocker: l,
3✔
865
                        CoinLeaser:       l,
3✔
866
                        Signer:           l.Cfg.Signer,
3✔
867
                        DustLimit: DustLimitForSize(
3✔
868
                                input.P2WSHSize,
3✔
869
                        ),
3✔
870
                        CoinSelectionStrategy: l.Cfg.CoinSelectionStrategy,
3✔
871
                }
3✔
872
                req.ChanFunder = chanfunding.NewWalletAssembler(cfg)
3✔
873
        } else {
6✔
874
                _, isPsbtFunder := req.ChanFunder.(*chanfunding.PsbtAssembler)
3✔
875
                enforceNewReservedValue = !isPsbtFunder
3✔
876
        }
3✔
877

878
        localFundingAmt := req.LocalFundingAmt
3✔
879
        remoteFundingAmt := req.RemoteFundingAmt
3✔
880
        hasAnchors := req.CommitType.HasAnchors()
3✔
881

3✔
882
        var (
3✔
883
                fundingIntent chanfunding.Intent
3✔
884
                err           error
3✔
885
        )
3✔
886

3✔
887
        // If we've just received an inbound funding request that we have a
3✔
888
        // registered shim intent to, then we'll obtain the backing intent now.
3✔
889
        // In this case, we're doing a special funding workflow that allows
3✔
890
        // more advanced constructions such as channel factories to be
3✔
891
        // instantiated.
3✔
892
        l.intentMtx.Lock()
3✔
893
        fundingIntent, ok := l.fundingIntents[req.PendingChanID]
3✔
894
        l.intentMtx.Unlock()
3✔
895

3✔
896
        // Otherwise, this is a normal funding flow, so we'll use the chan
3✔
897
        // funder in the attached request to provision the inputs/outputs
3✔
898
        // that'll ultimately be used to construct the funding transaction.
3✔
899
        if !ok {
6✔
900
                var err error
3✔
901
                var numAnchorChans int
3✔
902

3✔
903
                // Get the number of anchor channels to determine if there is a
3✔
904
                // reserved value that must be respected when funding up to the
3✔
905
                // maximum amount. Since private channels (most likely) won't be
3✔
906
                // used for routing other than the last hop, they bear a smaller
3✔
907
                // risk that we must force close them in order to resolve a HTLC
3✔
908
                // up/downstream. Hence we exclude them from the count of anchor
3✔
909
                // channels in order to attribute the respective anchor amount
3✔
910
                // to the channel capacity.
3✔
911
                if req.FundUpToMaxAmt > 0 && req.MinFundAmt > 0 {
6✔
912
                        numAnchorChans, err = l.CurrentNumAnchorChans()
3✔
913
                        if err != nil {
3✔
914
                                req.err <- err
×
915
                                req.resp <- nil
×
916
                                return
×
917
                        }
×
918

919
                        isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
3✔
920
                        if hasAnchors && isPublic {
6✔
921
                                numAnchorChans++
3✔
922
                        }
3✔
923
                }
924

925
                // Coin selection is done on the basis of sat/kw, so we'll use
926
                // the fee rate passed in to perform coin selection.
927
                fundingReq := &chanfunding.Request{
3✔
928
                        RemoteAmt:         req.RemoteFundingAmt,
3✔
929
                        LocalAmt:          req.LocalFundingAmt,
3✔
930
                        FundUpToMaxAmt:    req.FundUpToMaxAmt,
3✔
931
                        MinFundAmt:        req.MinFundAmt,
3✔
932
                        RemoteChanReserve: req.RemoteChanReserve,
3✔
933
                        PushAmt: lnwire.MilliSatoshi.ToSatoshis(
3✔
934
                                req.PushMSat,
3✔
935
                        ),
3✔
936
                        WalletReserve: l.RequiredReserve(
3✔
937
                                uint32(numAnchorChans),
3✔
938
                        ),
3✔
939
                        Outpoints:    req.Outpoints,
3✔
940
                        MinConfs:     req.MinConfs,
3✔
941
                        SubtractFees: req.SubtractFees,
3✔
942
                        FeeRate:      req.FundingFeePerKw,
3✔
943
                        ChangeAddr: func() (btcutil.Address, error) {
6✔
944
                                return l.NewAddress(
3✔
945
                                        TaprootPubkey, true, DefaultAccountName,
3✔
946
                                )
3✔
947
                        },
3✔
948
                        Musig2: req.CommitType == CommitmentTypeSimpleTaproot,
949
                }
950
                fundingIntent, err = req.ChanFunder.ProvisionChannel(
3✔
951
                        fundingReq,
3✔
952
                )
3✔
953
                if err != nil {
6✔
954
                        req.err <- err
3✔
955
                        req.resp <- nil
3✔
956
                        return
3✔
957
                }
3✔
958

959
                // Register the funding intent now in case we need to access it
960
                // again later, as it's the case for the PSBT state machine for
961
                // example.
962
                err = l.RegisterFundingIntent(req.PendingChanID, fundingIntent)
3✔
963
                if err != nil {
3✔
964
                        req.err <- err
×
965
                        req.resp <- nil
×
966
                        return
×
967
                }
×
968

969
                walletLog.Debugf("Registered funding intent for "+
3✔
970
                        "PendingChanID: %x", req.PendingChanID)
3✔
971

3✔
972
                localFundingAmt = fundingIntent.LocalFundingAmt()
3✔
973
                remoteFundingAmt = fundingIntent.RemoteFundingAmt()
3✔
974
        }
975

976
        // At this point there _has_ to be a funding intent, otherwise something
977
        // went really wrong.
978
        if fundingIntent == nil {
3✔
979
                req.err <- fmt.Errorf("no funding intent present")
×
980
                req.resp <- nil
×
981
                return
×
982
        }
×
983

984
        // If this is a shim intent, then it may be attempting to use an
985
        // existing set of keys for the funding workflow. In this case, we'll
986
        // make a simple wrapper keychain.KeyRing that will proxy certain
987
        // derivation calls to future callers.
988
        var (
3✔
989
                keyRing    keychain.KeyRing = l.SecretKeyRing
3✔
990
                thawHeight uint32
3✔
991
        )
3✔
992
        if shimIntent, ok := fundingIntent.(*chanfunding.ShimIntent); ok {
6✔
993
                keyRing = &shimKeyRing{
3✔
994
                        KeyRing:    keyRing,
3✔
995
                        ShimIntent: shimIntent,
3✔
996
                }
3✔
997

3✔
998
                // As this was a registered shim intent, we'll obtain the thaw
3✔
999
                // height of the intent, if present at all. If this is
3✔
1000
                // non-zero, then we'll mark this as the proper channel type.
3✔
1001
                thawHeight = shimIntent.ThawHeight()
3✔
1002
        }
3✔
1003

1004
        // Now that we have a funding intent, we'll check whether funding a
1005
        // channel using it would violate our reserved value for anchor channel
1006
        // fee bumping.
1007
        //
1008
        // Check the reserved value using the inputs and outputs given by the
1009
        // intent. Note that for the PSBT intent type we don't yet have the
1010
        // funding tx ready, so this will always pass.  We'll do another check
1011
        // when the PSBT has been verified.
1012
        isPublic := req.Flags&lnwire.FFAnnounceChannel != 0
3✔
1013
        if enforceNewReservedValue {
6✔
1014
                err = l.enforceNewReservedValue(fundingIntent, isPublic, hasAnchors)
3✔
1015
                if err != nil {
6✔
1016
                        fundingIntent.Cancel()
3✔
1017

3✔
1018
                        req.err <- err
3✔
1019
                        req.resp <- nil
3✔
1020
                        return
3✔
1021
                }
3✔
1022
        }
1023

1024
        // The total channel capacity will be the size of the funding output we
1025
        // created plus the remote contribution.
1026
        capacity := localFundingAmt + remoteFundingAmt
3✔
1027

3✔
1028
        id := atomic.AddUint64(&l.nextFundingID, 1)
3✔
1029
        reservation, err := NewChannelReservation(
3✔
1030
                capacity, localFundingAmt, l, id, l.Cfg.NetParams.GenesisHash,
3✔
1031
                thawHeight, req,
3✔
1032
        )
3✔
1033
        if err != nil {
6✔
1034
                fundingIntent.Cancel()
3✔
1035

3✔
1036
                req.err <- err
3✔
1037
                req.resp <- nil
3✔
1038
                return
3✔
1039
        }
3✔
1040

1041
        err = l.initOurContribution(
3✔
1042
                reservation, fundingIntent, req.NodeAddr, req.NodeID, keyRing,
3✔
1043
        )
3✔
1044
        if err != nil {
3✔
1045
                fundingIntent.Cancel()
×
1046

×
1047
                req.err <- err
×
1048
                req.resp <- nil
×
1049
                return
×
1050
        }
×
1051

1052
        // Create a limbo and record entry for this newly pending funding
1053
        // request.
1054
        l.limboMtx.Lock()
3✔
1055
        l.fundingLimbo[id] = reservation
3✔
1056
        l.reservationIDs[req.PendingChanID] = id
3✔
1057
        l.limboMtx.Unlock()
3✔
1058

3✔
1059
        // Funding reservation request successfully handled. The funding inputs
3✔
1060
        // will be marked as unavailable until the reservation is either
3✔
1061
        // completed, or canceled.
3✔
1062
        req.resp <- reservation
3✔
1063
        req.err <- nil
3✔
1064

3✔
1065
        walletLog.Debugf("Successfully handled funding reservation with "+
3✔
1066
                "pendingChanID: %x, reservationID: %v",
3✔
1067
                reservation.pendingChanID, reservation.reservationID)
3✔
1068
}
1069

1070
// enforceReservedValue enforces that the wallet, upon a new channel being
1071
// opened, meets the minimum amount of funds required for each advertised anchor
1072
// channel.
1073
//
1074
// We only enforce the reserve if we are contributing funds to the channel. This
1075
// is done to still allow incoming channels even though we have no UTXOs
1076
// available, as in bootstrapping phases.
1077
func (l *LightningWallet) enforceNewReservedValue(fundingIntent chanfunding.Intent,
1078
        isPublic, hasAnchors bool) error {
3✔
1079

3✔
1080
        // Only enforce the reserve when an advertised channel is being opened
3✔
1081
        // in which we are contributing funds to. This ensures we never dip
3✔
1082
        // below the reserve.
3✔
1083
        if !isPublic || fundingIntent.LocalFundingAmt() == 0 {
6✔
1084
                return nil
3✔
1085
        }
3✔
1086

1087
        numAnchors, err := l.CurrentNumAnchorChans()
3✔
1088
        if err != nil {
3✔
1089
                return err
×
1090
        }
×
1091

1092
        // Add the to-be-opened channel.
1093
        if hasAnchors {
6✔
1094
                numAnchors++
3✔
1095
        }
3✔
1096

1097
        return l.WithCoinSelectLock(func() error {
6✔
1098
                _, err := l.CheckReservedValue(
3✔
1099
                        fundingIntent.Inputs(), fundingIntent.Outputs(),
3✔
1100
                        numAnchors,
3✔
1101
                )
3✔
1102
                return err
3✔
1103
        })
3✔
1104
}
1105

1106
// CurrentNumAnchorChans returns the current number of non-private anchor
1107
// channels the wallet should be ready to fee bump if needed.
1108
func (l *LightningWallet) CurrentNumAnchorChans() (int, error) {
3✔
1109
        // Count all anchor channels that are open or pending
3✔
1110
        // open, or waiting close.
3✔
1111
        chans, err := l.Cfg.Database.FetchAllChannels()
3✔
1112
        if err != nil {
3✔
1113
                return 0, err
×
1114
        }
×
1115

1116
        var numAnchors int
3✔
1117
        cntChannel := func(c *channeldb.OpenChannel) {
6✔
1118
                // We skip private channels, as we assume they won't be used
3✔
1119
                // for routing.
3✔
1120
                if c.ChannelFlags&lnwire.FFAnnounceChannel == 0 {
6✔
1121
                        return
3✔
1122
                }
3✔
1123

1124
                // Count anchor channels.
1125
                if c.ChanType.HasAnchors() {
6✔
1126
                        numAnchors++
3✔
1127
                }
3✔
1128
        }
1129

1130
        for _, c := range chans {
6✔
1131
                cntChannel(c)
3✔
1132
        }
3✔
1133

1134
        // We also count pending close channels.
1135
        pendingClosed, err := l.Cfg.Database.FetchClosedChannels(
3✔
1136
                true,
3✔
1137
        )
3✔
1138
        if err != nil {
3✔
1139
                return 0, err
×
1140
        }
×
1141

1142
        for _, c := range pendingClosed {
6✔
1143
                c, err := l.Cfg.Database.FetchHistoricalChannel(
3✔
1144
                        &c.ChanPoint,
3✔
1145
                )
3✔
1146
                if err != nil {
3✔
1147
                        // We don't have a guarantee that all channels re found
×
1148
                        // in the historical channels bucket, so we continue.
×
1149
                        walletLog.Warnf("Unable to fetch historical "+
×
1150
                                "channel: %v", err)
×
1151
                        continue
×
1152
                }
1153

1154
                cntChannel(c)
3✔
1155
        }
1156

1157
        return numAnchors, nil
3✔
1158
}
1159

1160
// CheckReservedValue checks whether publishing a transaction with the given
1161
// inputs and outputs would violate the value we reserve in the wallet for
1162
// bumping the fee of anchor channels. The numAnchorChans argument should be
1163
// set the number of open anchor channels controlled by the wallet after
1164
// the transaction has been published.
1165
//
1166
// If the reserved value is violated, the returned error will be
1167
// ErrReservedValueInvalidated. The method will also return the current
1168
// reserved value, both in case of success and in case of
1169
// ErrReservedValueInvalidated.
1170
//
1171
// NOTE: This method should only be run with the CoinSelectLock held.
1172
func (l *LightningWallet) CheckReservedValue(in []wire.OutPoint,
1173
        out []*wire.TxOut, numAnchorChans int) (btcutil.Amount, error) {
3✔
1174

3✔
1175
        // Get all unspent coins in the wallet. We only care about those part of
3✔
1176
        // the wallet's default account as we know we can readily sign for those
3✔
1177
        // at any time.
3✔
1178
        witnessOutputs, err := l.ListUnspentWitnessFromDefaultAccount(
3✔
1179
                0, math.MaxInt32,
3✔
1180
        )
3✔
1181
        if err != nil {
3✔
1182
                return 0, err
×
1183
        }
×
1184

1185
        ourInput := make(map[wire.OutPoint]struct{})
3✔
1186
        for _, op := range in {
6✔
1187
                ourInput[op] = struct{}{}
3✔
1188
        }
3✔
1189

1190
        // When crafting a transaction with inputs from the wallet, these coins
1191
        // will usually be locked in the process, and not be returned when
1192
        // listing unspents. In this case they have already been deducted from
1193
        // the wallet balance. In case they haven't been properly locked, we
1194
        // check whether they are still listed among our unspents and deduct
1195
        // them.
1196
        var walletBalance btcutil.Amount
3✔
1197
        for _, in := range witnessOutputs {
6✔
1198
                // Spending an unlocked wallet UTXO, don't add it to the
3✔
1199
                // balance.
3✔
1200
                if _, ok := ourInput[in.OutPoint]; ok {
6✔
1201
                        continue
3✔
1202
                }
1203

1204
                walletBalance += in.Value
3✔
1205
        }
1206

1207
        // Now we go through the outputs of the transaction, if any of the
1208
        // outputs are paying into the wallet (likely a change output), we add
1209
        // it to our final balance.
1210
        for _, txOut := range out {
6✔
1211
                _, addrs, _, err := txscript.ExtractPkScriptAddrs(
3✔
1212
                        txOut.PkScript, &l.Cfg.NetParams,
3✔
1213
                )
3✔
1214
                if err != nil {
3✔
1215
                        // Non-standard outputs can safely be skipped because
×
1216
                        // they're not supported by the wallet.
×
1217
                        continue
×
1218
                }
1219

1220
                for _, addr := range addrs {
6✔
1221
                        if !l.IsOurAddress(addr) {
6✔
1222
                                continue
3✔
1223
                        }
1224

1225
                        walletBalance += btcutil.Amount(txOut.Value)
3✔
1226

3✔
1227
                        // We break since we don't want to double count the output.
3✔
1228
                        break
3✔
1229
                }
1230
        }
1231

1232
        // We reserve a given amount for each anchor channel.
1233
        reserved := l.RequiredReserve(uint32(numAnchorChans))
3✔
1234

3✔
1235
        if walletBalance < reserved {
6✔
1236
                walletLog.Debugf("Reserved value=%v above final "+
3✔
1237
                        "walletbalance=%v with %d anchor channels open",
3✔
1238
                        reserved, walletBalance, numAnchorChans)
3✔
1239
                return reserved, ErrReservedValueInvalidated
3✔
1240
        }
3✔
1241

1242
        return reserved, nil
3✔
1243
}
1244

1245
// CheckReservedValueTx calls CheckReservedValue with the inputs and outputs
1246
// from the given tx, with the number of anchor channels currently open in the
1247
// database.
1248
//
1249
// NOTE: This method should only be run with the CoinSelectLock held.
1250
func (l *LightningWallet) CheckReservedValueTx(req CheckReservedValueTxReq) (
1251
        btcutil.Amount, error) {
3✔
1252

3✔
1253
        numAnchors, err := l.CurrentNumAnchorChans()
3✔
1254
        if err != nil {
3✔
1255
                return 0, err
×
1256
        }
×
1257

1258
        var inputs []wire.OutPoint
3✔
1259
        for _, txIn := range req.Tx.TxIn {
6✔
1260
                inputs = append(inputs, txIn.PreviousOutPoint)
3✔
1261
        }
3✔
1262

1263
        reservedVal, err := l.CheckReservedValue(
3✔
1264
                inputs, req.Tx.TxOut, numAnchors,
3✔
1265
        )
3✔
1266
        switch {
3✔
1267
        // If the error returned from CheckReservedValue is
1268
        // ErrReservedValueInvalidated, then it did nonetheless return
1269
        // the required reserved value and we check for the optional
1270
        // change index.
1271
        case errors.Is(err, ErrReservedValueInvalidated):
3✔
1272
                // Without a change index provided there is nothing more to
3✔
1273
                // check and the error is returned.
3✔
1274
                if req.ChangeIndex == nil {
6✔
1275
                        return reservedVal, err
3✔
1276
                }
3✔
1277

1278
                // If a change index was provided we make only sure that it
1279
                // would leave sufficient funds for the reserved balance value.
1280
                //
1281
                // Note: This is used if a change output index is explicitly set
1282
                // but that may not be watched by the wallet and therefore is
1283
                // not picked up by the call to CheckReservedValue above.
1284
                chIdx := *req.ChangeIndex
×
1285
                if chIdx < 0 || chIdx >= len(req.Tx.TxOut) ||
×
1286
                        req.Tx.TxOut[chIdx].Value < int64(reservedVal) {
×
1287

×
1288
                        return reservedVal, err
×
1289
                }
×
1290

1291
        case err != nil:
×
1292
                return reservedVal, err
×
1293
        }
1294

1295
        return reservedVal, nil
3✔
1296
}
1297

1298
// initOurContribution initializes the given ChannelReservation with our coins
1299
// and change reserved for the channel, and derives the keys to use for this
1300
// channel.
1301
func (l *LightningWallet) initOurContribution(reservation *ChannelReservation,
1302
        fundingIntent chanfunding.Intent, nodeAddr net.Addr,
1303
        nodeID *btcec.PublicKey, keyRing keychain.KeyRing) error {
3✔
1304

3✔
1305
        // Grab the mutex on the ChannelReservation to ensure thread-safety
3✔
1306
        reservation.Lock()
3✔
1307
        defer reservation.Unlock()
3✔
1308

3✔
1309
        // At this point, if we have a funding intent, we'll use it to populate
3✔
1310
        // the existing reservation state entries for our coin selection.
3✔
1311
        if fundingIntent != nil {
6✔
1312
                if intent, ok := fundingIntent.(*chanfunding.FullIntent); ok {
6✔
1313
                        for _, coin := range intent.InputCoins {
6✔
1314
                                reservation.ourContribution.Inputs = append(
3✔
1315
                                        reservation.ourContribution.Inputs,
3✔
1316
                                        &wire.TxIn{
3✔
1317
                                                PreviousOutPoint: coin.OutPoint,
3✔
1318
                                        },
3✔
1319
                                )
3✔
1320
                        }
3✔
1321
                        reservation.ourContribution.ChangeOutputs = intent.ChangeOutputs
3✔
1322
                }
1323

1324
                reservation.fundingIntent = fundingIntent
3✔
1325
        }
1326

1327
        reservation.nodeAddr = nodeAddr
3✔
1328
        reservation.partialState.IdentityPub = nodeID
3✔
1329

3✔
1330
        var err error
3✔
1331
        reservation.ourContribution.MultiSigKey, err = keyRing.DeriveNextKey(
3✔
1332
                keychain.KeyFamilyMultiSig,
3✔
1333
        )
3✔
1334
        if err != nil {
3✔
1335
                return err
×
1336
        }
×
1337
        reservation.ourContribution.RevocationBasePoint, err = keyRing.DeriveNextKey(
3✔
1338
                keychain.KeyFamilyRevocationBase,
3✔
1339
        )
3✔
1340
        if err != nil {
3✔
1341
                return err
×
1342
        }
×
1343
        reservation.ourContribution.HtlcBasePoint, err = keyRing.DeriveNextKey(
3✔
1344
                keychain.KeyFamilyHtlcBase,
3✔
1345
        )
3✔
1346
        if err != nil {
3✔
1347
                return err
×
1348
        }
×
1349
        reservation.ourContribution.PaymentBasePoint, err = keyRing.DeriveNextKey(
3✔
1350
                keychain.KeyFamilyPaymentBase,
3✔
1351
        )
3✔
1352
        if err != nil {
3✔
1353
                return err
×
1354
        }
×
1355
        reservation.ourContribution.DelayBasePoint, err = keyRing.DeriveNextKey(
3✔
1356
                keychain.KeyFamilyDelayBase,
3✔
1357
        )
3✔
1358
        if err != nil {
3✔
1359
                return err
×
1360
        }
×
1361

1362
        // With the above keys created, we'll also need to initialize our
1363
        // revocation tree state, and from that generate the per-commitment
1364
        // point.
1365
        producer, taprootNonceProducer, err := l.nextRevocationProducer(
3✔
1366
                reservation, keyRing,
3✔
1367
        )
3✔
1368
        if err != nil {
3✔
1369
                return err
×
1370
        }
×
1371

1372
        firstPreimage, err := producer.AtIndex(0)
3✔
1373
        if err != nil {
3✔
1374
                return err
×
1375
        }
×
1376
        reservation.ourContribution.FirstCommitmentPoint = input.ComputeCommitmentPoint(
3✔
1377
                firstPreimage[:],
3✔
1378
        )
3✔
1379

3✔
1380
        reservation.partialState.RevocationProducer = producer
3✔
1381
        reservation.ourContribution.ChannelConstraints.DustLimit =
3✔
1382
                DustLimitUnknownWitness()
3✔
1383

3✔
1384
        // If taproot channels are active, then we'll generate our verification
3✔
1385
        // nonce here. We'll use this nonce to verify the signature for our
3✔
1386
        // local commitment transaction. If we need to force close, then this
3✔
1387
        // is also what'll be used to sign that transaction.
3✔
1388
        if reservation.partialState.ChanType.IsTaproot() {
6✔
1389
                firstNoncePreimage, err := taprootNonceProducer.AtIndex(0)
3✔
1390
                if err != nil {
3✔
1391
                        return err
×
1392
                }
×
1393

1394
                // As we'd like the local nonce we send over to be generated
1395
                // deterministically, we'll provide a custom reader that
1396
                // actually just uses our sha-chain pre-image as the primary
1397
                // randomness source.
1398
                shaChainRand := musig2.WithCustomRand(
3✔
1399
                        bytes.NewBuffer(firstNoncePreimage[:]),
3✔
1400
                )
3✔
1401
                pubKeyOpt := musig2.WithPublicKey(
3✔
1402
                        reservation.ourContribution.MultiSigKey.PubKey,
3✔
1403
                )
3✔
1404
                reservation.ourContribution.LocalNonce, err = musig2.GenNonces(
3✔
1405
                        pubKeyOpt, shaChainRand,
3✔
1406
                )
3✔
1407
                if err != nil {
3✔
1408
                        return err
×
1409
                }
×
1410
        }
1411

1412
        return nil
3✔
1413
}
1414

1415
// handleFundingReserveCancel cancels an existing channel reservation. As part
1416
// of the cancellation, outputs previously selected as inputs for the funding
1417
// transaction via coin selection are freed allowing future reservations to
1418
// include them.
1419
func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMsg) {
3✔
1420
        // TODO(roasbeef): holding lock too long
3✔
1421
        l.limboMtx.Lock()
3✔
1422
        defer l.limboMtx.Unlock()
3✔
1423

3✔
1424
        pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
3✔
1425
        if !ok {
3✔
1426
                // TODO(roasbeef): make new error, "unknown funding state" or something
×
1427
                req.err <- fmt.Errorf("attempted to cancel non-existent funding state")
×
1428
                return
×
1429
        }
×
1430

1431
        // Grab the mutex on the ChannelReservation to ensure thread-safety
1432
        pendingReservation.Lock()
3✔
1433
        defer pendingReservation.Unlock()
3✔
1434

3✔
1435
        // Mark all previously locked outpoints as usable for future funding
3✔
1436
        // requests.
3✔
1437
        for _, unusedInput := range pendingReservation.ourContribution.Inputs {
6✔
1438
                delete(l.lockedOutPoints, unusedInput.PreviousOutPoint)
3✔
1439
                _ = l.ReleaseOutput(
3✔
1440
                        chanfunding.LndInternalLockID,
3✔
1441
                        unusedInput.PreviousOutPoint,
3✔
1442
                )
3✔
1443
        }
3✔
1444

1445
        // TODO(roasbeef): is it even worth it to keep track of unused keys?
1446

1447
        // TODO(roasbeef): Is it possible to mark the unused change also as
1448
        // available?
1449

1450
        delete(l.fundingLimbo, req.pendingFundingID)
3✔
1451

3✔
1452
        pid := pendingReservation.pendingChanID
3✔
1453
        delete(l.reservationIDs, pid)
3✔
1454

3✔
1455
        l.intentMtx.Lock()
3✔
1456
        if intent, ok := l.fundingIntents[pid]; ok {
6✔
1457
                intent.Cancel()
3✔
1458

3✔
1459
                delete(l.fundingIntents, pendingReservation.pendingChanID)
3✔
1460
        }
3✔
1461
        l.intentMtx.Unlock()
3✔
1462

3✔
1463
        req.err <- nil
3✔
1464
}
1465

1466
// CreateCommitmentTxns is a helper function that creates the initial
1467
// commitment transaction for both parties. This function is used during the
1468
// initial funding workflow as both sides must generate a signature for the
1469
// remote party's commitment transaction, and verify the signature for their
1470
// version of the commitment transaction.
1471
func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
1472
        ourChanCfg, theirChanCfg *channeldb.ChannelConfig,
1473
        localCommitPoint, remoteCommitPoint *btcec.PublicKey,
1474
        fundingTxIn wire.TxIn, chanType channeldb.ChannelType, initiator bool,
1475
        leaseExpiry uint32) (*wire.MsgTx, *wire.MsgTx, error) {
3✔
1476

3✔
1477
        localCommitmentKeys := DeriveCommitmentKeys(
3✔
1478
                localCommitPoint, true, chanType, ourChanCfg, theirChanCfg,
3✔
1479
        )
3✔
1480
        remoteCommitmentKeys := DeriveCommitmentKeys(
3✔
1481
                remoteCommitPoint, false, chanType, ourChanCfg, theirChanCfg,
3✔
1482
        )
3✔
1483

3✔
1484
        ourCommitTx, err := CreateCommitTx(
3✔
1485
                chanType, fundingTxIn, localCommitmentKeys, ourChanCfg,
3✔
1486
                theirChanCfg, localBalance, remoteBalance, 0, initiator,
3✔
1487
                leaseExpiry,
3✔
1488
        )
3✔
1489
        if err != nil {
3✔
1490
                return nil, nil, err
×
1491
        }
×
1492

1493
        otxn := btcutil.NewTx(ourCommitTx)
3✔
1494
        if err := blockchain.CheckTransactionSanity(otxn); err != nil {
3✔
1495
                return nil, nil, err
×
1496
        }
×
1497

1498
        theirCommitTx, err := CreateCommitTx(
3✔
1499
                chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg,
3✔
1500
                ourChanCfg, remoteBalance, localBalance, 0, !initiator,
3✔
1501
                leaseExpiry,
3✔
1502
        )
3✔
1503
        if err != nil {
3✔
1504
                return nil, nil, err
×
1505
        }
×
1506

1507
        ttxn := btcutil.NewTx(theirCommitTx)
3✔
1508
        if err := blockchain.CheckTransactionSanity(ttxn); err != nil {
3✔
1509
                return nil, nil, err
×
1510
        }
×
1511

1512
        return ourCommitTx, theirCommitTx, nil
3✔
1513
}
1514

1515
// handleContributionMsg processes the second workflow step for the lifetime of
1516
// a channel reservation. Upon completion, the reservation will carry a
1517
// completed funding transaction (minus the counterparty's input signatures),
1518
// both versions of the commitment transaction, and our signature for their
1519
// version of the commitment transaction.
1520
func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
3✔
1521

3✔
1522
        l.limboMtx.Lock()
3✔
1523
        pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
3✔
1524
        l.limboMtx.Unlock()
3✔
1525
        if !ok {
3✔
1526
                req.err <- fmt.Errorf("attempted to update non-existent funding state")
×
1527
                return
×
1528
        }
×
1529

1530
        // Grab the mutex on the ChannelReservation to ensure thread-safety
1531
        pendingReservation.Lock()
3✔
1532
        defer pendingReservation.Unlock()
3✔
1533

3✔
1534
        // If UpfrontShutdownScript is set, validate that it is a valid script.
3✔
1535
        shutdown := req.contribution.UpfrontShutdown
3✔
1536
        if len(shutdown) > 0 {
3✔
1537
                // Validate the shutdown script.
×
1538
                if !ValidateUpfrontShutdown(shutdown, &l.Cfg.NetParams) {
×
1539
                        req.err <- fmt.Errorf("invalid shutdown script")
×
1540
                        return
×
1541
                }
×
1542
        }
1543

1544
        // Some temporary variables to cut down on the resolution verbosity.
1545
        pendingReservation.theirContribution = req.contribution
3✔
1546
        theirContribution := req.contribution
3✔
1547
        ourContribution := pendingReservation.ourContribution
3✔
1548

3✔
1549
        // Perform bounds-checking on both ChannelReserve and DustLimit
3✔
1550
        // parameters.
3✔
1551
        if !pendingReservation.validateReserveBounds() {
3✔
1552
                req.err <- fmt.Errorf("invalid reserve and dust bounds")
×
1553
                return
×
1554
        }
×
1555

1556
        var (
3✔
1557
                chanPoint *wire.OutPoint
3✔
1558
                err       error
3✔
1559
        )
3✔
1560

3✔
1561
        // At this point, we can now construct our channel point. Depending on
3✔
1562
        // which type of intent we obtained from our chanfunding.Assembler,
3✔
1563
        // we'll carry out a distinct set of steps.
3✔
1564
        switch fundingIntent := pendingReservation.fundingIntent.(type) {
3✔
1565
        // The transaction was created outside of the wallet and might already
1566
        // be published. Nothing left to do other than using the correct
1567
        // outpoint.
1568
        case *chanfunding.ShimIntent:
3✔
1569
                chanPoint, err = fundingIntent.ChanPoint()
3✔
1570
                if err != nil {
3✔
1571
                        req.err <- fmt.Errorf("unable to obtain chan point: %w",
×
1572
                                err)
×
1573
                        return
×
1574
                }
×
1575

1576
                pendingReservation.partialState.FundingOutpoint = *chanPoint
3✔
1577

1578
        // The user has signaled that they want to use a PSBT to construct the
1579
        // funding transaction. Because we now have the multisig keys from both
1580
        // parties, we can create the multisig script that needs to be funded
1581
        // and then pause the process until the user supplies the PSBT
1582
        // containing the eventual funding transaction.
1583
        case *chanfunding.PsbtIntent:
3✔
1584
                if fundingIntent.PendingPsbt != nil {
3✔
1585
                        req.err <- fmt.Errorf("PSBT funding already in" +
×
1586
                                "progress")
×
1587
                        return
×
1588
                }
×
1589

1590
                // Now that we know our contribution, we can bind both the local
1591
                // and remote key which will be needed to calculate the multisig
1592
                // funding output in a next step.
1593
                pendingChanID := pendingReservation.pendingChanID
3✔
1594
                walletLog.Debugf("Advancing PSBT funding flow for "+
3✔
1595
                        "pending_id(%x), binding keys local_key=%v, "+
3✔
1596
                        "remote_key=%x", pendingChanID,
3✔
1597
                        &ourContribution.MultiSigKey,
3✔
1598
                        theirContribution.MultiSigKey.PubKey.SerializeCompressed())
3✔
1599
                fundingIntent.BindKeys(
3✔
1600
                        &ourContribution.MultiSigKey,
3✔
1601
                        theirContribution.MultiSigKey.PubKey,
3✔
1602
                )
3✔
1603

3✔
1604
                // Exit early because we can't continue the funding flow yet.
3✔
1605
                req.err <- &PsbtFundingRequired{
3✔
1606
                        Intent: fundingIntent,
3✔
1607
                }
3✔
1608
                return
3✔
1609

1610
        case *chanfunding.FullIntent:
3✔
1611
                // Now that we know their public key, we can bind theirs as
3✔
1612
                // well as ours to the funding intent.
3✔
1613
                fundingIntent.BindKeys(
3✔
1614
                        &pendingReservation.ourContribution.MultiSigKey,
3✔
1615
                        theirContribution.MultiSigKey.PubKey,
3✔
1616
                )
3✔
1617

3✔
1618
                // With our keys bound, we can now construct+sign the final
3✔
1619
                // funding transaction and also obtain the chanPoint that
3✔
1620
                // creates the channel.
3✔
1621
                fundingTx, err := fundingIntent.CompileFundingTx(
3✔
1622
                        theirContribution.Inputs,
3✔
1623
                        theirContribution.ChangeOutputs,
3✔
1624
                )
3✔
1625
                if err != nil {
3✔
1626
                        req.err <- fmt.Errorf("unable to construct funding "+
×
1627
                                "tx: %v", err)
×
1628
                        return
×
1629
                }
×
1630
                chanPoint, err = fundingIntent.ChanPoint()
3✔
1631
                if err != nil {
3✔
1632
                        req.err <- fmt.Errorf("unable to obtain chan "+
×
1633
                                "point: %v", err)
×
1634
                        return
×
1635
                }
×
1636

1637
                // Finally, we'll populate the relevant information in our
1638
                // pendingReservation so the rest of the funding flow can
1639
                // continue as normal.
1640
                pendingReservation.fundingTx = fundingTx
3✔
1641
                pendingReservation.partialState.FundingOutpoint = *chanPoint
3✔
1642
                pendingReservation.ourFundingInputScripts = make(
3✔
1643
                        []*input.Script, 0, len(ourContribution.Inputs),
3✔
1644
                )
3✔
1645
                for _, txIn := range fundingTx.TxIn {
6✔
1646
                        _, err := l.FetchInputInfo(&txIn.PreviousOutPoint)
3✔
1647
                        if err != nil {
3✔
1648
                                continue
×
1649
                        }
1650

1651
                        pendingReservation.ourFundingInputScripts = append(
3✔
1652
                                pendingReservation.ourFundingInputScripts,
3✔
1653
                                &input.Script{
3✔
1654
                                        Witness:   txIn.Witness,
3✔
1655
                                        SigScript: txIn.SignatureScript,
3✔
1656
                                },
3✔
1657
                        )
3✔
1658
                }
1659

1660
                walletLog.Tracef("Funding tx for ChannelPoint(%v) "+
3✔
1661
                        "generated: %v", chanPoint, spew.Sdump(fundingTx))
3✔
1662
        }
1663

1664
        // If we landed here and didn't exit early, it means we already have
1665
        // the channel point ready. We can jump directly to the next step.
1666
        l.handleChanPointReady(&continueContributionMsg{
3✔
1667
                pendingFundingID: req.pendingFundingID,
3✔
1668
                err:              req.err,
3✔
1669
        })
3✔
1670
}
1671

1672
// genMusigSession generates a new musig2 pair session that we can use to sign
1673
// the commitment transaction for the remote party, and verify their incoming
1674
// partial signature.
1675
func genMusigSession(ourContribution, theirContribution *ChannelContribution,
1676
        signer input.MuSig2Signer,
1677
        fundingOutput *wire.TxOut) *MusigPairSession {
3✔
1678

3✔
1679
        return NewMusigPairSession(&MusigSessionCfg{
3✔
1680
                LocalKey:    ourContribution.MultiSigKey,
3✔
1681
                RemoteKey:   theirContribution.MultiSigKey,
3✔
1682
                LocalNonce:  *ourContribution.LocalNonce,
3✔
1683
                RemoteNonce: *theirContribution.LocalNonce,
3✔
1684
                Signer:      signer,
3✔
1685
                InputTxOut:  fundingOutput,
3✔
1686
        })
3✔
1687
}
3✔
1688

1689
// signCommitTx generates a valid input.Signature to send to the remote party
1690
// for their version of the commitment transaction. For regular channels, this
1691
// will be a normal ECDSA signature. For taproot channels, this will instead be
1692
// a musig2 partial signature that also includes the nonce used to generate it.
1693
func (l *LightningWallet) signCommitTx(pendingReservation *ChannelReservation,
1694
        commitTx *wire.MsgTx, fundingOutput *wire.TxOut,
1695
        fundingWitnessScript []byte) (input.Signature, error) {
3✔
1696

3✔
1697
        ourContribution := pendingReservation.ourContribution
3✔
1698
        theirContribution := pendingReservation.theirContribution
3✔
1699

3✔
1700
        var (
3✔
1701
                sigTheirCommit input.Signature
3✔
1702
                err            error
3✔
1703
        )
3✔
1704
        switch {
3✔
1705
        // For regular channels, we can just send over a normal ECDSA signature
1706
        // w/o any extra steps.
1707
        case !pendingReservation.partialState.ChanType.IsTaproot():
3✔
1708
                ourKey := ourContribution.MultiSigKey
3✔
1709
                signDesc := input.SignDescriptor{
3✔
1710
                        WitnessScript: fundingWitnessScript,
3✔
1711
                        KeyDesc:       ourKey,
3✔
1712
                        Output:        fundingOutput,
3✔
1713
                        HashType:      txscript.SigHashAll,
3✔
1714
                        SigHashes: input.NewTxSigHashesV0Only(
3✔
1715
                                commitTx,
3✔
1716
                        ),
3✔
1717
                        InputIndex: 0,
3✔
1718
                }
3✔
1719
                sigTheirCommit, err = l.Cfg.Signer.SignOutputRaw(
3✔
1720
                        commitTx, &signDesc,
3✔
1721
                )
3✔
1722
                if err != nil {
3✔
1723
                        return nil, err
×
1724
                }
×
1725

1726
        // If this is a taproot channel, then we'll need to create an initial
1727
        // musig2 session here as we'll be sending over a _partial_ signature.
1728
        default:
3✔
1729
                // We're now ready to sign the first commitment. However, we'll
3✔
1730
                // only create the session if that hasn't been done already.
3✔
1731
                if pendingReservation.musigSessions == nil {
6✔
1732
                        musigSessions := genMusigSession(
3✔
1733
                                ourContribution, theirContribution,
3✔
1734
                                l.Cfg.Signer, fundingOutput,
3✔
1735
                        )
3✔
1736
                        pendingReservation.musigSessions = musigSessions
3✔
1737
                }
3✔
1738

1739
                // Now that we have the funding outpoint, we'll generate a
1740
                // musig2 signature for their version of the commitment
1741
                // transaction. We use the remote session as this is for the
1742
                // remote commitment transaction.
1743
                musigSessions := pendingReservation.musigSessions
3✔
1744
                partialSig, err := musigSessions.RemoteSession.SignCommit(
3✔
1745
                        commitTx,
3✔
1746
                )
3✔
1747
                if err != nil {
3✔
1748
                        return nil, fmt.Errorf("unable to sign "+
×
1749
                                "commitment: %w", err)
×
1750
                }
×
1751

1752
                sigTheirCommit = partialSig
3✔
1753
        }
1754

1755
        return sigTheirCommit, nil
3✔
1756
}
1757

1758
// handleChanPointReady continues the funding process once the channel point is
1759
// known and the funding transaction can be completed.
1760
func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
3✔
1761
        l.limboMtx.Lock()
3✔
1762
        pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
3✔
1763
        l.limboMtx.Unlock()
3✔
1764
        if !ok {
3✔
1765
                req.err <- fmt.Errorf("attempted to update non-existent " +
×
1766
                        "funding state")
×
1767
                return
×
1768
        }
×
1769

1770
        ourContribution := pendingReservation.ourContribution
3✔
1771
        theirContribution := pendingReservation.theirContribution
3✔
1772
        chanPoint := pendingReservation.partialState.FundingOutpoint
3✔
1773

3✔
1774
        // If we're in the PSBT funding flow, we now should have everything that
3✔
1775
        // is needed to construct and publish the full funding transaction.
3✔
1776
        intent := pendingReservation.fundingIntent
3✔
1777
        if psbtIntent, ok := intent.(*chanfunding.PsbtIntent); ok {
6✔
1778
                // With our keys bound, we can now construct and possibly sign
3✔
1779
                // the final funding transaction and also obtain the chanPoint
3✔
1780
                // that creates the channel. We _have_ to call CompileFundingTx
3✔
1781
                // even if we don't publish ourselves as that sets the actual
3✔
1782
                // funding outpoint in stone for this channel.
3✔
1783
                fundingTx, err := psbtIntent.CompileFundingTx()
3✔
1784
                if err != nil {
3✔
1785
                        req.err <- fmt.Errorf("unable to construct funding "+
×
1786
                                "tx: %v", err)
×
1787
                        return
×
1788
                }
×
1789
                chanPointPtr, err := psbtIntent.ChanPoint()
3✔
1790
                if err != nil {
3✔
1791
                        req.err <- fmt.Errorf("unable to obtain chan "+
×
1792
                                "point: %v", err)
×
1793
                        return
×
1794
                }
×
1795

1796
                pendingReservation.partialState.FundingOutpoint = *chanPointPtr
3✔
1797
                chanPoint = *chanPointPtr
3✔
1798

3✔
1799
                // Finally, we'll populate the relevant information in our
3✔
1800
                // pendingReservation so the rest of the funding flow can
3✔
1801
                // continue as normal in case we are going to publish ourselves.
3✔
1802
                if psbtIntent.ShouldPublishFundingTX() {
6✔
1803
                        pendingReservation.fundingTx = fundingTx
3✔
1804
                        pendingReservation.ourFundingInputScripts = make(
3✔
1805
                                []*input.Script, 0, len(ourContribution.Inputs),
3✔
1806
                        )
3✔
1807
                        for _, txIn := range fundingTx.TxIn {
6✔
1808
                                pendingReservation.ourFundingInputScripts = append(
3✔
1809
                                        pendingReservation.ourFundingInputScripts,
3✔
1810
                                        &input.Script{
3✔
1811
                                                Witness:   txIn.Witness,
3✔
1812
                                                SigScript: txIn.SignatureScript,
3✔
1813
                                        },
3✔
1814
                                )
3✔
1815
                        }
3✔
1816
                }
1817
        }
1818

1819
        // Initialize an empty sha-chain for them, tracking the current pending
1820
        // revocation hash (we don't yet know the preimage so we can't add it
1821
        // to the chain).
1822
        s := shachain.NewRevocationStore()
3✔
1823
        pendingReservation.partialState.RevocationStore = s
3✔
1824

3✔
1825
        // Store their current commitment point. We'll need this after the
3✔
1826
        // first state transition in order to verify the authenticity of the
3✔
1827
        // revocation.
3✔
1828
        chanState := pendingReservation.partialState
3✔
1829
        chanState.RemoteCurrentRevocation = theirContribution.FirstCommitmentPoint
3✔
1830

3✔
1831
        // Create the txin to our commitment transaction; required to construct
3✔
1832
        // the commitment transactions.
3✔
1833
        fundingTxIn := wire.TxIn{
3✔
1834
                PreviousOutPoint: chanPoint,
3✔
1835
        }
3✔
1836

3✔
1837
        // With the funding tx complete, create both commitment transactions.
3✔
1838
        localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
3✔
1839
        remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
3✔
1840
        var leaseExpiry uint32
3✔
1841
        if pendingReservation.partialState.ChanType.HasLeaseExpiration() {
6✔
1842
                leaseExpiry = pendingReservation.partialState.ThawHeight
3✔
1843
        }
3✔
1844
        ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
3✔
1845
                localBalance, remoteBalance, ourContribution.ChannelConfig,
3✔
1846
                theirContribution.ChannelConfig,
3✔
1847
                ourContribution.FirstCommitmentPoint,
3✔
1848
                theirContribution.FirstCommitmentPoint, fundingTxIn,
3✔
1849
                pendingReservation.partialState.ChanType,
3✔
1850
                pendingReservation.partialState.IsInitiator, leaseExpiry,
3✔
1851
        )
3✔
1852
        if err != nil {
3✔
1853
                req.err <- err
×
1854
                return
×
1855
        }
×
1856

1857
        // With both commitment transactions constructed, generate the state
1858
        // obfuscator then use it to encode the current state number within
1859
        // both commitment transactions.
1860
        var stateObfuscator [StateHintSize]byte
3✔
1861
        if chanState.ChanType.IsSingleFunder() {
6✔
1862
                stateObfuscator = DeriveStateHintObfuscator(
3✔
1863
                        ourContribution.PaymentBasePoint.PubKey,
3✔
1864
                        theirContribution.PaymentBasePoint.PubKey,
3✔
1865
                )
3✔
1866
        } else {
3✔
1867
                ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed()
×
1868
                theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed()
×
1869
                switch bytes.Compare(ourSer, theirSer) {
×
1870
                case -1:
×
1871
                        stateObfuscator = DeriveStateHintObfuscator(
×
1872
                                ourContribution.PaymentBasePoint.PubKey,
×
1873
                                theirContribution.PaymentBasePoint.PubKey,
×
1874
                        )
×
1875
                default:
×
1876
                        stateObfuscator = DeriveStateHintObfuscator(
×
1877
                                theirContribution.PaymentBasePoint.PubKey,
×
1878
                                ourContribution.PaymentBasePoint.PubKey,
×
1879
                        )
×
1880
                }
1881
        }
1882
        err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator)
3✔
1883
        if err != nil {
3✔
1884
                req.err <- err
×
1885
                return
×
1886
        }
×
1887

1888
        // Sort both transactions according to the agreed upon canonical
1889
        // ordering. This lets us skip sending the entire transaction over,
1890
        // instead we'll just send signatures.
1891
        txsort.InPlaceSort(ourCommitTx)
3✔
1892
        txsort.InPlaceSort(theirCommitTx)
3✔
1893

3✔
1894
        walletLog.Tracef("Local commit tx for ChannelPoint(%v): %v",
3✔
1895
                chanPoint, spew.Sdump(ourCommitTx))
3✔
1896
        walletLog.Tracef("Remote commit tx for ChannelPoint(%v): %v",
3✔
1897
                chanPoint, spew.Sdump(theirCommitTx))
3✔
1898

3✔
1899
        // Record newly available information within the open channel state.
3✔
1900
        chanState.FundingOutpoint = chanPoint
3✔
1901
        chanState.LocalCommitment.CommitTx = ourCommitTx
3✔
1902
        chanState.RemoteCommitment.CommitTx = theirCommitTx
3✔
1903

3✔
1904
        // Next, we'll obtain the funding witness script, and the funding
3✔
1905
        // output itself so we can generate a valid signature for the remote
3✔
1906
        // party.
3✔
1907
        fundingIntent := pendingReservation.fundingIntent
3✔
1908
        fundingWitnessScript, fundingOutput, err := fundingIntent.FundingOutput()
3✔
1909
        if err != nil {
3✔
1910
                req.err <- fmt.Errorf("unable to obtain funding "+
×
1911
                        "output: %w", err)
×
1912
                return
×
1913
        }
×
1914

1915
        // Generate a signature for their version of the initial commitment
1916
        // transaction.
1917
        sigTheirCommit, err := l.signCommitTx(
3✔
1918
                pendingReservation, theirCommitTx, fundingOutput,
3✔
1919
                fundingWitnessScript,
3✔
1920
        )
3✔
1921
        if err != nil {
3✔
1922
                req.err <- err
×
1923
                return
×
1924
        }
×
1925

1926
        pendingReservation.ourCommitmentSig = sigTheirCommit
3✔
1927

3✔
1928
        req.err <- nil
3✔
1929
}
1930

1931
// handleSingleContribution is called as the second step to a single funder
1932
// workflow to which we are the responder. It simply saves the remote peer's
1933
// contribution to the channel, as solely the remote peer will contribute any
1934
// funds to the channel.
1935
func (l *LightningWallet) handleSingleContribution(req *addSingleContributionMsg) {
3✔
1936
        l.limboMtx.Lock()
3✔
1937
        pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
3✔
1938
        l.limboMtx.Unlock()
3✔
1939
        if !ok {
3✔
1940
                req.err <- fmt.Errorf("attempted to update non-existent funding state")
×
1941
                return
×
1942
        }
×
1943

1944
        // Grab the mutex on the channelReservation to ensure thread-safety.
1945
        pendingReservation.Lock()
3✔
1946
        defer pendingReservation.Unlock()
3✔
1947

3✔
1948
        // Validate that the remote's UpfrontShutdownScript is a valid script
3✔
1949
        // if it's set.
3✔
1950
        shutdown := req.contribution.UpfrontShutdown
3✔
1951
        if len(shutdown) > 0 {
6✔
1952
                // Validate the shutdown script.
3✔
1953
                if !ValidateUpfrontShutdown(shutdown, &l.Cfg.NetParams) {
3✔
1954
                        req.err <- fmt.Errorf("invalid shutdown script")
×
1955
                        return
×
1956
                }
×
1957
        }
1958

1959
        // Simply record the counterparty's contribution into the pending
1960
        // reservation data as they'll be solely funding the channel entirely.
1961
        pendingReservation.theirContribution = req.contribution
3✔
1962
        theirContribution := pendingReservation.theirContribution
3✔
1963
        chanState := pendingReservation.partialState
3✔
1964

3✔
1965
        // Perform bounds checking on both ChannelReserve and DustLimit
3✔
1966
        // parameters. The ChannelReserve may have been changed by the
3✔
1967
        // ChannelAcceptor RPC, so this is necessary.
3✔
1968
        if !pendingReservation.validateReserveBounds() {
3✔
1969
                req.err <- fmt.Errorf("invalid reserve and dust bounds")
×
1970
                return
×
1971
        }
×
1972

1973
        // Initialize an empty sha-chain for them, tracking the current pending
1974
        // revocation hash (we don't yet know the preimage so we can't add it
1975
        // to the chain).
1976
        remotePreimageStore := shachain.NewRevocationStore()
3✔
1977
        chanState.RevocationStore = remotePreimageStore
3✔
1978

3✔
1979
        // Now that we've received their first commitment point, we'll store it
3✔
1980
        // within the channel state so we can sync it to disk once the funding
3✔
1981
        // process is complete.
3✔
1982
        chanState.RemoteCurrentRevocation = theirContribution.FirstCommitmentPoint
3✔
1983

3✔
1984
        req.err <- nil
3✔
1985
}
1986

1987
// verifyFundingInputs attempts to verify all remote inputs to the funding
1988
// transaction.
1989
func (l *LightningWallet) verifyFundingInputs(fundingTx *wire.MsgTx,
1990
        remoteInputScripts []*input.Script) error {
3✔
1991

3✔
1992
        sigIndex := 0
3✔
1993
        fundingHashCache := input.NewTxSigHashesV0Only(fundingTx)
3✔
1994
        inputScripts := remoteInputScripts
3✔
1995
        for i, txin := range fundingTx.TxIn {
6✔
1996
                if len(inputScripts) != 0 && len(txin.Witness) == 0 {
3✔
1997
                        // Attach the input scripts so we can verify it below.
×
1998
                        txin.Witness = inputScripts[sigIndex].Witness
×
1999
                        txin.SignatureScript = inputScripts[sigIndex].SigScript
×
2000

×
2001
                        // Fetch the alleged previous output along with the
×
2002
                        // pkscript referenced by this input.
×
2003
                        //
×
2004
                        // TODO(roasbeef): when dual funder pass actual
×
2005
                        // height-hint
×
2006
                        //
×
2007
                        // TODO(roasbeef): this fails for neutrino always as it
×
2008
                        // treats the height hint as an exact birthday of the
×
2009
                        // utxo rather than a lower bound
×
2010
                        pkScript, err := txscript.ComputePkScript(
×
2011
                                txin.SignatureScript, txin.Witness,
×
2012
                        )
×
2013
                        if err != nil {
×
2014
                                return fmt.Errorf("cannot create script: %w",
×
2015
                                        err)
×
2016
                        }
×
2017
                        output, err := l.Cfg.ChainIO.GetUtxo(
×
2018
                                &txin.PreviousOutPoint,
×
2019
                                pkScript.Script(), 0, l.quit,
×
2020
                        )
×
2021
                        if output == nil {
×
2022
                                return fmt.Errorf("input to funding tx does "+
×
2023
                                        "not exist: %v", err)
×
2024
                        }
×
2025

2026
                        // Ensure that the witness+sigScript combo is valid.
2027
                        vm, err := txscript.NewEngine(
×
2028
                                output.PkScript, fundingTx, i,
×
2029
                                txscript.StandardVerifyFlags, nil,
×
2030
                                fundingHashCache, output.Value,
×
2031
                                txscript.NewCannedPrevOutputFetcher(
×
2032
                                        output.PkScript, output.Value,
×
2033
                                ),
×
2034
                        )
×
2035
                        if err != nil {
×
2036
                                return fmt.Errorf("cannot create script "+
×
2037
                                        "engine: %s", err)
×
2038
                        }
×
2039
                        if err = vm.Execute(); err != nil {
×
2040
                                return fmt.Errorf("cannot validate "+
×
2041
                                        "transaction: %s", err)
×
2042
                        }
×
2043

2044
                        sigIndex++
×
2045
                }
2046
        }
2047

2048
        return nil
3✔
2049
}
2050

2051
// verifyCommitSig verifies an incoming signature for our version of the
2052
// commitment transaction. For normal channels, this will verify that the ECDSA
2053
// signature is valid. For taproot channels, we'll verify that their partial
2054
// signature is valid, so it can properly be combined with our eventual
2055
// signature when we need to broadcast.
2056
func (l *LightningWallet) verifyCommitSig(res *ChannelReservation,
2057
        commitSig input.Signature, commitTx *wire.MsgTx) error {
3✔
2058

3✔
2059
        localKey := res.ourContribution.MultiSigKey.PubKey
3✔
2060
        remoteKey := res.theirContribution.MultiSigKey.PubKey
3✔
2061
        channelValue := int64(res.partialState.Capacity)
3✔
2062

3✔
2063
        switch {
3✔
2064
        // If this isn't a taproot channel, then we'll construct a segwit v0
2065
        // p2wsh sighash.
2066
        case !res.partialState.ChanType.IsTaproot():
3✔
2067
                hashCache := input.NewTxSigHashesV0Only(commitTx)
3✔
2068
                witnessScript, _, err := input.GenFundingPkScript(
3✔
2069
                        localKey.SerializeCompressed(),
3✔
2070
                        remoteKey.SerializeCompressed(), channelValue,
3✔
2071
                )
3✔
2072
                if err != nil {
3✔
2073
                        return err
×
2074
                }
×
2075

2076
                sigHash, err := txscript.CalcWitnessSigHash(
3✔
2077
                        witnessScript, hashCache, txscript.SigHashAll,
3✔
2078
                        commitTx, 0, channelValue,
3✔
2079
                )
3✔
2080
                if err != nil {
3✔
2081
                        return err
×
2082
                }
×
2083

2084
                // Verify that we've received a valid signature from the remote
2085
                // party for our version of the commitment transaction.
2086
                if !commitSig.Verify(sigHash, remoteKey) {
3✔
2087
                        return fmt.Errorf("counterparty's commitment " +
×
2088
                                "signature is invalid")
×
2089
                }
×
2090

2091
                return nil
3✔
2092

2093
        // Otherwise for taproot channels, we'll compute the segwit v1 sighash,
2094
        // which is slightly different.
2095
        default:
3✔
2096
                // First, check to see if we've generated the musig session
3✔
2097
                // already. If we're the responder in the funding flow, we may
3✔
2098
                // not have generated it already.
3✔
2099
                if res.musigSessions == nil {
6✔
2100
                        _, fundingOutput, err := input.GenTaprootFundingScript(
3✔
2101
                                localKey, remoteKey, channelValue,
3✔
2102
                        )
3✔
2103
                        if err != nil {
3✔
2104
                                return err
×
2105
                        }
×
2106

2107
                        res.musigSessions = genMusigSession(
3✔
2108
                                res.ourContribution, res.theirContribution,
3✔
2109
                                l.Cfg.Signer, fundingOutput,
3✔
2110
                        )
3✔
2111
                }
2112

2113
                // For the musig2 based channels, we'll use the generated local
2114
                // musig2 session to verify the signature.
2115
                localSession := res.musigSessions.LocalSession
3✔
2116

3✔
2117
                // At this point, the commitment signature passed in should
3✔
2118
                // actually be a wrapped musig2 signature, so we'll do a type
3✔
2119
                // asset to the get the signature we actually need.
3✔
2120
                partialSig, ok := commitSig.(*MusigPartialSig)
3✔
2121
                if !ok {
3✔
2122
                        return fmt.Errorf("expected *musig2.PartialSignature, "+
×
2123
                                "got: %T", commitSig)
×
2124
                }
×
2125

2126
                _, err := localSession.VerifyCommitSig(
3✔
2127
                        commitTx, partialSig.ToWireSig(),
3✔
2128
                )
3✔
2129

3✔
2130
                return err
3✔
2131
        }
2132
}
2133

2134
// handleFundingCounterPartySigs is the final step in the channel reservation
2135
// workflow. During this step, we validate *all* the received signatures for
2136
// inputs to the funding transaction. If any of these are invalid, we bail,
2137
// and forcibly cancel this funding request. Additionally, we ensure that the
2138
// signature we received from the counterparty for our version of the commitment
2139
// transaction allows us to spend from the funding output with the addition of
2140
// our signature.
2141
func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigsMsg) {
3✔
2142
        l.limboMtx.RLock()
3✔
2143
        res, ok := l.fundingLimbo[msg.pendingFundingID]
3✔
2144
        l.limboMtx.RUnlock()
3✔
2145
        if !ok {
3✔
2146
                msg.err <- fmt.Errorf("attempted to update non-existent funding state")
×
2147
                return
×
2148
        }
×
2149

2150
        // Grab the mutex on the ChannelReservation to ensure thread-safety
2151
        res.Lock()
3✔
2152
        defer res.Unlock()
3✔
2153

3✔
2154
        // Now we can complete the funding transaction by adding their
3✔
2155
        // signatures to their inputs.
3✔
2156
        res.theirFundingInputScripts = msg.theirFundingInputScripts
3✔
2157
        inputScripts := msg.theirFundingInputScripts
3✔
2158

3✔
2159
        // Only if we have the final funding transaction do we need to verify
3✔
2160
        // the final set of inputs. Otherwise, it may be the case that the
3✔
2161
        // channel was funded via an external wallet.
3✔
2162
        fundingTx := res.fundingTx
3✔
2163
        if res.partialState.ChanType.HasFundingTx() {
6✔
2164
                err := l.verifyFundingInputs(fundingTx, inputScripts)
3✔
2165
                if err != nil {
3✔
2166
                        msg.err <- err
×
2167
                        msg.completeChan <- nil
×
2168
                        return
×
2169
                }
×
2170
        }
2171

2172
        // At this point, we can also record and verify their signature for our
2173
        // commitment transaction.
2174
        res.theirCommitmentSig = msg.theirCommitmentSig
3✔
2175
        commitTx := res.partialState.LocalCommitment.CommitTx
3✔
2176

3✔
2177
        err := l.verifyCommitSig(res, msg.theirCommitmentSig, commitTx)
3✔
2178
        if err != nil {
3✔
2179
                msg.err <- fmt.Errorf("counterparty's commitment signature is "+
×
2180
                        "invalid: %w", err)
×
2181
                msg.completeChan <- nil
×
2182
                return
×
2183
        }
×
2184

2185
        theirCommitSigBytes := msg.theirCommitmentSig.Serialize()
3✔
2186
        res.partialState.LocalCommitment.CommitSig = theirCommitSigBytes
3✔
2187

3✔
2188
        // Funding complete, this entry can be removed from limbo.
3✔
2189
        l.limboMtx.Lock()
3✔
2190
        delete(l.fundingLimbo, res.reservationID)
3✔
2191
        delete(l.reservationIDs, res.pendingChanID)
3✔
2192
        l.limboMtx.Unlock()
3✔
2193

3✔
2194
        l.intentMtx.Lock()
3✔
2195
        delete(l.fundingIntents, res.pendingChanID)
3✔
2196
        l.intentMtx.Unlock()
3✔
2197

3✔
2198
        // As we're about to broadcast the funding transaction, we'll take note
3✔
2199
        // of the current height for record keeping purposes.
3✔
2200
        //
3✔
2201
        // TODO(roasbeef): this info can also be piped into light client's
3✔
2202
        // basic fee estimation?
3✔
2203
        _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock()
3✔
2204
        if err != nil {
3✔
2205
                msg.err <- err
×
2206
                msg.completeChan <- nil
×
2207
                return
×
2208
        }
×
2209

2210
        // As we've completed the funding process, we'll no convert the
2211
        // contribution structs into their underlying channel config objects to
2212
        // he stored within the database.
2213
        res.partialState.LocalChanCfg = res.ourContribution.toChanConfig()
3✔
2214
        res.partialState.RemoteChanCfg = res.theirContribution.toChanConfig()
3✔
2215

3✔
2216
        // We'll also record the finalized funding txn, which will allow us to
3✔
2217
        // rebroadcast on startup in case we fail.
3✔
2218
        res.partialState.FundingTxn = fundingTx
3✔
2219

3✔
2220
        // Set optional upfront shutdown scripts on the channel state so that they
3✔
2221
        // are persisted. These values may be nil.
3✔
2222
        res.partialState.LocalShutdownScript =
3✔
2223
                res.ourContribution.UpfrontShutdown
3✔
2224
        res.partialState.RemoteShutdownScript =
3✔
2225
                res.theirContribution.UpfrontShutdown
3✔
2226

3✔
2227
        res.partialState.RevocationKeyLocator = res.nextRevocationKeyLoc
3✔
2228

3✔
2229
        // Add the complete funding transaction to the DB, in its open bucket
3✔
2230
        // which will be used for the lifetime of this channel.
3✔
2231
        nodeAddr := res.nodeAddr
3✔
2232
        err = res.partialState.SyncPending(nodeAddr, uint32(bestHeight))
3✔
2233
        if err != nil {
3✔
2234
                msg.err <- err
×
2235
                msg.completeChan <- nil
×
2236
                return
×
2237
        }
×
2238

2239
        msg.completeChan <- res.partialState
3✔
2240
        msg.err <- nil
3✔
2241
}
2242

2243
// handleSingleFunderSigs is called once the remote peer who initiated the
2244
// single funder workflow has assembled the funding transaction, and generated
2245
// a signature for our version of the commitment transaction. This method
2246
// progresses the workflow by generating a signature for the remote peer's
2247
// version of the commitment transaction.
2248
func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
3✔
2249
        l.limboMtx.RLock()
3✔
2250
        pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
3✔
2251
        l.limboMtx.RUnlock()
3✔
2252
        if !ok {
3✔
2253
                req.err <- fmt.Errorf("attempted to update non-existent funding state")
×
2254
                req.completeChan <- nil
×
2255
                return
×
2256
        }
×
2257

2258
        // Grab the mutex on the ChannelReservation to ensure thread-safety
2259
        pendingReservation.Lock()
3✔
2260
        defer pendingReservation.Unlock()
3✔
2261

3✔
2262
        chanState := pendingReservation.partialState
3✔
2263
        chanType := pendingReservation.partialState.ChanType
3✔
2264
        chanState.FundingOutpoint = *req.fundingOutpoint
3✔
2265
        fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil)
3✔
2266

3✔
2267
        // Now that we have the funding outpoint, we can generate both versions
3✔
2268
        // of the commitment transaction, and generate a signature for the
3✔
2269
        // remote node's commitment transactions.
3✔
2270
        localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
3✔
2271
        remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
3✔
2272
        var leaseExpiry uint32
3✔
2273
        if pendingReservation.partialState.ChanType.HasLeaseExpiration() {
6✔
2274
                leaseExpiry = pendingReservation.partialState.ThawHeight
3✔
2275
        }
3✔
2276
        ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
3✔
2277
                localBalance, remoteBalance,
3✔
2278
                pendingReservation.ourContribution.ChannelConfig,
3✔
2279
                pendingReservation.theirContribution.ChannelConfig,
3✔
2280
                pendingReservation.ourContribution.FirstCommitmentPoint,
3✔
2281
                pendingReservation.theirContribution.FirstCommitmentPoint,
3✔
2282
                *fundingTxIn, chanType,
3✔
2283
                pendingReservation.partialState.IsInitiator, leaseExpiry,
3✔
2284
        )
3✔
2285
        if err != nil {
3✔
2286
                req.err <- err
×
2287
                req.completeChan <- nil
×
2288
                return
×
2289
        }
×
2290

2291
        // With both commitment transactions constructed, we can now use the
2292
        // generator state obfuscator to encode the current state number within
2293
        // both commitment transactions.
2294
        stateObfuscator := DeriveStateHintObfuscator(
3✔
2295
                pendingReservation.theirContribution.PaymentBasePoint.PubKey,
3✔
2296
                pendingReservation.ourContribution.PaymentBasePoint.PubKey,
3✔
2297
        )
3✔
2298
        err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator)
3✔
2299
        if err != nil {
3✔
2300
                req.err <- err
×
2301
                req.completeChan <- nil
×
2302
                return
×
2303
        }
×
2304

2305
        // Sort both transactions according to the agreed upon canonical
2306
        // ordering. This ensures that both parties sign the same sighash
2307
        // without further synchronization.
2308
        txsort.InPlaceSort(ourCommitTx)
3✔
2309
        txsort.InPlaceSort(theirCommitTx)
3✔
2310
        chanState.LocalCommitment.CommitTx = ourCommitTx
3✔
2311
        chanState.RemoteCommitment.CommitTx = theirCommitTx
3✔
2312

3✔
2313
        walletLog.Debugf("Local commit tx for ChannelPoint(%v): %v",
3✔
2314
                req.fundingOutpoint, spew.Sdump(ourCommitTx))
3✔
2315
        walletLog.Debugf("Remote commit tx for ChannelPoint(%v): %v",
3✔
2316
                req.fundingOutpoint, spew.Sdump(theirCommitTx))
3✔
2317

3✔
2318
        // With both commitment transactions created, we'll now verify their
3✔
2319
        // signature on our commitment.
3✔
2320
        err = l.verifyCommitSig(
3✔
2321
                pendingReservation, req.theirCommitmentSig, ourCommitTx,
3✔
2322
        )
3✔
2323
        if err != nil {
3✔
2324
                req.err <- err
×
2325
                req.completeChan <- nil
×
2326
                return
×
2327
        }
×
2328

2329
        theirCommitSigBytes := req.theirCommitmentSig.Serialize()
3✔
2330
        chanState.LocalCommitment.CommitSig = theirCommitSigBytes
3✔
2331

3✔
2332
        channelValue := int64(pendingReservation.partialState.Capacity)
3✔
2333
        theirKey := pendingReservation.theirContribution.MultiSigKey
3✔
2334
        ourKey := pendingReservation.ourContribution.MultiSigKey
3✔
2335

3✔
2336
        var (
3✔
2337
                fundingWitnessScript []byte
3✔
2338
                fundingTxOut         *wire.TxOut
3✔
2339
        )
3✔
2340
        if chanType.IsTaproot() {
6✔
2341
                fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( //nolint:lll
3✔
2342
                        ourKey.PubKey, theirKey.PubKey, channelValue,
3✔
2343
                )
3✔
2344
        } else {
6✔
2345
                fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( //nolint:lll
3✔
2346
                        ourKey.PubKey.SerializeCompressed(),
3✔
2347
                        theirKey.PubKey.SerializeCompressed(), channelValue,
3✔
2348
                )
3✔
2349
        }
3✔
2350
        if err != nil {
3✔
2351
                req.err <- err
×
2352
                req.completeChan <- nil
×
2353
                return
×
2354
        }
×
2355

2356
        // With their signature for our version of the commitment transactions
2357
        // verified, we can now generate a signature for their version,
2358
        // allowing the funding transaction to be safely broadcast.
2359
        sigTheirCommit, err := l.signCommitTx(
3✔
2360
                pendingReservation, theirCommitTx, fundingTxOut,
3✔
2361
                fundingWitnessScript,
3✔
2362
        )
3✔
2363
        if err != nil {
3✔
2364
                req.err <- err
×
2365
                req.completeChan <- nil
×
2366
                return
×
2367
        }
×
2368

2369
        pendingReservation.ourCommitmentSig = sigTheirCommit
3✔
2370

3✔
2371
        _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock()
3✔
2372
        if err != nil {
3✔
2373
                req.err <- err
×
2374
                req.completeChan <- nil
×
2375
                return
×
2376
        }
×
2377

2378
        // Set optional upfront shutdown scripts on the channel state so that they
2379
        // are persisted. These values may be nil.
2380
        chanState.LocalShutdownScript =
3✔
2381
                pendingReservation.ourContribution.UpfrontShutdown
3✔
2382
        chanState.RemoteShutdownScript =
3✔
2383
                pendingReservation.theirContribution.UpfrontShutdown
3✔
2384

3✔
2385
        // Add the complete funding transaction to the DB, in it's open bucket
3✔
2386
        // which will be used for the lifetime of this channel.
3✔
2387
        chanState.LocalChanCfg = pendingReservation.ourContribution.toChanConfig()
3✔
2388
        chanState.RemoteChanCfg = pendingReservation.theirContribution.toChanConfig()
3✔
2389

3✔
2390
        chanState.RevocationKeyLocator = pendingReservation.nextRevocationKeyLoc
3✔
2391

3✔
2392
        err = chanState.SyncPending(pendingReservation.nodeAddr, uint32(bestHeight))
3✔
2393
        if err != nil {
3✔
2394
                req.err <- err
×
2395
                req.completeChan <- nil
×
2396
                return
×
2397
        }
×
2398

2399
        req.completeChan <- chanState
3✔
2400
        req.err <- nil
3✔
2401

3✔
2402
        l.limboMtx.Lock()
3✔
2403
        delete(l.fundingLimbo, req.pendingFundingID)
3✔
2404
        delete(l.reservationIDs, pendingReservation.pendingChanID)
3✔
2405
        l.limboMtx.Unlock()
3✔
2406

3✔
2407
        l.intentMtx.Lock()
3✔
2408
        delete(l.fundingIntents, pendingReservation.pendingChanID)
3✔
2409
        l.intentMtx.Unlock()
3✔
2410
}
2411

2412
// WithCoinSelectLock will execute the passed function closure in a
2413
// synchronized manner preventing any coin selection operations from proceeding
2414
// while the closure is executing. This can be seen as the ability to execute a
2415
// function closure under an exclusive coin selection lock.
2416
func (l *LightningWallet) WithCoinSelectLock(f func() error) error {
3✔
2417
        l.coinSelectMtx.Lock()
3✔
2418
        defer l.coinSelectMtx.Unlock()
3✔
2419

3✔
2420
        return f()
3✔
2421
}
3✔
2422

2423
// DeriveStateHintObfuscator derives the bytes to be used for obfuscating the
2424
// state hints from the root to be used for a new channel. The obfuscator is
2425
// generated via the following computation:
2426
//
2427
//   - sha256(initiatorKey || responderKey)[26:]
2428
//     -- where both keys are the multi-sig keys of the respective parties
2429
//
2430
// The first 6 bytes of the resulting hash are used as the state hint.
2431
func DeriveStateHintObfuscator(key1, key2 *btcec.PublicKey) [StateHintSize]byte {
3✔
2432
        h := sha256.New()
3✔
2433
        h.Write(key1.SerializeCompressed())
3✔
2434
        h.Write(key2.SerializeCompressed())
3✔
2435

3✔
2436
        sha := h.Sum(nil)
3✔
2437

3✔
2438
        var obfuscator [StateHintSize]byte
3✔
2439
        copy(obfuscator[:], sha[26:])
3✔
2440

3✔
2441
        return obfuscator
3✔
2442
}
3✔
2443

2444
// initStateHints properly sets the obfuscated state hints on both commitment
2445
// transactions using the passed obfuscator.
2446
func initStateHints(commit1, commit2 *wire.MsgTx,
2447
        obfuscator [StateHintSize]byte) error {
3✔
2448

3✔
2449
        if err := SetStateNumHint(commit1, 0, obfuscator); err != nil {
3✔
2450
                return err
×
2451
        }
×
2452
        if err := SetStateNumHint(commit2, 0, obfuscator); err != nil {
3✔
2453
                return err
×
2454
        }
×
2455

2456
        return nil
3✔
2457
}
2458

2459
// ValidateChannel will attempt to fully validate a newly mined channel, given
2460
// its funding transaction and existing channel state. If this method returns
2461
// an error, then the mined channel is invalid, and shouldn't be used.
2462
func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel,
2463
        fundingTx *wire.MsgTx) error {
3✔
2464

3✔
2465
        // First, we'll obtain a fully signed commitment transaction so we can
3✔
2466
        // pass into it on the chanvalidate package for verification.
3✔
2467
        channel, err := NewLightningChannel(l.Cfg.Signer, channelState, nil)
3✔
2468
        if err != nil {
3✔
2469
                return err
×
2470
        }
×
2471

2472
        localKey := channelState.LocalChanCfg.MultiSigKey.PubKey
3✔
2473
        remoteKey := channelState.RemoteChanCfg.MultiSigKey.PubKey
3✔
2474

3✔
2475
        // We'll also need the multi-sig witness script itself so the
3✔
2476
        // chanvalidate package can check it for correctness against the
3✔
2477
        // funding transaction, and also commitment validity.
3✔
2478
        var fundingScript []byte
3✔
2479
        if channelState.ChanType.IsTaproot() {
6✔
2480
                fundingScript, _, err = input.GenTaprootFundingScript(
3✔
2481
                        localKey, remoteKey, int64(channel.Capacity),
3✔
2482
                )
3✔
2483
                if err != nil {
3✔
2484
                        return err
×
2485
                }
×
2486
        } else {
3✔
2487
                witnessScript, err := input.GenMultiSigScript(
3✔
2488
                        localKey.SerializeCompressed(),
3✔
2489
                        remoteKey.SerializeCompressed(),
3✔
2490
                )
3✔
2491
                if err != nil {
3✔
2492
                        return err
×
2493
                }
×
2494
                fundingScript, err = input.WitnessScriptHash(witnessScript)
3✔
2495
                if err != nil {
3✔
2496
                        return err
×
2497
                }
×
2498
        }
2499

2500
        signedCommitTx, err := channel.getSignedCommitTx()
3✔
2501
        if err != nil {
3✔
2502
                return err
×
2503
        }
×
2504
        commitCtx := &chanvalidate.CommitmentContext{
3✔
2505
                Value:               channel.Capacity,
3✔
2506
                FullySignedCommitTx: signedCommitTx,
3✔
2507
        }
3✔
2508

3✔
2509
        // Finally, we'll pass in all the necessary context needed to fully
3✔
2510
        // validate that this channel is indeed what we expect, and can be
3✔
2511
        // used.
3✔
2512
        _, err = chanvalidate.Validate(&chanvalidate.Context{
3✔
2513
                Locator: &chanvalidate.OutPointChanLocator{
3✔
2514
                        ChanPoint: channelState.FundingOutpoint,
3✔
2515
                },
3✔
2516
                MultiSigPkScript: fundingScript,
3✔
2517
                FundingTx:        fundingTx,
3✔
2518
                CommitCtx:        commitCtx,
3✔
2519
        })
3✔
2520
        if err != nil {
3✔
2521
                return err
×
2522
        }
×
2523

2524
        return nil
3✔
2525
}
2526

2527
// CancelRebroadcast cancels the rebroadcast of the given transaction.
2528
func (l *LightningWallet) CancelRebroadcast(txid chainhash.Hash) {
3✔
2529
        // For neutrino, we don't config the rebroadcaster for the wallet as it
3✔
2530
        // manages the rebroadcasting logic in neutrino itself.
3✔
2531
        if l.Cfg.Rebroadcaster != nil {
6✔
2532
                l.Cfg.Rebroadcaster.MarkAsConfirmed(txid)
3✔
2533
        }
3✔
2534
}
2535

2536
// CoinSource is a wrapper around the wallet that implements the
2537
// chanfunding.CoinSource interface.
2538
type CoinSource struct {
2539
        wallet    *LightningWallet
2540
        allowUtxo func(Utxo) bool
2541
}
2542

2543
// NewCoinSource creates a new instance of the CoinSource wrapper struct.
2544
func NewCoinSource(w *LightningWallet, allowUtxo func(Utxo) bool) *CoinSource {
3✔
2545
        return &CoinSource{
3✔
2546
                wallet:    w,
3✔
2547
                allowUtxo: allowUtxo,
3✔
2548
        }
3✔
2549
}
3✔
2550

2551
// ListCoins returns all UTXOs from the source that have between
2552
// minConfs and maxConfs number of confirmations.
2553
func (c *CoinSource) ListCoins(minConfs int32,
2554
        maxConfs int32) ([]wallet.Coin, error) {
3✔
2555

3✔
2556
        utxos, err := c.wallet.ListUnspentWitnessFromDefaultAccount(
3✔
2557
                minConfs, maxConfs,
3✔
2558
        )
3✔
2559
        if err != nil {
3✔
2560
                return nil, err
×
2561
        }
×
2562

2563
        var coins []wallet.Coin
3✔
2564

3✔
2565
        for _, utxo := range utxos {
6✔
2566
                // If there is a filter function supplied all utxos not adhering
3✔
2567
                // to these conditions will be discarded.
3✔
2568
                if c.allowUtxo != nil && !c.allowUtxo(*utxo) {
6✔
2569
                        walletLog.Infof("Cannot use unconfirmed "+
3✔
2570
                                "utxo=%v because it is unstable and could be "+
3✔
2571
                                "replaced", utxo.OutPoint)
3✔
2572

3✔
2573
                        continue
3✔
2574
                }
2575

2576
                coins = append(coins, wallet.Coin{
3✔
2577
                        TxOut: wire.TxOut{
3✔
2578
                                Value:    int64(utxo.Value),
3✔
2579
                                PkScript: utxo.PkScript,
3✔
2580
                        },
3✔
2581
                        OutPoint: utxo.OutPoint,
3✔
2582
                })
3✔
2583
        }
2584

2585
        return coins, nil
3✔
2586
}
2587

2588
// CoinFromOutPoint attempts to locate details pertaining to a coin based on
2589
// its outpoint. If the coin isn't under the control of the backing CoinSource,
2590
// then an error should be returned.
2591
func (c *CoinSource) CoinFromOutPoint(op wire.OutPoint) (*wallet.Coin, error) {
3✔
2592
        inputInfo, err := c.wallet.FetchInputInfo(&op)
3✔
2593
        if err != nil {
3✔
2594
                return nil, err
×
2595
        }
×
2596

2597
        return &wallet.Coin{
3✔
2598
                TxOut: wire.TxOut{
3✔
2599
                        Value:    int64(inputInfo.Value),
3✔
2600
                        PkScript: inputInfo.PkScript,
3✔
2601
                },
3✔
2602
                OutPoint: inputInfo.OutPoint,
3✔
2603
        }, nil
3✔
2604
}
2605

2606
// shimKeyRing is a wrapper struct that's used to provide the proper multi-sig
2607
// key for an initiated external funding flow.
2608
type shimKeyRing struct {
2609
        keychain.KeyRing
2610

2611
        *chanfunding.ShimIntent
2612
}
2613

2614
// DeriveNextKey intercepts the normal DeriveNextKey call to a keychain.KeyRing
2615
// instance, and supplies the multi-sig key specified by the ShimIntent. This
2616
// allows us to transparently insert new keys into the existing funding flow,
2617
// as these keys may not come from the wallet itself.
2618
func (s *shimKeyRing) DeriveNextKey(keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) {
3✔
2619
        if keyFam != keychain.KeyFamilyMultiSig {
6✔
2620
                return s.KeyRing.DeriveNextKey(keyFam)
3✔
2621
        }
3✔
2622

2623
        fundingKeys, err := s.ShimIntent.MultiSigKeys()
3✔
2624
        if err != nil {
3✔
2625
                return keychain.KeyDescriptor{}, err
×
2626
        }
×
2627

2628
        return *fundingKeys.LocalKey, nil
3✔
2629
}
2630

2631
// ValidateUpfrontShutdown checks whether the provided upfront_shutdown_script
2632
// is of a valid type that we accept.
2633
func ValidateUpfrontShutdown(shutdown lnwire.DeliveryAddress,
2634
        params *chaincfg.Params) bool {
3✔
2635

3✔
2636
        // We don't need to worry about a large UpfrontShutdownScript since it
3✔
2637
        // was already checked in lnwire when decoding from the wire.
3✔
2638
        scriptClass, _, _, _ := txscript.ExtractPkScriptAddrs(shutdown, params)
3✔
2639

3✔
2640
        switch {
3✔
2641
        case scriptClass == txscript.WitnessV0PubKeyHashTy,
2642
                scriptClass == txscript.WitnessV0ScriptHashTy,
2643
                scriptClass == txscript.WitnessV1TaprootTy:
3✔
2644

3✔
2645
                // The above three types are permitted according to BOLT#02 and
3✔
2646
                // BOLT#05.  Everything else is disallowed.
3✔
2647
                return true
3✔
2648

2649
        // In this case, we don't know about the actual script template, but it
2650
        // might be a witness program with versions 2-16. So we'll check that
2651
        // now
2652
        case txscript.IsWitnessProgram(shutdown):
×
2653
                version, _, err := txscript.ExtractWitnessProgramInfo(shutdown)
×
2654
                if err != nil {
×
2655
                        walletLog.Warnf("unable to extract witness program "+
×
2656
                                "version (script=%x): %v", shutdown, err)
×
2657
                        return false
×
2658
                }
×
2659

2660
                return version >= 1 && version <= 16
×
2661

2662
        default:
×
2663
                return false
×
2664
        }
2665
}
2666

2667
// WalletPrevOutputFetcher is a txscript.PrevOutputFetcher that can fetch
2668
// outputs from a given wallet controller.
2669
type WalletPrevOutputFetcher struct {
2670
        wc WalletController
2671
}
2672

2673
// A compile time assertion that WalletPrevOutputFetcher implements the
2674
// txscript.PrevOutputFetcher interface.
2675
var _ txscript.PrevOutputFetcher = (*WalletPrevOutputFetcher)(nil)
2676

2677
// NewWalletPrevOutputFetcher creates a new WalletPrevOutputFetcher that fetches
2678
// previous outputs from the given wallet controller.
2679
func NewWalletPrevOutputFetcher(wc WalletController) *WalletPrevOutputFetcher {
3✔
2680
        return &WalletPrevOutputFetcher{
3✔
2681
                wc: wc,
3✔
2682
        }
3✔
2683
}
3✔
2684

2685
// FetchPrevOutput attempts to fetch the previous output referenced by the
2686
// passed outpoint. A nil value will be returned if the passed outpoint doesn't
2687
// exist.
2688
func (w *WalletPrevOutputFetcher) FetchPrevOutput(op wire.OutPoint) *wire.TxOut {
3✔
2689
        utxo, err := w.wc.FetchInputInfo(&op)
3✔
2690
        if err != nil {
3✔
2691
                return nil
×
2692
        }
×
2693

2694
        return &wire.TxOut{
3✔
2695
                Value:    int64(utxo.Value),
3✔
2696
                PkScript: utxo.PkScript,
3✔
2697
        }
3✔
2698
}
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