• 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

63.92
/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/btcutil"
10
        "github.com/btcsuite/btcd/btcutil/psbt"
11
        "github.com/btcsuite/btcd/chaincfg"
12
        "github.com/btcsuite/btcd/txscript"
13
        "github.com/btcsuite/btcd/wire"
14
        "github.com/lightningnetwork/lnd/input"
15
        "github.com/lightningnetwork/lnd/keychain"
16
)
17

18
// PsbtState is a type for the state of the PSBT intent state machine.
19
type PsbtState uint8
20

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

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

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

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

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

51
        // PsbtInitiatorCanceled denotes that the user has canceled the intent.
52
        PsbtInitiatorCanceled PsbtState = 6
53

54
        // PsbtResponderCanceled denotes that the remote peer has canceled the
55
        // funding, likely due to a timeout.
56
        PsbtResponderCanceled PsbtState = 7
57
)
58

59
// String returns a string representation of the PsbtState.
60
func (s PsbtState) String() string {
3✔
61
        switch s {
3✔
62
        case PsbtShimRegistered:
3✔
63
                return "shim_registered"
3✔
64

65
        case PsbtOutputKnown:
3✔
66
                return "output_known"
3✔
67

68
        case PsbtVerified:
×
69
                return "verified"
×
70

71
        case PsbtFinalized:
×
72
                return "finalized"
×
73

74
        case PsbtFundingTxCompiled:
×
75
                return "funding_tx_compiled"
×
76

77
        case PsbtInitiatorCanceled:
×
78
                return "user_canceled"
×
79

80
        case PsbtResponderCanceled:
3✔
81
                return "remote_canceled"
3✔
82

83
        default:
×
84
                return fmt.Sprintf("<unknown(%d)>", s)
×
85
        }
86
}
87

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

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

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

108
        // State is the current state the intent state machine is in.
109
        State PsbtState
110

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

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

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

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

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

146
        // signalPsbtReady is a Once guard to make sure the PsbtReady channel is
147
        // only closed exactly once.
148
        signalPsbtReady sync.Once
149

150
        // netParams are the network parameters used to encode the P2WSH funding
151
        // address.
152
        netParams *chaincfg.Params
153
}
154

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

3✔
160
        i.localKey = localKey
3✔
161
        i.remoteKey = remoteKey
3✔
162
        i.State = PsbtOutputKnown
3✔
163
}
3✔
164

165
// FundingParams returns the parameters that are necessary to start funding the
166
// channel output this intent was created for. It returns the P2WSH funding
167
// address, the exact funding amount and a PSBT packet that contains exactly one
168
// output that encodes the previous two parameters.
169
func (i *PsbtIntent) FundingParams() (btcutil.Address, int64, *psbt.Packet,
170
        error) {
3✔
171

3✔
172
        if i.State != PsbtOutputKnown {
3✔
173
                return nil, 0, nil, fmt.Errorf("invalid state, got %v "+
×
174
                        "expected %v", i.State, PsbtOutputKnown)
×
175
        }
×
176

177
        // The funding output needs to be known already at this point, which
178
        // means we need to have the local and remote multisig keys bound
179
        // already.
180
        _, out, err := i.FundingOutput()
3✔
181
        if err != nil {
3✔
182
                return nil, 0, nil, fmt.Errorf("unable to create funding "+
×
183
                        "output: %v", err)
×
184
        }
×
185

186
        script, err := txscript.ParsePkScript(out.PkScript)
3✔
187
        if err != nil {
3✔
188
                return nil, 0, nil, fmt.Errorf("unable to parse funding "+
×
189
                        "output script: %w", err)
×
190
        }
×
191

192
        // Encode the address in the human-readable bech32 format.
193
        addr, err := script.Address(i.netParams)
3✔
194
        if err != nil {
3✔
195
                return nil, 0, nil, fmt.Errorf("unable to encode address: %w",
×
196
                        err)
×
197
        }
×
198

199
        // We'll also encode the address/amount in a machine-readable raw PSBT
200
        // format. If the user supplied a base PSBT, we'll add the output to
201
        // that one, otherwise we'll create a new one.
202
        packet := i.BasePsbt
3✔
203
        if packet == nil {
6✔
204
                packet, err = psbt.New(nil, nil, 2, 0, nil)
3✔
205
                if err != nil {
3✔
206
                        return nil, 0, nil, fmt.Errorf("unable to create "+
×
207
                                "PSBT: %w", err)
×
208
                }
×
209
        }
210
        packet.UnsignedTx.TxOut = append(packet.UnsignedTx.TxOut, out)
3✔
211
        packet.Outputs = append(packet.Outputs, psbt.POutput{})
3✔
212
        return addr, out.Value, packet, nil
3✔
213
}
214

215
// Verify makes sure the PSBT that is given to the intent has an output that
216
// sends to the channel funding multisig address with the correct amount. A
217
// simple check that at least a single input has been specified is performed.
218
func (i *PsbtIntent) Verify(packet *psbt.Packet, skipFinalize bool) error {
3✔
219
        if packet == nil {
3✔
220
                return fmt.Errorf("PSBT is nil")
×
221
        }
×
222
        if i.State != PsbtOutputKnown {
3✔
223
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
224
                        PsbtOutputKnown)
×
225
        }
×
226

227
        // Try to locate the channel funding multisig output.
228
        _, expectedOutput, err := i.FundingOutput()
3✔
229
        if err != nil {
3✔
230
                return fmt.Errorf("funding output cannot be created: %w", err)
×
231
        }
×
232
        outputFound := false
3✔
233
        outputSum := int64(0)
3✔
234
        for _, out := range packet.UnsignedTx.TxOut {
6✔
235
                outputSum += out.Value
3✔
236
                if psbt.TxOutsEqual(out, expectedOutput) {
6✔
237
                        outputFound = true
3✔
238
                }
3✔
239
        }
240
        if !outputFound {
3✔
241
                return fmt.Errorf("funding output not found in PSBT")
×
242
        }
×
243

244
        // At least one input needs to be specified and it must be large enough
245
        // to pay for all outputs. We don't want to dive into fee estimation
246
        // here so we just assume that if the input amount exceeds the output
247
        // amount, the chosen fee is sufficient.
248
        if len(packet.UnsignedTx.TxIn) == 0 {
3✔
249
                return fmt.Errorf("PSBT has no inputs")
×
250
        }
×
251
        sum, err := psbt.SumUtxoInputValues(packet)
3✔
252
        if err != nil {
3✔
253
                return fmt.Errorf("error determining input sum: %w", err)
×
254
        }
×
255
        if sum <= outputSum {
3✔
256
                return fmt.Errorf("input amount sum must be larger than " +
×
257
                        "output amount sum")
×
258
        }
×
259

260
        // To avoid possible malleability, all inputs to a funding transaction
261
        // must be SegWit spends.
262
        err = verifyAllInputsSegWit(packet.UnsignedTx.TxIn, packet.Inputs)
3✔
263
        if err != nil {
3✔
264
                return fmt.Errorf("cannot use TX for channel funding, "+
×
265
                        "not all inputs are SegWit spends, risk of "+
×
266
                        "malleability: %v", err)
×
267
        }
×
268

269
        // In case we aren't going to publish any transaction, we now have
270
        // everything we need and can skip the Finalize step.
271
        i.PendingPsbt = packet
3✔
272
        if !i.shouldPublish && skipFinalize {
6✔
273
                i.FinalTX = packet.UnsignedTx
3✔
274
                i.State = PsbtFinalized
3✔
275

3✔
276
                // Signal the funding manager that it can now continue with its
3✔
277
                // funding flow as the PSBT is now complete .
3✔
278
                i.signalPsbtReady.Do(func() {
6✔
279
                        close(i.PsbtReady)
3✔
280
                })
3✔
281

282
                return nil
3✔
283
        }
284

285
        i.State = PsbtVerified
3✔
286
        return nil
3✔
287
}
288

289
// Finalize makes sure the final PSBT that is given to the intent is fully valid
290
// and signed but still contains the same UTXOs and outputs as the pending
291
// transaction we previously verified. If everything checks out, the funding
292
// manager is informed that the channel can now be opened and the funding
293
// transaction be broadcast.
294
func (i *PsbtIntent) Finalize(packet *psbt.Packet) error {
3✔
295
        if packet == nil {
3✔
296
                return fmt.Errorf("PSBT is nil")
×
297
        }
×
298
        if i.State != PsbtVerified {
3✔
299
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
300
                        PsbtVerified)
×
301
        }
×
302

303
        // Make sure the PSBT itself thinks it's finalized and ready to be
304
        // broadcast.
305
        err := psbt.MaybeFinalizeAll(packet)
3✔
306
        if err != nil {
3✔
307
                return fmt.Errorf("error finalizing PSBT: %w", err)
×
308
        }
×
309
        rawTx, err := psbt.Extract(packet)
3✔
310
        if err != nil {
3✔
311
                return fmt.Errorf("unable to extract funding TX: %w", err)
×
312
        }
×
313

314
        return i.FinalizeRawTX(rawTx)
3✔
315
}
316

317
// FinalizeRawTX makes sure the final raw transaction that is given to the
318
// intent is fully valid and signed but still contains the same UTXOs and
319
// outputs as the pending transaction we previously verified. If everything
320
// checks out, the funding manager is informed that the channel can now be
321
// opened and the funding transaction be broadcast.
322
func (i *PsbtIntent) FinalizeRawTX(rawTx *wire.MsgTx) error {
3✔
323
        if rawTx == nil {
3✔
324
                return fmt.Errorf("raw transaction is nil")
×
325
        }
×
326
        if i.State != PsbtVerified {
3✔
327
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
328
                        PsbtVerified)
×
329
        }
×
330

331
        // Do a basic check that this is still the same TX that we verified in
332
        // the previous step. This is to protect the user from unwanted
333
        // modifications. We only check the outputs and previous outpoints of
334
        // the inputs of the wire transaction because the fields in the PSBT
335
        // part are allowed to change.
336
        if i.PendingPsbt == nil {
3✔
337
                return fmt.Errorf("PSBT was not verified first")
×
338
        }
×
339
        err := psbt.VerifyOutputsEqual(
3✔
340
                rawTx.TxOut, i.PendingPsbt.UnsignedTx.TxOut,
3✔
341
        )
3✔
342
        if err != nil {
3✔
343
                return fmt.Errorf("outputs differ from verified PSBT: %w", err)
×
344
        }
×
345
        err = psbt.VerifyInputPrevOutpointsEqual(
3✔
346
                rawTx.TxIn, i.PendingPsbt.UnsignedTx.TxIn,
3✔
347
        )
3✔
348
        if err != nil {
3✔
349
                return fmt.Errorf("inputs differ from verified PSBT: %w", err)
×
350
        }
×
351

352
        // We also check that we have a signed TX. This is only necessary if the
353
        // FinalizeRawTX is called directly with a wire format TX instead of
354
        // extracting the TX from a PSBT.
355
        err = verifyInputsSigned(rawTx.TxIn)
3✔
356
        if err != nil {
3✔
357
                return fmt.Errorf("inputs not signed: %w", err)
×
358
        }
×
359

360
        // As far as we can tell, this TX is ok to be used as a funding
361
        // transaction.
362
        i.State = PsbtFinalized
3✔
363
        i.FinalTX = rawTx
3✔
364

3✔
365
        // Signal the funding manager that it can now finally continue with its
3✔
366
        // funding flow as the PSBT is now ready to be converted into a real
3✔
367
        // transaction and be published.
3✔
368
        i.signalPsbtReady.Do(func() {
6✔
369
                close(i.PsbtReady)
3✔
370
        })
3✔
371
        return nil
3✔
372
}
373

374
// CompileFundingTx finalizes the previously verified PSBT and returns the
375
// extracted binary serialized transaction from it. It also prepares the channel
376
// point for which this funding intent was initiated for.
377
func (i *PsbtIntent) CompileFundingTx() (*wire.MsgTx, error) {
3✔
378
        if i.State != PsbtFinalized {
3✔
379
                return nil, fmt.Errorf("invalid state. got %v expected %v",
×
380
                        i.State, PsbtFinalized)
×
381
        }
×
382

383
        // Identify our funding outpoint now that we know everything's ready.
384
        _, txOut, err := i.FundingOutput()
3✔
385
        if err != nil {
3✔
386
                return nil, fmt.Errorf("cannot get funding output: %w", err)
×
387
        }
×
388
        ok, idx := input.FindScriptOutputIndex(i.FinalTX, txOut.PkScript)
3✔
389
        if !ok {
3✔
390
                return nil, fmt.Errorf("funding output not found in PSBT")
×
391
        }
×
392
        i.chanPoint = &wire.OutPoint{
3✔
393
                Hash:  i.FinalTX.TxHash(),
3✔
394
                Index: idx,
3✔
395
        }
3✔
396
        i.State = PsbtFundingTxCompiled
3✔
397

3✔
398
        return i.FinalTX, nil
3✔
399
}
400

401
// RemoteCanceled informs the listener of the PSBT ready channel that the
402
// funding has been canceled by the remote peer and that we can no longer
403
// continue with it.
404
func (i *PsbtIntent) RemoteCanceled() {
3✔
405
        log.Debugf("PSBT funding intent canceled by remote, state=%v", i.State)
3✔
406
        i.signalPsbtReady.Do(func() {
6✔
407
                i.PsbtReady <- ErrRemoteCanceled
3✔
408
                i.State = PsbtResponderCanceled
3✔
409
        })
3✔
410
        i.ShimIntent.Cancel()
3✔
411
}
412

413
// Cancel allows the caller to cancel a funding Intent at any time. This will
414
// return make sure the channel funding flow with the remote peer is failed and
415
// any reservations are canceled.
416
//
417
// NOTE: Part of the chanfunding.Intent interface.
418
func (i *PsbtIntent) Cancel() {
3✔
419
        log.Debugf("PSBT funding intent canceled, state=%v", i.State)
3✔
420
        i.signalPsbtReady.Do(func() {
6✔
421
                i.PsbtReady <- ErrUserCanceled
3✔
422
                i.State = PsbtInitiatorCanceled
3✔
423
        })
3✔
424
        i.ShimIntent.Cancel()
3✔
425
}
426

427
// Inputs returns all inputs to the final funding transaction that we know
428
// about. These are only known after the PSBT has been verified.
429
func (i *PsbtIntent) Inputs() []wire.OutPoint {
3✔
430
        var inputs []wire.OutPoint
3✔
431

3✔
432
        switch i.State {
3✔
433
        // We return the inputs to the pending psbt.
434
        case PsbtVerified:
3✔
435
                for _, in := range i.PendingPsbt.UnsignedTx.TxIn {
6✔
436
                        inputs = append(inputs, in.PreviousOutPoint)
3✔
437
                }
3✔
438

439
        // We return the inputs to the final funding tx.
440
        case PsbtFinalized, PsbtFundingTxCompiled:
3✔
441
                for _, in := range i.FinalTX.TxIn {
6✔
442
                        inputs = append(inputs, in.PreviousOutPoint)
3✔
443
                }
3✔
444

445
        // In all other states we cannot know the inputs to the funding tx, and
446
        // return an empty list.
447
        default:
×
448
        }
449

450
        return inputs
3✔
451
}
452

453
// Outputs returns all outputs of the final funding transaction that we
454
// know about. These are only known after the PSBT has been verified.
455
func (i *PsbtIntent) Outputs() []*wire.TxOut {
3✔
456
        switch i.State {
3✔
457
        // We return the outputs of the pending psbt.
458
        case PsbtVerified:
3✔
459
                return i.PendingPsbt.UnsignedTx.TxOut
3✔
460

461
        // We return the outputs of the final funding tx.
462
        case PsbtFinalized, PsbtFundingTxCompiled:
3✔
463
                return i.FinalTX.TxOut
3✔
464

465
        // In all other states we cannot know the final outputs, and return an
466
        // empty list.
467
        default:
×
468
                return nil
×
469
        }
470
}
471

472
// ShouldPublishFundingTX returns true if the intent assumes that its assembler
473
// should publish the funding TX once the funding negotiation is complete.
474
func (i *PsbtIntent) ShouldPublishFundingTX() bool {
3✔
475
        return i.shouldPublish
3✔
476
}
3✔
477

478
// PsbtAssembler is a type of chanfunding.Assembler wherein the funding
479
// transaction is constructed outside of lnd by using partially signed bitcoin
480
// transactions (PSBT).
481
type PsbtAssembler struct {
482
        // fundingAmt is the total amount of coins in the funding output.
483
        fundingAmt btcutil.Amount
484

485
        // basePsbt is the user-supplied base PSBT the channel output should be
486
        // added to.
487
        basePsbt *psbt.Packet
488

489
        // netParams are the network parameters used to encode the P2WSH funding
490
        // address.
491
        netParams *chaincfg.Params
492

493
        // shouldPublish specifies if the assembler should publish the
494
        // transaction once the channel funding has completed.
495
        shouldPublish bool
496
}
497

498
// NewPsbtAssembler creates a new CannedAssembler from the material required
499
// to construct a funding output and channel point. An optional base PSBT can
500
// be supplied which will be used to add the channel output to instead of
501
// creating a new one.
502
func NewPsbtAssembler(fundingAmt btcutil.Amount, basePsbt *psbt.Packet,
503
        netParams *chaincfg.Params, shouldPublish bool) *PsbtAssembler {
3✔
504

3✔
505
        return &PsbtAssembler{
3✔
506
                fundingAmt:    fundingAmt,
3✔
507
                basePsbt:      basePsbt,
3✔
508
                netParams:     netParams,
3✔
509
                shouldPublish: shouldPublish,
3✔
510
        }
3✔
511
}
3✔
512

513
// ProvisionChannel creates a new ShimIntent given the passed funding Request.
514
// The returned intent is immediately able to provide the channel point and
515
// funding output as they've already been created outside lnd.
516
//
517
// NOTE: This method satisfies the chanfunding.Assembler interface.
518
func (p *PsbtAssembler) ProvisionChannel(req *Request) (Intent, error) {
3✔
519
        // We'll exit out if SubtractFees is set as the funding transaction will
3✔
520
        // be assembled externally, so we don't influence coin selection.
3✔
521
        if req.SubtractFees {
3✔
522
                return nil, fmt.Errorf("SubtractFees not supported for PSBT")
×
523
        }
×
524

525
        // We'll exit out if FundUpToMaxAmt or MinFundAmt is set as the funding
526
        // transaction will be assembled externally, so we don't influence coin
527
        // selection.
528
        if req.FundUpToMaxAmt != 0 || req.MinFundAmt != 0 {
3✔
529
                return nil, fmt.Errorf("FundUpToMaxAmt and MinFundAmt not " +
×
530
                        "supported for PSBT")
×
531
        }
×
532

533
        intent := &PsbtIntent{
3✔
534
                ShimIntent: ShimIntent{
3✔
535
                        localFundingAmt: p.fundingAmt,
3✔
536
                        musig2:          req.Musig2,
3✔
537
                },
3✔
538
                State:         PsbtShimRegistered,
3✔
539
                BasePsbt:      p.basePsbt,
3✔
540
                PsbtReady:     make(chan error, 1),
3✔
541
                shouldPublish: p.shouldPublish,
3✔
542
                netParams:     p.netParams,
3✔
543
        }
3✔
544

3✔
545
        // A simple sanity check to ensure the provisioned request matches the
3✔
546
        // re-made shim intent.
3✔
547
        if req.LocalAmt+req.RemoteAmt != p.fundingAmt {
3✔
548
                return nil, fmt.Errorf("intent doesn't match PSBT "+
×
549
                        "assembler: local_amt=%v, remote_amt=%v, funding_amt=%v",
×
550
                        req.LocalAmt, req.RemoteAmt, p.fundingAmt)
×
551
        }
×
552

553
        return intent, nil
3✔
554
}
555

556
// ShouldPublishFundingTx is a method of the assembler that signals if the
557
// funding transaction should be published after the channel negotiations are
558
// completed with the remote peer.
559
//
560
// NOTE: This method is a part of the ConditionalPublishAssembler interface.
561
func (p *PsbtAssembler) ShouldPublishFundingTx() bool {
3✔
562
        return p.shouldPublish
3✔
563
}
3✔
564

565
// A compile-time assertion to ensure PsbtAssembler meets the
566
// ConditionalPublishAssembler interface.
567
var _ ConditionalPublishAssembler = (*PsbtAssembler)(nil)
568

569
// verifyInputsSigned verifies that the given list of inputs is non-empty and
570
// that all the inputs either contain a script signature or a witness stack.
571
func verifyInputsSigned(ins []*wire.TxIn) error {
3✔
572
        if len(ins) == 0 {
3✔
573
                return fmt.Errorf("no inputs in transaction")
×
574
        }
×
575
        for idx, in := range ins {
6✔
576
                if len(in.SignatureScript) == 0 && len(in.Witness) == 0 {
3✔
577
                        return fmt.Errorf("input %d has no signature data "+
×
578
                                "attached", idx)
×
579
                }
×
580
        }
581
        return nil
3✔
582
}
583

584
// verifyAllInputsSegWit makes sure all inputs to a transaction are SegWit
585
// spends. This is a bit tricky because the PSBT spec doesn't require the
586
// WitnessUtxo field to be set. Therefore if only a NonWitnessUtxo is given, we
587
// need to look at it and make sure it's either a witness pkScript or a nested
588
// SegWit spend.
589
func verifyAllInputsSegWit(txIns []*wire.TxIn, ins []psbt.PInput) error {
3✔
590
        for idx, in := range ins {
6✔
591
                switch {
3✔
592
                // The optimal case is that the witness UTXO is set explicitly.
593
                case in.WitnessUtxo != nil:
3✔
594

595
                // Only the non witness UTXO field is set, we need to inspect it
596
                // to make sure it's not P2PKH or bare P2SH.
597
                case in.NonWitnessUtxo != nil:
×
598
                        utxo := in.NonWitnessUtxo
×
599
                        txIn := txIns[idx]
×
600
                        txOut := utxo.TxOut[txIn.PreviousOutPoint.Index]
×
601

×
602
                        if !txscript.IsWitnessProgram(txOut.PkScript) &&
×
603
                                !txscript.IsWitnessProgram(in.RedeemScript) {
×
604

×
605
                                return fmt.Errorf("input %d is non-SegWit "+
×
606
                                        "spend or missing redeem script", idx)
×
607
                        }
×
608

609
                // This should've already been caught by a previous check but we
610
                // keep it in for completeness' sake.
611
                default:
×
612
                        return fmt.Errorf("input %d has no UTXO information",
×
613
                                idx)
×
614
                }
615
        }
616

617
        return nil
3✔
618
}
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