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

lightningnetwork / lnd / 12199391122

06 Dec 2024 01:10PM UTC coverage: 49.807% (-9.1%) from 58.933%
12199391122

push

github

web-flow
Merge pull request #9337 from Guayaba221/patch-1

chore: fix typo in ruby.md

100137 of 201051 relevant lines covered (49.81%)

2.07 hits per line

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

68.41
/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) {
4✔
83

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

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

4✔
93
        switch accountName {
4✔
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:
4✔
99
                if changeScope == nil {
8✔
100
                        changeScope = &waddrmgr.KeyScopeBIP0084
4✔
101
                }
4✔
102

103
                accountNum = defaultAccount
4✔
104

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

110
                accountNum = importedAccount
4✔
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:
4✔
120
                if changeScope != nil {
4✔
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)
4✔
126
                if err != nil {
4✔
127
                        return 0, err
×
128
                }
×
129
                keyScope = &scope
4✔
130
                changeScope = keyScope
4✔
131
                accountNum = account
4✔
132
        }
133

134
        var opts []wallet.TxCreateOption
4✔
135
        if changeScope != nil {
8✔
136
                opts = append(opts, wallet.WithCustomChangeScope(changeScope))
4✔
137
        }
4✔
138
        if allowUtxo != nil {
8✔
139
                opts = append(opts, wallet.WithUtxoFilter(allowUtxo))
4✔
140
        }
4✔
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(
4✔
145
                packet, keyScope, minConfs, accountNum, feeSatPerKB,
4✔
146
                strategy, opts...,
4✔
147
        )
4✔
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) {
4✔
161
        // In signedInputs we return the indices of psbt inputs that were signed
4✔
162
        // by our wallet. This way the caller can check if any inputs were signed.
4✔
163
        var signedInputs []uint32
4✔
164

4✔
165
        // Let's check that this is actually something we can and want to sign.
4✔
166
        // We need at least one input and one output. In addition each
4✔
167
        // input needs nonWitness Utxo or witness Utxo data specified.
4✔
168
        err := psbt.InputsReadyToSign(packet)
4✔
169
        if err != nil {
4✔
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
4✔
178
        prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
4✔
179
        sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
4✔
180
        for idx := range tx.TxIn {
8✔
181
                in := &packet.Inputs[idx]
4✔
182

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

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

196
                // Skip this input if there is no BIP32 derivation info
197
                // available.
198
                if len(in.Bip32Derivation) == 0 {
8✔
199
                        continue
4✔
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]
4✔
210
                privKey, err := b.deriveKeyByBIP32Path(derivationInfo.Bip32Path)
4✔
211
                if err != nil {
4✔
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(
4✔
220
                        derivationInfo.PubKey,
4✔
221
                        privKey.PubKey().SerializeCompressed(),
4✔
222
                )
4✔
223
                if !pubKeysEqual {
8✔
224
                        log.Warnf("SignPsbt: Skipping input %d, derived "+
4✔
225
                                "public key %x does not match bip32 "+
4✔
226
                                "derivation info public key %x", idx,
4✔
227
                                privKey.PubKey().SerializeCompressed(),
4✔
228
                                derivationInfo.PubKey)
4✔
229
                        continue
4✔
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)
4✔
235

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

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

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

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

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

273
                default:
×
274
                        err = fmt.Errorf("unsupported signing method for "+
×
275
                                "PSBT signing: %v", signMethod)
×
276
                }
277
                if err != nil {
4✔
278
                        return nil, err
×
279
                }
×
280
                signedInputs = append(signedInputs, uint32(idx))
4✔
281
        }
282
        return signedInputs, nil
4✔
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) {
4✔
289
        script, err := txscript.ParsePkScript(in.WitnessUtxo.PkScript)
4✔
290
        if err != nil {
4✔
291
                return 0, fmt.Errorf("error detecting signing method, "+
×
292
                        "couldn't parse pkScript: %v", err)
×
293
        }
×
294

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

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

301
        case txscript.WitnessV1TaprootTy:
4✔
302
                if len(in.TaprootBip32Derivation) == 0 {
4✔
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 {
4✔
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]
4✔
319
                switch {
4✔
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:
4✔
325

4✔
326
                        return input.TaprootKeySpendBIP0086SignMethod, nil
4✔
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:
4✔
331
                        // Getting here means the merkle root isn't empty, but
4✔
332
                        // is it exactly the length we need?
4✔
333
                        if len(in.TaprootMerkleRoot) != sha256.Size {
4✔
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
4✔
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:
4✔
347
                        // If we're supposed to be signing for a leaf hash, we
4✔
348
                        // also expect the leaf script that hashes to that hash
4✔
349
                        // in the appropriate field.
4✔
350
                        if len(in.TaprootLeafScript) != 1 {
4✔
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]
4✔
357
                        leaf := txscript.TapLeaf{
4✔
358
                                LeafVersion: leafScript.LeafVersion,
4✔
359
                                Script:      leafScript.Script,
4✔
360
                        }
4✔
361
                        leafHash := leaf.TapHash()
4✔
362
                        if !bytes.Equal(leafHash[:], derivation.LeafHashes[0]) {
4✔
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
4✔
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 {
4✔
388
        if in.WitnessUtxo == nil {
4✔
389
                return ErrInputMissingUTXOInfo
×
390
        }
×
391

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

397
        case txscript.IsPayToWitnessPubKeyHash(pkScript):
×
398
                w.AddP2WKHInput()
×
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):
4✔
406
                signMethod, err := validateSigningMethod(in)
4✔
407
                if err != nil {
4✔
408
                        return fmt.Errorf("error determining p2tr signing "+
×
409
                                "method: %w", err)
×
410
                }
×
411

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

4✔
417
                        w.AddTaprootKeySpendInput(in.SighashType)
4✔
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
4✔
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 {
4✔
445

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

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

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

4✔
468
        return nil
4✔
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 {
4✔
476

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

487
        in.TaprootKeySpendSig = rawSig
4✔
488

4✔
489
        return nil
4✔
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 {
4✔
497

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

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

4✔
519
        return nil
4✔
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 {
4✔
525
        switch {
4✔
526
        // It's a NP2WKH input:
527
        case len(in.RedeemScript) > 0:
4✔
528
                return in.RedeemScript
4✔
529

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

534
        // It's a P2WKH input:
535
        default:
4✔
536
                return in.WitnessUtxo.PkScript
4✔
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 {
4✔
545

4✔
546
        // There can be other custom/unknown keys in a PSBT that we just ignore.
4✔
547
        // Key tweaking is optional and only one tweak (single _or_ double) can
4✔
548
        // ever be applied (at least for any use cases described in the BOLT
4✔
549
        // spec).
4✔
550
        for _, u := range unknowns {
8✔
551
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakSingle) {
8✔
552
                        return input.TweakPrivKey(privKey, u.Value)
4✔
553
                }
4✔
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
4✔
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 {
4✔
581
        var (
4✔
582
                keyScope   *waddrmgr.KeyScope
4✔
583
                accountNum uint32
4✔
584
        )
4✔
585
        switch accountName {
4✔
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:
4✔
590
                accountNum = defaultAccount
4✔
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)
4✔
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 {
4✔
618

4✔
619
        return b.wallet.DecorateInputs(packet, failOnUnknown)
4✔
620
}
4✔
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) {
4✔
629

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

644
                keyScope = scope
4✔
645

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

652
        return keyScope, account.AccountNumber, nil
4✔
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) {
4✔
691

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

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

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

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

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