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 }