btcwallet: prepare for supporting new signing methods

This commit is contained in:
Oliver Gugger committed 2022-05-11 10:07:49 +02:00
1 parent 0e098ed9da
commit e4aa32fdf7
1 file changed
+73 -23
+73 -23
View File
@@ -8,6 +8,7 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
@@ -111,7 +112,7 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet) prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher) sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
for idx := range tx.TxIn { 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 can only sign if we have UTXO information available. Since
// we don't finalize, we just skip over any input that we know // we don't finalize, we just skip over any input that we know
@@ -165,21 +166,74 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
// Do we need to tweak anything? Single or double tweaks are // Do we need to tweak anything? Single or double tweaks are
// sent as custom/proprietary fields in the PSBT input section. // sent as custom/proprietary fields in the PSBT input section.
privKey = maybeTweakPrivKeyPsbt(in.Unknowns, privKey) privKey = maybeTweakPrivKeyPsbt(in.Unknowns, privKey)
// What kind of signature is expected from us and do we have all
// information we need?
signMethod, err := validateSigningMethod(in)
if err != nil {
return 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
}
}
return nil
}
// 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() pubKeyBytes := privKey.PubKey().SerializeCompressed()
// Extract the correct witness and/or legacy scripts now, // Extract the correct witness and/or legacy scripts now, depending on
// depending on the type of input we sign. The txscript package // the type of input we sign. The txscript package has the peculiar
// has the peculiar requirement that the PkScript of a P2PKH // requirement that the PkScript of a P2PKH must be given as the witness
// must be given as the witness script in order for it to arrive // script in order for it to arrive at the correct sighash. That's why
// at the correct sighash. That's why we call it subScript here // we call it subScript here instead of witness script.
// instead of witness script. subScript, scriptSig, err := prepareScriptsV0(in)
subScript, scriptSig, err := prepareScripts(in)
if err != nil { if err != nil {
// We derived the correct key so we _are_ expected to // We derived the correct key so we _are_ expected to sign this.
// sign this. Not being able to sign at this point means // Not being able to sign at this point means there's something
// there's something wrong. // wrong.
return fmt.Errorf("error deriving script for input "+ return fmt.Errorf("error deriving script for input %d: %v", idx,
"%d: %v", idx, err) err)
} }
// We have everything we need for signing the input now. // We have everything we need for signing the input now.
@@ -188,24 +242,20 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
in.SighashType, privKey, in.SighashType, privKey,
) )
if err != nil { if err != nil {
return fmt.Errorf("error signing input %d: %v", idx, return fmt.Errorf("error signing input %d: %v", idx, err)
err)
} }
packet.Inputs[idx].FinalScriptSig = scriptSig in.FinalScriptSig = scriptSig
packet.Inputs[idx].PartialSigs = append( in.PartialSigs = append(in.PartialSigs, &psbt.PartialSig{
packet.Inputs[idx].PartialSigs, &psbt.PartialSig{
PubKey: pubKeyBytes, PubKey: pubKeyBytes,
Signature: sig, Signature: sig,
}, })
)
}
return nil return nil
} }
// prepareScripts returns the appropriate witness and/or legacy scripts, // prepareScriptsV0 returns the appropriate witness v0 and/or legacy scripts,
// depending on the type of input that should be signed. // 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 { switch {
// It's a NP2WKH input: // It's a NP2WKH input:
case len(in.RedeemScript) > 0: case len(in.RedeemScript) > 0: