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

lightningnetwork / lnd / 12986279612

27 Jan 2025 09:51AM UTC coverage: 57.652% (-1.1%) from 58.788%
12986279612

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

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

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

19578 existing lines in 256 files now uncovered.

103448 of 179434 relevant lines covered (57.65%)

24884.58 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