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

lightningnetwork / lnd / 12558220912

31 Dec 2024 09:54AM UTC coverage: 58.601% (+0.003%) from 58.598%
12558220912

Pull #9398

github

guggero
rpcserver: add silent payment support to SendCoins RPC
Pull Request #9398: Add Silent Payment send support

63 of 335 new or added lines in 2 files covered. (18.81%)

62 existing lines in 18 files now uncovered.

135175 of 230672 relevant lines covered (58.6%)

19150.32 hits per line

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

61.92
/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/btcutil/silentpayments"
15
        "github.com/btcsuite/btcd/chaincfg"
16
        "github.com/btcsuite/btcd/txscript"
17
        "github.com/btcsuite/btcd/wire"
18
        "github.com/btcsuite/btcwallet/waddrmgr"
19
        "github.com/btcsuite/btcwallet/wallet"
20
        "github.com/btcsuite/btcwallet/wtxmgr"
21
        "github.com/lightningnetwork/lnd/fn/v2"
22
        "github.com/lightningnetwork/lnd/input"
23
        "github.com/lightningnetwork/lnd/keychain"
24
        "github.com/lightningnetwork/lnd/lnwallet"
25
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
26
)
27

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

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

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

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

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

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

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

3✔
91
        var (
3✔
92
                keyScope   *waddrmgr.KeyScope
3✔
93
                accountNum uint32
3✔
94
        )
3✔
95

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

106
                accountNum = defaultAccount
3✔
107

108
        case waddrmgr.ImportedAddrAccountName:
3✔
109
                if changeScope == nil {
6✔
110
                        changeScope = &waddrmgr.KeyScopeBIP0084
3✔
111
                }
3✔
112

113
                accountNum = importedAccount
3✔
114

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

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

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

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

153
// signInfo is a helper struct that holds the private key and signing method for
154
// a given input.
155
type signInfo struct {
156
        privKey    btcec.PrivateKey
157
        signMethod input.SignMethod
158
}
159

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

8✔
175
        // Let's check that this is actually something we can and want to sign.
8✔
176
        // We need at least one input and one output. In addition each
8✔
177
        // input needs nonWitness Utxo or witness Utxo data specified.
8✔
178
        err := psbt.InputsReadyToSign(packet)
8✔
179
        if err != nil {
8✔
180
                return nil, err
×
181
        }
×
182

183
        // Because we need to potentially create Silent Payment shares with our
184
        // private keys before we can sign, we'll keep track of the input
185
        // information in a map so we don't have to fetch it twice.
186
        inputInfo := make(map[wire.OutPoint]*signInfo)
8✔
187

8✔
188
        // We'll start by fetching all private keys and determining the signing
8✔
189
        // method for each input that we can sign.
8✔
190
        tx := packet.UnsignedTx
8✔
191
        for idx := range tx.TxIn {
16✔
192
                in := &packet.Inputs[idx]
8✔
193

8✔
194
                // We can only sign if we have UTXO information available. Since
8✔
195
                // we don't finalize, we just skip over any input that we know
8✔
196
                // we can't do anything with. Since we only support signing
8✔
197
                // witness inputs, we only look at the witness UTXO being set.
8✔
198
                if in.WitnessUtxo == nil {
8✔
199
                        continue
×
200
                }
201

202
                // Skip this input if it's got final witness data attached.
203
                if len(in.FinalScriptWitness) > 0 {
11✔
204
                        continue
3✔
205
                }
206

207
                // Skip this input if there is no BIP32 derivation info
208
                // available.
209
                if len(in.Bip32Derivation) == 0 {
11✔
210
                        continue
3✔
211
                }
212

213
                // TODO(guggero): For multisig, we'll need to find out what key
214
                // to use and there should be multiple derivation paths in the
215
                // BIP32 derivation field.
216

217
                // Let's try and derive the key now. This method will decide if
218
                // it's a BIP49/84 key for normal on-chain funds or a key of the
219
                // custom purpose 1017 key scope.
220
                derivationInfo := in.Bip32Derivation[0]
8✔
221
                privKey, err := b.deriveKeyByBIP32Path(derivationInfo.Bip32Path)
8✔
222
                if err != nil {
8✔
223
                        log.Warnf("SignPsbt: Skipping input %d, error "+
×
224
                                "deriving signing key: %v", idx, err)
×
225
                        continue
×
226
                }
227

228
                // We need to make sure we actually derived the key that was
229
                // expected to be derived.
230
                pubKeysEqual := bytes.Equal(
8✔
231
                        derivationInfo.PubKey,
8✔
232
                        privKey.PubKey().SerializeCompressed(),
8✔
233
                )
8✔
234
                if !pubKeysEqual {
11✔
235
                        log.Warnf("SignPsbt: Skipping input %d, derived "+
3✔
236
                                "public key %x does not match bip32 "+
3✔
237
                                "derivation info public key %x", idx,
3✔
238
                                privKey.PubKey().SerializeCompressed(),
3✔
239
                                derivationInfo.PubKey)
3✔
240
                        continue
3✔
241
                }
242

243
                // Do we need to tweak anything? Single or double tweaks are
244
                // sent as custom/proprietary fields in the PSBT input section.
245
                privKey = maybeTweakPrivKeyPsbt(in.Unknowns, privKey)
8✔
246

8✔
247
                // What kind of signature is expected from us and do we have all
8✔
248
                // information we need?
8✔
249
                signMethod, err := validateSigningMethod(in)
8✔
250
                if err != nil {
8✔
251
                        return nil, err
×
252
                }
×
253

254
                inputInfo[tx.TxIn[idx].PreviousOutPoint] = &signInfo{
8✔
255
                        privKey:    *privKey,
8✔
256
                        signMethod: signMethod,
8✔
257
                }
8✔
258
        }
259

260
        // Now we can create the shares of all inputs for any potential silent
261
        // payment outputs.
262
        err = maybeCreateSilentPaymentShares(b.cfg.NetParams, packet, inputInfo)
8✔
263
        if err != nil {
8✔
NEW
264
                return nil, err
×
NEW
265
        }
×
266

267
        // Go through each input that doesn't have final witness data attached
268
        // to it already and try to sign it. If there is nothing more to sign or
269
        // there are inputs that we don't know how to sign, we won't return any
270
        // error. So it's possible we're not the final signer.
271
        prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
8✔
272
        sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
8✔
273
        for idx := range tx.TxIn {
16✔
274
                in := &packet.Inputs[idx]
8✔
275

8✔
276
                info, ok := inputInfo[tx.TxIn[idx].PreviousOutPoint]
8✔
277
                if !ok {
11✔
278
                        // We don't have any information about this input, so we
3✔
279
                        // can't sign it.
3✔
280
                        continue
3✔
281
                }
282

283
                var rootHash []byte
8✔
284
                switch info.signMethod {
8✔
285
                // For p2wkh, np2wkh and p2wsh.
286
                case input.WitnessV0SignMethod:
8✔
287
                        err = signSegWitV0(
8✔
288
                                in, tx, sigHashes, idx, &info.privKey,
8✔
289
                        )
8✔
290

291
                // For p2tr BIP0086 key spend only.
292
                case input.TaprootKeySpendBIP0086SignMethod:
3✔
293
                        rootHash = make([]byte, 0)
3✔
294
                        err = signSegWitV1KeySpend(
3✔
295
                                in, tx, sigHashes, idx, &info.privKey, rootHash,
3✔
296
                        )
3✔
297

298
                // For p2tr with script commitment key spend path.
299
                case input.TaprootKeySpendSignMethod:
3✔
300
                        rootHash = in.TaprootMerkleRoot
3✔
301
                        err = signSegWitV1KeySpend(
3✔
302
                                in, tx, sigHashes, idx, &info.privKey, rootHash,
3✔
303
                        )
3✔
304

305
                // For p2tr script spend path.
306
                case input.TaprootScriptSpendSignMethod:
3✔
307
                        leafScript := in.TaprootLeafScript[0]
3✔
308
                        leaf := txscript.TapLeaf{
3✔
309
                                LeafVersion: leafScript.LeafVersion,
3✔
310
                                Script:      leafScript.Script,
3✔
311
                        }
3✔
312
                        err = signSegWitV1ScriptSpend(
3✔
313
                                in, tx, sigHashes, idx, &info.privKey, leaf,
3✔
314
                        )
3✔
315

316
                default:
×
317
                        err = fmt.Errorf("unsupported signing method for "+
×
NEW
318
                                "PSBT signing: %v", info.signMethod)
×
319
                }
320
                if err != nil {
8✔
321
                        return nil, err
×
322
                }
×
323
                signedInputs = append(signedInputs, uint32(idx))
8✔
324
        }
325

326
        return signedInputs, nil
8✔
327
}
328

329
// maybeCreateSilentPaymentShares creates the silent payment shares for all
330
// silent payment outputs in the PSBT packet that we can sign for.
331
func maybeCreateSilentPaymentShares(params *chaincfg.Params,
332
        packet *psbt.Packet, signInfo map[wire.OutPoint]*signInfo) error {
8✔
333

8✔
334
        // First, check if we have any silent payment outputs in the PSBT. If we
8✔
335
        // do, we collect the information, so we can sort it correctly for
8✔
336
        // determining the order in case there are multiple addresses with the
8✔
337
        // same scan key.
8✔
338
        type spOutputInfo struct {
8✔
339
                spAddrInfo *psbt.SilentPaymentInfo
8✔
340
                spAddr     *silentpayments.Address
8✔
341
                outIndex   int
8✔
342
        }
8✔
343
        spOutputs := make([]spOutputInfo, 0, len(packet.Outputs))
8✔
344
        for idx := range packet.Outputs {
16✔
345
                pOut := &packet.Outputs[idx]
8✔
346

8✔
347
                if pOut.SilentPaymentInfo == nil {
16✔
348
                        continue
8✔
349
                }
350

NEW
351
                info := pOut.SilentPaymentInfo
×
NEW
352
                addr, err := silentpayments.ParseAddress(
×
NEW
353
                        params, info.ScanKey, info.SpendKey,
×
NEW
354
                )
×
NEW
355
                if err != nil {
×
NEW
356
                        return fmt.Errorf("error parsing silent payment "+
×
NEW
357
                                "address: %w", err)
×
NEW
358
                }
×
359

NEW
360
                spOutputs = append(spOutputs, spOutputInfo{
×
NEW
361
                        spAddrInfo: info,
×
NEW
362
                        spAddr:     addr,
×
NEW
363
                        outIndex:   idx,
×
NEW
364
                })
×
365
        }
366

367
        // If there are no silent payment outputs, we can return early.
368
        if len(spOutputs) == 0 {
16✔
369
                return nil
8✔
370
        }
8✔
371

372
        // For now, we only support creating shares if we're the only signer.
373
        //
374
        // TODO(guggero): Implement verifying shares from other signers and
375
        // creating our own shares.
NEW
376
        if len(signInfo) != len(packet.Inputs) {
×
NEW
377
                return fmt.Errorf("cannot create silent payment shares with " +
×
NEW
378
                        "multiple signers, not implemented yet")
×
NEW
379
        }
×
380

NEW
381
        A, err := sumInputPubKeys(packet, nil)
×
NEW
382
        if err != nil {
×
NEW
383
                return fmt.Errorf("error summing input public keys: %w", err)
×
NEW
384
        }
×
385

NEW
386
        a, err := sumInputPrivKeys(packet, signInfo)
×
NEW
387
        if err != nil {
×
NEW
388
                return fmt.Errorf("error summing input private keys: %w", err)
×
NEW
389
        }
×
390

391
        // Make sure our sum is correct.
NEW
392
        if !a.PubKey().IsEqual(A) {
×
NEW
393
                return fmt.Errorf("sum of input private keys does not match " +
×
NEW
394
                        "sum of input public keys")
×
NEW
395
        }
×
396

397
        // Prepare our list of silent payment recipients.
NEW
398
        spAddresses := make([]silentpayments.Address, len(spOutputs))
×
NEW
399
        for i, spOutput := range spOutputs {
×
NEW
400
                spAddresses[i] = *spOutput.spAddr
×
NEW
401
        }
×
402

403
        // Calculate the input hash tweak for the silent payment shares.
NEW
404
        inputOutpoints := make([]wire.OutPoint, 0, len(packet.Inputs))
×
NEW
405
        for op := range signInfo {
×
NEW
406
                inputOutpoints = append(inputOutpoints, op)
×
NEW
407
        }
×
408

NEW
409
        inputHash, err := silentpayments.CalculateInputHashTweak(
×
NEW
410
                inputOutpoints, A,
×
NEW
411
        )
×
NEW
412
        if err != nil {
×
NEW
413
                return fmt.Errorf("error calculating input hash tweak: %w", err)
×
NEW
414
        }
×
415

416
        // Now we have everything to calculate the actual on-chain output keys
417
        // for the silent payment outputs.
NEW
418
        outputKeys, err := silentpayments.AddressOutputKeys(
×
NEW
419
                spAddresses, a.Key, *inputHash,
×
NEW
420
        )
×
NEW
421
        if err != nil {
×
NEW
422
                return fmt.Errorf("error creating output keys: %w", err)
×
NEW
423
        }
×
424

425
        // If there was only a single input, we can omit it in the shares and
426
        // proofs list, to make the PSBT smaller.
NEW
427
        if len(inputOutpoints) == 1 {
×
NEW
428
                inputOutpoints = nil
×
NEW
429
        }
×
430

431
        // And then we just have to map them back to the PSBT packet.
NEW
432
        for _, outputKey := range outputKeys {
×
NEW
433
                for _, spOut := range spOutputs {
×
NEW
434
                        if !outputKey.Address.Equal(spOut.spAddr) {
×
NEW
435
                                continue
×
436
                        }
437

NEW
438
                        txOut := packet.UnsignedTx.TxOut[spOut.outIndex]
×
NEW
439
                        txOut.PkScript, err = txscript.PayToTaprootScript(
×
NEW
440
                                outputKey.OutputKey,
×
NEW
441
                        )
×
NEW
442
                        if err != nil {
×
NEW
443
                                return fmt.Errorf("error creating taproot "+
×
NEW
444
                                        "script: %w", err)
×
NEW
445
                        }
×
446

NEW
447
                        share, proof, err := silentpayments.CreateShare(
×
NEW
448
                                a, &spOut.spAddr.ScanKey,
×
NEW
449
                        )
×
NEW
450
                        if err != nil {
×
NEW
451
                                return fmt.Errorf("error creating share: %w",
×
NEW
452
                                        err)
×
NEW
453
                        }
×
454

NEW
455
                        packet.SilentPaymentShares = append(
×
NEW
456
                                packet.SilentPaymentShares,
×
NEW
457
                                psbt.SilentPaymentShare{
×
NEW
458
                                        ScanKey:   spOut.spAddrInfo.ScanKey,
×
NEW
459
                                        OutPoints: inputOutpoints,
×
NEW
460
                                        Share:     share.SerializeCompressed(),
×
NEW
461
                                },
×
NEW
462
                        )
×
NEW
463
                        packet.SilentPaymentDLEQs = append(
×
NEW
464
                                packet.SilentPaymentDLEQs,
×
NEW
465
                                psbt.SilentPaymentDLEQ{
×
NEW
466
                                        ScanKey:   spOut.spAddrInfo.ScanKey,
×
NEW
467
                                        OutPoints: inputOutpoints,
×
NEW
468
                                        Proof:     proof[:],
×
NEW
469
                                },
×
NEW
470
                        )
×
471
                }
472
        }
473

474
        // Since we updated the output keys, all signatures have now become
475
        // invalid. We'll remove them from the PSBT packet.
NEW
476
        for idx := range packet.Inputs {
×
NEW
477
                packet.Inputs[idx].FinalScriptWitness = nil
×
NEW
478
                packet.Inputs[idx].PartialSigs = nil
×
NEW
479
                packet.Inputs[idx].TaprootScriptSpendSig = nil
×
NEW
480
                packet.Inputs[idx].TaprootKeySpendSig = nil
×
NEW
481
        }
×
482

NEW
483
        return nil
×
484
}
485

486
// sumInputPubKeys returns the sum of all public keys of the inputs of a PSBT
487
// packet, by looking up the BIP-0032 derivation information for each input. If
488
// the input set is empty, the sum of _all_ inputs in the packet is returned.
489
func sumInputPubKeys(packet *psbt.Packet,
NEW
490
        inputs fn.Set[wire.OutPoint]) (*btcec.PublicKey, error) {
×
NEW
491

×
NEW
492
        var result *btcec.PublicKey
×
NEW
493
        for idx := range packet.Inputs {
×
NEW
494
                prevOut := packet.UnsignedTx.TxIn[idx].PreviousOutPoint
×
NEW
495

×
NEW
496
                // If we're only interested in a subset of inputs, we skip
×
NEW
497
                // those that are not in the set.
×
NEW
498
                if !inputs.IsEmpty() && !inputs.Contains(prevOut) {
×
NEW
499
                        continue
×
500
                }
501

NEW
502
                var (
×
NEW
503
                        in     = &packet.Inputs[idx]
×
NEW
504
                        pubKey *btcec.PublicKey
×
NEW
505
                        err    error
×
NEW
506
                )
×
NEW
507
                switch {
×
NEW
508
                case txscript.IsPayToTaproot(in.WitnessUtxo.PkScript):
×
NEW
509
                        pubKey, err = schnorr.ParsePubKey(
×
NEW
510
                                in.WitnessUtxo.PkScript[2:34],
×
NEW
511
                        )
×
512

NEW
513
                case len(in.Bip32Derivation) > 0:
×
NEW
514
                        derivation := in.Bip32Derivation[0]
×
NEW
515
                        pubKey, err = btcec.ParsePubKey(derivation.PubKey)
×
516

NEW
517
                default:
×
NEW
518
                        return nil, fmt.Errorf("missing BIP32 derivation "+
×
NEW
519
                                "information for input %v", prevOut)
×
520
                }
NEW
521
                if err != nil {
×
NEW
522
                        return nil, fmt.Errorf("error parsing public key: %w",
×
NEW
523
                                err)
×
NEW
524
                }
×
525

NEW
526
                if result == nil {
×
NEW
527
                        result = pubKey
×
NEW
528
                } else {
×
NEW
529
                        silentpayments.Add(result, pubKey)
×
NEW
530
                }
×
531
        }
532

NEW
533
        return result, nil
×
534
}
535

536
// sumInputPrivKeys returns the sum of all private keys of the inputs of a PSBT
537
// packet, by looking up the private key for each input. If we don't know the
538
// private key of an input, it's not included in the sum.
539
func sumInputPrivKeys(packet *psbt.Packet,
NEW
540
        signInfo map[wire.OutPoint]*signInfo) (*btcec.PrivateKey, error) {
×
NEW
541

×
NEW
542
        var result *btcec.PrivateKey
×
NEW
543
        for idx := range packet.Inputs {
×
NEW
544
                txIn := packet.UnsignedTx.TxIn[idx]
×
NEW
545
                vIn := &packet.Inputs[idx]
×
NEW
546

×
NEW
547
                info, ok := signInfo[txIn.PreviousOutPoint]
×
NEW
548
                if !ok {
×
NEW
549
                        continue
×
550
                }
551

NEW
552
                var privKey *btcec.PrivateKey
×
NEW
553
                switch info.signMethod {
×
NEW
554
                case input.WitnessV0SignMethod:
×
NEW
555
                        privKey = &info.privKey
×
556

NEW
557
                case input.TaprootKeySpendBIP0086SignMethod:
×
NEW
558
                        privKey = txscript.TweakTaprootPrivKey(
×
NEW
559
                                info.privKey, make([]byte, 0),
×
NEW
560
                        )
×
561

NEW
562
                case input.TaprootKeySpendSignMethod:
×
NEW
563
                        privKey = txscript.TweakTaprootPrivKey(
×
NEW
564
                                info.privKey, vIn.TaprootMerkleRoot,
×
NEW
565
                        )
×
566

NEW
567
                default:
×
NEW
568
                        return nil, fmt.Errorf("unsupported signing method "+
×
NEW
569
                                "for silent payments: %v", info.signMethod)
×
570
                }
571

NEW
572
                if result == nil {
×
NEW
573
                        result = privKey
×
NEW
574
                } else {
×
NEW
575
                        result.Key.Add(&privKey.Key)
×
NEW
576
                }
×
577
        }
578

NEW
579
        return result, nil
×
580
}
581

582
// validateSigningMethod attempts to detect the signing method that is required
583
// to sign for the given PSBT input and makes sure all information is available
584
// to do so.
585
func validateSigningMethod(in *psbt.PInput) (input.SignMethod, error) {
11✔
586
        script, err := txscript.ParsePkScript(in.WitnessUtxo.PkScript)
11✔
587
        if err != nil {
11✔
588
                return 0, fmt.Errorf("error detecting signing method, "+
×
589
                        "couldn't parse pkScript: %v", err)
×
590
        }
×
591

592
        switch script.Class() {
11✔
593
        case txscript.WitnessV0PubKeyHashTy, txscript.ScriptHashTy,
594
                txscript.WitnessV0ScriptHashTy:
8✔
595

8✔
596
                return input.WitnessV0SignMethod, nil
8✔
597

598
        case txscript.WitnessV1TaprootTy:
6✔
599
                if len(in.TaprootBip32Derivation) == 0 {
7✔
600
                        return 0, fmt.Errorf("cannot sign for taproot input " +
1✔
601
                                "without taproot BIP0032 derivation info")
1✔
602
                }
1✔
603

604
                // Currently, we only support creating one signature per input.
605
                //
606
                // TODO(guggero): Should we support signing multiple paths at
607
                // the same time? What are the performance and security
608
                // implications?
609
                if len(in.TaprootBip32Derivation) > 1 {
5✔
610
                        return 0, fmt.Errorf("unsupported multiple taproot " +
×
611
                                "BIP0032 derivation info found, can only " +
×
612
                                "sign for one at a time")
×
613
                }
×
614

615
                derivation := in.TaprootBip32Derivation[0]
5✔
616
                switch {
5✔
617
                // No leaf hashes means this is the internal key we're signing
618
                // with, so it's a key spend. And no merkle root means this is
619
                // a BIP0086 output we're signing for.
620
                case len(derivation.LeafHashes) == 0 &&
621
                        len(in.TaprootMerkleRoot) == 0:
4✔
622

4✔
623
                        return input.TaprootKeySpendBIP0086SignMethod, nil
4✔
624

625
                // A non-empty merkle root means we committed to a taproot hash
626
                // that we need to use in the tap tweak.
627
                case len(derivation.LeafHashes) == 0:
3✔
628
                        // Getting here means the merkle root isn't empty, but
3✔
629
                        // is it exactly the length we need?
3✔
630
                        if len(in.TaprootMerkleRoot) != sha256.Size {
3✔
631
                                return 0, fmt.Errorf("invalid taproot merkle "+
×
632
                                        "root length, got %d expected %d",
×
633
                                        len(in.TaprootMerkleRoot), sha256.Size)
×
634
                        }
×
635

636
                        return input.TaprootKeySpendSignMethod, nil
3✔
637

638
                // Currently, we only support signing for one leaf at a time.
639
                //
640
                // TODO(guggero): Should we support signing multiple paths at
641
                // the same time? What are the performance and security
642
                // implications?
643
                case len(derivation.LeafHashes) == 1:
4✔
644
                        // If we're supposed to be signing for a leaf hash, we
4✔
645
                        // also expect the leaf script that hashes to that hash
4✔
646
                        // in the appropriate field.
4✔
647
                        if len(in.TaprootLeafScript) != 1 {
4✔
648
                                return 0, fmt.Errorf("specified leaf hash in " +
×
649
                                        "taproot BIP0032 derivation but " +
×
650
                                        "missing taproot leaf script")
×
651
                        }
×
652

653
                        leafScript := in.TaprootLeafScript[0]
4✔
654
                        leaf := txscript.TapLeaf{
4✔
655
                                LeafVersion: leafScript.LeafVersion,
4✔
656
                                Script:      leafScript.Script,
4✔
657
                        }
4✔
658
                        leafHash := leaf.TapHash()
4✔
659
                        if !bytes.Equal(leafHash[:], derivation.LeafHashes[0]) {
4✔
660
                                return 0, fmt.Errorf("specified leaf hash in" +
×
661
                                        "taproot BIP0032 derivation but " +
×
662
                                        "corresponding taproot leaf script " +
×
663
                                        "was not found")
×
664
                        }
×
665

666
                        return input.TaprootScriptSpendSignMethod, nil
4✔
667

668
                default:
×
669
                        return 0, fmt.Errorf("unsupported number of leaf " +
×
670
                                "hashes in taproot BIP0032 derivation info, " +
×
671
                                "can only sign for one at a time")
×
672
                }
673

674
        default:
×
675
                return 0, fmt.Errorf("unsupported script class for signing "+
×
676
                        "PSBT: %v", script.Class())
×
677
        }
678
}
679

680
// EstimateInputWeight estimates the weight of a PSBT input and adds it to the
681
// passed in TxWeightEstimator. It returns an error if the input type is
682
// unknown or unsupported. Only inputs that have a known witness size are
683
// supported, which is P2WKH, NP2WKH and P2TR (key spend path).
684
func EstimateInputWeight(in *psbt.PInput, w *input.TxWeightEstimator) error {
11✔
685
        if in.WitnessUtxo == nil {
12✔
686
                return ErrInputMissingUTXOInfo
1✔
687
        }
1✔
688

689
        pkScript := in.WitnessUtxo.PkScript
10✔
690
        switch {
10✔
691
        case txscript.IsPayToScriptHash(pkScript):
1✔
692
                w.AddNestedP2WKHInput()
1✔
693

694
        case txscript.IsPayToWitnessPubKeyHash(pkScript):
4✔
695
                w.AddP2WKHInput()
4✔
696

697
        case txscript.IsPayToWitnessScriptHash(pkScript):
1✔
698
                return fmt.Errorf("P2WSH inputs are not supported, cannot "+
1✔
699
                        "estimate witness size for script spend: %w",
1✔
700
                        ErrScriptSpendFeeEstimationUnsupported)
1✔
701

702
        case txscript.IsPayToTaproot(pkScript):
3✔
703
                signMethod, err := validateSigningMethod(in)
3✔
704
                if err != nil {
4✔
705
                        return fmt.Errorf("error determining p2tr signing "+
1✔
706
                                "method: %w", err)
1✔
707
                }
1✔
708

709
                switch signMethod {
2✔
710
                // For p2tr key spend paths.
711
                case input.TaprootKeySpendBIP0086SignMethod,
712
                        input.TaprootKeySpendSignMethod:
1✔
713

1✔
714
                        w.AddTaprootKeySpendInput(in.SighashType)
1✔
715

716
                // For p2tr script spend path.
717
                case input.TaprootScriptSpendSignMethod:
1✔
718
                        return fmt.Errorf("P2TR inputs are not supported, "+
1✔
719
                                "cannot estimate witness size for script "+
1✔
720
                                "spend: %w",
1✔
721
                                ErrScriptSpendFeeEstimationUnsupported)
1✔
722

723
                default:
×
724
                        return fmt.Errorf("unsupported signing method for "+
×
725
                                "PSBT signing: %v", signMethod)
×
726
                }
727

728
        default:
1✔
729
                return fmt.Errorf("unknown input type for script %x: %w",
1✔
730
                        pkScript, ErrUnsupportedScript)
1✔
731
        }
732

733
        return nil
6✔
734
}
735

736
// SignSegWitV0 attempts to generate a signature for a SegWit version 0 input
737
// and stores it in the PartialSigs (and FinalScriptSig for np2wkh addresses)
738
// field.
739
func signSegWitV0(in *psbt.PInput, tx *wire.MsgTx,
740
        sigHashes *txscript.TxSigHashes, idx int,
741
        privKey *btcec.PrivateKey) error {
8✔
742

8✔
743
        pubKeyBytes := privKey.PubKey().SerializeCompressed()
8✔
744

8✔
745
        // Extract the correct witness and/or legacy scripts now, depending on
8✔
746
        // the type of input we sign. The txscript package has the peculiar
8✔
747
        // requirement that the PkScript of a P2PKH must be given as the witness
8✔
748
        // script in order for it to arrive at the correct sighash. That's why
8✔
749
        // we call it subScript here instead of witness script.
8✔
750
        subScript := prepareScriptsV0(in)
8✔
751

8✔
752
        // We have everything we need for signing the input now.
8✔
753
        sig, err := txscript.RawTxInWitnessSignature(
8✔
754
                tx, sigHashes, idx, in.WitnessUtxo.Value, subScript,
8✔
755
                in.SighashType, privKey,
8✔
756
        )
8✔
757
        if err != nil {
8✔
758
                return fmt.Errorf("error signing input %d: %w", idx, err)
×
759
        }
×
760
        in.PartialSigs = append(in.PartialSigs, &psbt.PartialSig{
8✔
761
                PubKey:    pubKeyBytes,
8✔
762
                Signature: sig,
8✔
763
        })
8✔
764

8✔
765
        return nil
8✔
766
}
767

768
// signSegWitV1KeySpend attempts to generate a signature for a SegWit version 1
769
// (p2tr) input and stores it in the TaprootKeySpendSig field.
770
func signSegWitV1KeySpend(in *psbt.PInput, tx *wire.MsgTx,
771
        sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
772
        tapscriptRootHash []byte) error {
3✔
773

3✔
774
        rawSig, err := txscript.RawTxInTaprootSignature(
3✔
775
                tx, sigHashes, idx, in.WitnessUtxo.Value,
3✔
776
                in.WitnessUtxo.PkScript, tapscriptRootHash, in.SighashType,
3✔
777
                privKey,
3✔
778
        )
3✔
779
        if err != nil {
3✔
780
                return fmt.Errorf("error signing taproot input %d: %w", idx,
×
781
                        err)
×
782
        }
×
783

784
        in.TaprootKeySpendSig = rawSig
3✔
785

3✔
786
        return nil
3✔
787
}
788

789
// signSegWitV1ScriptSpend attempts to generate a signature for a SegWit version
790
// 1 (p2tr) input and stores it in the TaprootScriptSpendSig field.
791
func signSegWitV1ScriptSpend(in *psbt.PInput, tx *wire.MsgTx,
792
        sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
793
        leaf txscript.TapLeaf) error {
3✔
794

3✔
795
        rawSig, err := txscript.RawTxInTapscriptSignature(
3✔
796
                tx, sigHashes, idx, in.WitnessUtxo.Value,
3✔
797
                in.WitnessUtxo.PkScript, leaf, in.SighashType, privKey,
3✔
798
        )
3✔
799
        if err != nil {
3✔
800
                return fmt.Errorf("error signing taproot script input %d: %w",
×
801
                        idx, err)
×
802
        }
×
803

804
        leafHash := leaf.TapHash()
3✔
805
        in.TaprootScriptSpendSig = append(
3✔
806
                in.TaprootScriptSpendSig, &psbt.TaprootScriptSpendSig{
3✔
807
                        XOnlyPubKey: in.TaprootBip32Derivation[0].XOnlyPubKey,
3✔
808
                        LeafHash:    leafHash[:],
3✔
809
                        // We snip off the sighash flag from the end (if it was
3✔
810
                        // specified in the first place.)
3✔
811
                        Signature: rawSig[:schnorr.SignatureSize],
3✔
812
                        SigHash:   in.SighashType,
3✔
813
                },
3✔
814
        )
3✔
815

3✔
816
        return nil
3✔
817
}
818

819
// prepareScriptsV0 returns the appropriate witness v0 and/or legacy scripts,
820
// depending on the type of input that should be signed.
821
func prepareScriptsV0(in *psbt.PInput) []byte {
8✔
822
        switch {
8✔
823
        // It's a NP2WKH input:
824
        case len(in.RedeemScript) > 0:
4✔
825
                return in.RedeemScript
4✔
826

827
        // It's a P2WSH input:
828
        case len(in.WitnessScript) > 0:
5✔
829
                return in.WitnessScript
5✔
830

831
        // It's a P2WKH input:
832
        default:
5✔
833
                return in.WitnessUtxo.PkScript
5✔
834
        }
835
}
836

837
// maybeTweakPrivKeyPsbt examines if there are any tweak parameters given in the
838
// custom/proprietary PSBT fields and may perform a mapping on the passed
839
// private key in order to utilize the tweaks, if populated.
840
func maybeTweakPrivKeyPsbt(unknowns []*psbt.Unknown,
841
        privKey *btcec.PrivateKey) *btcec.PrivateKey {
8✔
842

8✔
843
        // There can be other custom/unknown keys in a PSBT that we just ignore.
8✔
844
        // Key tweaking is optional and only one tweak (single _or_ double) can
8✔
845
        // ever be applied (at least for any use cases described in the BOLT
8✔
846
        // spec).
8✔
847
        for _, u := range unknowns {
13✔
848
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakSingle) {
9✔
849
                        return input.TweakPrivKey(privKey, u.Value)
4✔
850
                }
4✔
851

852
                if bytes.Equal(u.Key, PsbtKeyTypeInputSignatureTweakDouble) {
2✔
853
                        doubleTweakKey, _ := btcec.PrivKeyFromBytes(
1✔
854
                                u.Value,
1✔
855
                        )
1✔
856
                        return input.DeriveRevocationPrivKey(
1✔
857
                                privKey, doubleTweakKey,
1✔
858
                        )
1✔
859
                }
1✔
860
        }
861

862
        return privKey
6✔
863
}
864

865
// FinalizePsbt expects a partial transaction with all inputs and outputs fully
866
// declared and tries to sign all inputs that belong to the specified account.
867
// Lnd must be the last signer of the transaction. That means, if there are any
868
// unsigned non-witness inputs or inputs without UTXO information attached or
869
// inputs without witness data that do not belong to lnd's wallet, this method
870
// will fail. If no error is returned, the PSBT is ready to be extracted and the
871
// final TX within to be broadcast.
872
//
873
// NOTE: This method does NOT publish the transaction after it's been
874
// finalized successfully.
875
//
876
// This is a part of the WalletController interface.
877
func (b *BtcWallet) FinalizePsbt(packet *psbt.Packet, accountName string) error {
3✔
878
        var (
3✔
879
                keyScope   *waddrmgr.KeyScope
3✔
880
                accountNum uint32
3✔
881
        )
3✔
882
        switch accountName {
3✔
883
        // If the default/imported account name was specified, we'll provide a
884
        // nil key scope to FundPsbt, allowing it to sign inputs from both key
885
        // scopes (NP2WKH, P2WKH).
886
        case lnwallet.DefaultAccountName:
3✔
887
                accountNum = defaultAccount
3✔
888

889
        case waddrmgr.ImportedAddrAccountName:
×
890
                accountNum = importedAccount
×
891

892
        // Otherwise, map the account name to its key scope and internal account
893
        // number to determine if the inputs belonging to this account should be
894
        // signed.
895
        default:
×
896
                scope, account, err := b.lookupFirstCustomAccount(accountName)
×
897
                if err != nil {
×
898
                        return err
×
899
                }
×
900
                keyScope = &scope
×
901
                accountNum = account
×
902
        }
903

904
        return b.wallet.FinalizePsbt(keyScope, accountNum, packet)
3✔
905
}
906

907
// DecorateInputs fetches the UTXO information of all inputs it can identify and
908
// adds the required information to the package's inputs. The failOnUnknown
909
// boolean controls whether the method should return an error if it cannot
910
// identify an input or if it should just skip it.
911
//
912
// This is a part of the WalletController interface.
913
func (b *BtcWallet) DecorateInputs(packet *psbt.Packet,
914
        failOnUnknown bool) error {
3✔
915

3✔
916
        return b.wallet.DecorateInputs(packet, failOnUnknown)
3✔
917
}
3✔
918

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

3✔
927
        var (
3✔
928
                account  *waddrmgr.AccountProperties
3✔
929
                keyScope waddrmgr.KeyScope
3✔
930
        )
3✔
931
        for _, scope := range waddrmgr.DefaultKeyScopes {
6✔
932
                var err error
3✔
933
                account, err = b.wallet.AccountPropertiesByName(scope, name)
3✔
934
                if waddrmgr.IsError(err, waddrmgr.ErrAccountNotFound) {
6✔
935
                        continue
3✔
936
                }
937
                if err != nil {
3✔
938
                        return keyScope, 0, err
×
939
                }
×
940

941
                keyScope = scope
3✔
942

3✔
943
                break
3✔
944
        }
945
        if account == nil {
3✔
946
                return waddrmgr.KeyScope{}, 0, newAccountNotFoundError(name)
×
947
        }
×
948

949
        return keyScope, account.AccountNumber, nil
3✔
950
}
951

952
// Bip32DerivationFromKeyDesc returns the default and Taproot BIP-0032 key
953
// derivation information from the given key descriptor information.
954
func Bip32DerivationFromKeyDesc(keyDesc keychain.KeyDescriptor,
955
        coinType uint32) (*psbt.Bip32Derivation, *psbt.TaprootBip32Derivation,
956
        string) {
2✔
957

2✔
958
        bip32Derivation := &psbt.Bip32Derivation{
2✔
959
                PubKey: keyDesc.PubKey.SerializeCompressed(),
2✔
960
                Bip32Path: []uint32{
2✔
961
                        keychain.BIP0043Purpose + hdkeychain.HardenedKeyStart,
2✔
962
                        coinType + hdkeychain.HardenedKeyStart,
2✔
963
                        uint32(keyDesc.Family) +
2✔
964
                                uint32(hdkeychain.HardenedKeyStart),
2✔
965
                        0,
2✔
966
                        keyDesc.Index,
2✔
967
                },
2✔
968
        }
2✔
969

2✔
970
        derivationPath := fmt.Sprintf(
2✔
971
                "m/%d'/%d'/%d'/%d/%d", keychain.BIP0043Purpose, coinType,
2✔
972
                keyDesc.Family, 0, keyDesc.Index,
2✔
973
        )
2✔
974

2✔
975
        return bip32Derivation, &psbt.TaprootBip32Derivation{
2✔
976
                XOnlyPubKey:          bip32Derivation.PubKey[1:],
2✔
977
                MasterKeyFingerprint: bip32Derivation.MasterKeyFingerprint,
2✔
978
                Bip32Path:            bip32Derivation.Bip32Path,
2✔
979
                LeafHashes:           make([][]byte, 0),
2✔
980
        }, derivationPath
2✔
981
}
2✔
982

983
// Bip32DerivationFromAddress returns the default and Taproot BIP-0032 key
984
// derivation information from the given managed address.
985
func Bip32DerivationFromAddress(
986
        addr waddrmgr.ManagedAddress) (*psbt.Bip32Derivation,
987
        *psbt.TaprootBip32Derivation, string, error) {
5✔
988

5✔
989
        pubKeyAddr, ok := addr.(waddrmgr.ManagedPubKeyAddress)
5✔
990
        if !ok {
5✔
991
                return nil, nil, "", fmt.Errorf("address is not a pubkey " +
×
992
                        "address")
×
993
        }
×
994

995
        scope, derivationInfo, haveInfo := pubKeyAddr.DerivationInfo()
5✔
996
        if !haveInfo {
5✔
997
                return nil, nil, "", fmt.Errorf("address is an imported " +
×
998
                        "public key, can't derive BIP32 path")
×
999
        }
×
1000

1001
        bip32Derivation := &psbt.Bip32Derivation{
5✔
1002
                PubKey: pubKeyAddr.PubKey().SerializeCompressed(),
5✔
1003
                Bip32Path: []uint32{
5✔
1004
                        scope.Purpose + hdkeychain.HardenedKeyStart,
5✔
1005
                        scope.Coin + hdkeychain.HardenedKeyStart,
5✔
1006
                        derivationInfo.InternalAccount +
5✔
1007
                                hdkeychain.HardenedKeyStart,
5✔
1008
                        derivationInfo.Branch,
5✔
1009
                        derivationInfo.Index,
5✔
1010
                },
5✔
1011
        }
5✔
1012

5✔
1013
        derivationPath := fmt.Sprintf(
5✔
1014
                "m/%d'/%d'/%d'/%d/%d", scope.Purpose, scope.Coin,
5✔
1015
                derivationInfo.InternalAccount, derivationInfo.Branch,
5✔
1016
                derivationInfo.Index,
5✔
1017
        )
5✔
1018

5✔
1019
        return bip32Derivation, &psbt.TaprootBip32Derivation{
5✔
1020
                XOnlyPubKey:          bip32Derivation.PubKey[1:],
5✔
1021
                MasterKeyFingerprint: bip32Derivation.MasterKeyFingerprint,
5✔
1022
                Bip32Path:            bip32Derivation.Bip32Path,
5✔
1023
                LeafHashes:           make([][]byte, 0),
5✔
1024
        }, derivationPath, nil
5✔
1025
}
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