lnd/lnwallet/rpcwallet/rpcwallet.go
2022-03-24 18:02:37 +01:00

906 lines
31 KiB
Go

package rpcwallet
import (
"bytes"
"context"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
basewallet "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"gopkg.in/macaroon.v2"
)
var (
// ErrRemoteSigningPrivateKeyNotAvailable is the error that is returned
// if an operation is requested from the RPC wallet that is not
// supported in remote signing mode.
ErrRemoteSigningPrivateKeyNotAvailable = errors.New("deriving " +
"private key is not supported by RPC based key ring")
)
// RPCKeyRing is an implementation of the SecretKeyRing interface that uses a
// local watch-only wallet for keeping track of addresses and transactions but
// delegates any signing or ECDH operations to a remote node through RPC.
type RPCKeyRing struct {
// WalletController is the embedded wallet controller of the watch-only
// base wallet. We need to overwrite/shadow certain of the implemented
// methods to make sure we can mirror them to the remote wallet.
lnwallet.WalletController
watchOnlyKeyRing keychain.SecretKeyRing
netParams *chaincfg.Params
rpcTimeout time.Duration
signerClient signrpc.SignerClient
walletClient walletrpc.WalletKitClient
}
var _ keychain.SecretKeyRing = (*RPCKeyRing)(nil)
var _ input.Signer = (*RPCKeyRing)(nil)
var _ keychain.MessageSignerRing = (*RPCKeyRing)(nil)
var _ lnwallet.WalletController = (*RPCKeyRing)(nil)
// NewRPCKeyRing creates a new remote signing secret key ring that uses the
// given watch-only base wallet to keep track of addresses and transactions but
// delegates any signing or ECDH operations to the remove signer through RPC.
func NewRPCKeyRing(watchOnlyKeyRing keychain.SecretKeyRing,
watchOnlyWalletController lnwallet.WalletController,
remoteSigner *lncfg.RemoteSigner,
netParams *chaincfg.Params) (*RPCKeyRing, error) {
rpcConn, err := connectRPC(
remoteSigner.RPCHost, remoteSigner.TLSCertPath,
remoteSigner.MacaroonPath, remoteSigner.Timeout,
)
if err != nil {
return nil, fmt.Errorf("error connecting to the remote "+
"signing node through RPC: %v", err)
}
return &RPCKeyRing{
WalletController: watchOnlyWalletController,
watchOnlyKeyRing: watchOnlyKeyRing,
netParams: netParams,
rpcTimeout: remoteSigner.Timeout,
signerClient: signrpc.NewSignerClient(rpcConn),
walletClient: walletrpc.NewWalletKitClient(rpcConn),
}, nil
}
// NewAddress returns the next external or internal address for the
// wallet dictated by the value of the `change` parameter. If change is
// true, then an internal address should be used, otherwise an external
// address should be returned. The type of address returned is dictated
// by the wallet's capabilities, and may be of type: p2sh, p2wkh,
// p2wsh, etc. The account parameter must be non-empty as it determines
// which account the address should be generated from.
func (r *RPCKeyRing) NewAddress(addrType lnwallet.AddressType, change bool,
account string) (btcutil.Address, error) {
return r.WalletController.NewAddress(addrType, change, account)
}
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
// the specified outputs. In the case the wallet has insufficient funds, or the
// outputs are non-standard, a non-nil error will be returned.
//
// NOTE: This method requires the global coin selection lock to be held.
//
// NOTE: This is a part of the WalletController interface.
//
// NOTE: This method only signs with BIP49/84 keys.
func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32,
label string) (*wire.MsgTx, error) {
tx, err := r.WalletController.SendOutputs(
outputs, feeRate, minConfs, label,
)
if err != nil && err != basewallet.ErrTxUnsigned {
return nil, err
}
if err == nil {
// This shouldn't happen since our wallet controller is watch-
// only and can't sign the TX.
return tx, nil
}
// We know at this point that we only have inputs from our own wallet.
// So we can just compute the input script using the remote signer.
outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController)
signDesc := input.SignDescriptor{
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(tx, outputFetcher),
PrevOutputFetcher: outputFetcher,
}
for i, txIn := range tx.TxIn {
// We can only sign this input if it's ours, so we'll ask the
// watch-only wallet if it can map this outpoint into a coin we
// own. If not, then we can't continue because our wallet state
// is out of sync.
info, err := r.WalletController.FetchInputInfo(
&txIn.PreviousOutPoint,
)
if err != nil {
return nil, fmt.Errorf("error looking up utxo: %v", err)
}
// Now that we know the input is ours, we'll populate the
// signDesc with the per input unique information.
signDesc.Output = &wire.TxOut{
Value: int64(info.Value),
PkScript: info.PkScript,
}
signDesc.InputIndex = i
// Finally, we'll sign the input as is, and populate the input
// with the witness and sigScript (if needed).
inputScript, err := r.ComputeInputScript(tx, &signDesc)
if err != nil {
return nil, err
}
txIn.SignatureScript = inputScript.SigScript
txIn.Witness = inputScript.Witness
}
return tx, r.WalletController.PublishTransaction(tx, label)
}
// SignPsbt expects a partial transaction with all inputs and outputs fully
// declared and tries to sign all unsigned inputs that have all required fields
// (UTXO information, BIP32 derivation information, witness or sig scripts) set.
// If no error is returned, the PSBT is ready to be given to the next signer or
// to be finalized if lnd was the last signer.
//
// NOTE: This RPC only signs inputs (and only those it can sign), it does not
// perform any other tasks (such as coin selection, UTXO locking or
// input/output/fee value validation, PSBT finalization). Any input that is
// incomplete will be skipped.
func (r *RPCKeyRing) SignPsbt(packet *psbt.Packet) error {
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
defer cancel()
var buf bytes.Buffer
if err := packet.Serialize(&buf); err != nil {
return fmt.Errorf("error serializing PSBT: %v", err)
}
resp, err := r.walletClient.SignPsbt(ctxt, &walletrpc.SignPsbtRequest{
FundedPsbt: buf.Bytes(),
})
if err != nil {
err = fmt.Errorf("error signing PSBT in remote signer "+
"instance: %v", err)
// Log as critical as we should shut down if there is no signer.
log.Criticalf("RPC signer error: %v", err)
return err
}
signedPacket, err := psbt.NewFromRawBytes(
bytes.NewReader(resp.SignedPsbt), false,
)
if err != nil {
return fmt.Errorf("error parsing signed PSBT: %v", err)
}
// The caller expects the packet to be modified instead of a new
// instance to be returned. So we just overwrite all fields in the
// original packet.
packet.UnsignedTx = signedPacket.UnsignedTx
packet.Inputs = signedPacket.Inputs
packet.Outputs = signedPacket.Outputs
packet.Unknowns = signedPacket.Unknowns
return nil
}
// FinalizePsbt expects a partial transaction with all inputs and outputs fully
// declared and tries to sign all inputs that belong to the specified account.
// Lnd must be the last signer of the transaction. That means, if there are any
// unsigned non-witness inputs or inputs without UTXO information attached or
// inputs without witness data that do not belong to lnd's wallet, this method
// will fail. If no error is returned, the PSBT is ready to be extracted and the
// final TX within to be broadcast.
//
// NOTE: This method does NOT publish the transaction after it's been
// finalized successfully.
//
// NOTE: This is a part of the WalletController interface.
//
// NOTE: We need to overwrite this method because we need to redirect the call
// to ComputeInputScript to the RPC key ring's implementation. If we forward
// the call to the default WalletController implementation, we get an error
// since that wallet is watch-only. If we forward the call to the remote signer,
// we get an error because the signer doesn't know the UTXO information required
// in ComputeInputScript.
//
// TODO(guggero): Refactor btcwallet to accept ComputeInputScript as a function
// parameter in FinalizePsbt so we can get rid of this code duplication.
func (r *RPCKeyRing) FinalizePsbt(packet *psbt.Packet, _ string) error {
// Let's check that this is actually something we can and want to sign.
// We need at least one input and one output.
err := psbt.VerifyInputOutputLen(packet, true, true)
if err != nil {
return err
}
// Go through each input that doesn't have final witness data attached
// to it already and try to sign it. We do expect that we're the last
// ones to sign. If there is any input without witness data that we
// cannot sign because it's not our UTXO, this will be a hard failure.
tx := packet.UnsignedTx
sigHashes := input.NewTxSigHashesV0Only(tx)
for idx, txIn := range tx.TxIn {
in := packet.Inputs[idx]
// We can only sign if we have UTXO information available. We
// can just continue here as a later step will fail with a more
// precise error message.
if in.WitnessUtxo == nil && in.NonWitnessUtxo == nil {
continue
}
// Skip this input if it's got final witness data attached.
if len(in.FinalScriptWitness) > 0 {
continue
}
// We can only sign this input if it's ours, so we try to map it
// to a coin we own. If we can't, then we'll continue as it
// isn't our input.
utxo, err := r.FetchInputInfo(&txIn.PreviousOutPoint)
if err != nil {
continue
}
fullTx := utxo.PrevTx
signDesc := &input.SignDescriptor{
KeyDesc: keychain.KeyDescriptor{},
Output: &wire.TxOut{
Value: int64(utxo.Value),
PkScript: utxo.PkScript,
},
HashType: in.SighashType,
SigHashes: sigHashes,
InputIndex: idx,
}
// Find out what UTXO we are signing. Wallets _should_ always
// provide the full non-witness UTXO for segwit v0.
var signOutput *wire.TxOut
if in.NonWitnessUtxo != nil {
prevIndex := txIn.PreviousOutPoint.Index
signOutput = in.NonWitnessUtxo.TxOut[prevIndex]
if !psbt.TxOutsEqual(signDesc.Output, signOutput) {
return fmt.Errorf("found UTXO %#v but it "+
"doesn't match PSBT's input %v",
signDesc.Output, signOutput)
}
if fullTx.TxHash() != txIn.PreviousOutPoint.Hash {
return fmt.Errorf("found UTXO tx %v but it "+
"doesn't match PSBT's input %v",
fullTx.TxHash(),
txIn.PreviousOutPoint.Hash)
}
}
// Fall back to witness UTXO only for older wallets.
if in.WitnessUtxo != nil {
signOutput = in.WitnessUtxo
if !psbt.TxOutsEqual(signDesc.Output, signOutput) {
return fmt.Errorf("found UTXO %#v but it "+
"doesn't match PSBT's input %v",
signDesc.Output, signOutput)
}
}
// Do the actual signing in ComputeInputScript which in turn
// will invoke the remote signer.
script, err := r.ComputeInputScript(tx, signDesc)
if err != nil {
return fmt.Errorf("error computing input script for "+
"input %d: %v", idx, err)
}
// Serialize the witness format from the stack representation to
// the wire representation.
var witnessBytes bytes.Buffer
err = psbt.WriteTxWitness(&witnessBytes, script.Witness)
if err != nil {
return fmt.Errorf("error serializing witness: %v", err)
}
packet.Inputs[idx].FinalScriptWitness = witnessBytes.Bytes()
packet.Inputs[idx].FinalScriptSig = script.SigScript
}
// Make sure the PSBT itself thinks it's finalized and ready to be
// broadcast.
err = psbt.MaybeFinalizeAll(packet)
if err != nil {
return fmt.Errorf("error finalizing PSBT: %v", err)
}
return nil
}
// DeriveNextKey attempts to derive the *next* key within the key family
// (account in BIP43) specified. This method should return the next external
// child within this branch.
//
// NOTE: This method is part of the keychain.KeyRing interface.
func (r *RPCKeyRing) DeriveNextKey(
keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) {
return r.watchOnlyKeyRing.DeriveNextKey(keyFam)
}
// DeriveKey attempts to derive an arbitrary key specified by the passed
// KeyLocator. This may be used in several recovery scenarios, or when manually
// rotating something like our current default node key.
//
// NOTE: This method is part of the keychain.KeyRing interface.
func (r *RPCKeyRing) DeriveKey(
keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) {
return r.watchOnlyKeyRing.DeriveKey(keyLoc)
}
// ECDH performs a scalar multiplication (ECDH-like operation) between the
// target key descriptor and remote public key. The output returned will be the
// sha256 of the resulting shared point serialized in compressed format. If k is
// our private key, and P is the public key, we perform the following operation:
//
// sx := k*P
// s := sha256(sx.SerializeCompressed())
//
// NOTE: This method is part of the keychain.ECDHRing interface.
func (r *RPCKeyRing) ECDH(keyDesc keychain.KeyDescriptor,
pubKey *btcec.PublicKey) ([32]byte, error) {
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
defer cancel()
key := [32]byte{}
req := &signrpc.SharedKeyRequest{
EphemeralPubkey: pubKey.SerializeCompressed(),
KeyDesc: &signrpc.KeyDescriptor{
KeyLoc: &signrpc.KeyLocator{
KeyFamily: int32(keyDesc.Family),
KeyIndex: int32(keyDesc.Index),
},
},
}
if keyDesc.Index == 0 && keyDesc.PubKey != nil {
req.KeyDesc.RawKeyBytes = keyDesc.PubKey.SerializeCompressed()
}
resp, err := r.signerClient.DeriveSharedKey(ctxt, req)
if err != nil {
err = fmt.Errorf("error deriving shared key in remote signer "+
"instance: %v", err)
// Log as critical as we should shut down if there is no signer.
log.Criticalf("RPC signer error: %v", err)
return key, err
}
copy(key[:], resp.SharedKey)
return key, nil
}
// SignMessage attempts to sign a target message with the private key described
// in the key locator. If the target private key is unable to be found, then an
// error will be returned. The actual digest signed is the single or double
// SHA-256 of the passed message.
//
// NOTE: This method is part of the keychain.MessageSignerRing interface.
func (r *RPCKeyRing) SignMessage(keyLoc keychain.KeyLocator,
msg []byte, doubleHash bool) (*ecdsa.Signature, error) {
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
defer cancel()
resp, err := r.signerClient.SignMessage(ctxt, &signrpc.SignMessageReq{
Msg: msg,
KeyLoc: &signrpc.KeyLocator{
KeyFamily: int32(keyLoc.Family),
KeyIndex: int32(keyLoc.Index),
},
DoubleHash: doubleHash,
})
if err != nil {
err = fmt.Errorf("error signing message in remote signer "+
"instance: %v", err)
// Log as critical as we should shut down if there is no signer.
log.Criticalf("RPC signer error: %v", err)
return nil, err
}
wireSig, err := lnwire.NewSigFromRawSignature(resp.Signature)
if err != nil {
return nil, fmt.Errorf("error parsing raw signature: %v", err)
}
return wireSig.ToSignature()
}
// SignMessageCompact signs the given message, single or double SHA256 hashing
// it first, with the private key described in the key locator and returns the
// signature in the compact, public key recoverable format.
//
// NOTE: This method is part of the keychain.MessageSignerRing interface.
func (r *RPCKeyRing) SignMessageCompact(keyLoc keychain.KeyLocator,
msg []byte, doubleHash bool) ([]byte, error) {
if keyLoc.Family != keychain.KeyFamilyNodeKey {
return nil, fmt.Errorf("error compact signing with key "+
"locator %v, can only sign with node key", keyLoc)
}
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
defer cancel()
resp, err := r.signerClient.SignMessage(ctxt, &signrpc.SignMessageReq{
Msg: msg,
KeyLoc: &signrpc.KeyLocator{
KeyFamily: int32(keyLoc.Family),
KeyIndex: int32(keyLoc.Index),
},
DoubleHash: doubleHash,
CompactSig: true,
})
if err != nil {
err = fmt.Errorf("error signing message in remote signer "+
"instance: %v", err)
// Log as critical as we should shut down if there is no signer.
log.Criticalf("RPC signer error: %v", err)
return nil, err
}
// The signature in the response is zbase32 encoded, so we need to
// decode it before returning.
return resp.Signature, nil
}
// DerivePrivKey attempts to derive the private key that corresponds to the
// passed key descriptor. If the public key is set, then this method will
// perform an in-order scan over the key set, with a max of MaxKeyRangeScan
// keys. In order for this to work, the caller MUST set the KeyFamily within the
// partially populated KeyLocator.
//
// NOTE: This method is part of the keychain.SecretKeyRing interface.
func (r *RPCKeyRing) DerivePrivKey(_ keychain.KeyDescriptor) (*btcec.PrivateKey,
error) {
// This operation is not supported with remote signing. There should be
// no need for invoking this method unless a channel backup (SCB) file
// for pre-0.13.0 channels are attempted to be restored. In that case
// it is recommended to restore the channels using a node with the full
// seed available.
return nil, ErrRemoteSigningPrivateKeyNotAvailable
}
// SignOutputRaw generates a signature for the passed transaction
// according to the data within the passed SignDescriptor.
//
// NOTE: The resulting signature should be void of a sighash byte.
//
// NOTE: This method is part of the input.Signer interface.
//
// NOTE: This method only signs with BIP1017 (internal) keys!
func (r *RPCKeyRing) SignOutputRaw(tx *wire.MsgTx,
signDesc *input.SignDescriptor) (input.Signature, error) {
// Forward the call to the remote signing instance. This call is only
// ever called for signing witness (p2pkh or p2wsh) inputs and never
// nested witness inputs, so the sigScript is always nil.
return r.remoteSign(tx, signDesc, nil)
}
// ComputeInputScript generates a complete InputIndex for the passed
// transaction with the signature as defined within the passed
// SignDescriptor. This method should be capable of generating the
// proper input script for both regular p2wkh output and p2wkh outputs
// nested within a regular p2sh output.
//
// NOTE: This method will ignore any tweak parameters set within the
// passed SignDescriptor as it assumes a set of typical script
// templates (p2wkh, np2wkh, etc).
//
// NOTE: This method is part of the input.Signer interface.
func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
signDesc *input.SignDescriptor) (*input.Script, error) {
addr, witnessProgram, sigScript, err := r.WalletController.ScriptForOutput(
signDesc.Output,
)
if err != nil {
return nil, err
}
signDesc.WitnessScript = witnessProgram
// Let's give the TX to the remote instance now, so it can sign the
// input.
sig, err := r.remoteSign(tx, signDesc, sigScript)
if err != nil {
return nil, fmt.Errorf("error signing with remote instance: %v",
err)
}
// ComputeInputScript currently is only used for P2WKH and NP2WKH
// addresses. So the last item on the stack is always the compressed
// public key.
return &input.Script{
Witness: wire.TxWitness{
append(sig.Serialize(), byte(signDesc.HashType)),
addr.PubKey().SerializeCompressed(),
},
SigScript: sigScript,
}, nil
}
// remoteSign signs the input specified in signDesc of the given transaction tx
// using the remote signing instance.
func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
sigScript []byte) (input.Signature, error) {
packet, err := packetFromTx(tx)
if err != nil {
return nil, fmt.Errorf("error converting TX into PSBT: %v", err)
}
// We need to add witness information for all inputs! Otherwise, we'll
// have a problem when attempting to sign a taproot input!
for idx := range packet.Inputs {
// Skip the input we're signing for, that will get a special
// treatment later on.
if idx == signDesc.InputIndex {
continue
}
txIn := tx.TxIn[idx]
info, err := r.WalletController.FetchInputInfo(
&txIn.PreviousOutPoint,
)
if err != nil {
log.Warnf("No UTXO info found for index %d "+
"(prev_outpoint=%v), won't be able to sign "+
"for taproot output!", idx,
txIn.PreviousOutPoint)
continue
}
packet.Inputs[idx].WitnessUtxo = &wire.TxOut{
Value: int64(info.Value),
PkScript: info.PkScript,
}
}
// Catch incorrect signing input index, just in case.
if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) {
return nil, fmt.Errorf("invalid input index in sign descriptor")
}
in := &packet.Inputs[signDesc.InputIndex]
txIn := tx.TxIn[signDesc.InputIndex]
// Things are a bit tricky with the sign descriptor. There basically are
// four ways to describe a key:
// 1. By public key only. To match this case both family and index
// must be set to 0.
// 2. By family and index only. To match this case the public key
// must be nil and either the family or index must be non-zero.
// 3. All values are set and locator is non-empty. To match this case
// the public key must be set and either the family or index must
// be non-zero.
// 4. All values are set and locator is empty. This is a special case
// for the very first channel ever created (with the multi-sig key
// family which is 0 and the index which is 0 as well). This looks
// identical to case 1 and will also be handled like that case.
// We only really handle case 1 and 2 here, since 3 is no problem and 4
// is identical to 1.
switch {
// Case 1: Public key only. We need to find out the derivation path for
// this public key by asking the wallet. This is only possible for our
// internal, custom 1017 scope since we know all keys derived there are
// internally stored as p2wkh addresses.
case signDesc.KeyDesc.PubKey != nil && signDesc.KeyDesc.IsEmpty():
pubKeyBytes := signDesc.KeyDesc.PubKey.SerializeCompressed()
addr, err := btcutil.NewAddressWitnessPubKeyHash(
btcutil.Hash160(pubKeyBytes), r.netParams,
)
if err != nil {
return nil, fmt.Errorf("error deriving address from "+
"public key %x: %v", pubKeyBytes, err)
}
managedAddr, err := r.AddressInfo(addr)
if err != nil {
return nil, fmt.Errorf("error fetching address info "+
"for public key %x: %v", pubKeyBytes, err)
}
pubKeyAddr, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
if !ok {
return nil, fmt.Errorf("address derived for public "+
"key %x is not a p2wkh address", pubKeyBytes)
}
scope, path, _ := pubKeyAddr.DerivationInfo()
if scope.Purpose != keychain.BIP0043Purpose {
return nil, fmt.Errorf("address derived for public "+
"key %x is not in custom key scope %d'",
pubKeyBytes, keychain.BIP0043Purpose)
}
// We now have all the information we need to complete our key
// locator information.
signDesc.KeyDesc.KeyLocator = keychain.KeyLocator{
Family: keychain.KeyFamily(path.InternalAccount),
Index: path.Index,
}
// Case 2: Family and index only. This case is easy, we can just go
// ahead and derive the public key from the family and index and then
// supply that information in the BIP32 derivation field.
case signDesc.KeyDesc.PubKey == nil && !signDesc.KeyDesc.IsEmpty():
fullDesc, err := r.watchOnlyKeyRing.DeriveKey(
signDesc.KeyDesc.KeyLocator,
)
if err != nil {
return nil, fmt.Errorf("error deriving key with "+
"family %d and index %d from the watch-only "+
"wallet: %v",
signDesc.KeyDesc.KeyLocator.Family,
signDesc.KeyDesc.KeyLocator.Index, err)
}
signDesc.KeyDesc.PubKey = fullDesc.PubKey
}
// Make sure we actually know about the input. We either have been
// watching the UTXO on-chain or we have been given all the required
// info in the sign descriptor.
info, err := r.WalletController.FetchInputInfo(&txIn.PreviousOutPoint)
switch {
// No error, we do have the full UTXO and derivation info available.
case err == nil:
in.WitnessUtxo = &wire.TxOut{
Value: int64(info.Value),
PkScript: info.PkScript,
}
in.NonWitnessUtxo = info.PrevTx
in.Bip32Derivation = []*psbt.Bip32Derivation{info.Derivation}
// The wallet doesn't know about this UTXO, so it's probably a TX that
// we haven't published yet (e.g. a channel funding TX). So we need to
// assemble everything from the sign descriptor. We won't be able to
// supply a non-witness UTXO (=full TX of the input being spent) in this
// case. That is no problem if the signing instance is another lnd
// instance since we don't require it for pure witness inputs. But a
// hardware wallet might require it for security reasons.
case signDesc.KeyDesc.PubKey != nil && signDesc.Output != nil:
in.WitnessUtxo = signDesc.Output
in.Bip32Derivation = []*psbt.Bip32Derivation{{
Bip32Path: []uint32{
keychain.BIP0043Purpose +
hdkeychain.HardenedKeyStart,
r.netParams.HDCoinType +
hdkeychain.HardenedKeyStart,
uint32(signDesc.KeyDesc.Family) +
hdkeychain.HardenedKeyStart,
0,
signDesc.KeyDesc.Index,
},
PubKey: signDesc.KeyDesc.PubKey.SerializeCompressed(),
}}
// We need to specify a pk script in the witness UTXO, otherwise
// the field becomes invalid when serialized as a PSBT. To avoid
// running into a generic "Invalid PSBT serialization format"
// error later, we return a more descriptive error now.
if len(in.WitnessUtxo.PkScript) == 0 {
return nil, fmt.Errorf("error assembling UTXO " +
"information, output not known to wallet and " +
"no UTXO pk script provided in sign descriptor")
}
default:
return nil, fmt.Errorf("error assembling UTXO information, "+
"wallet returned err='%v' and sign descriptor is "+
"incomplete", err)
}
// Assemble all other information about the input we have.
in.RedeemScript = sigScript
in.SighashType = signDesc.HashType
in.WitnessScript = signDesc.WitnessScript
if len(signDesc.SingleTweak) > 0 {
in.Unknowns = append(in.Unknowns, &psbt.Unknown{
Key: btcwallet.PsbtKeyTypeInputSignatureTweakSingle,
Value: signDesc.SingleTweak,
})
}
if signDesc.DoubleTweak != nil {
in.Unknowns = append(in.Unknowns, &psbt.Unknown{
Key: btcwallet.PsbtKeyTypeInputSignatureTweakDouble,
Value: signDesc.DoubleTweak.Serialize(),
})
}
// Okay, let's sign the input by the remote signer now.
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
defer cancel()
var buf bytes.Buffer
if err := packet.Serialize(&buf); err != nil {
return nil, fmt.Errorf("error serializing PSBT: %v", err)
}
resp, err := r.walletClient.SignPsbt(
ctxt, &walletrpc.SignPsbtRequest{FundedPsbt: buf.Bytes()},
)
if err != nil {
err = fmt.Errorf("error signing PSBT in remote signer "+
"instance: %v", err)
// Log as critical as we should shut down if there is no signer.
log.Criticalf("RPC signer error: %v", err)
return nil, err
}
signedPacket, err := psbt.NewFromRawBytes(
bytes.NewReader(resp.SignedPsbt), false,
)
if err != nil {
return nil, fmt.Errorf("error parsing signed PSBT: %v", err)
}
// We expect a signature in the input now.
if signDesc.InputIndex >= len(signedPacket.Inputs) {
return nil, fmt.Errorf("remote signer returned invalid PSBT")
}
in = &signedPacket.Inputs[signDesc.InputIndex]
if len(in.PartialSigs) != 1 {
return nil, fmt.Errorf("remote signer returned invalid "+
"partial signature, wanted 1, got %d",
len(in.PartialSigs))
}
sigWithSigHash := in.PartialSigs[0]
if sigWithSigHash == nil {
return nil, fmt.Errorf("remote signer returned nil signature")
}
// The remote signer always adds the sighash type, so we need to account
// for that.
if len(sigWithSigHash.Signature) < ecdsa.MinSigLen+1 {
return nil, fmt.Errorf("remote signer returned invalid "+
"partial signature: signature too short with %d bytes",
len(sigWithSigHash.Signature))
}
// Parse the signature, but chop off the last byte which is the sighash
// type.
sig := sigWithSigHash.Signature[0 : len(sigWithSigHash.Signature)-1]
return ecdsa.ParseDERSignature(sig)
}
// connectRPC tries to establish an RPC connection to the given host:port with
// the supplied certificate and macaroon.
func connectRPC(hostPort, tlsCertPath, macaroonPath string,
timeout time.Duration) (*grpc.ClientConn, error) {
certBytes, err := ioutil.ReadFile(tlsCertPath)
if err != nil {
return nil, fmt.Errorf("error reading TLS cert file %v: %v",
tlsCertPath, err)
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(certBytes) {
return nil, fmt.Errorf("credentials: failed to append " +
"certificate")
}
macBytes, err := ioutil.ReadFile(macaroonPath)
if err != nil {
return nil, fmt.Errorf("error reading macaroon file %v: %v",
macaroonPath, err)
}
mac := &macaroon.Macaroon{}
if err := mac.UnmarshalBinary(macBytes); err != nil {
return nil, fmt.Errorf("error decoding macaroon: %v", err)
}
macCred, err := macaroons.NewMacaroonCredential(mac)
if err != nil {
return nil, fmt.Errorf("error creating creds: %v", err)
}
opts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(
cp, "",
)),
grpc.WithPerRPCCredentials(macCred),
grpc.WithBlock(),
}
ctxt, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
conn, err := grpc.DialContext(ctxt, hostPort, opts...)
if err != nil {
return nil, fmt.Errorf("unable to connect to RPC server: %v",
err)
}
return conn, nil
}
// packetFromTx creates a PSBT from a tx that potentially already contains
// signed inputs.
func packetFromTx(original *wire.MsgTx) (*psbt.Packet, error) {
// The psbt.NewFromUnsignedTx function complains if there are any
// scripts or witness content on a TX. So we create a copy of the TX and
// nil out all the offending data, but also keep a backup around that we
// add to the PSBT afterwards.
noSigs := original.Copy()
for idx := range noSigs.TxIn {
noSigs.TxIn[idx].SignatureScript = nil
noSigs.TxIn[idx].Witness = nil
}
// With all the data that is seen as "signed", we can now create the
// empty packet.
packet, err := psbt.NewFromUnsignedTx(noSigs)
if err != nil {
return nil, err
}
var buf bytes.Buffer
for idx, txIn := range original.TxIn {
if len(txIn.SignatureScript) > 0 {
packet.Inputs[idx].FinalScriptSig = txIn.SignatureScript
}
if len(txIn.Witness) > 0 {
buf.Reset()
err = psbt.WriteTxWitness(&buf, txIn.Witness)
if err != nil {
return nil, err
}
packet.Inputs[idx].FinalScriptWitness = buf.Bytes()
}
}
return packet, nil
}