btcwallet: prepare for supporting new signing methods

This commit is contained in:
Oliver Gugger 2022-04-27 21:05:44 +02:00
parent 0e098ed9da
commit e4aa32fdf7
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

View File

@ -8,6 +8,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/input"
@ -111,7 +112,7 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
for idx := range tx.TxIn {
in := packet.Inputs[idx]
in := &packet.Inputs[idx]
// We can only sign if we have UTXO information available. Since
// we don't finalize, we just skip over any input that we know
@ -165,47 +166,96 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
// Do we need to tweak anything? Single or double tweaks are
// sent as custom/proprietary fields in the PSBT input section.
privKey = maybeTweakPrivKeyPsbt(in.Unknowns, privKey)
pubKeyBytes := privKey.PubKey().SerializeCompressed()
// Extract the correct witness and/or legacy scripts now,
// depending on the type of input we sign. The txscript package
// has the peculiar requirement that the PkScript of a P2PKH
// must be given as the witness script in order for it to arrive
// at the correct sighash. That's why we call it subScript here
// instead of witness script.
subScript, scriptSig, err := prepareScripts(in)
// What kind of signature is expected from us and do we have all
// information we need?
signMethod, err := validateSigningMethod(in)
if err != nil {
// We derived the correct key so we _are_ expected to
// sign this. Not being able to sign at this point means
// there's something wrong.
return fmt.Errorf("error deriving script for input "+
"%d: %v", idx, err)
return err
}
// We have everything we need for signing the input now.
sig, err := txscript.RawTxInWitnessSignature(
tx, sigHashes, idx, in.WitnessUtxo.Value, subScript,
in.SighashType, privKey,
)
if err != nil {
return fmt.Errorf("error signing input %d: %v", idx,
err)
switch signMethod {
// For p2wkh, np2wkh and p2wsh.
case input.WitnessV0SignMethod:
err = signSegWitV0(in, tx, sigHashes, idx, privKey)
default:
err = fmt.Errorf("unsupported signing method for "+
"PSBT signing: %v", signMethod)
}
if err != nil {
return err
}
packet.Inputs[idx].FinalScriptSig = scriptSig
packet.Inputs[idx].PartialSigs = append(
packet.Inputs[idx].PartialSigs, &psbt.PartialSig{
PubKey: pubKeyBytes,
Signature: sig,
},
)
}
return nil
}
// prepareScripts returns the appropriate witness and/or legacy scripts,
// validateSigningMethod attempts to detect the signing method that is required
// to sign for the given PSBT input and makes sure all information is available
// to do so.
func validateSigningMethod(in *psbt.PInput) (input.SignMethod, error) {
script, err := txscript.ParsePkScript(in.WitnessUtxo.PkScript)
if err != nil {
return 0, fmt.Errorf("error detecting signing method, "+
"couldn't parse pkScript: %v", err)
}
switch script.Class() {
case txscript.WitnessV0PubKeyHashTy, txscript.ScriptHashTy,
txscript.WitnessV0ScriptHashTy:
return input.WitnessV0SignMethod, nil
default:
return 0, fmt.Errorf("unsupported script class for signing "+
"PSBT: %v", script.Class())
}
}
// SignSegWitV0 attempts to generate a signature for a SegWit version 0 input
// and stores it in the PartialSigs (and FinalScriptSig for np2wkh addresses)
// field.
func signSegWitV0(in *psbt.PInput, tx *wire.MsgTx,
sigHashes *txscript.TxSigHashes, idx int,
privKey *btcec.PrivateKey) error {
pubKeyBytes := privKey.PubKey().SerializeCompressed()
// Extract the correct witness and/or legacy scripts now, depending on
// the type of input we sign. The txscript package has the peculiar
// requirement that the PkScript of a P2PKH must be given as the witness
// script in order for it to arrive at the correct sighash. That's why
// we call it subScript here instead of witness script.
subScript, scriptSig, err := prepareScriptsV0(in)
if err != nil {
// We derived the correct key so we _are_ expected to sign this.
// Not being able to sign at this point means there's something
// wrong.
return fmt.Errorf("error deriving script for input %d: %v", idx,
err)
}
// We have everything we need for signing the input now.
sig, err := txscript.RawTxInWitnessSignature(
tx, sigHashes, idx, in.WitnessUtxo.Value, subScript,
in.SighashType, privKey,
)
if err != nil {
return fmt.Errorf("error signing input %d: %v", idx, err)
}
in.FinalScriptSig = scriptSig
in.PartialSigs = append(in.PartialSigs, &psbt.PartialSig{
PubKey: pubKeyBytes,
Signature: sig,
})
return nil
}
// prepareScriptsV0 returns the appropriate witness v0 and/or legacy scripts,
// depending on the type of input that should be signed.
func prepareScripts(in psbt.PInput) ([]byte, []byte, error) {
func prepareScriptsV0(in *psbt.PInput) ([]byte, []byte, error) {
switch {
// It's a NP2WKH input:
case len(in.RedeemScript) > 0: