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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 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