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

lightningnetwork / lnd / 12284350326

11 Dec 2024 08:27PM UTC coverage: 57.485% (+7.9%) from 49.54%
12284350326

Pull #9348

github

ziggie1984
github: update goveralls tool
Pull Request #9348: github: update goveralls tool

101901 of 177264 relevant lines covered (57.49%)

24841.21 hits per line

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

0.0
/lnwallet/rpcwallet/rpcwallet.go
1
package rpcwallet
2

3
import (
4
        "bytes"
5
        "context"
6
        "crypto/sha256"
7
        "crypto/x509"
8
        "errors"
9
        "fmt"
10
        "os"
11
        "time"
12

13
        "github.com/btcsuite/btcd/btcec/v2"
14
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
15
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
16
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
17
        "github.com/btcsuite/btcd/btcutil"
18
        "github.com/btcsuite/btcd/btcutil/hdkeychain"
19
        "github.com/btcsuite/btcd/btcutil/psbt"
20
        "github.com/btcsuite/btcd/chaincfg"
21
        "github.com/btcsuite/btcd/txscript"
22
        "github.com/btcsuite/btcd/wire"
23
        "github.com/btcsuite/btcwallet/waddrmgr"
24
        basewallet "github.com/btcsuite/btcwallet/wallet"
25
        "github.com/lightningnetwork/lnd/fn/v2"
26
        "github.com/lightningnetwork/lnd/input"
27
        "github.com/lightningnetwork/lnd/keychain"
28
        "github.com/lightningnetwork/lnd/lncfg"
29
        "github.com/lightningnetwork/lnd/lnrpc/signrpc"
30
        "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
31
        "github.com/lightningnetwork/lnd/lnwallet"
32
        "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
33
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
34
        "github.com/lightningnetwork/lnd/lnwire"
35
        "github.com/lightningnetwork/lnd/macaroons"
36
        "google.golang.org/grpc"
37
        "google.golang.org/grpc/codes"
38
        "google.golang.org/grpc/credentials"
39
        "google.golang.org/grpc/status"
40
        "gopkg.in/macaroon.v2"
41
)
42

43
var (
44
        // ErrRemoteSigningPrivateKeyNotAvailable is the error that is returned
45
        // if an operation is requested from the RPC wallet that is not
46
        // supported in remote signing mode.
47
        ErrRemoteSigningPrivateKeyNotAvailable = errors.New("deriving " +
48
                "private key is not supported by RPC based key ring")
49
)
50

51
// RPCKeyRing is an implementation of the SecretKeyRing interface that uses a
52
// local watch-only wallet for keeping track of addresses and transactions but
53
// delegates any signing or ECDH operations to a remote node through RPC.
54
type RPCKeyRing struct {
55
        // WalletController is the embedded wallet controller of the watch-only
56
        // base wallet. We need to overwrite/shadow certain of the implemented
57
        // methods to make sure we can mirror them to the remote wallet.
58
        lnwallet.WalletController
59

60
        watchOnlyKeyRing keychain.SecretKeyRing
61

62
        netParams *chaincfg.Params
63

64
        rpcTimeout time.Duration
65

66
        signerClient signrpc.SignerClient
67
        walletClient walletrpc.WalletKitClient
68
}
69

70
var _ keychain.SecretKeyRing = (*RPCKeyRing)(nil)
71
var _ input.Signer = (*RPCKeyRing)(nil)
72
var _ keychain.MessageSignerRing = (*RPCKeyRing)(nil)
73
var _ lnwallet.WalletController = (*RPCKeyRing)(nil)
74

75
// NewRPCKeyRing creates a new remote signing secret key ring that uses the
76
// given watch-only base wallet to keep track of addresses and transactions but
77
// delegates any signing or ECDH operations to the remove signer through RPC.
78
func NewRPCKeyRing(watchOnlyKeyRing keychain.SecretKeyRing,
79
        watchOnlyWalletController lnwallet.WalletController,
80
        remoteSigner *lncfg.RemoteSigner,
81
        netParams *chaincfg.Params) (*RPCKeyRing, error) {
×
82

×
83
        rpcConn, err := connectRPC(
×
84
                remoteSigner.RPCHost, remoteSigner.TLSCertPath,
×
85
                remoteSigner.MacaroonPath, remoteSigner.Timeout,
×
86
        )
×
87
        if err != nil {
×
88
                return nil, fmt.Errorf("error connecting to the remote "+
×
89
                        "signing node through RPC: %v", err)
×
90
        }
×
91

92
        return &RPCKeyRing{
×
93
                WalletController: watchOnlyWalletController,
×
94
                watchOnlyKeyRing: watchOnlyKeyRing,
×
95
                netParams:        netParams,
×
96
                rpcTimeout:       remoteSigner.Timeout,
×
97
                signerClient:     signrpc.NewSignerClient(rpcConn),
×
98
                walletClient:     walletrpc.NewWalletKitClient(rpcConn),
×
99
        }, nil
×
100
}
101

102
// NewAddress returns the next external or internal address for the
103
// wallet dictated by the value of the `change` parameter. If change is
104
// true, then an internal address should be used, otherwise an external
105
// address should be returned. The type of address returned is dictated
106
// by the wallet's capabilities, and may be of type: p2sh, p2wkh,
107
// p2wsh, etc. The account parameter must be non-empty as it determines
108
// which account the address should be generated from.
109
func (r *RPCKeyRing) NewAddress(addrType lnwallet.AddressType, change bool,
110
        account string) (btcutil.Address, error) {
×
111

×
112
        return r.WalletController.NewAddress(addrType, change, account)
×
113
}
×
114

115
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
116
// the specified outputs. In the case the wallet has insufficient funds, or the
117
// outputs are non-standard, a non-nil error will be returned.
118
//
119
// NOTE: This method requires the global coin selection lock to be held.
120
//
121
// NOTE: This is a part of the WalletController interface.
122
//
123
// NOTE: This method only signs with BIP49/84 keys.
124
func (r *RPCKeyRing) SendOutputs(inputs fn.Set[wire.OutPoint],
125
        outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
126
        minConfs int32, label string,
127
        strategy basewallet.CoinSelectionStrategy) (*wire.MsgTx, error) {
×
128

×
129
        tx, err := r.WalletController.SendOutputs(
×
130
                inputs, outputs, feeRate, minConfs, label, strategy,
×
131
        )
×
132
        if err != nil && err != basewallet.ErrTxUnsigned {
×
133
                return nil, err
×
134
        }
×
135
        if err == nil {
×
136
                // This shouldn't happen since our wallet controller is watch-
×
137
                // only and can't sign the TX.
×
138
                return tx, nil
×
139
        }
×
140

141
        // We know at this point that we only have inputs from our own wallet.
142
        // So we can just compute the input script using the remote signer.
143
        outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController)
×
144
        for i, txIn := range tx.TxIn {
×
145
                signDesc := input.SignDescriptor{
×
146
                        HashType: txscript.SigHashAll,
×
147
                        SigHashes: txscript.NewTxSigHashes(
×
148
                                tx, outputFetcher,
×
149
                        ),
×
150
                        PrevOutputFetcher: outputFetcher,
×
151
                }
×
152

×
153
                // We can only sign this input if it's ours, so we'll ask the
×
154
                // watch-only wallet if it can map this outpoint into a coin we
×
155
                // own. If not, then we can't continue because our wallet state
×
156
                // is out of sync.
×
157
                info, err := r.WalletController.FetchOutpointInfo(
×
158
                        &txIn.PreviousOutPoint,
×
159
                )
×
160
                if err != nil {
×
161
                        return nil, fmt.Errorf("error looking up utxo: %w", err)
×
162
                }
×
163

164
                if txscript.IsPayToTaproot(info.PkScript) {
×
165
                        signDesc.HashType = txscript.SigHashDefault
×
166
                }
×
167

168
                // Now that we know the input is ours, we'll populate the
169
                // signDesc with the per input unique information.
170
                signDesc.Output = &wire.TxOut{
×
171
                        Value:    int64(info.Value),
×
172
                        PkScript: info.PkScript,
×
173
                }
×
174
                signDesc.InputIndex = i
×
175

×
176
                // Finally, we'll sign the input as is, and populate the input
×
177
                // with the witness and sigScript (if needed).
×
178
                inputScript, err := r.ComputeInputScript(tx, &signDesc)
×
179
                if err != nil {
×
180
                        return nil, err
×
181
                }
×
182

183
                txIn.SignatureScript = inputScript.SigScript
×
184
                txIn.Witness = inputScript.Witness
×
185
        }
186

187
        return tx, r.WalletController.PublishTransaction(tx, label)
×
188
}
189

190
// SignPsbt expects a partial transaction with all inputs and outputs fully
191
// declared and tries to sign all unsigned inputs that have all required fields
192
// (UTXO information, BIP32 derivation information, witness or sig scripts) set.
193
// If no error is returned, the PSBT is ready to be given to the next signer or
194
// to be finalized if lnd was the last signer.
195
//
196
// NOTE: This RPC only signs inputs (and only those it can sign), it does not
197
// perform any other tasks (such as coin selection, UTXO locking or
198
// input/output/fee value validation, PSBT finalization). Any input that is
199
// incomplete will be skipped.
200
func (r *RPCKeyRing) SignPsbt(packet *psbt.Packet) ([]uint32, error) {
×
201
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
202
        defer cancel()
×
203

×
204
        var buf bytes.Buffer
×
205
        if err := packet.Serialize(&buf); err != nil {
×
206
                return nil, fmt.Errorf("error serializing PSBT: %w", err)
×
207
        }
×
208

209
        resp, err := r.walletClient.SignPsbt(ctxt, &walletrpc.SignPsbtRequest{
×
210
                FundedPsbt: buf.Bytes(),
×
211
        })
×
212
        if err != nil {
×
213
                considerShutdown(err)
×
214
                return nil, fmt.Errorf("error signing PSBT in remote signer "+
×
215
                        "instance: %v", err)
×
216
        }
×
217

218
        signedPacket, err := psbt.NewFromRawBytes(
×
219
                bytes.NewReader(resp.SignedPsbt), false,
×
220
        )
×
221
        if err != nil {
×
222
                return nil, fmt.Errorf("error parsing signed PSBT: %w", err)
×
223
        }
×
224

225
        // The caller expects the packet to be modified instead of a new
226
        // instance to be returned. So we just overwrite all fields in the
227
        // original packet.
228
        packet.UnsignedTx = signedPacket.UnsignedTx
×
229
        packet.Inputs = signedPacket.Inputs
×
230
        packet.Outputs = signedPacket.Outputs
×
231
        packet.Unknowns = signedPacket.Unknowns
×
232

×
233
        return resp.SignedInputs, nil
×
234
}
235

236
// FinalizePsbt expects a partial transaction with all inputs and outputs fully
237
// declared and tries to sign all inputs that belong to the specified account.
238
// Lnd must be the last signer of the transaction. That means, if there are any
239
// unsigned non-witness inputs or inputs without UTXO information attached or
240
// inputs without witness data that do not belong to lnd's wallet, this method
241
// will fail. If no error is returned, the PSBT is ready to be extracted and the
242
// final TX within to be broadcast.
243
//
244
// NOTE: This method does NOT publish the transaction after it's been
245
// finalized successfully.
246
//
247
// NOTE: This is a part of the WalletController interface.
248
//
249
// NOTE: We need to overwrite this method because we need to redirect the call
250
// to ComputeInputScript to the RPC key ring's implementation. If we forward
251
// the call to the default WalletController implementation, we get an error
252
// since that wallet is watch-only. If we forward the call to the remote signer,
253
// we get an error because the signer doesn't know the UTXO information required
254
// in ComputeInputScript.
255
//
256
// TODO(guggero): Refactor btcwallet to accept ComputeInputScript as a function
257
// parameter in FinalizePsbt so we can get rid of this code duplication.
258
func (r *RPCKeyRing) FinalizePsbt(packet *psbt.Packet, _ string) error {
×
259
        // Let's check that this is actually something we can and want to sign.
×
260
        // We need at least one input and one output. In addition each
×
261
        // input needs nonWitness Utxo or witness Utxo data specified.
×
262
        err := psbt.InputsReadyToSign(packet)
×
263
        if err != nil {
×
264
                return err
×
265
        }
×
266

267
        // Go through each input that doesn't have final witness data attached
268
        // to it already and try to sign it. We do expect that we're the last
269
        // ones to sign. If there is any input without witness data that we
270
        // cannot sign because it's not our UTXO, this will be a hard failure.
271
        tx := packet.UnsignedTx
×
272
        prevOutFetcher := basewallet.PsbtPrevOutputFetcher(packet)
×
273
        sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher)
×
274
        for idx, txIn := range tx.TxIn {
×
275
                in := packet.Inputs[idx]
×
276

×
277
                // We can only sign if we have UTXO information available. We
×
278
                // can just continue here as a later step will fail with a more
×
279
                // precise error message.
×
280
                if in.WitnessUtxo == nil && in.NonWitnessUtxo == nil {
×
281
                        continue
×
282
                }
283

284
                // Skip this input if it's got final witness data attached.
285
                if len(in.FinalScriptWitness) > 0 {
×
286
                        continue
×
287
                }
288

289
                // We can only sign this input if it's ours, so we try to map it
290
                // to a coin we own. If we can't, then we'll continue as it
291
                // isn't our input.
292
                utxo, err := r.FetchOutpointInfo(&txIn.PreviousOutPoint)
×
293
                if err != nil {
×
294
                        continue
×
295
                }
296
                fullTx := utxo.PrevTx
×
297
                signDesc := &input.SignDescriptor{
×
298
                        KeyDesc: keychain.KeyDescriptor{},
×
299
                        Output: &wire.TxOut{
×
300
                                Value:    int64(utxo.Value),
×
301
                                PkScript: utxo.PkScript,
×
302
                        },
×
303
                        HashType:          in.SighashType,
×
304
                        SigHashes:         sigHashes,
×
305
                        InputIndex:        idx,
×
306
                        PrevOutputFetcher: prevOutFetcher,
×
307
                }
×
308

×
309
                // Find out what UTXO we are signing. Wallets _should_ always
×
310
                // provide the full non-witness UTXO for segwit v0.
×
311
                var signOutput *wire.TxOut
×
312
                if in.NonWitnessUtxo != nil {
×
313
                        prevIndex := txIn.PreviousOutPoint.Index
×
314
                        signOutput = in.NonWitnessUtxo.TxOut[prevIndex]
×
315

×
316
                        if !psbt.TxOutsEqual(signDesc.Output, signOutput) {
×
317
                                return fmt.Errorf("found UTXO %#v but it "+
×
318
                                        "doesn't match PSBT's input %v",
×
319
                                        signDesc.Output, signOutput)
×
320
                        }
×
321

322
                        if fullTx.TxHash() != txIn.PreviousOutPoint.Hash {
×
323
                                return fmt.Errorf("found UTXO tx %v but it "+
×
324
                                        "doesn't match PSBT's input %v",
×
325
                                        fullTx.TxHash(),
×
326
                                        txIn.PreviousOutPoint.Hash)
×
327
                        }
×
328
                }
329

330
                // Fall back to witness UTXO only for older wallets.
331
                if in.WitnessUtxo != nil {
×
332
                        signOutput = in.WitnessUtxo
×
333

×
334
                        if !psbt.TxOutsEqual(signDesc.Output, signOutput) {
×
335
                                return fmt.Errorf("found UTXO %#v but it "+
×
336
                                        "doesn't match PSBT's input %v",
×
337
                                        signDesc.Output, signOutput)
×
338
                        }
×
339
                }
340

341
                // Do the actual signing in ComputeInputScript which in turn
342
                // will invoke the remote signer.
343
                script, err := r.ComputeInputScript(tx, signDesc)
×
344
                if err != nil {
×
345
                        return fmt.Errorf("error computing input script for "+
×
346
                                "input %d: %v", idx, err)
×
347
                }
×
348

349
                // Serialize the witness format from the stack representation to
350
                // the wire representation.
351
                var witnessBytes bytes.Buffer
×
352
                err = psbt.WriteTxWitness(&witnessBytes, script.Witness)
×
353
                if err != nil {
×
354
                        return fmt.Errorf("error serializing witness: %w", err)
×
355
                }
×
356
                packet.Inputs[idx].FinalScriptWitness = witnessBytes.Bytes()
×
357
                packet.Inputs[idx].FinalScriptSig = script.SigScript
×
358
        }
359

360
        // Make sure the PSBT itself thinks it's finalized and ready to be
361
        // broadcast.
362
        err = psbt.MaybeFinalizeAll(packet)
×
363
        if err != nil {
×
364
                return fmt.Errorf("error finalizing PSBT: %w", err)
×
365
        }
×
366

367
        return nil
×
368
}
369

370
// DeriveNextKey attempts to derive the *next* key within the key family
371
// (account in BIP43) specified. This method should return the next external
372
// child within this branch.
373
//
374
// NOTE: This method is part of the keychain.KeyRing interface.
375
func (r *RPCKeyRing) DeriveNextKey(
376
        keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) {
×
377

×
378
        return r.watchOnlyKeyRing.DeriveNextKey(keyFam)
×
379
}
×
380

381
// DeriveKey attempts to derive an arbitrary key specified by the passed
382
// KeyLocator. This may be used in several recovery scenarios, or when manually
383
// rotating something like our current default node key.
384
//
385
// NOTE: This method is part of the keychain.KeyRing interface.
386
func (r *RPCKeyRing) DeriveKey(
387
        keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) {
×
388

×
389
        return r.watchOnlyKeyRing.DeriveKey(keyLoc)
×
390
}
×
391

392
// ECDH performs a scalar multiplication (ECDH-like operation) between the
393
// target key descriptor and remote public key. The output returned will be the
394
// sha256 of the resulting shared point serialized in compressed format. If k is
395
// our private key, and P is the public key, we perform the following operation:
396
//
397
//        sx := k*P
398
//        s := sha256(sx.SerializeCompressed())
399
//
400
// NOTE: This method is part of the keychain.ECDHRing interface.
401
func (r *RPCKeyRing) ECDH(keyDesc keychain.KeyDescriptor,
402
        pubKey *btcec.PublicKey) ([32]byte, error) {
×
403

×
404
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
405
        defer cancel()
×
406

×
407
        key := [32]byte{}
×
408
        req := &signrpc.SharedKeyRequest{
×
409
                EphemeralPubkey: pubKey.SerializeCompressed(),
×
410
                KeyDesc: &signrpc.KeyDescriptor{
×
411
                        KeyLoc: &signrpc.KeyLocator{
×
412
                                KeyFamily: int32(keyDesc.Family),
×
413
                                KeyIndex:  int32(keyDesc.Index),
×
414
                        },
×
415
                },
×
416
        }
×
417

×
418
        if keyDesc.Index == 0 && keyDesc.PubKey != nil {
×
419
                req.KeyDesc.RawKeyBytes = keyDesc.PubKey.SerializeCompressed()
×
420
        }
×
421

422
        resp, err := r.signerClient.DeriveSharedKey(ctxt, req)
×
423
        if err != nil {
×
424
                considerShutdown(err)
×
425
                return key, fmt.Errorf("error deriving shared key in remote "+
×
426
                        "signer instance: %v", err)
×
427
        }
×
428

429
        copy(key[:], resp.SharedKey)
×
430
        return key, nil
×
431
}
432

433
// SignMessage attempts to sign a target message with the private key described
434
// in the key locator. If the target private key is unable to be found, then an
435
// error will be returned. The actual digest signed is the single or double
436
// SHA-256 of the passed message.
437
//
438
// NOTE: This method is part of the keychain.MessageSignerRing interface.
439
func (r *RPCKeyRing) SignMessage(keyLoc keychain.KeyLocator,
440
        msg []byte, doubleHash bool) (*ecdsa.Signature, error) {
×
441

×
442
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
443
        defer cancel()
×
444

×
445
        resp, err := r.signerClient.SignMessage(ctxt, &signrpc.SignMessageReq{
×
446
                Msg: msg,
×
447
                KeyLoc: &signrpc.KeyLocator{
×
448
                        KeyFamily: int32(keyLoc.Family),
×
449
                        KeyIndex:  int32(keyLoc.Index),
×
450
                },
×
451
                DoubleHash: doubleHash,
×
452
        })
×
453
        if err != nil {
×
454
                considerShutdown(err)
×
455
                return nil, fmt.Errorf("error signing message in remote "+
×
456
                        "signer instance: %v", err)
×
457
        }
×
458

459
        wireSig, err := lnwire.NewSigFromECDSARawSignature(resp.Signature)
×
460
        if err != nil {
×
461
                return nil, fmt.Errorf("unable to create sig: %w", err)
×
462
        }
×
463
        sig, err := wireSig.ToSignature()
×
464
        if err != nil {
×
465
                return nil, fmt.Errorf("unable to parse sig: %w", err)
×
466
        }
×
467
        ecdsaSig, ok := sig.(*ecdsa.Signature)
×
468
        if !ok {
×
469
                return nil, fmt.Errorf("unexpected signature type: %T", sig)
×
470
        }
×
471

472
        return ecdsaSig, nil
×
473
}
474

475
// SignMessageCompact signs the given message, single or double SHA256 hashing
476
// it first, with the private key described in the key locator and returns the
477
// signature in the compact, public key recoverable format.
478
//
479
// NOTE: This method is part of the keychain.MessageSignerRing interface.
480
func (r *RPCKeyRing) SignMessageCompact(keyLoc keychain.KeyLocator,
481
        msg []byte, doubleHash bool) ([]byte, error) {
×
482

×
483
        if keyLoc.Family != keychain.KeyFamilyNodeKey {
×
484
                return nil, fmt.Errorf("error compact signing with key "+
×
485
                        "locator %v, can only sign with node key", keyLoc)
×
486
        }
×
487

488
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
489
        defer cancel()
×
490

×
491
        resp, err := r.signerClient.SignMessage(ctxt, &signrpc.SignMessageReq{
×
492
                Msg: msg,
×
493
                KeyLoc: &signrpc.KeyLocator{
×
494
                        KeyFamily: int32(keyLoc.Family),
×
495
                        KeyIndex:  int32(keyLoc.Index),
×
496
                },
×
497
                DoubleHash: doubleHash,
×
498
                CompactSig: true,
×
499
        })
×
500
        if err != nil {
×
501
                considerShutdown(err)
×
502
                return nil, fmt.Errorf("error signing message in remote "+
×
503
                        "signer instance: %v", err)
×
504
        }
×
505

506
        // The signature in the response is zbase32 encoded, so we need to
507
        // decode it before returning.
508
        return resp.Signature, nil
×
509
}
510

511
// SignMessageSchnorr attempts to sign a target message with the private key
512
// described in the key locator. If the target private key is unable to be
513
// found, then an error will be returned. The actual digest signed is the
514
// single or double SHA-256 of the passed message.
515
//
516
// NOTE: This method is part of the keychain.MessageSignerRing interface.
517
func (r *RPCKeyRing) SignMessageSchnorr(keyLoc keychain.KeyLocator,
518
        msg []byte, doubleHash bool, taprootTweak []byte,
519
        tag []byte) (*schnorr.Signature, error) {
×
520

×
521
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
522
        defer cancel()
×
523

×
524
        resp, err := r.signerClient.SignMessage(ctxt, &signrpc.SignMessageReq{
×
525
                Msg: msg,
×
526
                KeyLoc: &signrpc.KeyLocator{
×
527
                        KeyFamily: int32(keyLoc.Family),
×
528
                        KeyIndex:  int32(keyLoc.Index),
×
529
                },
×
530
                DoubleHash:         doubleHash,
×
531
                SchnorrSig:         true,
×
532
                SchnorrSigTapTweak: taprootTweak,
×
533
                Tag:                tag,
×
534
        })
×
535
        if err != nil {
×
536
                considerShutdown(err)
×
537
                return nil, fmt.Errorf("error signing message in remote "+
×
538
                        "signer instance: %w", err)
×
539
        }
×
540

541
        sigParsed, err := schnorr.ParseSignature(resp.Signature)
×
542
        if err != nil {
×
543
                return nil, fmt.Errorf("can't parse schnorr signature: %w",
×
544
                        err)
×
545
        }
×
546
        return sigParsed, nil
×
547
}
548

549
// DerivePrivKey attempts to derive the private key that corresponds to the
550
// passed key descriptor.  If the public key is set, then this method will
551
// perform an in-order scan over the key set, with a max of MaxKeyRangeScan
552
// keys. In order for this to work, the caller MUST set the KeyFamily within the
553
// partially populated KeyLocator.
554
//
555
// NOTE: This method is part of the keychain.SecretKeyRing interface.
556
func (r *RPCKeyRing) DerivePrivKey(_ keychain.KeyDescriptor) (*btcec.PrivateKey,
557
        error) {
×
558

×
559
        // This operation is not supported with remote signing. There should be
×
560
        // no need for invoking this method unless a channel backup (SCB) file
×
561
        // for pre-0.13.0 channels are attempted to be restored. In that case
×
562
        // it is recommended to restore the channels using a node with the full
×
563
        // seed available.
×
564
        return nil, ErrRemoteSigningPrivateKeyNotAvailable
×
565
}
×
566

567
// SignOutputRaw generates a signature for the passed transaction
568
// according to the data within the passed SignDescriptor.
569
//
570
// NOTE: The resulting signature should be void of a sighash byte.
571
//
572
// NOTE: This method is part of the input.Signer interface.
573
//
574
// NOTE: This method only signs with BIP1017 (internal) keys!
575
func (r *RPCKeyRing) SignOutputRaw(tx *wire.MsgTx,
576
        signDesc *input.SignDescriptor) (input.Signature, error) {
×
577

×
578
        // Forward the call to the remote signing instance. This call is only
×
579
        // ever called for signing witness (p2pkh or p2wsh) inputs and never
×
580
        // nested witness inputs, so the sigScript is always nil.
×
581
        return r.remoteSign(tx, signDesc, nil)
×
582
}
×
583

584
// ComputeInputScript generates a complete InputIndex for the passed
585
// transaction with the signature as defined within the passed
586
// SignDescriptor. This method should be capable of generating the
587
// proper input script for both regular p2wkh output and p2wkh outputs
588
// nested within a regular p2sh output.
589
//
590
// NOTE: This method will ignore any tweak parameters set within the
591
// passed SignDescriptor as it assumes a set of typical script
592
// templates (p2wkh, np2wkh, BIP0086 p2tr, etc).
593
//
594
// NOTE: This method is part of the input.Signer interface.
595
func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
596
        signDesc *input.SignDescriptor) (*input.Script, error) {
×
597

×
598
        addr, witnessProgram, sigScript, err := r.WalletController.ScriptForOutput(
×
599
                signDesc.Output,
×
600
        )
×
601
        if err != nil {
×
602
                return nil, err
×
603
        }
×
604
        signDesc.WitnessScript = witnessProgram
×
605

×
606
        // If this is a p2tr address, then it must be a BIP0086 key spend if we
×
607
        // are coming through this path (instead of SignOutputRaw).
×
608
        switch addr.AddrType() {
×
609
        case waddrmgr.TaprootPubKey:
×
610
                signDesc.SignMethod = input.TaprootKeySpendBIP0086SignMethod
×
611
                signDesc.WitnessScript = nil
×
612

×
613
                sig, err := r.remoteSign(tx, signDesc, nil)
×
614
                if err != nil {
×
615
                        return nil, fmt.Errorf("error signing with remote"+
×
616
                                "instance: %v", err)
×
617
                }
×
618

619
                rawSig := sig.Serialize()
×
620
                if signDesc.HashType != txscript.SigHashDefault {
×
621
                        rawSig = append(rawSig, byte(signDesc.HashType))
×
622
                }
×
623

624
                return &input.Script{
×
625
                        Witness: wire.TxWitness{
×
626
                                rawSig,
×
627
                        },
×
628
                }, nil
×
629

630
        case waddrmgr.TaprootScript:
×
631
                return nil, fmt.Errorf("computing input script for taproot " +
×
632
                        "script address not supported")
×
633
        }
634

635
        // Let's give the TX to the remote instance now, so it can sign the
636
        // input.
637
        sig, err := r.remoteSign(tx, signDesc, witnessProgram)
×
638
        if err != nil {
×
639
                return nil, fmt.Errorf("error signing with remote instance: %w",
×
640
                        err)
×
641
        }
×
642

643
        // ComputeInputScript currently is only used for P2WKH and NP2WKH
644
        // addresses. So the last item on the stack is always the compressed
645
        // public key.
646
        return &input.Script{
×
647
                Witness: wire.TxWitness{
×
648
                        append(sig.Serialize(), byte(signDesc.HashType)),
×
649
                        addr.PubKey().SerializeCompressed(),
×
650
                },
×
651
                SigScript: sigScript,
×
652
        }, nil
×
653
}
654

655
// MuSig2CreateSession creates a new MuSig2 signing session using the local
656
// key identified by the key locator. The complete list of all public keys of
657
// all signing parties must be provided, including the public key of the local
658
// signing key. If nonces of other parties are already known, they can be
659
// submitted as well to reduce the number of method calls necessary later on.
660
func (r *RPCKeyRing) MuSig2CreateSession(bipVersion input.MuSig2Version,
661
        keyLoc keychain.KeyLocator, pubKeys []*btcec.PublicKey,
662
        tweaks *input.MuSig2Tweaks, otherNonces [][musig2.PubNonceSize]byte,
663
        localNonces *musig2.Nonces) (*input.MuSig2SessionInfo, error) {
×
664

×
665
        apiVersion, err := signrpc.MarshalMuSig2Version(bipVersion)
×
666
        if err != nil {
×
667
                return nil, err
×
668
        }
×
669

670
        // We need to serialize all data for the RPC call. We can do that by
671
        // putting everything directly into the request struct.
672
        req := &signrpc.MuSig2SessionRequest{
×
673
                KeyLoc: &signrpc.KeyLocator{
×
674
                        KeyFamily: int32(keyLoc.Family),
×
675
                        KeyIndex:  int32(keyLoc.Index),
×
676
                },
×
677
                AllSignerPubkeys: make([][]byte, len(pubKeys)),
×
678
                Tweaks: make(
×
679
                        []*signrpc.TweakDesc, len(tweaks.GenericTweaks),
×
680
                ),
×
681
                OtherSignerPublicNonces: make([][]byte, len(otherNonces)),
×
682
                Version:                 apiVersion,
×
683
        }
×
684
        for idx, pubKey := range pubKeys {
×
685
                switch bipVersion {
×
686
                case input.MuSig2Version040:
×
687
                        req.AllSignerPubkeys[idx] = schnorr.SerializePubKey(
×
688
                                pubKey,
×
689
                        )
×
690

691
                case input.MuSig2Version100RC2:
×
692
                        req.AllSignerPubkeys[idx] = pubKey.SerializeCompressed()
×
693
                }
694
        }
695
        for idx, genericTweak := range tweaks.GenericTweaks {
×
696
                req.Tweaks[idx] = &signrpc.TweakDesc{
×
697
                        Tweak:   genericTweak.Tweak[:],
×
698
                        IsXOnly: genericTweak.IsXOnly,
×
699
                }
×
700
        }
×
701
        for idx, nonce := range otherNonces {
×
702
                req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
×
703
                copy(req.OtherSignerPublicNonces[idx], nonce[:])
×
704
        }
×
705
        if tweaks.HasTaprootTweak() {
×
706
                req.TaprootTweak = &signrpc.TaprootTweakDesc{
×
707
                        KeySpendOnly: tweaks.TaprootBIP0086Tweak,
×
708
                        ScriptRoot:   tweaks.TaprootTweak,
×
709
                }
×
710
        }
×
711

712
        if localNonces != nil {
×
713
                req.PregeneratedLocalNonce = localNonces.SecNonce[:]
×
714
        }
×
715

716
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
717
        defer cancel()
×
718

×
719
        resp, err := r.signerClient.MuSig2CreateSession(ctxt, req)
×
720
        if err != nil {
×
721
                considerShutdown(err)
×
722
                return nil, fmt.Errorf("error creating MuSig2 session in "+
×
723
                        "remote signer instance: %v", err)
×
724
        }
×
725

726
        // De-Serialize all the info back into our native struct.
727
        info := &input.MuSig2SessionInfo{
×
728
                Version:       bipVersion,
×
729
                TaprootTweak:  tweaks.HasTaprootTweak(),
×
730
                HaveAllNonces: resp.HaveAllNonces,
×
731
        }
×
732
        copy(info.SessionID[:], resp.SessionId)
×
733
        copy(info.PublicNonce[:], resp.LocalPublicNonces)
×
734

×
735
        info.CombinedKey, err = schnorr.ParsePubKey(resp.CombinedKey)
×
736
        if err != nil {
×
737
                return nil, fmt.Errorf("error parsing combined key: %w", err)
×
738
        }
×
739

740
        if tweaks.HasTaprootTweak() {
×
741
                info.TaprootInternalKey, err = schnorr.ParsePubKey(
×
742
                        resp.TaprootInternalKey,
×
743
                )
×
744
                if err != nil {
×
745
                        return nil, fmt.Errorf("error parsing internal key: %w",
×
746
                                err)
×
747
                }
×
748
        }
749

750
        return info, nil
×
751
}
752

753
// MuSig2RegisterNonces registers one or more public nonces of other signing
754
// participants for a session identified by its ID. This method returns true
755
// once we have all nonces for all other signing participants.
756
func (r *RPCKeyRing) MuSig2RegisterNonces(sessionID input.MuSig2SessionID,
757
        pubNonces [][musig2.PubNonceSize]byte) (bool, error) {
×
758

×
759
        // We need to serialize all data for the RPC call. We can do that by
×
760
        // putting everything directly into the request struct.
×
761
        req := &signrpc.MuSig2RegisterNoncesRequest{
×
762
                SessionId:               sessionID[:],
×
763
                OtherSignerPublicNonces: make([][]byte, len(pubNonces)),
×
764
        }
×
765
        for idx, nonce := range pubNonces {
×
766
                req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
×
767
                copy(req.OtherSignerPublicNonces[idx], nonce[:])
×
768
        }
×
769

770
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
771
        defer cancel()
×
772

×
773
        resp, err := r.signerClient.MuSig2RegisterNonces(ctxt, req)
×
774
        if err != nil {
×
775
                considerShutdown(err)
×
776
                return false, fmt.Errorf("error registering MuSig2 nonces in "+
×
777
                        "remote signer instance: %v", err)
×
778
        }
×
779

780
        return resp.HaveAllNonces, nil
×
781
}
782

783
// MuSig2Sign creates a partial signature using the local signing key
784
// that was specified when the session was created. This can only be
785
// called when all public nonces of all participants are known and have
786
// been registered with the session. If this node isn't responsible for
787
// combining all the partial signatures, then the cleanup parameter
788
// should be set, indicating that the session can be removed from memory
789
// once the signature was produced.
790
func (r *RPCKeyRing) MuSig2Sign(sessionID input.MuSig2SessionID,
791
        msg [sha256.Size]byte, cleanUp bool) (*musig2.PartialSignature, error) {
×
792

×
793
        // We need to serialize all data for the RPC call. We can do that by
×
794
        // putting everything directly into the request struct.
×
795
        req := &signrpc.MuSig2SignRequest{
×
796
                SessionId:     sessionID[:],
×
797
                MessageDigest: msg[:],
×
798
                Cleanup:       cleanUp,
×
799
        }
×
800

×
801
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
802
        defer cancel()
×
803

×
804
        resp, err := r.signerClient.MuSig2Sign(ctxt, req)
×
805
        if err != nil {
×
806
                considerShutdown(err)
×
807
                return nil, fmt.Errorf("error signing MuSig2 session in "+
×
808
                        "remote signer instance: %v", err)
×
809
        }
×
810

811
        partialSig, err := input.DeserializePartialSignature(
×
812
                resp.LocalPartialSignature,
×
813
        )
×
814
        if err != nil {
×
815
                return nil, fmt.Errorf("error parsing partial signature from "+
×
816
                        "remote signer: %v", err)
×
817
        }
×
818

819
        return partialSig, nil
×
820
}
821

822
// MuSig2CombineSig combines the given partial signature(s) with the
823
// local one, if it already exists. Once a partial signature of all
824
// participants is registered, the final signature will be combined and
825
// returned.
826
func (r *RPCKeyRing) MuSig2CombineSig(sessionID input.MuSig2SessionID,
827
        partialSigs []*musig2.PartialSignature) (*schnorr.Signature, bool,
828
        error) {
×
829

×
830
        // We need to serialize all data for the RPC call. We can do that by
×
831
        // putting everything directly into the request struct.
×
832
        req := &signrpc.MuSig2CombineSigRequest{
×
833
                SessionId:              sessionID[:],
×
834
                OtherPartialSignatures: make([][]byte, len(partialSigs)),
×
835
        }
×
836
        for idx, partialSig := range partialSigs {
×
837
                rawSig, err := input.SerializePartialSignature(partialSig)
×
838
                if err != nil {
×
839
                        return nil, false, fmt.Errorf("error serializing "+
×
840
                                "partial signature: %v", err)
×
841
                }
×
842
                req.OtherPartialSignatures[idx] = rawSig[:]
×
843
        }
844

845
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
846
        defer cancel()
×
847

×
848
        resp, err := r.signerClient.MuSig2CombineSig(ctxt, req)
×
849
        if err != nil {
×
850
                considerShutdown(err)
×
851
                return nil, false, fmt.Errorf("error combining MuSig2 "+
×
852
                        "signatures in remote signer instance: %v", err)
×
853
        }
×
854

855
        // The final signature is only available when we have all the other
856
        // partial signatures from all participants.
857
        if !resp.HaveAllSignatures {
×
858
                return nil, resp.HaveAllSignatures, nil
×
859
        }
×
860

861
        finalSig, err := schnorr.ParseSignature(resp.FinalSignature)
×
862
        if err != nil {
×
863
                return nil, false, fmt.Errorf("error parsing final signature: "+
×
864
                        "%v", err)
×
865
        }
×
866

867
        return finalSig, resp.HaveAllSignatures, nil
×
868
}
869

870
// MuSig2Cleanup removes a session from memory to free up resources.
871
func (r *RPCKeyRing) MuSig2Cleanup(sessionID input.MuSig2SessionID) error {
×
872
        req := &signrpc.MuSig2CleanupRequest{
×
873
                SessionId: sessionID[:],
×
874
        }
×
875

×
876
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
877
        defer cancel()
×
878

×
879
        _, err := r.signerClient.MuSig2Cleanup(ctxt, req)
×
880
        if err != nil {
×
881
                considerShutdown(err)
×
882
                return fmt.Errorf("error cleaning up MuSig2 session in remote "+
×
883
                        "signer instance: %v", err)
×
884
        }
×
885

886
        return nil
×
887
}
888

889
// remoteSign signs the input specified in signDesc of the given transaction tx
890
// using the remote signing instance.
891
func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
892
        sigScript []byte) (input.Signature, error) {
×
893

×
894
        packet, err := packetFromTx(tx)
×
895
        if err != nil {
×
896
                return nil, fmt.Errorf("error converting TX into PSBT: %w", err)
×
897
        }
×
898

899
        // We need to add witness information for all inputs! Otherwise, we'll
900
        // have a problem when attempting to sign a taproot input!
901
        for idx := range packet.Inputs {
×
902
                // Skip the input we're signing for, that will get a special
×
903
                // treatment later on.
×
904
                if idx == signDesc.InputIndex {
×
905
                        continue
×
906
                }
907

908
                txIn := tx.TxIn[idx]
×
909
                info, err := r.WalletController.FetchOutpointInfo(
×
910
                        &txIn.PreviousOutPoint,
×
911
                )
×
912
                if err != nil {
×
913
                        // Maybe we have an UTXO in the previous output fetcher?
×
914
                        if signDesc.PrevOutputFetcher != nil {
×
915
                                utxo := signDesc.PrevOutputFetcher.FetchPrevOutput(
×
916
                                        txIn.PreviousOutPoint,
×
917
                                )
×
918
                                if utxo != nil && utxo.Value != 0 &&
×
919
                                        len(utxo.PkScript) > 0 {
×
920

×
921
                                        packet.Inputs[idx].WitnessUtxo = utxo
×
922
                                        continue
×
923
                                }
924
                        }
925

926
                        log.Warnf("No UTXO info found for index %d "+
×
927
                                "(prev_outpoint=%v), won't be able to sign "+
×
928
                                "for taproot output!", idx,
×
929
                                txIn.PreviousOutPoint)
×
930
                        continue
×
931
                }
932
                packet.Inputs[idx].WitnessUtxo = &wire.TxOut{
×
933
                        Value:    int64(info.Value),
×
934
                        PkScript: info.PkScript,
×
935
                }
×
936
        }
937

938
        // Catch incorrect signing input index, just in case.
939
        if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) {
×
940
                return nil, fmt.Errorf("invalid input index in sign descriptor")
×
941
        }
×
942
        in := &packet.Inputs[signDesc.InputIndex]
×
943
        txIn := tx.TxIn[signDesc.InputIndex]
×
944

×
945
        // Things are a bit tricky with the sign descriptor. There basically are
×
946
        // four ways to describe a key:
×
947
        //   1. By public key only. To match this case both family and index
×
948
        //      must be set to 0.
×
949
        //   2. By family and index only. To match this case the public key
×
950
        //      must be nil and either the family or index must be non-zero.
×
951
        //   3. All values are set and locator is non-empty. To match this case
×
952
        //      the public key must be set and either the family or index must
×
953
        //      be non-zero.
×
954
        //   4. All values are set and locator is empty. This is a special case
×
955
        //      for the very first channel ever created (with the multi-sig key
×
956
        //      family which is 0 and the index which is 0 as well). This looks
×
957
        //      identical to case 1 and will also be handled like that case.
×
958
        // We only really handle case 1 and 2 here, since 3 is no problem and 4
×
959
        // is identical to 1.
×
960
        switch {
×
961
        // Case 1: Public key only. We need to find out the derivation path for
962
        // this public key by asking the wallet. This is only possible for our
963
        // internal, custom 1017 scope since we know all keys derived there are
964
        // internally stored as p2wkh addresses.
965
        case signDesc.KeyDesc.PubKey != nil && signDesc.KeyDesc.IsEmpty():
×
966
                pubKeyBytes := signDesc.KeyDesc.PubKey.SerializeCompressed()
×
967
                addr, err := btcutil.NewAddressWitnessPubKeyHash(
×
968
                        btcutil.Hash160(pubKeyBytes), r.netParams,
×
969
                )
×
970
                if err != nil {
×
971
                        return nil, fmt.Errorf("error deriving address from "+
×
972
                                "public key %x: %v", pubKeyBytes, err)
×
973
                }
×
974

975
                managedAddr, err := r.AddressInfo(addr)
×
976
                if err != nil {
×
977
                        return nil, fmt.Errorf("error fetching address info "+
×
978
                                "for public key %x: %v", pubKeyBytes, err)
×
979
                }
×
980

981
                pubKeyAddr, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
×
982
                if !ok {
×
983
                        return nil, fmt.Errorf("address derived for public "+
×
984
                                "key %x is not a p2wkh address", pubKeyBytes)
×
985
                }
×
986

987
                scope, path, _ := pubKeyAddr.DerivationInfo()
×
988
                if scope.Purpose != keychain.BIP0043Purpose {
×
989
                        return nil, fmt.Errorf("address derived for public "+
×
990
                                "key %x is not in custom key scope %d'",
×
991
                                pubKeyBytes, keychain.BIP0043Purpose)
×
992
                }
×
993

994
                // We now have all the information we need to complete our key
995
                // locator information.
996
                signDesc.KeyDesc.KeyLocator = keychain.KeyLocator{
×
997
                        Family: keychain.KeyFamily(path.InternalAccount),
×
998
                        Index:  path.Index,
×
999
                }
×
1000

1001
        // Case 2: Family and index only. This case is easy, we can just go
1002
        // ahead and derive the public key from the family and index and then
1003
        // supply that information in the BIP32 derivation field.
1004
        case signDesc.KeyDesc.PubKey == nil && !signDesc.KeyDesc.IsEmpty():
×
1005
                fullDesc, err := r.watchOnlyKeyRing.DeriveKey(
×
1006
                        signDesc.KeyDesc.KeyLocator,
×
1007
                )
×
1008
                if err != nil {
×
1009
                        return nil, fmt.Errorf("error deriving key with "+
×
1010
                                "family %d and index %d from the watch-only "+
×
1011
                                "wallet: %v",
×
1012
                                signDesc.KeyDesc.KeyLocator.Family,
×
1013
                                signDesc.KeyDesc.KeyLocator.Index, err)
×
1014
                }
×
1015
                signDesc.KeyDesc.PubKey = fullDesc.PubKey
×
1016
        }
1017

1018
        var derivation *psbt.Bip32Derivation
×
1019

×
1020
        // Make sure we actually know about the input. We either have been
×
1021
        // watching the UTXO on-chain or we have been given all the required
×
1022
        // info in the sign descriptor.
×
1023
        info, err := r.WalletController.FetchOutpointInfo(
×
1024
                &txIn.PreviousOutPoint,
×
1025
        )
×
1026

×
1027
        // If the wallet is aware of this outpoint, we go ahead and fetch the
×
1028
        // derivation info.
×
1029
        if err == nil {
×
1030
                derivation, err = r.WalletController.FetchDerivationInfo(
×
1031
                        info.PkScript,
×
1032
                )
×
1033
        }
×
1034

1035
        switch {
×
1036
        // No error, we do have the full UTXO info available.
1037
        case err == nil:
×
1038
                in.WitnessUtxo = &wire.TxOut{
×
1039
                        Value:    int64(info.Value),
×
1040
                        PkScript: info.PkScript,
×
1041
                }
×
1042
                in.NonWitnessUtxo = info.PrevTx
×
1043
                in.Bip32Derivation = []*psbt.Bip32Derivation{derivation}
×
1044

1045
        // The wallet doesn't know about this UTXO, so it's probably a TX that
1046
        // we haven't published yet (e.g. a channel funding TX). So we need to
1047
        // assemble everything from the sign descriptor. We won't be able to
1048
        // supply a non-witness UTXO (=full TX of the input being spent) in this
1049
        // case. That is no problem if the signing instance is another lnd
1050
        // instance since we don't require it for pure witness inputs. But a
1051
        // hardware wallet might require it for security reasons.
1052
        case signDesc.KeyDesc.PubKey != nil && signDesc.Output != nil:
×
1053
                in.WitnessUtxo = signDesc.Output
×
1054
                in.Bip32Derivation = []*psbt.Bip32Derivation{{
×
1055
                        Bip32Path: []uint32{
×
1056
                                keychain.BIP0043Purpose +
×
1057
                                        hdkeychain.HardenedKeyStart,
×
1058
                                r.netParams.HDCoinType +
×
1059
                                        hdkeychain.HardenedKeyStart,
×
1060
                                uint32(signDesc.KeyDesc.Family) +
×
1061
                                        hdkeychain.HardenedKeyStart,
×
1062
                                0,
×
1063
                                signDesc.KeyDesc.Index,
×
1064
                        },
×
1065
                        PubKey: signDesc.KeyDesc.PubKey.SerializeCompressed(),
×
1066
                }}
×
1067

×
1068
                // We need to specify a pk script in the witness UTXO, otherwise
×
1069
                // the field becomes invalid when serialized as a PSBT. To avoid
×
1070
                // running into a generic "Invalid PSBT serialization format"
×
1071
                // error later, we return a more descriptive error now.
×
1072
                if len(in.WitnessUtxo.PkScript) == 0 {
×
1073
                        return nil, fmt.Errorf("error assembling UTXO " +
×
1074
                                "information, output not known to wallet and " +
×
1075
                                "no UTXO pk script provided in sign descriptor")
×
1076
                }
×
1077

1078
        default:
×
1079
                return nil, fmt.Errorf("error assembling UTXO information, "+
×
1080
                        "wallet returned err='%v' and sign descriptor is "+
×
1081
                        "incomplete", err)
×
1082
        }
1083

1084
        // Assemble all other information about the input we have.
1085
        in.RedeemScript = sigScript
×
1086
        in.SighashType = signDesc.HashType
×
1087
        in.WitnessScript = signDesc.WitnessScript
×
1088

×
1089
        if len(signDesc.SingleTweak) > 0 {
×
1090
                in.Unknowns = append(in.Unknowns, &psbt.Unknown{
×
1091
                        Key:   btcwallet.PsbtKeyTypeInputSignatureTweakSingle,
×
1092
                        Value: signDesc.SingleTweak,
×
1093
                })
×
1094
        }
×
1095
        if signDesc.DoubleTweak != nil {
×
1096
                in.Unknowns = append(in.Unknowns, &psbt.Unknown{
×
1097
                        Key:   btcwallet.PsbtKeyTypeInputSignatureTweakDouble,
×
1098
                        Value: signDesc.DoubleTweak.Serialize(),
×
1099
                })
×
1100
        }
×
1101

1102
        // Add taproot specific fields.
1103
        switch signDesc.SignMethod {
×
1104
        case input.TaprootKeySpendBIP0086SignMethod,
1105
                input.TaprootKeySpendSignMethod:
×
1106

×
1107
                // The key identifying factor for a key spend is that we don't
×
1108
                // provide any leaf hashes to signal we want a signature for the
×
1109
                // key spend path (with the internal key).
×
1110
                d := in.Bip32Derivation[0]
×
1111
                in.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
×
1112
                        // The x-only public key is just our compressed public
×
1113
                        // key without the first byte (type/parity).
×
1114
                        XOnlyPubKey:          d.PubKey[1:],
×
1115
                        LeafHashes:           nil,
×
1116
                        MasterKeyFingerprint: d.MasterKeyFingerprint,
×
1117
                        Bip32Path:            d.Bip32Path,
×
1118
                }}
×
1119

×
1120
                // If this is a BIP0086 key spend then the tap tweak is empty,
×
1121
                // otherwise it's set to the Taproot root hash.
×
1122
                in.TaprootMerkleRoot = signDesc.TapTweak
×
1123

1124
        case input.TaprootScriptSpendSignMethod:
×
1125
                // The script spend path is a bit more involved when doing it
×
1126
                // through the PSBT method. We need to specify the leaf hash
×
1127
                // that the signer should sign for.
×
1128
                leaf := txscript.TapLeaf{
×
1129
                        LeafVersion: txscript.BaseLeafVersion,
×
1130
                        Script:      signDesc.WitnessScript,
×
1131
                }
×
1132
                leafHash := leaf.TapHash()
×
1133

×
1134
                d := in.Bip32Derivation[0]
×
1135
                in.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
×
1136
                        XOnlyPubKey:          d.PubKey[1:],
×
1137
                        LeafHashes:           [][]byte{leafHash[:]},
×
1138
                        MasterKeyFingerprint: d.MasterKeyFingerprint,
×
1139
                        Bip32Path:            d.Bip32Path,
×
1140
                }}
×
1141

×
1142
                // We also need to supply a control block. But because we don't
×
1143
                // know the internal key nor the merkle proofs (both is not
×
1144
                // supplied through the SignOutputRaw RPC) and is technically
×
1145
                // not really needed by the signer (since we only want a
×
1146
                // signature, the full witness stack is assembled by the caller
×
1147
                // of this RPC), we can get by with faking certain information
×
1148
                // that we don't have.
×
1149
                fakeInternalKey, _ := btcec.ParsePubKey(d.PubKey)
×
1150
                fakeKeyIsOdd := d.PubKey[0] == input.PubKeyFormatCompressedOdd
×
1151
                controlBlock := txscript.ControlBlock{
×
1152
                        InternalKey:     fakeInternalKey,
×
1153
                        OutputKeyYIsOdd: fakeKeyIsOdd,
×
1154
                        LeafVersion:     leaf.LeafVersion,
×
1155
                }
×
1156
                blockBytes, err := controlBlock.ToBytes()
×
1157
                if err != nil {
×
1158
                        return nil, fmt.Errorf("error serializing control "+
×
1159
                                "block: %v", err)
×
1160
                }
×
1161

1162
                in.TaprootLeafScript = []*psbt.TaprootTapLeafScript{{
×
1163
                        ControlBlock: blockBytes,
×
1164
                        Script:       leaf.Script,
×
1165
                        LeafVersion:  leaf.LeafVersion,
×
1166
                }}
×
1167
        }
1168

1169
        // Okay, let's sign the input by the remote signer now.
1170
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
1171
        defer cancel()
×
1172

×
1173
        var buf bytes.Buffer
×
1174
        if err := packet.Serialize(&buf); err != nil {
×
1175
                return nil, fmt.Errorf("error serializing PSBT: %w", err)
×
1176
        }
×
1177

1178
        resp, err := r.walletClient.SignPsbt(
×
1179
                ctxt, &walletrpc.SignPsbtRequest{FundedPsbt: buf.Bytes()},
×
1180
        )
×
1181
        if err != nil {
×
1182
                considerShutdown(err)
×
1183
                return nil, fmt.Errorf("error signing PSBT in remote signer "+
×
1184
                        "instance: %v", err)
×
1185
        }
×
1186

1187
        signedPacket, err := psbt.NewFromRawBytes(
×
1188
                bytes.NewReader(resp.SignedPsbt), false,
×
1189
        )
×
1190
        if err != nil {
×
1191
                return nil, fmt.Errorf("error parsing signed PSBT: %w", err)
×
1192
        }
×
1193

1194
        // We expect a signature in the input now.
1195
        if signDesc.InputIndex >= len(signedPacket.Inputs) {
×
1196
                return nil, fmt.Errorf("remote signer returned invalid PSBT")
×
1197
        }
×
1198
        in = &signedPacket.Inputs[signDesc.InputIndex]
×
1199

×
1200
        return extractSignature(in, signDesc.SignMethod)
×
1201
}
1202

1203
// extractSignature attempts to extract the signature from the PSBT input,
1204
// looking at different fields depending on the signing method that was used.
1205
func extractSignature(in *psbt.PInput,
1206
        signMethod input.SignMethod) (input.Signature, error) {
×
1207

×
1208
        switch signMethod {
×
1209
        case input.WitnessV0SignMethod:
×
1210
                if len(in.PartialSigs) != 1 {
×
1211
                        return nil, fmt.Errorf("remote signer returned "+
×
1212
                                "invalid partial signature, wanted 1, got %d",
×
1213
                                len(in.PartialSigs))
×
1214
                }
×
1215
                sigWithSigHash := in.PartialSigs[0]
×
1216
                if sigWithSigHash == nil {
×
1217
                        return nil, fmt.Errorf("remote signer returned nil " +
×
1218
                                "signature")
×
1219
                }
×
1220

1221
                // The remote signer always adds the sighash type, so we need to
1222
                // account for that.
1223
                sigLen := len(sigWithSigHash.Signature)
×
1224
                if sigLen < ecdsa.MinSigLen+1 {
×
1225
                        return nil, fmt.Errorf("remote signer returned "+
×
1226
                                "invalid partial signature: signature too "+
×
1227
                                "short with %d bytes", sigLen)
×
1228
                }
×
1229

1230
                // Parse the signature, but chop off the last byte which is the
1231
                // sighash type.
1232
                sig := sigWithSigHash.Signature[0 : sigLen-1]
×
1233
                return ecdsa.ParseDERSignature(sig)
×
1234

1235
        // The type of key spend doesn't matter, the signature should be in the
1236
        // same field for both of those signing methods.
1237
        case input.TaprootKeySpendBIP0086SignMethod,
1238
                input.TaprootKeySpendSignMethod:
×
1239

×
1240
                sigLen := len(in.TaprootKeySpendSig)
×
1241
                if sigLen < schnorr.SignatureSize {
×
1242
                        return nil, fmt.Errorf("remote signer returned "+
×
1243
                                "invalid key spend signature: signature too "+
×
1244
                                "short with %d bytes", sigLen)
×
1245
                }
×
1246

1247
                return schnorr.ParseSignature(
×
1248
                        in.TaprootKeySpendSig[:schnorr.SignatureSize],
×
1249
                )
×
1250

1251
        case input.TaprootScriptSpendSignMethod:
×
1252
                if len(in.TaprootScriptSpendSig) != 1 {
×
1253
                        return nil, fmt.Errorf("remote signer returned "+
×
1254
                                "invalid taproot script spend signature, "+
×
1255
                                "wanted 1, got %d",
×
1256
                                len(in.TaprootScriptSpendSig))
×
1257
                }
×
1258
                scriptSpendSig := in.TaprootScriptSpendSig[0]
×
1259
                if scriptSpendSig == nil {
×
1260
                        return nil, fmt.Errorf("remote signer returned nil " +
×
1261
                                "taproot script spend signature")
×
1262
                }
×
1263

1264
                return schnorr.ParseSignature(scriptSpendSig.Signature)
×
1265

1266
        default:
×
1267
                return nil, fmt.Errorf("can't extract signature, unsupported "+
×
1268
                        "signing method: %v", signMethod)
×
1269
        }
1270
}
1271

1272
// connectRPC tries to establish an RPC connection to the given host:port with
1273
// the supplied certificate and macaroon.
1274
func connectRPC(hostPort, tlsCertPath, macaroonPath string,
1275
        timeout time.Duration) (*grpc.ClientConn, error) {
×
1276

×
1277
        certBytes, err := os.ReadFile(tlsCertPath)
×
1278
        if err != nil {
×
1279
                return nil, fmt.Errorf("error reading TLS cert file %v: %w",
×
1280
                        tlsCertPath, err)
×
1281
        }
×
1282

1283
        cp := x509.NewCertPool()
×
1284
        if !cp.AppendCertsFromPEM(certBytes) {
×
1285
                return nil, fmt.Errorf("credentials: failed to append " +
×
1286
                        "certificate")
×
1287
        }
×
1288

1289
        macBytes, err := os.ReadFile(macaroonPath)
×
1290
        if err != nil {
×
1291
                return nil, fmt.Errorf("error reading macaroon file %v: %w",
×
1292
                        macaroonPath, err)
×
1293
        }
×
1294
        mac := &macaroon.Macaroon{}
×
1295
        if err := mac.UnmarshalBinary(macBytes); err != nil {
×
1296
                return nil, fmt.Errorf("error decoding macaroon: %w", err)
×
1297
        }
×
1298

1299
        macCred, err := macaroons.NewMacaroonCredential(mac)
×
1300
        if err != nil {
×
1301
                return nil, fmt.Errorf("error creating creds: %w", err)
×
1302
        }
×
1303

1304
        opts := []grpc.DialOption{
×
1305
                grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(
×
1306
                        cp, "",
×
1307
                )),
×
1308
                grpc.WithPerRPCCredentials(macCred),
×
1309
                grpc.WithBlock(),
×
1310
        }
×
1311
        ctxt, cancel := context.WithTimeout(context.Background(), timeout)
×
1312
        defer cancel()
×
1313
        conn, err := grpc.DialContext(ctxt, hostPort, opts...)
×
1314
        if err != nil {
×
1315
                return nil, fmt.Errorf("unable to connect to RPC server: %w",
×
1316
                        err)
×
1317
        }
×
1318

1319
        return conn, nil
×
1320
}
1321

1322
// packetFromTx creates a PSBT from a tx that potentially already contains
1323
// signed inputs.
1324
func packetFromTx(original *wire.MsgTx) (*psbt.Packet, error) {
×
1325
        // The psbt.NewFromUnsignedTx function complains if there are any
×
1326
        // scripts or witness content on a TX. So we create a copy of the TX and
×
1327
        // nil out all the offending data, but also keep a backup around that we
×
1328
        // add to the PSBT afterwards.
×
1329
        noSigs := original.Copy()
×
1330
        for idx := range noSigs.TxIn {
×
1331
                noSigs.TxIn[idx].SignatureScript = nil
×
1332
                noSigs.TxIn[idx].Witness = nil
×
1333
        }
×
1334

1335
        // With all the data that is seen as "signed", we can now create the
1336
        // empty packet.
1337
        packet, err := psbt.NewFromUnsignedTx(noSigs)
×
1338
        if err != nil {
×
1339
                return nil, err
×
1340
        }
×
1341

1342
        var buf bytes.Buffer
×
1343
        for idx, txIn := range original.TxIn {
×
1344
                if len(txIn.SignatureScript) > 0 {
×
1345
                        packet.Inputs[idx].FinalScriptSig = txIn.SignatureScript
×
1346
                }
×
1347

1348
                if len(txIn.Witness) > 0 {
×
1349
                        buf.Reset()
×
1350
                        err = psbt.WriteTxWitness(&buf, txIn.Witness)
×
1351
                        if err != nil {
×
1352
                                return nil, err
×
1353
                        }
×
1354
                        packet.Inputs[idx].FinalScriptWitness = buf.Bytes()
×
1355
                }
1356
        }
1357

1358
        return packet, nil
×
1359
}
1360

1361
// considerShutdown inspects the error and issues a shutdown (through logging
1362
// a critical error, which will cause the logger to issue a clean shutdown
1363
// request) if the error looks like a connection or general availability error
1364
// and not some application specific problem.
1365
func considerShutdown(err error) {
×
1366
        statusErr, isStatusErr := status.FromError(err)
×
1367
        switch {
×
1368
        // The context attached to the client request has timed out. This can be
1369
        // due to not being able to reach the signing server, or it's taking too
1370
        // long to respond. In either case, request a shutdown.
1371
        case err == context.DeadlineExceeded:
×
1372
                fallthrough
×
1373

1374
        // The signing server's context timed out before the client's due to
1375
        // clock skew, request a shutdown anyway.
1376
        case isStatusErr && statusErr.Code() == codes.DeadlineExceeded:
×
1377
                log.Critical("RPC signing timed out: %v", err)
×
1378

1379
        case isStatusErr && statusErr.Code() == codes.Unavailable:
×
1380
                log.Critical("RPC signing server not available: %v", err)
×
1381
        }
1382
}
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