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

lightningnetwork / lnd / 13974489001

20 Mar 2025 04:32PM UTC coverage: 56.292% (-2.9%) from 59.168%
13974489001

Pull #8754

github

web-flow
Merge aed149e6b into ea050d06f
Pull Request #8754: Add `Outbound` Remote Signer implementation

594 of 1713 new or added lines in 26 files covered. (34.68%)

23052 existing lines in 272 files now uncovered.

105921 of 188165 relevant lines covered (56.29%)

23796.34 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
        "errors"
8
        "fmt"
9
        "time"
10

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

36
var (
37
        // ErrRemoteSigningPrivateKeyNotAvailable is the error that is returned
38
        // if an operation is requested from the RPC wallet that is not
39
        // supported in remote signing mode.
40
        ErrRemoteSigningPrivateKeyNotAvailable = errors.New("deriving " +
41
                "private key is not supported by RPC based key ring")
42
)
43

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

53
        watchOnlyKeyRing keychain.SecretKeyRing
54

55
        netParams *chaincfg.Params
56

57
        rpcTimeout time.Duration
58

59
        remoteSignerConn RemoteSignerConnection
60
}
61

62
var _ keychain.SecretKeyRing = (*RPCKeyRing)(nil)
63
var _ input.Signer = (*RPCKeyRing)(nil)
64
var _ keychain.MessageSignerRing = (*RPCKeyRing)(nil)
65
var _ lnwallet.WalletController = (*RPCKeyRing)(nil)
66

67
// NewRPCKeyRing creates a new remote signing secret key ring that uses the
68
// given watch-only base wallet to keep track of addresses and transactions but
69
// delegates any signing or ECDH operations to the remove signer through RPC.
70
func NewRPCKeyRing(watchOnlyKeyRing keychain.SecretKeyRing,
71
        watchOnlyWalletController lnwallet.WalletController,
72
        remoteSignerConn RemoteSignerConnection,
UNCOV
73
        netParams *chaincfg.Params) (*RPCKeyRing, error) {
×
UNCOV
74

×
UNCOV
75
        return &RPCKeyRing{
×
UNCOV
76
                WalletController: watchOnlyWalletController,
×
UNCOV
77
                watchOnlyKeyRing: watchOnlyKeyRing,
×
UNCOV
78
                netParams:        netParams,
×
NEW
79
                rpcTimeout:       remoteSignerConn.Timeout(),
×
NEW
80
                remoteSignerConn: remoteSignerConn,
×
UNCOV
81
        }, nil
×
UNCOV
82
}
×
83

84
// NewAddress returns the next external or internal address for the
85
// wallet dictated by the value of the `change` parameter. If change is
86
// true, then an internal address should be used, otherwise an external
87
// address should be returned. The type of address returned is dictated
88
// by the wallet's capabilities, and may be of type: p2sh, p2wkh,
89
// p2wsh, etc. The account parameter must be non-empty as it determines
90
// which account the address should be generated from.
91
func (r *RPCKeyRing) NewAddress(addrType lnwallet.AddressType, change bool,
UNCOV
92
        account string) (btcutil.Address, error) {
×
UNCOV
93

×
UNCOV
94
        return r.WalletController.NewAddress(addrType, change, account)
×
UNCOV
95
}
×
96

97
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
98
// the specified outputs. In the case the wallet has insufficient funds, or the
99
// outputs are non-standard, a non-nil error will be returned.
100
//
101
// NOTE: This method requires the global coin selection lock to be held.
102
//
103
// NOTE: This is a part of the WalletController interface.
104
//
105
// NOTE: This method only signs with BIP49/84 keys.
106
func (r *RPCKeyRing) SendOutputs(inputs fn.Set[wire.OutPoint],
107
        outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
108
        minConfs int32, label string,
UNCOV
109
        strategy basewallet.CoinSelectionStrategy) (*wire.MsgTx, error) {
×
UNCOV
110

×
UNCOV
111
        tx, err := r.WalletController.SendOutputs(
×
UNCOV
112
                inputs, outputs, feeRate, minConfs, label, strategy,
×
UNCOV
113
        )
×
UNCOV
114
        if err != nil && err != basewallet.ErrTxUnsigned {
×
115
                return nil, err
×
116
        }
×
UNCOV
117
        if err == nil {
×
118
                // This shouldn't happen since our wallet controller is watch-
×
119
                // only and can't sign the TX.
×
120
                return tx, nil
×
121
        }
×
122

123
        // We know at this point that we only have inputs from our own wallet.
124
        // So we can just compute the input script using the remote signer.
UNCOV
125
        outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController)
×
UNCOV
126
        for i, txIn := range tx.TxIn {
×
UNCOV
127
                signDesc := input.SignDescriptor{
×
UNCOV
128
                        HashType: txscript.SigHashAll,
×
UNCOV
129
                        SigHashes: txscript.NewTxSigHashes(
×
UNCOV
130
                                tx, outputFetcher,
×
UNCOV
131
                        ),
×
UNCOV
132
                        PrevOutputFetcher: outputFetcher,
×
UNCOV
133
                }
×
UNCOV
134

×
UNCOV
135
                // We can only sign this input if it's ours, so we'll ask the
×
UNCOV
136
                // watch-only wallet if it can map this outpoint into a coin we
×
UNCOV
137
                // own. If not, then we can't continue because our wallet state
×
UNCOV
138
                // is out of sync.
×
UNCOV
139
                info, err := r.WalletController.FetchOutpointInfo(
×
UNCOV
140
                        &txIn.PreviousOutPoint,
×
UNCOV
141
                )
×
UNCOV
142
                if err != nil {
×
143
                        return nil, fmt.Errorf("error looking up utxo: %w", err)
×
144
                }
×
145

UNCOV
146
                if txscript.IsPayToTaproot(info.PkScript) {
×
UNCOV
147
                        signDesc.HashType = txscript.SigHashDefault
×
UNCOV
148
                }
×
149

150
                // Now that we know the input is ours, we'll populate the
151
                // signDesc with the per input unique information.
UNCOV
152
                signDesc.Output = &wire.TxOut{
×
UNCOV
153
                        Value:    int64(info.Value),
×
UNCOV
154
                        PkScript: info.PkScript,
×
UNCOV
155
                }
×
UNCOV
156
                signDesc.InputIndex = i
×
UNCOV
157

×
UNCOV
158
                // Finally, we'll sign the input as is, and populate the input
×
UNCOV
159
                // with the witness and sigScript (if needed).
×
UNCOV
160
                inputScript, err := r.ComputeInputScript(tx, &signDesc)
×
UNCOV
161
                if err != nil {
×
162
                        return nil, err
×
163
                }
×
164

UNCOV
165
                txIn.SignatureScript = inputScript.SigScript
×
UNCOV
166
                txIn.Witness = inputScript.Witness
×
167
        }
168

UNCOV
169
        return tx, r.WalletController.PublishTransaction(tx, label)
×
170
}
171

172
// SignPsbt expects a partial transaction with all inputs and outputs fully
173
// declared and tries to sign all unsigned inputs that have all required fields
174
// (UTXO information, BIP32 derivation information, witness or sig scripts) set.
175
// If no error is returned, the PSBT is ready to be given to the next signer or
176
// to be finalized if lnd was the last signer.
177
//
178
// NOTE: This RPC only signs inputs (and only those it can sign), it does not
179
// perform any other tasks (such as coin selection, UTXO locking or
180
// input/output/fee value validation, PSBT finalization). Any input that is
181
// incomplete will be skipped.
UNCOV
182
func (r *RPCKeyRing) SignPsbt(packet *psbt.Packet) ([]uint32, error) {
×
UNCOV
183
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
184
        defer cancel()
×
UNCOV
185

×
UNCOV
186
        var buf bytes.Buffer
×
UNCOV
187
        if err := packet.Serialize(&buf); err != nil {
×
188
                return nil, fmt.Errorf("error serializing PSBT: %w", err)
×
189
        }
×
190

NEW
191
        resp, err := r.remoteSignerConn.SignPsbt(ctxt,
×
NEW
192
                &walletrpc.SignPsbtRequest{FundedPsbt: buf.Bytes()},
×
NEW
193
        )
×
194
        if err != nil {
×
195
                considerShutdown(err)
×
196
                return nil, fmt.Errorf("error signing PSBT in remote signer "+
×
197
                        "instance: %v", err)
×
198
        }
×
199

UNCOV
200
        signedPacket, err := psbt.NewFromRawBytes(
×
UNCOV
201
                bytes.NewReader(resp.SignedPsbt), false,
×
UNCOV
202
        )
×
UNCOV
203
        if err != nil {
×
204
                return nil, fmt.Errorf("error parsing signed PSBT: %w", err)
×
205
        }
×
206

207
        // The caller expects the packet to be modified instead of a new
208
        // instance to be returned. So we just overwrite all fields in the
209
        // original packet.
UNCOV
210
        packet.UnsignedTx = signedPacket.UnsignedTx
×
UNCOV
211
        packet.Inputs = signedPacket.Inputs
×
UNCOV
212
        packet.Outputs = signedPacket.Outputs
×
UNCOV
213
        packet.Unknowns = signedPacket.Unknowns
×
UNCOV
214

×
UNCOV
215
        return resp.SignedInputs, nil
×
216
}
217

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

249
        // Go through each input that doesn't have final witness data attached
250
        // to it already and try to sign it. We do expect that we're the last
251
        // ones to sign. If there is any input without witness data that we
252
        // cannot sign because it's not our UTXO, this will be a hard failure.
UNCOV
253
        tx := packet.UnsignedTx
×
UNCOV
254
        prevOutFetcher := basewallet.PsbtPrevOutputFetcher(packet)
×
UNCOV
255
        sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher)
×
UNCOV
256
        for idx, txIn := range tx.TxIn {
×
UNCOV
257
                in := packet.Inputs[idx]
×
UNCOV
258

×
UNCOV
259
                // We can only sign if we have UTXO information available. We
×
UNCOV
260
                // can just continue here as a later step will fail with a more
×
UNCOV
261
                // precise error message.
×
UNCOV
262
                if in.WitnessUtxo == nil && in.NonWitnessUtxo == nil {
×
263
                        continue
×
264
                }
265

266
                // Skip this input if it's got final witness data attached.
UNCOV
267
                if len(in.FinalScriptWitness) > 0 {
×
268
                        continue
×
269
                }
270

271
                // We can only sign this input if it's ours, so we try to map it
272
                // to a coin we own. If we can't, then we'll continue as it
273
                // isn't our input.
UNCOV
274
                utxo, err := r.FetchOutpointInfo(&txIn.PreviousOutPoint)
×
UNCOV
275
                if err != nil {
×
276
                        continue
×
277
                }
UNCOV
278
                fullTx := utxo.PrevTx
×
UNCOV
279
                signDesc := &input.SignDescriptor{
×
UNCOV
280
                        KeyDesc: keychain.KeyDescriptor{},
×
UNCOV
281
                        Output: &wire.TxOut{
×
UNCOV
282
                                Value:    int64(utxo.Value),
×
UNCOV
283
                                PkScript: utxo.PkScript,
×
UNCOV
284
                        },
×
UNCOV
285
                        HashType:          in.SighashType,
×
UNCOV
286
                        SigHashes:         sigHashes,
×
UNCOV
287
                        InputIndex:        idx,
×
UNCOV
288
                        PrevOutputFetcher: prevOutFetcher,
×
UNCOV
289
                }
×
UNCOV
290

×
UNCOV
291
                // Find out what UTXO we are signing. Wallets _should_ always
×
UNCOV
292
                // provide the full non-witness UTXO for segwit v0.
×
UNCOV
293
                var signOutput *wire.TxOut
×
UNCOV
294
                if in.NonWitnessUtxo != nil {
×
UNCOV
295
                        prevIndex := txIn.PreviousOutPoint.Index
×
UNCOV
296
                        signOutput = in.NonWitnessUtxo.TxOut[prevIndex]
×
UNCOV
297

×
UNCOV
298
                        if !psbt.TxOutsEqual(signDesc.Output, signOutput) {
×
299
                                return fmt.Errorf("found UTXO %#v but it "+
×
300
                                        "doesn't match PSBT's input %v",
×
301
                                        signDesc.Output, signOutput)
×
302
                        }
×
303

UNCOV
304
                        if fullTx.TxHash() != txIn.PreviousOutPoint.Hash {
×
305
                                return fmt.Errorf("found UTXO tx %v but it "+
×
306
                                        "doesn't match PSBT's input %v",
×
307
                                        fullTx.TxHash(),
×
308
                                        txIn.PreviousOutPoint.Hash)
×
309
                        }
×
310
                }
311

312
                // Fall back to witness UTXO only for older wallets.
UNCOV
313
                if in.WitnessUtxo != nil {
×
UNCOV
314
                        signOutput = in.WitnessUtxo
×
UNCOV
315

×
UNCOV
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

323
                // Do the actual signing in ComputeInputScript which in turn
324
                // will invoke the remote signer.
UNCOV
325
                script, err := r.ComputeInputScript(tx, signDesc)
×
UNCOV
326
                if err != nil {
×
327
                        return fmt.Errorf("error computing input script for "+
×
328
                                "input %d: %v", idx, err)
×
329
                }
×
330

331
                // Serialize the witness format from the stack representation to
332
                // the wire representation.
UNCOV
333
                var witnessBytes bytes.Buffer
×
UNCOV
334
                err = psbt.WriteTxWitness(&witnessBytes, script.Witness)
×
UNCOV
335
                if err != nil {
×
336
                        return fmt.Errorf("error serializing witness: %w", err)
×
337
                }
×
UNCOV
338
                packet.Inputs[idx].FinalScriptWitness = witnessBytes.Bytes()
×
UNCOV
339
                packet.Inputs[idx].FinalScriptSig = script.SigScript
×
340
        }
341

342
        // Make sure the PSBT itself thinks it's finalized and ready to be
343
        // broadcast.
UNCOV
344
        err = psbt.MaybeFinalizeAll(packet)
×
UNCOV
345
        if err != nil {
×
346
                return fmt.Errorf("error finalizing PSBT: %w", err)
×
347
        }
×
348

UNCOV
349
        return nil
×
350
}
351

352
// DeriveNextKey attempts to derive the *next* key within the key family
353
// (account in BIP43) specified. This method should return the next external
354
// child within this branch.
355
//
356
// NOTE: This method is part of the keychain.KeyRing interface.
357
func (r *RPCKeyRing) DeriveNextKey(
UNCOV
358
        keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) {
×
UNCOV
359

×
UNCOV
360
        return r.watchOnlyKeyRing.DeriveNextKey(keyFam)
×
UNCOV
361
}
×
362

363
// DeriveKey attempts to derive an arbitrary key specified by the passed
364
// KeyLocator. This may be used in several recovery scenarios, or when manually
365
// rotating something like our current default node key.
366
//
367
// NOTE: This method is part of the keychain.KeyRing interface.
368
func (r *RPCKeyRing) DeriveKey(
UNCOV
369
        keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) {
×
UNCOV
370

×
UNCOV
371
        return r.watchOnlyKeyRing.DeriveKey(keyLoc)
×
UNCOV
372
}
×
373

374
// ECDH performs a scalar multiplication (ECDH-like operation) between the
375
// target key descriptor and remote public key. The output returned will be the
376
// sha256 of the resulting shared point serialized in compressed format. If k is
377
// our private key, and P is the public key, we perform the following operation:
378
//
379
//        sx := k*P
380
//        s := sha256(sx.SerializeCompressed())
381
//
382
// NOTE: This method is part of the keychain.ECDHRing interface.
383
func (r *RPCKeyRing) ECDH(keyDesc keychain.KeyDescriptor,
UNCOV
384
        pubKey *btcec.PublicKey) ([32]byte, error) {
×
UNCOV
385

×
UNCOV
386
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
387
        defer cancel()
×
UNCOV
388

×
UNCOV
389
        key := [32]byte{}
×
UNCOV
390
        req := &signrpc.SharedKeyRequest{
×
UNCOV
391
                EphemeralPubkey: pubKey.SerializeCompressed(),
×
UNCOV
392
                KeyDesc: &signrpc.KeyDescriptor{
×
UNCOV
393
                        KeyLoc: &signrpc.KeyLocator{
×
UNCOV
394
                                KeyFamily: int32(keyDesc.Family),
×
UNCOV
395
                                KeyIndex:  int32(keyDesc.Index),
×
UNCOV
396
                        },
×
UNCOV
397
                },
×
UNCOV
398
        }
×
UNCOV
399

×
UNCOV
400
        if keyDesc.Index == 0 && keyDesc.PubKey != nil {
×
UNCOV
401
                req.KeyDesc.RawKeyBytes = keyDesc.PubKey.SerializeCompressed()
×
UNCOV
402
        }
×
403

NEW
404
        resp, err := r.remoteSignerConn.DeriveSharedKey(ctxt, req)
×
UNCOV
405
        if err != nil {
×
406
                considerShutdown(err)
×
407
                return key, fmt.Errorf("error deriving shared key in remote "+
×
408
                        "signer instance: %v", err)
×
409
        }
×
410

UNCOV
411
        copy(key[:], resp.SharedKey)
×
UNCOV
412
        return key, nil
×
413
}
414

415
// SignMessage attempts to sign a target message with the private key described
416
// in the key locator. If the target private key is unable to be found, then an
417
// error will be returned. The actual digest signed is the single or double
418
// SHA-256 of the passed message.
419
//
420
// NOTE: This method is part of the keychain.MessageSignerRing interface.
421
func (r *RPCKeyRing) SignMessage(keyLoc keychain.KeyLocator,
UNCOV
422
        msg []byte, doubleHash bool) (*ecdsa.Signature, error) {
×
UNCOV
423

×
UNCOV
424
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
425
        defer cancel()
×
UNCOV
426

×
NEW
427
        resp, err := r.remoteSignerConn.SignMessage(ctxt,
×
NEW
428
                &signrpc.SignMessageReq{
×
NEW
429
                        Msg: msg,
×
NEW
430
                        KeyLoc: &signrpc.KeyLocator{
×
NEW
431
                                KeyFamily: int32(keyLoc.Family),
×
NEW
432
                                KeyIndex:  int32(keyLoc.Index),
×
NEW
433
                        },
×
NEW
434
                        DoubleHash: doubleHash,
×
UNCOV
435
                },
×
NEW
436
        )
×
UNCOV
437
        if err != nil {
×
438
                considerShutdown(err)
×
439
                return nil, fmt.Errorf("error signing message in remote "+
×
440
                        "signer instance: %v", err)
×
441
        }
×
442

UNCOV
443
        wireSig, err := lnwire.NewSigFromECDSARawSignature(resp.Signature)
×
UNCOV
444
        if err != nil {
×
445
                return nil, fmt.Errorf("unable to create sig: %w", err)
×
446
        }
×
UNCOV
447
        sig, err := wireSig.ToSignature()
×
UNCOV
448
        if err != nil {
×
449
                return nil, fmt.Errorf("unable to parse sig: %w", err)
×
450
        }
×
UNCOV
451
        ecdsaSig, ok := sig.(*ecdsa.Signature)
×
UNCOV
452
        if !ok {
×
453
                return nil, fmt.Errorf("unexpected signature type: %T", sig)
×
454
        }
×
455

UNCOV
456
        return ecdsaSig, nil
×
457
}
458

459
// SignMessageCompact signs the given message, single or double SHA256 hashing
460
// it first, with the private key described in the key locator and returns the
461
// signature in the compact, public key recoverable format.
462
//
463
// NOTE: This method is part of the keychain.MessageSignerRing interface.
464
func (r *RPCKeyRing) SignMessageCompact(keyLoc keychain.KeyLocator,
UNCOV
465
        msg []byte, doubleHash bool) ([]byte, error) {
×
UNCOV
466

×
UNCOV
467
        if keyLoc.Family != keychain.KeyFamilyNodeKey {
×
468
                return nil, fmt.Errorf("error compact signing with key "+
×
469
                        "locator %v, can only sign with node key", keyLoc)
×
470
        }
×
471

UNCOV
472
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
473
        defer cancel()
×
UNCOV
474

×
NEW
475
        resp, err := r.remoteSignerConn.SignMessage(ctxt,
×
NEW
476
                &signrpc.SignMessageReq{
×
NEW
477
                        Msg: msg,
×
NEW
478
                        KeyLoc: &signrpc.KeyLocator{
×
NEW
479
                                KeyFamily: int32(keyLoc.Family),
×
NEW
480
                                KeyIndex:  int32(keyLoc.Index),
×
NEW
481
                        },
×
NEW
482
                        DoubleHash: doubleHash,
×
NEW
483
                        CompactSig: true,
×
UNCOV
484
                },
×
NEW
485
        )
×
UNCOV
486
        if err != nil {
×
487
                considerShutdown(err)
×
488
                return nil, fmt.Errorf("error signing message in remote "+
×
489
                        "signer instance: %v", err)
×
490
        }
×
491

492
        // The signature in the response is zbase32 encoded, so we need to
493
        // decode it before returning.
UNCOV
494
        return resp.Signature, nil
×
495
}
496

497
// SignMessageSchnorr attempts to sign a target message with the private key
498
// described in the key locator. If the target private key is unable to be
499
// found, then an error will be returned. The actual digest signed is the
500
// single or double SHA-256 of the passed message.
501
//
502
// NOTE: This method is part of the keychain.MessageSignerRing interface.
503
func (r *RPCKeyRing) SignMessageSchnorr(keyLoc keychain.KeyLocator,
504
        msg []byte, doubleHash bool, taprootTweak []byte,
UNCOV
505
        tag []byte) (*schnorr.Signature, error) {
×
UNCOV
506

×
UNCOV
507
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
508
        defer cancel()
×
UNCOV
509

×
NEW
510
        resp, err := r.remoteSignerConn.SignMessage(ctxt,
×
NEW
511
                &signrpc.SignMessageReq{
×
NEW
512
                        Msg: msg,
×
NEW
513
                        KeyLoc: &signrpc.KeyLocator{
×
NEW
514
                                KeyFamily: int32(keyLoc.Family),
×
NEW
515
                                KeyIndex:  int32(keyLoc.Index),
×
NEW
516
                        },
×
NEW
517
                        DoubleHash:         doubleHash,
×
NEW
518
                        SchnorrSig:         true,
×
NEW
519
                        SchnorrSigTapTweak: taprootTweak,
×
NEW
520
                        Tag:                tag,
×
UNCOV
521
                },
×
NEW
522
        )
×
UNCOV
523
        if err != nil {
×
524
                considerShutdown(err)
×
525
                return nil, fmt.Errorf("error signing message in remote "+
×
526
                        "signer instance: %w", err)
×
527
        }
×
528

UNCOV
529
        sigParsed, err := schnorr.ParseSignature(resp.Signature)
×
UNCOV
530
        if err != nil {
×
531
                return nil, fmt.Errorf("can't parse schnorr signature: %w",
×
532
                        err)
×
533
        }
×
UNCOV
534
        return sigParsed, nil
×
535
}
536

537
// DerivePrivKey attempts to derive the private key that corresponds to the
538
// passed key descriptor.  If the public key is set, then this method will
539
// perform an in-order scan over the key set, with a max of MaxKeyRangeScan
540
// keys. In order for this to work, the caller MUST set the KeyFamily within the
541
// partially populated KeyLocator.
542
//
543
// NOTE: This method is part of the keychain.SecretKeyRing interface.
544
func (r *RPCKeyRing) DerivePrivKey(_ keychain.KeyDescriptor) (*btcec.PrivateKey,
545
        error) {
×
546

×
547
        // This operation is not supported with remote signing. There should be
×
548
        // no need for invoking this method unless a channel backup (SCB) file
×
549
        // for pre-0.13.0 channels are attempted to be restored. In that case
×
550
        // it is recommended to restore the channels using a node with the full
×
551
        // seed available.
×
552
        return nil, ErrRemoteSigningPrivateKeyNotAvailable
×
553
}
×
554

555
// SignOutputRaw generates a signature for the passed transaction
556
// according to the data within the passed SignDescriptor.
557
//
558
// NOTE: The resulting signature should be void of a sighash byte.
559
//
560
// NOTE: This method is part of the input.Signer interface.
561
//
562
// NOTE: This method only signs with BIP1017 (internal) keys!
563
func (r *RPCKeyRing) SignOutputRaw(tx *wire.MsgTx,
UNCOV
564
        signDesc *input.SignDescriptor) (input.Signature, error) {
×
UNCOV
565

×
UNCOV
566
        // Forward the call to the remote signing instance. This call is only
×
UNCOV
567
        // ever called for signing witness (p2pkh or p2wsh) inputs and never
×
UNCOV
568
        // nested witness inputs, so the sigScript is always nil.
×
UNCOV
569
        return r.remoteSign(tx, signDesc, nil)
×
UNCOV
570
}
×
571

572
// ComputeInputScript generates a complete InputIndex for the passed
573
// transaction with the signature as defined within the passed
574
// SignDescriptor. This method should be capable of generating the
575
// proper input script for both regular p2wkh output and p2wkh outputs
576
// nested within a regular p2sh output.
577
//
578
// NOTE: This method will ignore any tweak parameters set within the
579
// passed SignDescriptor as it assumes a set of typical script
580
// templates (p2wkh, np2wkh, BIP0086 p2tr, etc).
581
//
582
// NOTE: This method is part of the input.Signer interface.
583
func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
UNCOV
584
        signDesc *input.SignDescriptor) (*input.Script, error) {
×
UNCOV
585

×
UNCOV
586
        addr, witnessProgram, sigScript, err := r.WalletController.ScriptForOutput(
×
UNCOV
587
                signDesc.Output,
×
UNCOV
588
        )
×
UNCOV
589
        if err != nil {
×
590
                return nil, err
×
591
        }
×
UNCOV
592
        signDesc.WitnessScript = witnessProgram
×
UNCOV
593

×
UNCOV
594
        // If this is a p2tr address, then it must be a BIP0086 key spend if we
×
UNCOV
595
        // are coming through this path (instead of SignOutputRaw).
×
UNCOV
596
        switch addr.AddrType() {
×
UNCOV
597
        case waddrmgr.TaprootPubKey:
×
UNCOV
598
                signDesc.SignMethod = input.TaprootKeySpendBIP0086SignMethod
×
UNCOV
599
                signDesc.WitnessScript = nil
×
UNCOV
600

×
UNCOV
601
                sig, err := r.remoteSign(tx, signDesc, nil)
×
UNCOV
602
                if err != nil {
×
603
                        return nil, fmt.Errorf("error signing with remote"+
×
604
                                "instance: %v", err)
×
605
                }
×
606

UNCOV
607
                rawSig := sig.Serialize()
×
UNCOV
608
                if signDesc.HashType != txscript.SigHashDefault {
×
609
                        rawSig = append(rawSig, byte(signDesc.HashType))
×
610
                }
×
611

UNCOV
612
                return &input.Script{
×
UNCOV
613
                        Witness: wire.TxWitness{
×
UNCOV
614
                                rawSig,
×
UNCOV
615
                        },
×
UNCOV
616
                }, nil
×
617

618
        case waddrmgr.TaprootScript:
×
619
                return nil, fmt.Errorf("computing input script for taproot " +
×
620
                        "script address not supported")
×
621
        }
622

623
        // Let's give the TX to the remote instance now, so it can sign the
624
        // input.
UNCOV
625
        sig, err := r.remoteSign(tx, signDesc, witnessProgram)
×
UNCOV
626
        if err != nil {
×
627
                return nil, fmt.Errorf("error signing with remote instance: %w",
×
628
                        err)
×
629
        }
×
630

631
        // ComputeInputScript currently is only used for P2WKH and NP2WKH
632
        // addresses. So the last item on the stack is always the compressed
633
        // public key.
UNCOV
634
        return &input.Script{
×
UNCOV
635
                Witness: wire.TxWitness{
×
UNCOV
636
                        append(sig.Serialize(), byte(signDesc.HashType)),
×
UNCOV
637
                        addr.PubKey().SerializeCompressed(),
×
UNCOV
638
                },
×
UNCOV
639
                SigScript: sigScript,
×
UNCOV
640
        }, nil
×
641
}
642

643
// MuSig2CreateSession creates a new MuSig2 signing session using the local
644
// key identified by the key locator. The complete list of all public keys of
645
// all signing parties must be provided, including the public key of the local
646
// signing key. If nonces of other parties are already known, they can be
647
// submitted as well to reduce the number of method calls necessary later on.
648
func (r *RPCKeyRing) MuSig2CreateSession(bipVersion input.MuSig2Version,
649
        keyLoc keychain.KeyLocator, pubKeys []*btcec.PublicKey,
650
        tweaks *input.MuSig2Tweaks, otherNonces [][musig2.PubNonceSize]byte,
UNCOV
651
        localNonces *musig2.Nonces) (*input.MuSig2SessionInfo, error) {
×
UNCOV
652

×
UNCOV
653
        apiVersion, err := signrpc.MarshalMuSig2Version(bipVersion)
×
UNCOV
654
        if err != nil {
×
655
                return nil, err
×
656
        }
×
657

658
        // We need to serialize all data for the RPC call. We can do that by
659
        // putting everything directly into the request struct.
UNCOV
660
        req := &signrpc.MuSig2SessionRequest{
×
UNCOV
661
                KeyLoc: &signrpc.KeyLocator{
×
UNCOV
662
                        KeyFamily: int32(keyLoc.Family),
×
UNCOV
663
                        KeyIndex:  int32(keyLoc.Index),
×
UNCOV
664
                },
×
UNCOV
665
                AllSignerPubkeys: make([][]byte, len(pubKeys)),
×
UNCOV
666
                Tweaks: make(
×
UNCOV
667
                        []*signrpc.TweakDesc, len(tweaks.GenericTweaks),
×
UNCOV
668
                ),
×
UNCOV
669
                OtherSignerPublicNonces: make([][]byte, len(otherNonces)),
×
UNCOV
670
                Version:                 apiVersion,
×
UNCOV
671
        }
×
UNCOV
672
        for idx, pubKey := range pubKeys {
×
UNCOV
673
                switch bipVersion {
×
UNCOV
674
                case input.MuSig2Version040:
×
UNCOV
675
                        req.AllSignerPubkeys[idx] = schnorr.SerializePubKey(
×
UNCOV
676
                                pubKey,
×
UNCOV
677
                        )
×
678

UNCOV
679
                case input.MuSig2Version100RC2:
×
UNCOV
680
                        req.AllSignerPubkeys[idx] = pubKey.SerializeCompressed()
×
681
                }
682
        }
UNCOV
683
        for idx, genericTweak := range tweaks.GenericTweaks {
×
684
                req.Tweaks[idx] = &signrpc.TweakDesc{
×
685
                        Tweak:   genericTweak.Tweak[:],
×
686
                        IsXOnly: genericTweak.IsXOnly,
×
687
                }
×
688
        }
×
UNCOV
689
        for idx, nonce := range otherNonces {
×
UNCOV
690
                req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
×
UNCOV
691
                copy(req.OtherSignerPublicNonces[idx], nonce[:])
×
UNCOV
692
        }
×
UNCOV
693
        if tweaks.HasTaprootTweak() {
×
UNCOV
694
                req.TaprootTweak = &signrpc.TaprootTweakDesc{
×
UNCOV
695
                        KeySpendOnly: tweaks.TaprootBIP0086Tweak,
×
UNCOV
696
                        ScriptRoot:   tweaks.TaprootTweak,
×
UNCOV
697
                }
×
UNCOV
698
        }
×
699

UNCOV
700
        if localNonces != nil {
×
UNCOV
701
                req.PregeneratedLocalNonce = localNonces.SecNonce[:]
×
UNCOV
702
        }
×
703

UNCOV
704
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
705
        defer cancel()
×
UNCOV
706

×
NEW
707
        resp, err := r.remoteSignerConn.MuSig2CreateSession(ctxt, req)
×
UNCOV
708
        if err != nil {
×
709
                considerShutdown(err)
×
710
                return nil, fmt.Errorf("error creating MuSig2 session in "+
×
711
                        "remote signer instance: %v", err)
×
712
        }
×
713

714
        // De-Serialize all the info back into our native struct.
UNCOV
715
        info := &input.MuSig2SessionInfo{
×
UNCOV
716
                Version:       bipVersion,
×
UNCOV
717
                TaprootTweak:  tweaks.HasTaprootTweak(),
×
UNCOV
718
                HaveAllNonces: resp.HaveAllNonces,
×
UNCOV
719
        }
×
UNCOV
720
        copy(info.SessionID[:], resp.SessionId)
×
UNCOV
721
        copy(info.PublicNonce[:], resp.LocalPublicNonces)
×
UNCOV
722

×
UNCOV
723
        info.CombinedKey, err = schnorr.ParsePubKey(resp.CombinedKey)
×
UNCOV
724
        if err != nil {
×
725
                return nil, fmt.Errorf("error parsing combined key: %w", err)
×
726
        }
×
727

UNCOV
728
        if tweaks.HasTaprootTweak() {
×
UNCOV
729
                info.TaprootInternalKey, err = schnorr.ParsePubKey(
×
UNCOV
730
                        resp.TaprootInternalKey,
×
UNCOV
731
                )
×
UNCOV
732
                if err != nil {
×
733
                        return nil, fmt.Errorf("error parsing internal key: %w",
×
734
                                err)
×
735
                }
×
736
        }
737

UNCOV
738
        return info, nil
×
739
}
740

741
// MuSig2RegisterNonces registers one or more public nonces of other signing
742
// participants for a session identified by its ID. This method returns true
743
// once we have all nonces for all other signing participants.
744
func (r *RPCKeyRing) MuSig2RegisterNonces(sessionID input.MuSig2SessionID,
UNCOV
745
        pubNonces [][musig2.PubNonceSize]byte) (bool, error) {
×
UNCOV
746

×
UNCOV
747
        // We need to serialize all data for the RPC call. We can do that by
×
UNCOV
748
        // putting everything directly into the request struct.
×
UNCOV
749
        req := &signrpc.MuSig2RegisterNoncesRequest{
×
UNCOV
750
                SessionId:               sessionID[:],
×
UNCOV
751
                OtherSignerPublicNonces: make([][]byte, len(pubNonces)),
×
UNCOV
752
        }
×
UNCOV
753
        for idx, nonce := range pubNonces {
×
UNCOV
754
                req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
×
UNCOV
755
                copy(req.OtherSignerPublicNonces[idx], nonce[:])
×
UNCOV
756
        }
×
757

UNCOV
758
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
759
        defer cancel()
×
UNCOV
760

×
NEW
761
        resp, err := r.remoteSignerConn.MuSig2RegisterNonces(ctxt, req)
×
UNCOV
762
        if err != nil {
×
763
                considerShutdown(err)
×
764
                return false, fmt.Errorf("error registering MuSig2 nonces in "+
×
765
                        "remote signer instance: %v", err)
×
766
        }
×
767

UNCOV
768
        return resp.HaveAllNonces, nil
×
769
}
770

771
// MuSig2Sign creates a partial signature using the local signing key
772
// that was specified when the session was created. This can only be
773
// called when all public nonces of all participants are known and have
774
// been registered with the session. If this node isn't responsible for
775
// combining all the partial signatures, then the cleanup parameter
776
// should be set, indicating that the session can be removed from memory
777
// once the signature was produced.
778
func (r *RPCKeyRing) MuSig2Sign(sessionID input.MuSig2SessionID,
UNCOV
779
        msg [sha256.Size]byte, cleanUp bool) (*musig2.PartialSignature, error) {
×
UNCOV
780

×
UNCOV
781
        // We need to serialize all data for the RPC call. We can do that by
×
UNCOV
782
        // putting everything directly into the request struct.
×
UNCOV
783
        req := &signrpc.MuSig2SignRequest{
×
UNCOV
784
                SessionId:     sessionID[:],
×
UNCOV
785
                MessageDigest: msg[:],
×
UNCOV
786
                Cleanup:       cleanUp,
×
UNCOV
787
        }
×
UNCOV
788

×
UNCOV
789
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
790
        defer cancel()
×
UNCOV
791

×
NEW
792
        resp, err := r.remoteSignerConn.MuSig2Sign(ctxt, req)
×
UNCOV
793
        if err != nil {
×
UNCOV
794
                considerShutdown(err)
×
UNCOV
795
                return nil, fmt.Errorf("error signing MuSig2 session in "+
×
UNCOV
796
                        "remote signer instance: %v", err)
×
UNCOV
797
        }
×
798

UNCOV
799
        partialSig, err := input.DeserializePartialSignature(
×
UNCOV
800
                resp.LocalPartialSignature,
×
UNCOV
801
        )
×
UNCOV
802
        if err != nil {
×
803
                return nil, fmt.Errorf("error parsing partial signature from "+
×
804
                        "remote signer: %v", err)
×
805
        }
×
806

UNCOV
807
        return partialSig, nil
×
808
}
809

810
// MuSig2CombineSig combines the given partial signature(s) with the
811
// local one, if it already exists. Once a partial signature of all
812
// participants is registered, the final signature will be combined and
813
// returned.
814
func (r *RPCKeyRing) MuSig2CombineSig(sessionID input.MuSig2SessionID,
815
        partialSigs []*musig2.PartialSignature) (*schnorr.Signature, bool,
UNCOV
816
        error) {
×
UNCOV
817

×
UNCOV
818
        // We need to serialize all data for the RPC call. We can do that by
×
UNCOV
819
        // putting everything directly into the request struct.
×
UNCOV
820
        req := &signrpc.MuSig2CombineSigRequest{
×
UNCOV
821
                SessionId:              sessionID[:],
×
UNCOV
822
                OtherPartialSignatures: make([][]byte, len(partialSigs)),
×
UNCOV
823
        }
×
UNCOV
824
        for idx, partialSig := range partialSigs {
×
UNCOV
825
                rawSig, err := input.SerializePartialSignature(partialSig)
×
UNCOV
826
                if err != nil {
×
827
                        return nil, false, fmt.Errorf("error serializing "+
×
828
                                "partial signature: %v", err)
×
829
                }
×
UNCOV
830
                req.OtherPartialSignatures[idx] = rawSig[:]
×
831
        }
832

UNCOV
833
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
834
        defer cancel()
×
UNCOV
835

×
NEW
836
        resp, err := r.remoteSignerConn.MuSig2CombineSig(ctxt, req)
×
UNCOV
837
        if err != nil {
×
838
                considerShutdown(err)
×
839
                return nil, false, fmt.Errorf("error combining MuSig2 "+
×
840
                        "signatures in remote signer instance: %v", err)
×
841
        }
×
842

843
        // The final signature is only available when we have all the other
844
        // partial signatures from all participants.
UNCOV
845
        if !resp.HaveAllSignatures {
×
UNCOV
846
                return nil, resp.HaveAllSignatures, nil
×
UNCOV
847
        }
×
848

UNCOV
849
        finalSig, err := schnorr.ParseSignature(resp.FinalSignature)
×
UNCOV
850
        if err != nil {
×
851
                return nil, false, fmt.Errorf("error parsing final signature: "+
×
852
                        "%v", err)
×
853
        }
×
854

UNCOV
855
        return finalSig, resp.HaveAllSignatures, nil
×
856
}
857

858
// RemoteSignerConnection returns the remote signer connection instance that is
859
// used by the RPC key ring to sign transactions.
NEW
860
func (r *RPCKeyRing) RemoteSignerConnection() RemoteSignerConnection {
×
NEW
861
        return r.remoteSignerConn
×
NEW
862
}
×
863

864
// ReadySignal returns a channel that signals once the wallet is ready to be
865
// used, i.e. once the remote signer is connected. If we time out while waiting,
866
// an error gets sent over the channel. This method overrides/shadows the
867
// default implementation of the WalletController interface.
868
//
869
// NOTE: This method is part of the WalletController interface.
NEW
870
func (r *RPCKeyRing) ReadySignal() chan error {
×
NEW
871
        return r.remoteSignerConn.Ready()
×
NEW
872
}
×
873

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

×
UNCOV
880
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
881
        defer cancel()
×
UNCOV
882

×
NEW
883
        _, err := r.remoteSignerConn.MuSig2Cleanup(ctxt, req)
×
UNCOV
884
        if err != nil {
×
885
                considerShutdown(err)
×
886
                return fmt.Errorf("error cleaning up MuSig2 session in remote "+
×
887
                        "signer instance: %v", err)
×
888
        }
×
889

UNCOV
890
        return nil
×
891
}
892

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

×
UNCOV
898
        packet, err := packetFromTx(tx)
×
UNCOV
899
        if err != nil {
×
900
                return nil, fmt.Errorf("error converting TX into PSBT: %w", err)
×
901
        }
×
902

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

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

×
925
                                        packet.Inputs[idx].WitnessUtxo = utxo
×
926
                                        continue
×
927
                                }
928
                        }
929

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

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

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

UNCOV
979
                managedAddr, err := r.AddressInfo(addr)
×
UNCOV
980
                if err != nil {
×
981
                        return nil, fmt.Errorf("error fetching address info "+
×
982
                                "for public key %x: %v", pubKeyBytes, err)
×
983
                }
×
984

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

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

998
                // We now have all the information we need to complete our key
999
                // locator information.
UNCOV
1000
                signDesc.KeyDesc.KeyLocator = keychain.KeyLocator{
×
UNCOV
1001
                        Family: keychain.KeyFamily(path.InternalAccount),
×
UNCOV
1002
                        Index:  path.Index,
×
UNCOV
1003
                }
×
1004

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

UNCOV
1022
        var derivation *psbt.Bip32Derivation
×
UNCOV
1023

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

×
UNCOV
1031
        // If the wallet is aware of this outpoint, we go ahead and fetch the
×
UNCOV
1032
        // derivation info.
×
UNCOV
1033
        if err == nil {
×
UNCOV
1034
                derivation, err = r.WalletController.FetchDerivationInfo(
×
UNCOV
1035
                        info.PkScript,
×
UNCOV
1036
                )
×
UNCOV
1037
        }
×
1038

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

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

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

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

1088
        // Assemble all other information about the input we have.
UNCOV
1089
        in.RedeemScript = sigScript
×
UNCOV
1090
        in.SighashType = signDesc.HashType
×
UNCOV
1091
        in.WitnessScript = signDesc.WitnessScript
×
UNCOV
1092

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

1106
        // Add taproot specific fields.
UNCOV
1107
        switch signDesc.SignMethod {
×
1108
        case input.TaprootKeySpendBIP0086SignMethod,
UNCOV
1109
                input.TaprootKeySpendSignMethod:
×
UNCOV
1110

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

×
UNCOV
1124
                // If this is a BIP0086 key spend then the tap tweak is empty,
×
UNCOV
1125
                // otherwise it's set to the Taproot root hash.
×
UNCOV
1126
                in.TaprootMerkleRoot = signDesc.TapTweak
×
1127

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

×
UNCOV
1138
                d := in.Bip32Derivation[0]
×
UNCOV
1139
                in.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
×
UNCOV
1140
                        XOnlyPubKey:          d.PubKey[1:],
×
UNCOV
1141
                        LeafHashes:           [][]byte{leafHash[:]},
×
UNCOV
1142
                        MasterKeyFingerprint: d.MasterKeyFingerprint,
×
UNCOV
1143
                        Bip32Path:            d.Bip32Path,
×
UNCOV
1144
                }}
×
UNCOV
1145

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

UNCOV
1166
                in.TaprootLeafScript = []*psbt.TaprootTapLeafScript{{
×
UNCOV
1167
                        ControlBlock: blockBytes,
×
UNCOV
1168
                        Script:       leaf.Script,
×
UNCOV
1169
                        LeafVersion:  leaf.LeafVersion,
×
UNCOV
1170
                }}
×
1171
        }
1172

1173
        // Okay, let's sign the input by the remote signer now.
UNCOV
1174
        ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
×
UNCOV
1175
        defer cancel()
×
UNCOV
1176

×
UNCOV
1177
        var buf bytes.Buffer
×
UNCOV
1178
        if err := packet.Serialize(&buf); err != nil {
×
1179
                return nil, fmt.Errorf("error serializing PSBT: %w", err)
×
1180
        }
×
1181

NEW
1182
        resp, err := r.remoteSignerConn.SignPsbt(
×
UNCOV
1183
                ctxt, &walletrpc.SignPsbtRequest{FundedPsbt: buf.Bytes()},
×
UNCOV
1184
        )
×
UNCOV
1185
        if err != nil {
×
1186
                considerShutdown(err)
×
1187
                return nil, fmt.Errorf("error signing PSBT in remote signer "+
×
1188
                        "instance: %v", err)
×
1189
        }
×
1190

UNCOV
1191
        signedPacket, err := psbt.NewFromRawBytes(
×
UNCOV
1192
                bytes.NewReader(resp.SignedPsbt), false,
×
UNCOV
1193
        )
×
UNCOV
1194
        if err != nil {
×
1195
                return nil, fmt.Errorf("error parsing signed PSBT: %w", err)
×
1196
        }
×
1197

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

×
UNCOV
1204
        return extractSignature(in, signDesc.SignMethod)
×
1205
}
1206

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

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

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

1234
                // Parse the signature, but chop off the last byte which is the
1235
                // sighash type.
UNCOV
1236
                sig := sigWithSigHash.Signature[0 : sigLen-1]
×
UNCOV
1237
                return ecdsa.ParseDERSignature(sig)
×
1238

1239
        // The type of key spend doesn't matter, the signature should be in the
1240
        // same field for both of those signing methods.
1241
        case input.TaprootKeySpendBIP0086SignMethod,
UNCOV
1242
                input.TaprootKeySpendSignMethod:
×
UNCOV
1243

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

UNCOV
1251
                return schnorr.ParseSignature(
×
UNCOV
1252
                        in.TaprootKeySpendSig[:schnorr.SignatureSize],
×
UNCOV
1253
                )
×
1254

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

UNCOV
1268
                return schnorr.ParseSignature(scriptSpendSig.Signature)
×
1269

1270
        default:
×
1271
                return nil, fmt.Errorf("can't extract signature, unsupported "+
×
1272
                        "signing method: %v", signMethod)
×
1273
        }
1274
}
1275

1276
// packetFromTx creates a PSBT from a tx that potentially already contains
1277
// signed inputs.
UNCOV
1278
func packetFromTx(original *wire.MsgTx) (*psbt.Packet, error) {
×
UNCOV
1279
        // The psbt.NewFromUnsignedTx function complains if there are any
×
UNCOV
1280
        // scripts or witness content on a TX. So we create a copy of the TX and
×
UNCOV
1281
        // nil out all the offending data, but also keep a backup around that we
×
UNCOV
1282
        // add to the PSBT afterwards.
×
UNCOV
1283
        noSigs := original.Copy()
×
UNCOV
1284
        for idx := range noSigs.TxIn {
×
UNCOV
1285
                noSigs.TxIn[idx].SignatureScript = nil
×
UNCOV
1286
                noSigs.TxIn[idx].Witness = nil
×
UNCOV
1287
        }
×
1288

1289
        // With all the data that is seen as "signed", we can now create the
1290
        // empty packet.
UNCOV
1291
        packet, err := psbt.NewFromUnsignedTx(noSigs)
×
UNCOV
1292
        if err != nil {
×
1293
                return nil, err
×
1294
        }
×
1295

UNCOV
1296
        var buf bytes.Buffer
×
UNCOV
1297
        for idx, txIn := range original.TxIn {
×
UNCOV
1298
                if len(txIn.SignatureScript) > 0 {
×
1299
                        packet.Inputs[idx].FinalScriptSig = txIn.SignatureScript
×
1300
                }
×
1301

UNCOV
1302
                if len(txIn.Witness) > 0 {
×
UNCOV
1303
                        buf.Reset()
×
UNCOV
1304
                        err = psbt.WriteTxWitness(&buf, txIn.Witness)
×
UNCOV
1305
                        if err != nil {
×
1306
                                return nil, err
×
1307
                        }
×
UNCOV
1308
                        packet.Inputs[idx].FinalScriptWitness = buf.Bytes()
×
1309
                }
1310
        }
1311

UNCOV
1312
        return packet, nil
×
1313
}
1314

1315
// considerShutdown inspects the error and issues a shutdown (through logging
1316
// a critical error, which will cause the logger to issue a clean shutdown
1317
// request) if the error looks like a connection or general availability error
1318
// and not some application specific problem.
UNCOV
1319
func considerShutdown(err error) {
×
UNCOV
1320
        statusErr, isStatusErr := status.FromError(err)
×
UNCOV
1321
        switch {
×
1322
        // The context attached to the client request has timed out. This can be
1323
        // due to not being able to reach the signing server, or it's taking too
1324
        // long to respond. In either case, request a shutdown.
1325
        case err == context.DeadlineExceeded:
×
1326
                fallthrough
×
1327

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

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