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

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

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

65.69
/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 {
3✔
64
        switch s {
3✔
65
        case PsbtShimRegistered:
3✔
66
                return "shim_registered"
3✔
67

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

71
        case PsbtVerified:
×
72
                return "verified"
×
73

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

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

80
        case PsbtInitiatorCanceled:
×
81
                return "user_canceled"
×
82

83
        case PsbtResponderCanceled:
3✔
84
                return "remote_canceled"
3✔
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) {
3✔
162

3✔
163
        i.localKey = localKey
3✔
164
        i.remoteKey = remoteKey
3✔
165
        i.State = PsbtOutputKnown
3✔
166
}
3✔
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.
171
func (i *PsbtIntent) BindTapscriptRoot(root fn.Option[chainhash.Hash]) {
3✔
172
        i.tapscriptRoot = root
3✔
173
}
3✔
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) {
3✔
181

3✔
182
        if i.State != PsbtOutputKnown {
3✔
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()
3✔
191
        if err != nil {
3✔
192
                return nil, 0, nil, fmt.Errorf("unable to create funding "+
×
193
                        "output: %v", err)
×
194
        }
×
195

196
        script, err := txscript.ParsePkScript(out.PkScript)
3✔
197
        if err != nil {
3✔
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)
3✔
204
        if err != nil {
3✔
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
3✔
213
        if packet == nil {
6✔
214
                packet, err = psbt.New(nil, nil, 2, 0, nil)
3✔
215
                if err != nil {
3✔
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)
3✔
221

3✔
222
        var pOut psbt.POutput
3✔
223

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

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

3✔
233
        return addr, out.Value, packet, nil
3✔
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 {
3✔
240
        if packet == nil {
3✔
241
                return fmt.Errorf("PSBT is nil")
×
242
        }
×
243
        if i.State != PsbtOutputKnown {
3✔
244
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
245
                        PsbtOutputKnown)
×
246
        }
×
247

248
        // Try to locate the channel funding multisig output.
249
        _, expectedOutput, err := i.FundingOutput()
3✔
250
        if err != nil {
3✔
251
                return fmt.Errorf("funding output cannot be created: %w", err)
×
252
        }
×
253
        outputFound := false
3✔
254
        outputSum := int64(0)
3✔
255
        for _, out := range packet.UnsignedTx.TxOut {
6✔
256
                outputSum += out.Value
3✔
257
                if psbt.TxOutsEqual(out, expectedOutput) {
6✔
258
                        outputFound = true
3✔
259
                }
3✔
260
        }
261
        if !outputFound {
3✔
262
                return fmt.Errorf("funding output not found in PSBT")
×
263
        }
×
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 {
3✔
270
                return fmt.Errorf("PSBT has no inputs")
×
271
        }
×
272
        sum, err := psbt.SumUtxoInputValues(packet)
3✔
273
        if err != nil {
3✔
274
                return fmt.Errorf("error determining input sum: %w", err)
×
275
        }
×
276
        if sum <= outputSum {
3✔
277
                return fmt.Errorf("input amount sum must be larger than " +
×
278
                        "output amount sum")
×
279
        }
×
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)
3✔
284
        if err != nil {
3✔
285
                return fmt.Errorf("cannot use TX for channel funding, "+
×
286
                        "not all inputs are SegWit spends, risk of "+
×
287
                        "malleability: %v", err)
×
288
        }
×
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
3✔
293
        if !i.shouldPublish && skipFinalize {
6✔
294
                i.FinalTX = packet.UnsignedTx
3✔
295
                i.State = PsbtFinalized
3✔
296

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

303
                return nil
3✔
304
        }
305

306
        i.State = PsbtVerified
3✔
307
        return nil
3✔
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 {
3✔
316
        if packet == nil {
3✔
317
                return fmt.Errorf("PSBT is nil")
×
318
        }
×
319
        if i.State != PsbtVerified {
3✔
320
                return fmt.Errorf("invalid state. got %v expected %v", i.State,
×
321
                        PsbtVerified)
×
322
        }
×
323

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

335
        return i.FinalizeRawTX(rawTx)
3✔
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 {
3✔
344
        if rawTx == nil {
3✔
345
                return fmt.Errorf("raw transaction is nil")
×
346
        }
×
347
        if i.State != PsbtVerified {
3✔
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 {
3✔
358
                return fmt.Errorf("PSBT was not verified first")
×
359
        }
×
360
        err := psbt.VerifyOutputsEqual(
3✔
361
                rawTx.TxOut, i.PendingPsbt.UnsignedTx.TxOut,
3✔
362
        )
3✔
363
        if err != nil {
3✔
364
                return fmt.Errorf("outputs differ from verified PSBT: %w", err)
×
365
        }
×
366
        err = psbt.VerifyInputPrevOutpointsEqual(
3✔
367
                rawTx.TxIn, i.PendingPsbt.UnsignedTx.TxIn,
3✔
368
        )
3✔
369
        if err != nil {
3✔
370
                return fmt.Errorf("inputs differ from verified PSBT: %w", err)
×
371
        }
×
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 {
3✔
378
                return fmt.Errorf("inputs not signed: %w", err)
×
379
        }
×
380

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

3✔
386
        // Signal the funding manager that it can now finally continue with its
3✔
387
        // funding flow as the PSBT is now ready to be converted into a real
3✔
388
        // transaction and be published.
3✔
389
        i.signalPsbtReady.Do(func() {
6✔
390
                close(i.PsbtReady)
3✔
391
        })
3✔
392
        return nil
3✔
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) {
3✔
399
        if i.State != PsbtFinalized {
3✔
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()
3✔
406
        if err != nil {
3✔
407
                return nil, fmt.Errorf("cannot get funding output: %w", err)
×
408
        }
×
409
        ok, idx := input.FindScriptOutputIndex(i.FinalTX, txOut.PkScript)
3✔
410
        if !ok {
3✔
411
                return nil, fmt.Errorf("funding output not found in PSBT")
×
412
        }
×
413
        i.chanPoint = &wire.OutPoint{
3✔
414
                Hash:  i.FinalTX.TxHash(),
3✔
415
                Index: idx,
3✔
416
        }
3✔
417
        i.State = PsbtFundingTxCompiled
3✔
418

3✔
419
        return i.FinalTX, nil
3✔
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.
425
func (i *PsbtIntent) RemoteCanceled() {
3✔
426
        log.Debugf("PSBT funding intent canceled by remote, state=%v", i.State)
3✔
427
        i.signalPsbtReady.Do(func() {
6✔
428
                i.PsbtReady <- ErrRemoteCanceled
3✔
429
                i.State = PsbtResponderCanceled
3✔
430
        })
3✔
431
        i.ShimIntent.Cancel()
3✔
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.
439
func (i *PsbtIntent) Cancel() {
3✔
440
        log.Debugf("PSBT funding intent canceled, state=%v", i.State)
3✔
441
        i.signalPsbtReady.Do(func() {
6✔
442
                i.PsbtReady <- ErrUserCanceled
3✔
443
                i.State = PsbtInitiatorCanceled
3✔
444
        })
3✔
445
        i.ShimIntent.Cancel()
3✔
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.
450
func (i *PsbtIntent) Inputs() []wire.OutPoint {
3✔
451
        var inputs []wire.OutPoint
3✔
452

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

460
        // We return the inputs to the final funding tx.
461
        case PsbtFinalized, PsbtFundingTxCompiled:
3✔
462
                for _, in := range i.FinalTX.TxIn {
6✔
463
                        inputs = append(inputs, in.PreviousOutPoint)
3✔
464
                }
3✔
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

471
        return inputs
3✔
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.
476
func (i *PsbtIntent) Outputs() []*wire.TxOut {
3✔
477
        switch i.State {
3✔
478
        // We return the outputs of the pending psbt.
479
        case PsbtVerified:
3✔
480
                return i.PendingPsbt.UnsignedTx.TxOut
3✔
481

482
        // We return the outputs of the final funding tx.
483
        case PsbtFinalized, PsbtFundingTxCompiled:
3✔
484
                return i.FinalTX.TxOut
3✔
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.
495
func (i *PsbtIntent) ShouldPublishFundingTX() bool {
3✔
496
        return i.shouldPublish
3✔
497
}
3✔
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 {
3✔
525

3✔
526
        return &PsbtAssembler{
3✔
527
                fundingAmt:    fundingAmt,
3✔
528
                basePsbt:      basePsbt,
3✔
529
                netParams:     netParams,
3✔
530
                shouldPublish: shouldPublish,
3✔
531
        }
3✔
532
}
3✔
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) {
3✔
540
        // We'll exit out if SubtractFees is set as the funding transaction will
3✔
541
        // be assembled externally, so we don't influence coin selection.
3✔
542
        if req.SubtractFees {
3✔
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 {
3✔
550
                return nil, fmt.Errorf("FundUpToMaxAmt and MinFundAmt not " +
×
551
                        "supported for PSBT")
×
552
        }
×
553

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

3✔
567
        // A simple sanity check to ensure the provisioned request matches the
3✔
568
        // re-made shim intent.
3✔
569
        if req.LocalAmt+req.RemoteAmt != p.fundingAmt {
3✔
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
3✔
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.
583
func (p *PsbtAssembler) ShouldPublishFundingTx() bool {
3✔
584
        return p.shouldPublish
3✔
585
}
3✔
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 {
3✔
599
                        return fmt.Errorf("input %d has no signature data "+
×
600
                                "attached", idx)
×
601
                }
×
602
        }
603
        return nil
3✔
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 {
3✔
612
        for idx, in := range ins {
6✔
613
                switch {
3✔
614
                // The optimal case is that the witness UTXO is set explicitly.
615
                case in.WitnessUtxo != nil:
3✔
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:
×
620
                        utxo := in.NonWitnessUtxo
×
621
                        txIn := txIns[idx]
×
622
                        txOut := utxo.TxOut[txIn.PreviousOutPoint.Index]
×
623

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

×
627
                                return fmt.Errorf("input %d is non-SegWit "+
×
628
                                        "spend or missing redeem script", idx)
×
629
                        }
×
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
3✔
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