• 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

67.22
/lnwallet/btcwallet/psbt.go
1
package btcwallet
2

3
import (
4
        "bytes"
5
        "crypto/sha256"
6
        "errors"
7
        "fmt"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
11
        "github.com/btcsuite/btcd/btcutil"
12
        "github.com/btcsuite/btcd/btcutil/hdkeychain"
13
        "github.com/btcsuite/btcd/btcutil/psbt"
14
        "github.com/btcsuite/btcd/txscript"
15
        "github.com/btcsuite/btcd/wire"
16
        "github.com/btcsuite/btcwallet/waddrmgr"
17
        "github.com/btcsuite/btcwallet/wallet"
18
        "github.com/btcsuite/btcwallet/wtxmgr"
19
        "github.com/lightningnetwork/lnd/input"
20
        "github.com/lightningnetwork/lnd/keychain"
21
        "github.com/lightningnetwork/lnd/lnwallet"
22
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
23
)
24

25
var (
26
        // PsbtKeyTypeInputSignatureTweakSingle is a custom/proprietary PSBT key
27
        // for an input that specifies what single tweak should be applied to
28
        // the key before signing the input. The value 51 is leet speak for
29
        // "si", short for "single".
30
        PsbtKeyTypeInputSignatureTweakSingle = []byte{0x51}
31

32
        // PsbtKeyTypeInputSignatureTweakDouble is a custom/proprietary PSBT key
33
        // for an input that specifies what double tweak should be applied to
34
        // the key before signing the input. The value d0 is leet speak for
35
        // "do", short for "double".
36
        PsbtKeyTypeInputSignatureTweakDouble = []byte{0xd0}
37

38
        // ErrInputMissingUTXOInfo is returned if a PSBT input is supplied that
39
        // does not specify the witness UTXO info.
40
        ErrInputMissingUTXOInfo = errors.New(
41
                "input doesn't specify any UTXO info",
42
        )
43

44
        // ErrScriptSpendFeeEstimationUnsupported is returned if a PSBT input is
45
        // of a script spend type.
46
        ErrScriptSpendFeeEstimationUnsupported = errors.New(
47
                "cannot estimate fee for script spend inputs",
48
        )
49

50
        // ErrUnsupportedScript is returned if a supplied pk script is not
51
        // known or supported.
52
        ErrUnsupportedScript = errors.New("unsupported or unknown pk script")
53
)
54

55
// FundPsbt creates a fully populated PSBT packet that contains enough inputs to
56
// fund the outputs specified in the passed in packet with the specified fee
57
// rate. If there is change left, a change output from the internal wallet is
58
// added and the index of the change output is returned. Otherwise no additional
59
// output is created and the index -1 is returned. If no custom change
60
// scope is specified, the BIP0084 will be used for default accounts and single
61
// imported public keys. For custom account, no key scope should be provided
62
// as the coin selection key scope will always be used to generate the change
63
// address.
64
// The function argument `allowUtxo` specifies a filter function for utxos
65
// during coin selection. It should return true for utxos that can be used and
66
// false for those that should be excluded.
67
//
68
// NOTE: If the packet doesn't contain any inputs, coin selection is performed
69
// automatically. The account parameter must be non-empty as it determines which
70
// set of coins are eligible for coin selection. If the packet does contain any
71
// inputs, it is assumed that full coin selection happened externally and no
72
// additional inputs are added. If the specified inputs aren't enough to fund
73
// the outputs with the given fee rate, an error is returned. No lock lease is
74
// acquired for any of the selected/validated inputs. It is in the caller's
75
// responsibility to lock the inputs before handing them out.
76
//
77
// This is a part of the WalletController interface.
78
func (b *BtcWallet) FundPsbt(packet *psbt.Packet, minConfs int32,
79
        feeRate chainfee.SatPerKWeight, accountName string,
80
        changeScope *waddrmgr.KeyScope,
81
        strategy wallet.CoinSelectionStrategy,
82
        allowUtxo func(wtxmgr.Credit) bool) (int32, error) {
3✔
83

3✔
84
        // The fee rate is passed in using units of sat/kw, so we'll convert
3✔
85
        // this to sat/KB as the CreateSimpleTx method requires this unit.
3✔
86
        feeSatPerKB := btcutil.Amount(feeRate.FeePerKVByte())
3✔
87

3✔
88
        var (
3✔
89
                keyScope   *waddrmgr.KeyScope
3✔
90
                accountNum uint32
3✔
91
        )
3✔
92

3✔
93
        switch accountName {
3✔
94
        // For default accounts and single imported public keys, we'll provide a
95
        // nil key scope to FundPsbt, allowing it to select inputs from all
96
        // scopes (NP2WKH, P2WKH, P2TR). By default, the change key scope for
97
        // these accounts will be P2WKH.
98
        case lnwallet.DefaultAccountName:
3✔
99
                if changeScope == nil {
6✔
100
                        changeScope = &waddrmgr.KeyScopeBIP0084
3✔
101
                }
3✔
102

103
                accountNum = defaultAccount
3✔
104

105
        case waddrmgr.ImportedAddrAccountName:
3✔
106
                if changeScope == nil {
6✔
107
                        changeScope = &waddrmgr.KeyScopeBIP0084
3✔
108
                }
3✔
109

110
                accountNum = importedAccount
3✔
111

112
        // Otherwise, map the account name to its key scope and internal account
113
        // number to only select inputs from said account. No change key scope
114
        // should have been specified as a custom account should only have one
115
        // key scope. Providing a change key scope would break this assumption
116
        // and lead to non-deterministic behavior by using a different change
117
        // key scope than the custom account key scope. The change key scope
118
        // will always be the same as the coin selection.
119
        default:
3✔
120
                if changeScope != nil {
3✔
121
                        return 0, fmt.Errorf("couldn't select a " +
×
122
                                "custom change type for custom accounts")
×
123
                }
×
124

125
                scope, account, err := b.lookupFirstCustomAccount(accountName)
3✔
126
                if err != nil {
3✔
127
                        return 0, err
×
128
                }
×
129
                keyScope = &scope
3✔
130
                changeScope = keyScope
3✔
131
                accountNum = account
3✔
132
        }
133

134
        var opts []wallet.TxCreateOption
3✔
135
        if changeScope != nil {
6✔
136
                opts = append(opts, wallet.WithCustomChangeScope(changeScope))
3✔
137
        }
3✔
138
        if allowUtxo != nil {
6✔
139
                opts = append(opts, wallet.WithUtxoFilter(allowUtxo))
3✔
140
        }
3✔
141

142
        // Let the wallet handle coin selection and/or fee estimation based on
143
        // the partial TX information in the packet.
144
        return b.wallet.FundPsbt(
3✔
145
                packet, keyScope, minConfs, accountNum, feeSatPerKB,
3✔
146
                strategy, opts...,
3✔
147
        )
3✔
148
}
149

150
// SignPsbt expects a partial transaction with all inputs and outputs fully
151
// declared and tries to sign all unsigned inputs that have all required fields
152
// (UTXO information, BIP32 derivation information, witness or sig scripts) set.
153
// If no error is returned, the PSBT is ready to be given to the next signer or
154
// to be finalized if lnd was the last signer.
155
//
156
// NOTE: This method only signs inputs (and only those it can sign), it does not
157
// perform any other tasks (such as coin selection, UTXO locking or
158
// input/output/fee value validation, PSBT finalization). Any input that is
159
// incomplete will be skipped.
160
func (b *BtcWallet) SignPsbt(packet *psbt.Packet) ([]uint32, error) {
3✔
161
        // In signedInputs we return the indices of psbt inputs that were signed
3✔
162
        // by our wallet. This way the caller can check if any inputs were signed.
3✔
163
        var signedInputs []uint32
3✔
164

3✔
165
        // Let's check that this is actually something we can and want to sign.
3✔
166
        // We need at least one input and one output. In addition each
3✔
167
        // input needs nonWitness Utxo or witness Utxo data specified.
3✔
168
        err := psbt.InputsReadyToSign(packet)
3✔
169
        if err != nil {
3✔
170
                return nil, err
×
171
        }
×
172

173
        // Go through each input that doesn't have final witness data attached
174
        // to it already and try to sign it. If there is nothing more to sign or
175
        // there are inputs that we don't know how to sign, we won't return any
176
        // error. So it's possible we're not the final signer.
177
        tx := packet.UnsignedTx
3✔
178
        prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
3✔
179
        sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
3✔
180
        for idx := range tx.TxIn {
6✔
181
                in := &packet.Inputs[idx]
3✔
182

3✔
183
                // We can only sign if we have UTXO information available. Since
3✔
184
                // we don't finalize, we just skip over any input that we know
3✔
185
                // we can't do anything with. Since we only support signing
3✔
186
                // witness inputs, we only look at the witness UTXO being set.
3✔
187
                if in.WitnessUtxo == nil {
3✔
188
                        continue
×
189
                }
190

191
                // Skip this input if it's got final witness data attached.
192
                if len(in.FinalScriptWitness) > 0 {
6✔
193
                        continue
3✔
194
                }
195

196
                // Skip this input if there is no BIP32 derivation info
197
                // available.
198
                if len(in.Bip32Derivation) == 0 {
6✔
199
                        continue
3✔
200
                }
201

202
                // TODO(guggero): For multisig, we'll need to find out what key
203
                // to use and there should be multiple derivation paths in the
204
                // BIP32 derivation field.
205

206
                // Let's try and derive the key now. This method will decide if
207
                // it's a BIP49/84 key for normal on-chain funds or a key of the
208
                // custom purpose 1017 key scope.
209
                derivationInfo := in.Bip32Derivation[0]
3✔
210
                privKey, err := b.deriveKeyByBIP32Path(derivationInfo.Bip32Path)
3✔
211
                if err != nil {
3✔
212
                        log.Warnf("SignPsbt: Skipping input %d, error "+
×
213
                                "deriving signing key: %v", idx, err)
×
214
                        continue
×
215
                }
216

217
                // We need to make sure we actually derived the key that was
218
                // expected to be derived.
219
                pubKeysEqual := bytes.Equal(
3✔
220
                        derivationInfo.PubKey,
3✔
221
                        privKey.PubKey().SerializeCompressed(),
3✔
222
                )
3✔
223
                if !pubKeysEqual {
6✔
224
                        log.Warnf("SignPsbt: Skipping input %d, derived "+
3✔
225
                                "public key %x does not match bip32 "+
3✔
226
                                "derivation info public key %x", idx,
3✔
227
                                privKey.PubKey().SerializeCompressed(),
3✔
228
                                derivationInfo.PubKey)
3✔
229
                        continue
3✔
230
                }
231

232
                // Do we need to tweak anything? Single or double tweaks are
233
                // sent as custom/proprietary fields in the PSBT input section.
234
                privKey = maybeTweakPrivKeyPsbt(in.Unknowns, privKey)
3✔
235

3✔
236
                // What kind of signature is expected from us and do we have all
3✔
237
                // information we need?
3✔
238
                signMethod, err := validateSigningMethod(in)
3✔
239
                if err != nil {
3✔
240
                        return nil, err
×
241
                }
×
242

243
                switch signMethod {
3✔
244
                // For p2wkh, np2wkh and p2wsh.
245
                case input.WitnessV0SignMethod:
3✔
246
                        err = signSegWitV0(in, tx, sigHashes, idx, privKey)
3✔
247

248
                // For p2tr BIP0086 key spend only.
249
                case input.TaprootKeySpendBIP0086SignMethod:
3✔
250
                        rootHash := make([]byte, 0)
3✔
251
                        err = signSegWitV1KeySpend(
3✔
252
                                in, tx, sigHashes, idx, privKey, rootHash,
3✔
253
                        )
3✔
254

255
                // For p2tr with script commitment key spend path.
256
                case input.TaprootKeySpendSignMethod:
3✔
257
                        rootHash := in.TaprootMerkleRoot
3✔
258
                        err = signSegWitV1KeySpend(
3✔
259
                                in, tx, sigHashes, idx, privKey, rootHash,
3✔
260
                        )
3✔
261

262
                // For p2tr script spend path.
263
                case input.TaprootScriptSpendSignMethod:
3✔
264
                        leafScript := in.TaprootLeafScript[0]
3✔
265
                        leaf := txscript.TapLeaf{
3✔
266
                                LeafVersion: leafScript.LeafVersion,
3✔
267
                                Script:      leafScript.Script,
3✔
268
                        }
3✔
269
                        err = signSegWitV1ScriptSpend(
3✔
270
                                in, tx, sigHashes, idx, privKey, leaf,
3✔
271
                        )
3✔
272

273
                default:
×
274
                        err = fmt.Errorf("unsupported signing method for "+
×
275
                                "PSBT signing: %v", signMethod)
×
276
                }
277
                if err != nil {
3✔
278
                        return nil, err
×
279
                }
×
280
                signedInputs = append(signedInputs, uint32(idx))
3✔
281
        }
282
        return signedInputs, nil
3✔
283
}
284

285
// validateSigningMethod attempts to detect the signing method that is required
286
// to sign for the given PSBT input and makes sure all information is available
287
// to do so.
288
func validateSigningMethod(in *psbt.PInput) (input.SignMethod, error) {
3✔
289
        script, err := txscript.ParsePkScript(in.WitnessUtxo.PkScript)
3✔
290
        if err != nil {
3✔
291
                return 0, fmt.Errorf("error detecting signing method, "+
×
292
                        "couldn't parse pkScript: %v", err)
×
293
        }
×
294

295
        switch script.Class() {
3✔
296
        case txscript.WitnessV0PubKeyHashTy, txscript.ScriptHashTy,
297
                txscript.WitnessV0ScriptHashTy:
3✔
298

3✔
299
                return input.WitnessV0SignMethod, nil
3✔
300

301
        case txscript.WitnessV1TaprootTy:
3✔
302
                if len(in.TaprootBip32Derivation) == 0 {
3✔
303
                        return 0, fmt.Errorf("cannot sign for taproot input " +
×
304
                                "without taproot BIP0032 derivation info")
×
305
                }
×
306

307
                // Currently, we only support creating one signature per input.
308
                //
309
                // TODO(guggero): Should we support signing multiple paths at
310
                // the same time? What are the performance and security
311
                // implications?
312
                if len(in.TaprootBip32Derivation) > 1 {
3✔
313
                        return 0, fmt.Errorf("unsupported multiple taproot " +
×
314
                                "BIP0032 derivation info found, can only " +
×
315
                                "sign for one at a time")
×
316
                }
×
317

318
                derivation := in.TaprootBip32Derivation[0]
3✔
319
                switch {
3✔
320
                // No leaf hashes means this is the internal key we're signing
321
                // with, so it's a key spend. And no merkle root means this is
322
                // a BIP0086 output we're signing for.
323
                case len(derivation.LeafHashes) == 0 &&
324
                        len(in.TaprootMerkleRoot) == 0:
3✔
325

3✔
326
                        return input.TaprootKeySpendBIP0086SignMethod, nil
3✔
327

328
                // A non-empty merkle root means we committed to a taproot hash
329
                // that we need to use in the tap tweak.
330
                case len(derivation.LeafHashes) == 0:
3✔
331
                        // Getting here means the merkle root isn't empty, but
3✔
332
                        // is it exactly the length we need?
3✔
333
                        if len(in.TaprootMerkleRoot) != sha256.Size {
3✔
334
                                return 0, fmt.Errorf("invalid taproot merkle "+
×
335
                                        "root length, got %d expected %d",
×
336
                                        len(in.TaprootMerkleRoot), sha256.Size)
×
337
                        }
×
338

339
                        return input.TaprootKeySpendSignMethod, nil
3✔
340

341
                // Currently, we only support signing for one leaf at a time.
342
                //
343
                // TODO(guggero): Should we support signing multiple paths at
344
                // the same time? What are the performance and security
345
                // implications?
346
                case len(derivation.LeafHashes) == 1:
3✔
347
                        // If we're supposed to be signing for a leaf hash, we
3✔
348
                        // also expect the leaf script that hashes to that hash
3✔
349
                        // in the appropriate field.
3✔
350
                        if len(in.TaprootLeafScript) != 1 {
3✔
351
                                return 0, fmt.Errorf("specified leaf hash in " +
×
352
                                        "taproot BIP0032 derivation but " +
×
353
                                        "missing taproot leaf script")
×
354
                        }
×
355

356
                        leafScript := in.TaprootLeafScript[0]
3✔
357
                        leaf := txscript.TapLeaf{
3✔
358
                                LeafVersion: leafScript.LeafVersion,
3✔
359
                                Script:      leafScript.Script,
3✔
360
                        }
3✔
361
                        leafHash := leaf.TapHash()
3✔
362
                        if !bytes.Equal(leafHash[:], derivation.LeafHashes[0]) {
3✔
363
                                return 0, fmt.Errorf("specified leaf hash in" +
×
364
                                        "taproot BIP0032 derivation but " +
×
365
                                        "corresponding taproot leaf script " +
×
366
                                        "was not found")
×
367
                        }
×
368

369
                        return input.TaprootScriptSpendSignMethod, nil
3✔
370

371
                default:
×
372
                        return 0, fmt.Errorf("unsupported number of leaf " +
×
373
                                "hashes in taproot BIP0032 derivation info, " +
×
374
                                "can only sign for one at a time")
×
375
                }
376

377
        default:
×
378
                return 0, fmt.Errorf("unsupported script class for signing "+
×
379
                        "PSBT: %v", script.Class())
×
380
        }
381
}
382

383
// EstimateInputWeight estimates the weight of a PSBT input and adds it to the
384
// passed in TxWeightEstimator. It returns an error if the input type is
385
// unknown or unsupported. Only inputs that have a known witness size are
386
// supported, which is P2WKH, NP2WKH and P2TR (key spend path).
387
func EstimateInputWeight(in *psbt.PInput, w *input.TxWeightEstimator) error {
3✔
388
        if in.WitnessUtxo == nil {
3✔
389
                return ErrInputMissingUTXOInfo
×
390
        }
×
391

392
        pkScript := in.WitnessUtxo.PkScript
3✔
393
        switch {
3✔
394
        case txscript.IsPayToScriptHash(pkScript):
×
395
                w.AddNestedP2WKHInput()
×
396

397
        case txscript.IsPayToWitnessPubKeyHash(pkScript):
3✔
398
                w.AddP2WKHInput()
3✔
399

400
        case txscript.IsPayToWitnessScriptHash(pkScript):
×
401
                return fmt.Errorf("P2WSH inputs are not supported, cannot "+
×
402
                        "estimate witness size for script spend: %w",
×
403
                        ErrScriptSpendFeeEstimationUnsupported)
×
404

405
        case txscript.IsPayToTaproot(pkScript):
×
406
                signMethod, err := validateSigningMethod(in)
×
407
                if err != nil {
×
408
                        return fmt.Errorf("error determining p2tr signing "+
×
409
                                "method: %w", err)
×
410
                }
×
411

412
                switch signMethod {
×
413
                // For p2tr key spend paths.
414
                case input.TaprootKeySpendBIP0086SignMethod,
415
                        input.TaprootKeySpendSignMethod:
×
416

×
417
                        w.AddTaprootKeySpendInput(in.SighashType)
×
418

419
                // For p2tr script spend path.
420
                case input.TaprootScriptSpendSignMethod:
×
421
                        return fmt.Errorf("P2TR inputs are not supported, "+
×
422
                                "cannot estimate witness size for script "+
×
423
                                "spend: %w",
×
424
                                ErrScriptSpendFeeEstimationUnsupported)
×
425

426
                default:
×
427
                        return fmt.Errorf("unsupported signing method for "+
×
428
                                "PSBT signing: %v", signMethod)
×
429
                }
430

431
        default:
×
432
                return fmt.Errorf("unknown input type for script %x: %w",
×
433
                        pkScript, ErrUnsupportedScript)
×
434
        }
435

436
        return nil
3✔
437
}
438

439
// SignSegWitV0 attempts to generate a signature for a SegWit version 0 input
440
// and stores it in the PartialSigs (and FinalScriptSig for np2wkh addresses)
441
// field.
442
func signSegWitV0(in *psbt.PInput, tx *wire.MsgTx,
443
        sigHashes *txscript.TxSigHashes, idx int,
444
        privKey *btcec.PrivateKey) error {
3✔
445

3✔
446
        pubKeyBytes := privKey.PubKey().SerializeCompressed()
3✔
447

3✔
448
        // Extract the correct witness and/or legacy scripts now, depending on
3✔
449
        // the type of input we sign. The txscript package has the peculiar
3✔
450
        // requirement that the PkScript of a P2PKH must be given as the witness
3✔
451
        // script in order for it to arrive at the correct sighash. That's why
3✔
452
        // we call it subScript here instead of witness script.
3✔
453
        subScript := prepareScriptsV0(in)
3✔
454

3✔
455
        // We have everything we need for signing the input now.
3✔
456
        sig, err := txscript.RawTxInWitnessSignature(
3✔
457
                tx, sigHashes, idx, in.WitnessUtxo.Value, subScript,
3✔
458
                in.SighashType, privKey,
3✔
459
        )
3✔
460
        if err != nil {
3✔
461
                return fmt.Errorf("error signing input %d: %w", idx, err)
×
462
        }
×
463
        in.PartialSigs = append(in.PartialSigs, &psbt.PartialSig{
3✔
464
                PubKey:    pubKeyBytes,
3✔
465
                Signature: sig,
3✔
466
        })
3✔
467

3✔
468
        return nil
3✔
469
}
470

471
// signSegWitV1KeySpend attempts to generate a signature for a SegWit version 1
472
// (p2tr) input and stores it in the TaprootKeySpendSig field.
473
func signSegWitV1KeySpend(in *psbt.PInput, tx *wire.MsgTx,
474
        sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
475
        tapscriptRootHash []byte) error {
3✔
476

3✔
477
        rawSig, err := txscript.RawTxInTaprootSignature(
3✔
478
                tx, sigHashes, idx, in.WitnessUtxo.Value,
3✔
479
                in.WitnessUtxo.PkScript, tapscriptRootHash, in.SighashType,
3✔
480
                privKey,
3✔
481
        )
3✔
482
        if err != nil {
3✔
483
                return fmt.Errorf("error signing taproot input %d: %w", idx,
×
484
                        err)
×
485
        }
×
486

487
        in.TaprootKeySpendSig = rawSig
3✔
488

3✔
489
        return nil
3✔
490
}
491

492
// signSegWitV1ScriptSpend attempts to generate a signature for a SegWit version
493
// 1 (p2tr) input and stores it in the TaprootScriptSpendSig field.
494
func signSegWitV1ScriptSpend(in *psbt.PInput, tx *wire.MsgTx,
495
        sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
496
        leaf txscript.TapLeaf) error {
3✔
497

3✔
498
        rawSig, err := txscript.RawTxInTapscriptSignature(
3✔
499
                tx, sigHashes, idx, in.WitnessUtxo.Value,
3✔
500
                in.WitnessUtxo.PkScript, leaf, in.SighashType, privKey,
3✔
501
        )
3✔
502
        if err != nil {
3✔
503
                return fmt.Errorf("error signing taproot script input %d: %w",
×
504
                        idx, err)
×
505
        }
×
506

507
        leafHash := leaf.TapHash()
3✔
508
        in.TaprootScriptSpendSig = append(
3✔
509
                in.TaprootScriptSpendSig, &psbt.TaprootScriptSpendSig{
3✔
510
                        XOnlyPubKey: in.TaprootBip32Derivation[0].XOnlyPubKey,
3✔
511
                        LeafHash:    leafHash[:],
3✔
512
                        // We snip off the sighash flag from the end (if it was
3✔
513
                        // specified in the first place.)
3✔
514
                        Signature: rawSig[:schnorr.SignatureSize],
3✔
515
                        SigHash:   in.SighashType,
3✔
516
                },
3✔
517
        )
3✔
518

3✔
519
        return nil
3✔
520
}
521

522
// prepareScriptsV0 returns the appropriate witness v0 and/or legacy scripts,
523
// depending on the type of input that should be signed.
524
func prepareScriptsV0(in *psbt.PInput) []byte {
3✔
525
        switch {
3✔
526
        // It's a NP2WKH input:
527
        case len(in.RedeemScript) > 0:
3✔
528
                return in.RedeemScript
3✔
529

530
        // It's a P2WSH input:
531
        case len(in.WitnessScript) > 0:
3✔
532
                return in.WitnessScript
3✔
533

534
        // It's a P2WKH input:
535
        default:
3✔
536
                return in.WitnessUtxo.PkScript
3✔
537
        }
538
}
539

540
// maybeTweakPrivKeyPsbt examines if there are any tweak parameters given in the
541
// custom/proprietary PSBT fields and may perform a mapping on the passed
542
// private key in order to utilize the tweaks, if populated.
543
func maybeTweakPrivKeyPsbt(unknowns []*psbt.Unknown,
544
        privKey *btcec.PrivateKey) *btcec.PrivateKey {
3✔
545

3✔
546
        // There can be other custom/unknown keys in a PSBT that we just ignore.
3✔
547
        // Key tweaking is optional and only one tweak (single _or_ double) can
3✔
548
        // ever be applied (at least for any use cases described in the BOLT
3✔
549
        // spec).
3✔
550
        for _, u := range unknowns {
6✔
551
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakSingle) {
6✔
552
                        return input.TweakPrivKey(privKey, u.Value)
3✔
553
                }
3✔
554

555
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakDouble) {
×
556
                        doubleTweakKey, _ := btcec.PrivKeyFromBytes(
×
557
                                u.Value,
×
558
                        )
×
559
                        return input.DeriveRevocationPrivKey(
×
560
                                privKey, doubleTweakKey,
×
561
                        )
×
562
                }
×
563
        }
564

565
        return privKey
3✔
566
}
567

568
// FinalizePsbt expects a partial transaction with all inputs and outputs fully
569
// declared and tries to sign all inputs that belong to the specified account.
570
// Lnd must be the last signer of the transaction. That means, if there are any
571
// unsigned non-witness inputs or inputs without UTXO information attached or
572
// inputs without witness data that do not belong to lnd's wallet, this method
573
// will fail. If no error is returned, the PSBT is ready to be extracted and the
574
// final TX within to be broadcast.
575
//
576
// NOTE: This method does NOT publish the transaction after it's been
577
// finalized successfully.
578
//
579
// This is a part of the WalletController interface.
580
func (b *BtcWallet) FinalizePsbt(packet *psbt.Packet, accountName string) error {
3✔
581
        var (
3✔
582
                keyScope   *waddrmgr.KeyScope
3✔
583
                accountNum uint32
3✔
584
        )
3✔
585
        switch accountName {
3✔
586
        // If the default/imported account name was specified, we'll provide a
587
        // nil key scope to FundPsbt, allowing it to sign inputs from both key
588
        // scopes (NP2WKH, P2WKH).
589
        case lnwallet.DefaultAccountName:
3✔
590
                accountNum = defaultAccount
3✔
591

592
        case waddrmgr.ImportedAddrAccountName:
×
593
                accountNum = importedAccount
×
594

595
        // Otherwise, map the account name to its key scope and internal account
596
        // number to determine if the inputs belonging to this account should be
597
        // signed.
598
        default:
×
599
                scope, account, err := b.lookupFirstCustomAccount(accountName)
×
600
                if err != nil {
×
601
                        return err
×
602
                }
×
603
                keyScope = &scope
×
604
                accountNum = account
×
605
        }
606

607
        return b.wallet.FinalizePsbt(keyScope, accountNum, packet)
3✔
608
}
609

610
// DecorateInputs fetches the UTXO information of all inputs it can identify and
611
// adds the required information to the package's inputs. The failOnUnknown
612
// boolean controls whether the method should return an error if it cannot
613
// identify an input or if it should just skip it.
614
//
615
// This is a part of the WalletController interface.
616
func (b *BtcWallet) DecorateInputs(packet *psbt.Packet,
617
        failOnUnknown bool) error {
3✔
618

3✔
619
        return b.wallet.DecorateInputs(packet, failOnUnknown)
3✔
620
}
3✔
621

622
// lookupFirstCustomAccount returns the first custom account found. In theory,
623
// there should be only one custom account for the given name. However, due to a
624
// lack of check, users could have created custom accounts with various key
625
// scopes. This behaviour has been fixed but, we still need to handle this
626
// specific case to avoid non-deterministic behaviour implied by LookupAccount.
627
func (b *BtcWallet) lookupFirstCustomAccount(
628
        name string) (waddrmgr.KeyScope, uint32, error) {
3✔
629

3✔
630
        var (
3✔
631
                account  *waddrmgr.AccountProperties
3✔
632
                keyScope waddrmgr.KeyScope
3✔
633
        )
3✔
634
        for _, scope := range waddrmgr.DefaultKeyScopes {
6✔
635
                var err error
3✔
636
                account, err = b.wallet.AccountPropertiesByName(scope, name)
3✔
637
                if waddrmgr.IsError(err, waddrmgr.ErrAccountNotFound) {
6✔
638
                        continue
3✔
639
                }
640
                if err != nil {
3✔
641
                        return keyScope, 0, err
×
642
                }
×
643

644
                keyScope = scope
3✔
645

3✔
646
                break
3✔
647
        }
648
        if account == nil {
3✔
649
                return waddrmgr.KeyScope{}, 0, newAccountNotFoundError(name)
×
650
        }
×
651

652
        return keyScope, account.AccountNumber, nil
3✔
653
}
654

655
// Bip32DerivationFromKeyDesc returns the default and Taproot BIP-0032 key
656
// derivation information from the given key descriptor information.
657
func Bip32DerivationFromKeyDesc(keyDesc keychain.KeyDescriptor,
658
        coinType uint32) (*psbt.Bip32Derivation, *psbt.TaprootBip32Derivation,
659
        string) {
×
660

×
661
        bip32Derivation := &psbt.Bip32Derivation{
×
662
                PubKey: keyDesc.PubKey.SerializeCompressed(),
×
663
                Bip32Path: []uint32{
×
664
                        keychain.BIP0043Purpose + hdkeychain.HardenedKeyStart,
×
665
                        coinType + hdkeychain.HardenedKeyStart,
×
666
                        uint32(keyDesc.Family) +
×
667
                                uint32(hdkeychain.HardenedKeyStart),
×
668
                        0,
×
669
                        keyDesc.Index,
×
670
                },
×
671
        }
×
672

×
673
        derivationPath := fmt.Sprintf(
×
674
                "m/%d'/%d'/%d'/%d/%d", keychain.BIP0043Purpose, coinType,
×
675
                keyDesc.Family, 0, keyDesc.Index,
×
676
        )
×
677

×
678
        return bip32Derivation, &psbt.TaprootBip32Derivation{
×
679
                XOnlyPubKey:          bip32Derivation.PubKey[1:],
×
680
                MasterKeyFingerprint: bip32Derivation.MasterKeyFingerprint,
×
681
                Bip32Path:            bip32Derivation.Bip32Path,
×
682
                LeafHashes:           make([][]byte, 0),
×
683
        }, derivationPath
×
684
}
×
685

686
// Bip32DerivationFromAddress returns the default and Taproot BIP-0032 key
687
// derivation information from the given managed address.
688
func Bip32DerivationFromAddress(
689
        addr waddrmgr.ManagedAddress) (*psbt.Bip32Derivation,
690
        *psbt.TaprootBip32Derivation, string, error) {
3✔
691

3✔
692
        pubKeyAddr, ok := addr.(waddrmgr.ManagedPubKeyAddress)
3✔
693
        if !ok {
3✔
694
                return nil, nil, "", fmt.Errorf("address is not a pubkey " +
×
695
                        "address")
×
696
        }
×
697

698
        scope, derivationInfo, haveInfo := pubKeyAddr.DerivationInfo()
3✔
699
        if !haveInfo {
3✔
700
                return nil, nil, "", fmt.Errorf("address is an imported " +
×
701
                        "public key, can't derive BIP32 path")
×
702
        }
×
703

704
        bip32Derivation := &psbt.Bip32Derivation{
3✔
705
                PubKey: pubKeyAddr.PubKey().SerializeCompressed(),
3✔
706
                Bip32Path: []uint32{
3✔
707
                        scope.Purpose + hdkeychain.HardenedKeyStart,
3✔
708
                        scope.Coin + hdkeychain.HardenedKeyStart,
3✔
709
                        derivationInfo.InternalAccount +
3✔
710
                                hdkeychain.HardenedKeyStart,
3✔
711
                        derivationInfo.Branch,
3✔
712
                        derivationInfo.Index,
3✔
713
                },
3✔
714
        }
3✔
715

3✔
716
        derivationPath := fmt.Sprintf(
3✔
717
                "m/%d'/%d'/%d'/%d/%d", scope.Purpose, scope.Coin,
3✔
718
                derivationInfo.InternalAccount, derivationInfo.Branch,
3✔
719
                derivationInfo.Index,
3✔
720
        )
3✔
721

3✔
722
        return bip32Derivation, &psbt.TaprootBip32Derivation{
3✔
723
                XOnlyPubKey:          bip32Derivation.PubKey[1:],
3✔
724
                MasterKeyFingerprint: bip32Derivation.MasterKeyFingerprint,
3✔
725
                Bip32Path:            bip32Derivation.Bip32Path,
3✔
726
                LeafHashes:           make([][]byte, 0),
3✔
727
        }, derivationPath, nil
3✔
728
}
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