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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

50.59
/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,
UNCOV
82
        allowUtxo func(wtxmgr.Credit) bool) (int32, error) {
×
UNCOV
83

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

×
UNCOV
88
        var (
×
UNCOV
89
                keyScope   *waddrmgr.KeyScope
×
UNCOV
90
                accountNum uint32
×
UNCOV
91
        )
×
UNCOV
92

×
UNCOV
93
        switch accountName {
×
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.
UNCOV
98
        case lnwallet.DefaultAccountName:
×
UNCOV
99
                if changeScope == nil {
×
UNCOV
100
                        changeScope = &waddrmgr.KeyScopeBIP0084
×
UNCOV
101
                }
×
102

UNCOV
103
                accountNum = defaultAccount
×
104

UNCOV
105
        case waddrmgr.ImportedAddrAccountName:
×
UNCOV
106
                if changeScope == nil {
×
UNCOV
107
                        changeScope = &waddrmgr.KeyScopeBIP0084
×
UNCOV
108
                }
×
109

UNCOV
110
                accountNum = importedAccount
×
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.
UNCOV
119
        default:
×
UNCOV
120
                if changeScope != nil {
×
121
                        return 0, fmt.Errorf("couldn't select a " +
×
122
                                "custom change type for custom accounts")
×
123
                }
×
124

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

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

142
        // Let the wallet handle coin selection and/or fee estimation based on
143
        // the partial TX information in the packet.
UNCOV
144
        return b.wallet.FundPsbt(
×
UNCOV
145
                packet, keyScope, minConfs, accountNum, feeSatPerKB,
×
UNCOV
146
                strategy, opts...,
×
UNCOV
147
        )
×
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) {
5✔
161
        // In signedInputs we return the indices of psbt inputs that were signed
5✔
162
        // by our wallet. This way the caller can check if any inputs were signed.
5✔
163
        var signedInputs []uint32
5✔
164

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

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

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

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

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

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

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

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

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

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

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

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

301
        case txscript.WitnessV1TaprootTy:
3✔
302
                if len(in.TaprootBip32Derivation) == 0 {
4✔
303
                        return 0, fmt.Errorf("cannot sign for taproot input " +
1✔
304
                                "without taproot BIP0032 derivation info")
1✔
305
                }
1✔
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 {
2✔
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]
2✔
319
                switch {
2✔
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:
1✔
325

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

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

UNCOV
339
                        return input.TaprootKeySpendSignMethod, nil
×
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:
1✔
347
                        // If we're supposed to be signing for a leaf hash, we
1✔
348
                        // also expect the leaf script that hashes to that hash
1✔
349
                        // in the appropriate field.
1✔
350
                        if len(in.TaprootLeafScript) != 1 {
1✔
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]
1✔
357
                        leaf := txscript.TapLeaf{
1✔
358
                                LeafVersion: leafScript.LeafVersion,
1✔
359
                                Script:      leafScript.Script,
1✔
360
                        }
1✔
361
                        leafHash := leaf.TapHash()
1✔
362
                        if !bytes.Equal(leafHash[:], derivation.LeafHashes[0]) {
1✔
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
1✔
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 {
8✔
388
        if in.WitnessUtxo == nil {
9✔
389
                return ErrInputMissingUTXOInfo
1✔
390
        }
1✔
391

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

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

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

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

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

1✔
417
                        w.AddTaprootKeySpendInput(in.SighashType)
1✔
418

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

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

431
        default:
1✔
432
                return fmt.Errorf("unknown input type for script %x: %w",
1✔
433
                        pkScript, ErrUnsupportedScript)
1✔
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 {
5✔
445

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

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

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

5✔
468
        return nil
5✔
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,
UNCOV
475
        tapscriptRootHash []byte) error {
×
UNCOV
476

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

UNCOV
487
        in.TaprootKeySpendSig = rawSig
×
UNCOV
488

×
UNCOV
489
        return nil
×
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,
UNCOV
496
        leaf txscript.TapLeaf) error {
×
UNCOV
497

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

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

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

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

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

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

555
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakDouble) {
2✔
556
                        doubleTweakKey, _ := btcec.PrivKeyFromBytes(
1✔
557
                                u.Value,
1✔
558
                        )
1✔
559
                        return input.DeriveRevocationPrivKey(
1✔
560
                                privKey, doubleTweakKey,
1✔
561
                        )
1✔
562
                }
1✔
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.
UNCOV
580
func (b *BtcWallet) FinalizePsbt(packet *psbt.Packet, accountName string) error {
×
UNCOV
581
        var (
×
UNCOV
582
                keyScope   *waddrmgr.KeyScope
×
UNCOV
583
                accountNum uint32
×
UNCOV
584
        )
×
UNCOV
585
        switch accountName {
×
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).
UNCOV
589
        case lnwallet.DefaultAccountName:
×
UNCOV
590
                accountNum = defaultAccount
×
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

UNCOV
607
        return b.wallet.FinalizePsbt(keyScope, accountNum, packet)
×
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,
UNCOV
617
        failOnUnknown bool) error {
×
UNCOV
618

×
UNCOV
619
        return b.wallet.DecorateInputs(packet, failOnUnknown)
×
UNCOV
620
}
×
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(
UNCOV
628
        name string) (waddrmgr.KeyScope, uint32, error) {
×
UNCOV
629

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

UNCOV
644
                keyScope = scope
×
UNCOV
645

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

UNCOV
652
        return keyScope, account.AccountNumber, nil
×
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) {
2✔
660

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

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

2✔
678
        return bip32Derivation, &psbt.TaprootBip32Derivation{
2✔
679
                XOnlyPubKey:          bip32Derivation.PubKey[1:],
2✔
680
                MasterKeyFingerprint: bip32Derivation.MasterKeyFingerprint,
2✔
681
                Bip32Path:            bip32Derivation.Bip32Path,
2✔
682
                LeafHashes:           make([][]byte, 0),
2✔
683
        }, derivationPath
2✔
684
}
2✔
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) {
2✔
691

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

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

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

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

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