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

lightningnetwork / lnd / 13536249039

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

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

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

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

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

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 hits per line

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

66.67
/lnwallet/chanfunding/psbt_assembler.go
1
package chanfunding
2

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

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/btcutil/psbt"
12
        "github.com/btcsuite/btcd/chaincfg"
13
        "github.com/btcsuite/btcd/chaincfg/chainhash"
14
        "github.com/btcsuite/btcd/txscript"
15
        "github.com/btcsuite/btcd/wire"
16
        "github.com/lightningnetwork/lnd/fn/v2"
17
        "github.com/lightningnetwork/lnd/input"
18
        "github.com/lightningnetwork/lnd/keychain"
19
)
20

21
// PsbtState is a type for the state of the PSBT intent state machine.
22
type PsbtState uint8
23

24
const (
25
        // PsbtShimRegistered denotes a channel funding process has started with
26
        // a PSBT shim attached. This is the default state for a PsbtIntent. We
27
        // don't use iota here because the values have to be in sync with the
28
        // RPC constants.
29
        PsbtShimRegistered PsbtState = 1
30

31
        // PsbtOutputKnown denotes that the local and remote peer have
32
        // negotiated the multisig keys to be used as the channel funding output
33
        // and therefore the PSBT funding process can now start.
34
        PsbtOutputKnown PsbtState = 2
35

36
        // PsbtVerified denotes that a potential PSBT has been presented to the
37
        // intent and passed all checks. The verified PSBT can be given to a/the
38
        // signer(s).
39
        PsbtVerified PsbtState = 3
40

41
        // PsbtFinalized denotes that a fully signed PSBT has been given to the
42
        // intent that looks identical to the previously verified transaction
43
        // but has all witness data added and is therefore completely signed.
44
        PsbtFinalized PsbtState = 4
45

46
        // PsbtFundingTxCompiled denotes that the PSBT processed by this intent
47
        // has been successfully converted into a protocol transaction. It is
48
        // not yet completely certain that the resulting transaction will be
49
        // published because the commitment transactions between the channel
50
        // peers first need to be counter signed. But the job of the intent is
51
        // hereby completed.
52
        PsbtFundingTxCompiled PsbtState = 5
53

54
        // PsbtInitiatorCanceled denotes that the user has canceled the intent.
55
        PsbtInitiatorCanceled PsbtState = 6
56

57
        // PsbtResponderCanceled denotes that the remote peer has canceled the
58
        // funding, likely due to a timeout.
59
        PsbtResponderCanceled PsbtState = 7
60
)
61

62
// String returns a string representation of the PsbtState.
63
func (s PsbtState) String() string {
4✔
64
        switch s {
4✔
UNCOV
65
        case PsbtShimRegistered:
×
UNCOV
66
                return "shim_registered"
×
67

68
        case PsbtOutputKnown:
1✔
69
                return "output_known"
1✔
70

71
        case PsbtVerified:
1✔
72
                return "verified"
1✔
73

74
        case PsbtFinalized:
×
75
                return "finalized"
×
76

77
        case PsbtFundingTxCompiled:
×
78
                return "funding_tx_compiled"
×
79

80
        case PsbtInitiatorCanceled:
2✔
81
                return "user_canceled"
2✔
82

UNCOV
83
        case PsbtResponderCanceled:
×
UNCOV
84
                return "remote_canceled"
×
85

86
        default:
×
87
                return fmt.Sprintf("<unknown(%d)>", s)
×
88
        }
89
}
90

91
var (
92
        // ErrRemoteCanceled is the error that is returned to the user if the
93
        // funding flow was canceled by the remote peer.
94
        ErrRemoteCanceled = errors.New("remote canceled funding, possibly " +
95
                "timed out")
96

97
        // ErrUserCanceled is the error that is returned through the PsbtReady
98
        // channel if the user canceled the funding flow.
99
        ErrUserCanceled = errors.New("user canceled funding")
100
)
101

102
// PsbtIntent is an intent created by the PsbtAssembler which represents a
103
// funding output to be created by a PSBT. This might be used when a hardware
104
// wallet, or a channel factory is the entity crafting the funding transaction,
105
// and not lnd.
106
type PsbtIntent struct {
107
        // ShimIntent is the wrapped basic intent that contains common fields
108
        // we also use in the PSBT funding case.
109
        ShimIntent
110

111
        // State is the current state the intent state machine is in.
112
        State PsbtState
113

114
        // BasePsbt is the user-supplied base PSBT the channel output should be
115
        // added to. If this is nil we will create a new, empty PSBT as the base
116
        // for the funding transaction.
117
        BasePsbt *psbt.Packet
118

119
        // PendingPsbt is the parsed version of the current PSBT. This can be
120
        // in two stages: If the user has not yet provided any PSBT, this is
121
        // nil. Once the user sends us an unsigned funded PSBT, we verify that
122
        // we have a valid transaction that sends to the channel output PK
123
        // script and has an input large enough to pay for it. We keep this
124
        // verified but not yet signed version around until the fully signed
125
        // transaction is submitted by the user. At that point we make sure the
126
        // inputs and outputs haven't changed to what was previously verified.
127
        // Only witness data should be added after the verification process.
128
        PendingPsbt *psbt.Packet
129

130
        // FinalTX is the final, signed and ready to be published wire format
131
        // transaction. This is only set after the PsbtFinalize step was
132
        // completed successfully.
133
        FinalTX *wire.MsgTx
134

135
        // PsbtReady is an error channel the funding manager will listen for
136
        // a signal about the PSBT being ready to continue the funding flow. In
137
        // the normal, happy flow, this channel is only ever closed. If a
138
        // non-nil error is sent through the channel, the funding flow will be
139
        // canceled.
140
        //
141
        // NOTE: This channel must always be buffered.
142
        PsbtReady chan error
143

144
        // shouldPublish specifies if the intent assumes its assembler should
145
        // publish the transaction once the channel funding has completed. If
146
        // this is set to false then the finalize step can be skipped.
147
        shouldPublish bool
148

149
        // signalPsbtReady is a Once guard to make sure the PsbtReady channel is
150
        // only closed exactly once.
151
        signalPsbtReady sync.Once
152

153
        // netParams are the network parameters used to encode the P2WSH funding
154
        // address.
155
        netParams *chaincfg.Params
156
}
157

158
// BindKeys sets both the remote and local node's keys that will be used for the
159
// channel funding multisig output.
160
func (i *PsbtIntent) BindKeys(localKey *keychain.KeyDescriptor,
161
        remoteKey *btcec.PublicKey) {
4✔
162

4✔
163
        i.localKey = localKey
4✔
164
        i.remoteKey = remoteKey
4✔
165
        i.State = PsbtOutputKnown
4✔
166
}
4✔
167

168
// BindTapscriptRoot takes an optional tapscript root and binds it to the
169
// underlying funding intent. This only applies to musig2 channels, and will be
170
// used to make the musig2 funding output.
UNCOV
171
func (i *PsbtIntent) BindTapscriptRoot(root fn.Option[chainhash.Hash]) {
×
UNCOV
172
        i.tapscriptRoot = root
×
UNCOV
173
}
×
174

175
// FundingParams returns the parameters that are necessary to start funding the
176
// channel output this intent was created for. It returns the P2WSH funding
177
// address, the exact funding amount and a PSBT packet that contains exactly one
178
// output that encodes the previous two parameters.
179
func (i *PsbtIntent) FundingParams() (btcutil.Address, int64, *psbt.Packet,
180
        error) {
21✔
181

21✔
182
        if i.State != PsbtOutputKnown {
21✔
183
                return nil, 0, nil, fmt.Errorf("invalid state, got %v "+
×
184
                        "expected %v", i.State, PsbtOutputKnown)
×
185
        }
×
186

187
        // The funding output needs to be known already at this point, which
188
        // means we need to have the local and remote multisig keys bound
189
        // already.
190
        _, out, err := i.FundingOutput()
21✔
191
        if err != nil {
21✔
192
                return nil, 0, nil, fmt.Errorf("unable to create funding "+
×
193
                        "output: %v", err)
×
194
        }
×
195

196
        script, err := txscript.ParsePkScript(out.PkScript)
21✔
197
        if err != nil {
21✔
198
                return nil, 0, nil, fmt.Errorf("unable to parse funding "+
×
199
                        "output script: %w", err)
×
200
        }
×
201

202
        // Encode the address in the human-readable bech32 format.
203
        addr, err := script.Address(i.netParams)
21✔
204
        if err != nil {
21✔
205
                return nil, 0, nil, fmt.Errorf("unable to encode address: %w",
×
206
                        err)
×
207
        }
×
208

209
        // We'll also encode the address/amount in a machine-readable raw PSBT
210
        // format. If the user supplied a base PSBT, we'll add the output to
211
        // that one, otherwise we'll create a new one.
212
        packet := i.BasePsbt
21✔
213
        if packet == nil {
41✔
214
                packet, err = psbt.New(nil, nil, 2, 0, nil)
20✔
215
                if err != nil {
20✔
216
                        return nil, 0, nil, fmt.Errorf("unable to create "+
×
217
                                "PSBT: %w", err)
×
218
                }
×
219
        }
220
        packet.UnsignedTx.TxOut = append(packet.UnsignedTx.TxOut, out)
21✔
221

21✔
222
        var pOut psbt.POutput
21✔
223

21✔
224
        // If this is a MuSig2 channel, we also need to communicate the internal
21✔
225
        // key to the caller. Otherwise, they cannot verify the construction of
21✔
226
        // the P2TR output script.
21✔
227
        pOut.TaprootInternalKey = fn.MapOptionZ(
21✔
228
                i.TaprootInternalKey(), schnorr.SerializePubKey,
21✔
229
        )
21✔
230

21✔
231
        packet.Outputs = append(packet.Outputs, pOut)
21✔
232

21✔
233
        return addr, out.Value, packet, nil
21✔
234
}
235

236
// Verify makes sure the PSBT that is given to the intent has an output that
237
// sends to the channel funding multisig address with the correct amount. A
238
// simple check that at least a single input has been specified is performed.
239
func (i *PsbtIntent) Verify(packet *psbt.Packet, skipFinalize bool) error {
20✔
240
        if packet == nil {
21✔
241
                return fmt.Errorf("PSBT is nil")
1✔
242
        }
1✔
243
        if i.State != PsbtOutputKnown {
20✔
244
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
1✔
245
                        PsbtOutputKnown)
1✔
246
        }
1✔
247

248
        // Try to locate the channel funding multisig output.
249
        _, expectedOutput, err := i.FundingOutput()
18✔
250
        if err != nil {
18✔
251
                return fmt.Errorf("funding output cannot be created: %w", err)
×
252
        }
×
253
        outputFound := false
18✔
254
        outputSum := int64(0)
18✔
255
        for _, out := range packet.UnsignedTx.TxOut {
36✔
256
                outputSum += out.Value
18✔
257
                if psbt.TxOutsEqual(out, expectedOutput) {
34✔
258
                        outputFound = true
16✔
259
                }
16✔
260
        }
261
        if !outputFound {
20✔
262
                return fmt.Errorf("funding output not found in PSBT")
2✔
263
        }
2✔
264

265
        // At least one input needs to be specified and it must be large enough
266
        // to pay for all outputs. We don't want to dive into fee estimation
267
        // here so we just assume that if the input amount exceeds the output
268
        // amount, the chosen fee is sufficient.
269
        if len(packet.UnsignedTx.TxIn) == 0 {
17✔
270
                return fmt.Errorf("PSBT has no inputs")
1✔
271
        }
1✔
272
        sum, err := psbt.SumUtxoInputValues(packet)
15✔
273
        if err != nil {
15✔
274
                return fmt.Errorf("error determining input sum: %w", err)
×
275
        }
×
276
        if sum <= outputSum {
16✔
277
                return fmt.Errorf("input amount sum must be larger than " +
1✔
278
                        "output amount sum")
1✔
279
        }
1✔
280

281
        // To avoid possible malleability, all inputs to a funding transaction
282
        // must be SegWit spends.
283
        err = verifyAllInputsSegWit(packet.UnsignedTx.TxIn, packet.Inputs)
14✔
284
        if err != nil {
15✔
285
                return fmt.Errorf("cannot use TX for channel funding, "+
1✔
286
                        "not all inputs are SegWit spends, risk of "+
1✔
287
                        "malleability: %v", err)
1✔
288
        }
1✔
289

290
        // In case we aren't going to publish any transaction, we now have
291
        // everything we need and can skip the Finalize step.
292
        i.PendingPsbt = packet
13✔
293
        if !i.shouldPublish && skipFinalize {
14✔
294
                i.FinalTX = packet.UnsignedTx
1✔
295
                i.State = PsbtFinalized
1✔
296

1✔
297
                // Signal the funding manager that it can now continue with its
1✔
298
                // funding flow as the PSBT is now complete .
1✔
299
                i.signalPsbtReady.Do(func() {
2✔
300
                        close(i.PsbtReady)
1✔
301
                })
1✔
302

303
                return nil
1✔
304
        }
305

306
        i.State = PsbtVerified
12✔
307
        return nil
12✔
308
}
309

310
// Finalize makes sure the final PSBT that is given to the intent is fully valid
311
// and signed but still contains the same UTXOs and outputs as the pending
312
// transaction we previously verified. If everything checks out, the funding
313
// manager is informed that the channel can now be opened and the funding
314
// transaction be broadcast.
315
func (i *PsbtIntent) Finalize(packet *psbt.Packet) error {
9✔
316
        if packet == nil {
10✔
317
                return fmt.Errorf("PSBT is nil")
1✔
318
        }
1✔
319
        if i.State != PsbtVerified {
9✔
320
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
1✔
321
                        PsbtVerified)
1✔
322
        }
1✔
323

324
        // Make sure the PSBT itself thinks it's finalized and ready to be
325
        // broadcast.
326
        err := psbt.MaybeFinalizeAll(packet)
7✔
327
        if err != nil {
7✔
328
                return fmt.Errorf("error finalizing PSBT: %w", err)
×
329
        }
×
330
        rawTx, err := psbt.Extract(packet)
7✔
331
        if err != nil {
7✔
332
                return fmt.Errorf("unable to extract funding TX: %w", err)
×
333
        }
×
334

335
        return i.FinalizeRawTX(rawTx)
7✔
336
}
337

338
// FinalizeRawTX makes sure the final raw transaction that is given to the
339
// intent is fully valid and signed but still contains the same UTXOs and
340
// outputs as the pending transaction we previously verified. If everything
341
// checks out, the funding manager is informed that the channel can now be
342
// opened and the funding transaction be broadcast.
343
func (i *PsbtIntent) FinalizeRawTX(rawTx *wire.MsgTx) error {
9✔
344
        if rawTx == nil {
10✔
345
                return fmt.Errorf("raw transaction is nil")
1✔
346
        }
1✔
347
        if i.State != PsbtVerified {
8✔
348
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
349
                        PsbtVerified)
×
350
        }
×
351

352
        // Do a basic check that this is still the same TX that we verified in
353
        // the previous step. This is to protect the user from unwanted
354
        // modifications. We only check the outputs and previous outpoints of
355
        // the inputs of the wire transaction because the fields in the PSBT
356
        // part are allowed to change.
357
        if i.PendingPsbt == nil {
9✔
358
                return fmt.Errorf("PSBT was not verified first")
1✔
359
        }
1✔
360
        err := psbt.VerifyOutputsEqual(
7✔
361
                rawTx.TxOut, i.PendingPsbt.UnsignedTx.TxOut,
7✔
362
        )
7✔
363
        if err != nil {
9✔
364
                return fmt.Errorf("outputs differ from verified PSBT: %w", err)
2✔
365
        }
2✔
366
        err = psbt.VerifyInputPrevOutpointsEqual(
5✔
367
                rawTx.TxIn, i.PendingPsbt.UnsignedTx.TxIn,
5✔
368
        )
5✔
369
        if err != nil {
7✔
370
                return fmt.Errorf("inputs differ from verified PSBT: %w", err)
2✔
371
        }
2✔
372

373
        // We also check that we have a signed TX. This is only necessary if the
374
        // FinalizeRawTX is called directly with a wire format TX instead of
375
        // extracting the TX from a PSBT.
376
        err = verifyInputsSigned(rawTx.TxIn)
3✔
377
        if err != nil {
4✔
378
                return fmt.Errorf("inputs not signed: %w", err)
1✔
379
        }
1✔
380

381
        // As far as we can tell, this TX is ok to be used as a funding
382
        // transaction.
383
        i.State = PsbtFinalized
2✔
384
        i.FinalTX = rawTx
2✔
385

2✔
386
        // Signal the funding manager that it can now finally continue with its
2✔
387
        // funding flow as the PSBT is now ready to be converted into a real
2✔
388
        // transaction and be published.
2✔
389
        i.signalPsbtReady.Do(func() {
4✔
390
                close(i.PsbtReady)
2✔
391
        })
2✔
392
        return nil
2✔
393
}
394

395
// CompileFundingTx finalizes the previously verified PSBT and returns the
396
// extracted binary serialized transaction from it. It also prepares the channel
397
// point for which this funding intent was initiated for.
398
func (i *PsbtIntent) CompileFundingTx() (*wire.MsgTx, error) {
1✔
399
        if i.State != PsbtFinalized {
1✔
400
                return nil, fmt.Errorf("invalid state. got %v expected %v",
×
401
                        i.State, PsbtFinalized)
×
402
        }
×
403

404
        // Identify our funding outpoint now that we know everything's ready.
405
        _, txOut, err := i.FundingOutput()
1✔
406
        if err != nil {
1✔
407
                return nil, fmt.Errorf("cannot get funding output: %w", err)
×
408
        }
×
409
        ok, idx := input.FindScriptOutputIndex(i.FinalTX, txOut.PkScript)
1✔
410
        if !ok {
1✔
411
                return nil, fmt.Errorf("funding output not found in PSBT")
×
412
        }
×
413
        i.chanPoint = &wire.OutPoint{
1✔
414
                Hash:  i.FinalTX.TxHash(),
1✔
415
                Index: idx,
1✔
416
        }
1✔
417
        i.State = PsbtFundingTxCompiled
1✔
418

1✔
419
        return i.FinalTX, nil
1✔
420
}
421

422
// RemoteCanceled informs the listener of the PSBT ready channel that the
423
// funding has been canceled by the remote peer and that we can no longer
424
// continue with it.
UNCOV
425
func (i *PsbtIntent) RemoteCanceled() {
×
UNCOV
426
        log.Debugf("PSBT funding intent canceled by remote, state=%v", i.State)
×
UNCOV
427
        i.signalPsbtReady.Do(func() {
×
UNCOV
428
                i.PsbtReady <- ErrRemoteCanceled
×
UNCOV
429
                i.State = PsbtResponderCanceled
×
UNCOV
430
        })
×
UNCOV
431
        i.ShimIntent.Cancel()
×
432
}
433

434
// Cancel allows the caller to cancel a funding Intent at any time. This will
435
// return make sure the channel funding flow with the remote peer is failed and
436
// any reservations are canceled.
437
//
438
// NOTE: Part of the chanfunding.Intent interface.
UNCOV
439
func (i *PsbtIntent) Cancel() {
×
UNCOV
440
        log.Debugf("PSBT funding intent canceled, state=%v", i.State)
×
UNCOV
441
        i.signalPsbtReady.Do(func() {
×
UNCOV
442
                i.PsbtReady <- ErrUserCanceled
×
UNCOV
443
                i.State = PsbtInitiatorCanceled
×
UNCOV
444
        })
×
UNCOV
445
        i.ShimIntent.Cancel()
×
446
}
447

448
// Inputs returns all inputs to the final funding transaction that we know
449
// about. These are only known after the PSBT has been verified.
UNCOV
450
func (i *PsbtIntent) Inputs() []wire.OutPoint {
×
UNCOV
451
        var inputs []wire.OutPoint
×
UNCOV
452

×
UNCOV
453
        switch i.State {
×
454
        // We return the inputs to the pending psbt.
UNCOV
455
        case PsbtVerified:
×
UNCOV
456
                for _, in := range i.PendingPsbt.UnsignedTx.TxIn {
×
UNCOV
457
                        inputs = append(inputs, in.PreviousOutPoint)
×
UNCOV
458
                }
×
459

460
        // We return the inputs to the final funding tx.
UNCOV
461
        case PsbtFinalized, PsbtFundingTxCompiled:
×
UNCOV
462
                for _, in := range i.FinalTX.TxIn {
×
UNCOV
463
                        inputs = append(inputs, in.PreviousOutPoint)
×
UNCOV
464
                }
×
465

466
        // In all other states we cannot know the inputs to the funding tx, and
467
        // return an empty list.
468
        default:
×
469
        }
470

UNCOV
471
        return inputs
×
472
}
473

474
// Outputs returns all outputs of the final funding transaction that we
475
// know about. These are only known after the PSBT has been verified.
UNCOV
476
func (i *PsbtIntent) Outputs() []*wire.TxOut {
×
UNCOV
477
        switch i.State {
×
478
        // We return the outputs of the pending psbt.
UNCOV
479
        case PsbtVerified:
×
UNCOV
480
                return i.PendingPsbt.UnsignedTx.TxOut
×
481

482
        // We return the outputs of the final funding tx.
UNCOV
483
        case PsbtFinalized, PsbtFundingTxCompiled:
×
UNCOV
484
                return i.FinalTX.TxOut
×
485

486
        // In all other states we cannot know the final outputs, and return an
487
        // empty list.
488
        default:
×
489
                return nil
×
490
        }
491
}
492

493
// ShouldPublishFundingTX returns true if the intent assumes that its assembler
494
// should publish the funding TX once the funding negotiation is complete.
UNCOV
495
func (i *PsbtIntent) ShouldPublishFundingTX() bool {
×
UNCOV
496
        return i.shouldPublish
×
UNCOV
497
}
×
498

499
// PsbtAssembler is a type of chanfunding.Assembler wherein the funding
500
// transaction is constructed outside of lnd by using partially signed bitcoin
501
// transactions (PSBT).
502
type PsbtAssembler struct {
503
        // fundingAmt is the total amount of coins in the funding output.
504
        fundingAmt btcutil.Amount
505

506
        // basePsbt is the user-supplied base PSBT the channel output should be
507
        // added to.
508
        basePsbt *psbt.Packet
509

510
        // netParams are the network parameters used to encode the P2WSH funding
511
        // address.
512
        netParams *chaincfg.Params
513

514
        // shouldPublish specifies if the assembler should publish the
515
        // transaction once the channel funding has completed.
516
        shouldPublish bool
517
}
518

519
// NewPsbtAssembler creates a new CannedAssembler from the material required
520
// to construct a funding output and channel point. An optional base PSBT can
521
// be supplied which will be used to add the channel output to instead of
522
// creating a new one.
523
func NewPsbtAssembler(fundingAmt btcutil.Amount, basePsbt *psbt.Packet,
524
        netParams *chaincfg.Params, shouldPublish bool) *PsbtAssembler {
12✔
525

12✔
526
        return &PsbtAssembler{
12✔
527
                fundingAmt:    fundingAmt,
12✔
528
                basePsbt:      basePsbt,
12✔
529
                netParams:     netParams,
12✔
530
                shouldPublish: shouldPublish,
12✔
531
        }
12✔
532
}
12✔
533

534
// ProvisionChannel creates a new ShimIntent given the passed funding Request.
535
// The returned intent is immediately able to provide the channel point and
536
// funding output as they've already been created outside lnd.
537
//
538
// NOTE: This method satisfies the chanfunding.Assembler interface.
539
func (p *PsbtAssembler) ProvisionChannel(req *Request) (Intent, error) {
4✔
540
        // We'll exit out if SubtractFees is set as the funding transaction will
4✔
541
        // be assembled externally, so we don't influence coin selection.
4✔
542
        if req.SubtractFees {
4✔
543
                return nil, fmt.Errorf("SubtractFees not supported for PSBT")
×
544
        }
×
545

546
        // We'll exit out if FundUpToMaxAmt or MinFundAmt is set as the funding
547
        // transaction will be assembled externally, so we don't influence coin
548
        // selection.
549
        if req.FundUpToMaxAmt != 0 || req.MinFundAmt != 0 {
4✔
550
                return nil, fmt.Errorf("FundUpToMaxAmt and MinFundAmt not " +
×
551
                        "supported for PSBT")
×
552
        }
×
553

554
        intent := &PsbtIntent{
4✔
555
                ShimIntent: ShimIntent{
4✔
556
                        localFundingAmt: p.fundingAmt,
4✔
557
                        musig2:          req.Musig2,
4✔
558
                        tapscriptRoot:   req.TapscriptRoot,
4✔
559
                },
4✔
560
                State:         PsbtShimRegistered,
4✔
561
                BasePsbt:      p.basePsbt,
4✔
562
                PsbtReady:     make(chan error, 1),
4✔
563
                shouldPublish: p.shouldPublish,
4✔
564
                netParams:     p.netParams,
4✔
565
        }
4✔
566

4✔
567
        // A simple sanity check to ensure the provisioned request matches the
4✔
568
        // re-made shim intent.
4✔
569
        if req.LocalAmt+req.RemoteAmt != p.fundingAmt {
4✔
570
                return nil, fmt.Errorf("intent doesn't match PSBT "+
×
571
                        "assembler: local_amt=%v, remote_amt=%v, funding_amt=%v",
×
572
                        req.LocalAmt, req.RemoteAmt, p.fundingAmt)
×
573
        }
×
574

575
        return intent, nil
4✔
576
}
577

578
// ShouldPublishFundingTx is a method of the assembler that signals if the
579
// funding transaction should be published after the channel negotiations are
580
// completed with the remote peer.
581
//
582
// NOTE: This method is a part of the ConditionalPublishAssembler interface.
UNCOV
583
func (p *PsbtAssembler) ShouldPublishFundingTx() bool {
×
UNCOV
584
        return p.shouldPublish
×
UNCOV
585
}
×
586

587
// A compile-time assertion to ensure PsbtAssembler meets the
588
// ConditionalPublishAssembler interface.
589
var _ ConditionalPublishAssembler = (*PsbtAssembler)(nil)
590

591
// verifyInputsSigned verifies that the given list of inputs is non-empty and
592
// that all the inputs either contain a script signature or a witness stack.
593
func verifyInputsSigned(ins []*wire.TxIn) error {
3✔
594
        if len(ins) == 0 {
3✔
595
                return fmt.Errorf("no inputs in transaction")
×
596
        }
×
597
        for idx, in := range ins {
6✔
598
                if len(in.SignatureScript) == 0 && len(in.Witness) == 0 {
4✔
599
                        return fmt.Errorf("input %d has no signature data "+
1✔
600
                                "attached", idx)
1✔
601
                }
1✔
602
        }
603
        return nil
2✔
604
}
605

606
// verifyAllInputsSegWit makes sure all inputs to a transaction are SegWit
607
// spends. This is a bit tricky because the PSBT spec doesn't require the
608
// WitnessUtxo field to be set. Therefore if only a NonWitnessUtxo is given, we
609
// need to look at it and make sure it's either a witness pkScript or a nested
610
// SegWit spend.
611
func verifyAllInputsSegWit(txIns []*wire.TxIn, ins []psbt.PInput) error {
19✔
612
        for idx, in := range ins {
41✔
613
                switch {
22✔
614
                // The optimal case is that the witness UTXO is set explicitly.
615
                case in.WitnessUtxo != nil:
17✔
616

617
                // Only the non witness UTXO field is set, we need to inspect it
618
                // to make sure it's not P2PKH or bare P2SH.
619
                case in.NonWitnessUtxo != nil:
5✔
620
                        utxo := in.NonWitnessUtxo
5✔
621
                        txIn := txIns[idx]
5✔
622
                        txOut := utxo.TxOut[txIn.PreviousOutPoint.Index]
5✔
623

5✔
624
                        if !txscript.IsWitnessProgram(txOut.PkScript) &&
5✔
625
                                !txscript.IsWitnessProgram(in.RedeemScript) {
8✔
626

3✔
627
                                return fmt.Errorf("input %d is non-SegWit "+
3✔
628
                                        "spend or missing redeem script", idx)
3✔
629
                        }
3✔
630

631
                // This should've already been caught by a previous check but we
632
                // keep it in for completeness' sake.
633
                default:
×
634
                        return fmt.Errorf("input %d has no UTXO information",
×
635
                                idx)
×
636
                }
637
        }
638

639
        return nil
16✔
640
}
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